Initial commit

This commit is contained in:
Raphael Zimmermann 2015-03-16 15:36:07 +01:00
commit f9c8b6f016
34 changed files with 15853 additions and 0 deletions

8
.gitattributes vendored Normal file
View file

@ -0,0 +1,8 @@
/apache export-ignore
/cache export-ignore
/tests export-ignore
/changelog.md export-ignore
/docker-compose.yml export-ignore
/.travis.yml export-ignore
/phpunit.xml export-ignore
/.scrutinizer.yml export-ignore

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/cache/
/vendor/
/cache/

35
.scrutinizer.yml Normal file
View file

@ -0,0 +1,35 @@
filter:
excluded_paths: [tests/*]
checks:
php:
code_rating: true
remove_extra_empty_lines: true
remove_php_closing_tag: true
remove_trailing_whitespace: true
fix_use_statements:
remove_unused: true
preserve_multiple: false
preserve_blanklines: true
order_alphabetically: true
fix_php_opening_tag: true
fix_linefeed: true
fix_line_ending: true
fix_identation_4spaces: true
fix_doc_comments: true
tools:
external_code_coverage:
timeout: 600
runs: 3
php_analyzer: true
php_code_coverage: false
php_code_sniffer:
config:
standard: PSR2
filter:
paths: ['src']
php_loc:
enabled: true
excluded_dirs: [vendor, tests]
php_cpd:
enabled: true
excluded_dirs: [vendor, tests]

10
.travis.yml Normal file
View file

@ -0,0 +1,10 @@
language: php
php:
- 5.6
- 5.5
- 5.4
- hhvm
before_script:
- composer self-update
- composer install

32
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,32 @@
# Contributing
Contributions are **welcome** and will be fully **credited**.
I accept contributions via Pull Requests on [Github](https://github.com/raphiz/passwordcards).
## Pull Requests
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option.
- **Create feature branches** - Don't ask us to pull from your master branch.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.
## Running Tests
``` bash
$ composer test
```
**Happy coding**!

21
LICENSE.md Normal file
View file

@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright (c) 2015 Raphael Zimmermann <mister.norbert ät gmail.com>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.

29
README.md Normal file
View file

@ -0,0 +1,29 @@
# passwordcards
[![Latest Version](https://img.shields.io/github/release/raphiz/passwordcards.svg?style=flat-square)](https://github.com/raphiz/passwordcards/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/raphiz/passwordcards/master.svg?style=flat-square)](https://travis-ci.org/raphiz/passwordcards)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/raphiz/passwordcards/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/raphiz/passwordcards/?branch=master)
[![Code Climate](https://codeclimate.com/github/raphiz/passwordcards/badges/gpa.svg)](https://codeclimate.com/github/raphiz/passwordcards)
[![Test Coverage](https://codeclimate.com/github/raphiz/passwordcards/badges/coverage.svg)](https://codeclimate.com/github/raphiz/passwordcards)
This is where your description should go. Try and limit it to a paragraph or two, and maybe throw in a mention of what
PSRs you support to avoid any confusion with users and contributors.
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
## Security
If you discover any security related issues, please email mister.norbert ät gmail.com instead of using the issue tracker.
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
## To be done:
add
* Analysis via https://insight.sensiolabs.com/
* make .htaccess that only allows requests to /resources OR /index.php!

2
apache/Dockerfile Normal file
View file

@ -0,0 +1,2 @@
FROM php:5.6-apache
COPY php.ini /usr/local/etc/php/

1938
apache/php.ini Normal file

File diff suppressed because it is too large Load diff

2
changelog.md Normal file
View file

@ -0,0 +1,2 @@
## [Unreleased][unreleased]
- Initial Release

23
composer.json Normal file
View file

@ -0,0 +1,23 @@
{
"name": "raphiz/passwordcards",
"require": {
"tecnick.com/tcpdf": "6.2.6",
"rain/raintpl": "3.1.0"
},
"require-dev": {
"phpunit/phpunit": "4.5.0"
},
"license": "MIT",
"authors": [
{
"name": "Raphael Zimmermann",
"email": "mister.norbert@gmail.com"
}
],
"autoload": {
"psr-4": { "raphiz\\passwordcards\\": "src/" }
},
"scripts": {
"test": "vendor/bin/phpunit"
}
}

1074
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

6
docker-compose.yml Normal file
View file

@ -0,0 +1,6 @@
web:
build: apache/
volumes:
- ./:/var/www/html:rw
ports:
- "8000:80"

53
index.php Normal file
View file

@ -0,0 +1,53 @@
<?php
namespace raphiz\passwordcards;
require_once 'vendor/autoload.php';
use \Rain\Tpl;
if (!RequestUtils::isPost()) {
// Render template
Tpl::configure(
array(
"tpl_dir" => __DIR__ . "/resources/",
)
);
$tpl = new Tpl;
$tpl->draw('index');
} else {
// Parse request
$pattern = RequestUtils::parsePattern();
$keyboardLayout = RequestUtils::parseKeyboardLayout();
$seed = RequestUtils::parseSeed();
$text = RequestUtils::parseText();
$primary = RequestUtils::parsePrimaryColor();
$secondary = RequestUtils::parseSecondaryColor();
$spaceBarSize = RequestUtils::parseSpacebarSize();
// Setup configuration
$cfg = new Configuration($seed, $pattern, $keyboardLayout, $spaceBarSize, $text, $primary, $secondary);
$creator = new CardCreator($cfg);
// Load SVG templates
$front_template = $creator->getSvgTemplate('simple_back');
$back_template = $creator->getSvgTemplate('simple_front');
// Render SVG into tempfiles
$front = $creator->renderIntoTempfile($front_template);
$back = $creator->renderIntoTempfile($back_template);
// Render the PDF
$doc = PDFRenderer::render($front, $back);
// Prepare response PDF file header
RequestUtils::preparePdfHeader(strlen($doc));
// Ignore user abort to cleanup afterwards
ignore_user_abort(true);
// Strem the PDF
echo $doc;
// Cleanup temporary SVG images
unlink($back);
unlink($front);
}

19
phpunit.xml Normal file
View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./vendor/autoload.php"
>
<testsuites>
<testsuite name="tests">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

BIN
resources/cards.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

69
resources/css/main.css Normal file
View file

@ -0,0 +1,69 @@
.header {
margin-top: 3em;
text-align: center;
}
.configure {
text-align: center;
margin-top: 30px;
}
#advancedOptionsLabel {
color:#1EAEDB;
cursor: pointer;
}
#advancedOptions {
display: none;
}
#hideMoreOptions {
display: none;
}
.hidden,
input.hidden{
display: none;
visibility: hidden;
}
#with-numbers:checked + .label-with-numbers,
#with-lower:checked + .label-with-lower,
#with-upper:checked + .label-with-upper,
#with-symbols:checked + .label-with-symbols,
#with-space:checked + .label-with-space,
#with-other:checked + .label-with-other
{
color: #FFF;
background-color: #33C3F0;
border-color: #33C3F0;
}
.tooltip{
display: inline-block;
position: relative;
}
.tooltip:hover:after{
background: #333;
background: rgba(0,0,0,.8);
border-radius: 5px;
bottom: 46px;
color: #fff;
content: attr(title);
text-transform: none;
left: 20%;
padding: 5px 15px;
position: absolute;
z-index: 98;
width: 220px;
}
.tooltip:hover:before{
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0 6px;
bottom: 40px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}

427
resources/css/normalize.css vendored Normal file
View file

@ -0,0 +1,427 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

418
resources/css/skeleton.css vendored Normal file
View file

@ -0,0 +1,418 @@
/*
* Skeleton V2.0.4
* Copyright 2014, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 12/29/2014
*/
/* Table of contents
- Grid
- Base Styles
- Typography
- Links
- Buttons
- Forms
- Lists
- Code
- Tables
- Spacing
- Utilities
- Clearing
- Media Queries
*/
/* Grid
*/
.container {
position: relative;
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box; }
.column,
.columns {
width: 100%;
float: left;
box-sizing: border-box; }
/* For devices larger than 400px */
@media (min-width: 400px) {
.container {
width: 85%;
padding: 0; }
}
/* For devices larger than 550px */
@media (min-width: 550px) {
.container {
width: 80%; }
.column,
.columns {
margin-left: 4%; }
.column:first-child,
.columns:first-child {
margin-left: 0; }
.one.column,
.one.columns { width: 4.66666666667%; }
.two.columns { width: 13.3333333333%; }
.three.columns { width: 22%; }
.four.columns { width: 30.6666666667%; }
.five.columns { width: 39.3333333333%; }
.six.columns { width: 48%; }
.seven.columns { width: 56.6666666667%; }
.eight.columns { width: 65.3333333333%; }
.nine.columns { width: 74.0%; }
.ten.columns { width: 82.6666666667%; }
.eleven.columns { width: 91.3333333333%; }
.twelve.columns { width: 100%; margin-left: 0; }
.one-third.column { width: 30.6666666667%; }
.two-thirds.column { width: 65.3333333333%; }
.one-half.column { width: 48%; }
/* Offsets */
.offset-by-one.column,
.offset-by-one.columns { margin-left: 8.66666666667%; }
.offset-by-two.column,
.offset-by-two.columns { margin-left: 17.3333333333%; }
.offset-by-three.column,
.offset-by-three.columns { margin-left: 26%; }
.offset-by-four.column,
.offset-by-four.columns { margin-left: 34.6666666667%; }
.offset-by-five.column,
.offset-by-five.columns { margin-left: 43.3333333333%; }
.offset-by-six.column,
.offset-by-six.columns { margin-left: 52%; }
.offset-by-seven.column,
.offset-by-seven.columns { margin-left: 60.6666666667%; }
.offset-by-eight.column,
.offset-by-eight.columns { margin-left: 69.3333333333%; }
.offset-by-nine.column,
.offset-by-nine.columns { margin-left: 78.0%; }
.offset-by-ten.column,
.offset-by-ten.columns { margin-left: 86.6666666667%; }
.offset-by-eleven.column,
.offset-by-eleven.columns { margin-left: 95.3333333333%; }
.offset-by-one-third.column,
.offset-by-one-third.columns { margin-left: 34.6666666667%; }
.offset-by-two-thirds.column,
.offset-by-two-thirds.columns { margin-left: 69.3333333333%; }
.offset-by-one-half.column,
.offset-by-one-half.columns { margin-left: 52%; }
}
/* Base Styles
*/
/* NOTE
html is set to 62.5% so that all the REM measurements throughout Skeleton
are based on 10px sizing. So basically 1.5rem = 15px :) */
html {
font-size: 62.5%; }
body {
font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */
line-height: 1.6;
font-weight: 400;
font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #222; }
/* Typography
*/
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 2rem;
font-weight: 300; }
h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;}
h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; }
h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; }
h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; }
h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; }
h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; }
/* Larger than phablet */
@media (min-width: 550px) {
h1 { font-size: 5.0rem; }
h2 { font-size: 4.2rem; }
h3 { font-size: 3.6rem; }
h4 { font-size: 3.0rem; }
h5 { font-size: 2.4rem; }
h6 { font-size: 1.5rem; }
}
p {
margin-top: 0; }
/* Links
*/
a {
color: #1EAEDB; }
a:hover {
color: #0FA0CE; }
/* Buttons
*/
.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
display: inline-block;
height: 38px;
padding: 0 30px;
color: #555;
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 38px;
letter-spacing: .1rem;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border-radius: 4px;
border: 1px solid #bbb;
cursor: pointer;
box-sizing: border-box; }
.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
.button:focus,
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus {
color: #333;
border-color: #888;
outline: 0; }
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
color: #FFF;
background-color: #33C3F0;
border-color: #33C3F0; }
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: #FFF;
background-color: #1EAEDB;
border-color: #1EAEDB; }
/* Forms
*/
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
height: 38px;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
background-color: #fff;
border: 1px solid #D1D1D1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box; }
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none; }
textarea {
min-height: 65px;
padding-top: 6px;
padding-bottom: 6px; }
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
border: 1px solid #33C3F0;
outline: 0; }
label,
legend {
display: block;
margin-bottom: .5rem;
font-weight: 600; }
fieldset {
padding: 0;
border-width: 0; }
input[type="checkbox"],
input[type="radio"] {
display: inline; }
label > .label-body {
display: inline-block;
margin-left: .5rem;
font-weight: normal; }
/* Lists
*/
ul {
list-style: circle inside; }
ol {
list-style: decimal inside; }
ol, ul {
padding-left: 0;
margin-top: 0; }
ul ul,
ul ol,
ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
font-size: 90%; }
li {
margin-bottom: 1rem; }
/* Code
*/
code {
padding: .2rem .5rem;
margin: 0 .2rem;
font-size: 90%;
white-space: nowrap;
background: #F1F1F1;
border: 1px solid #E1E1E1;
border-radius: 4px; }
pre > code {
display: block;
padding: 1rem 1.5rem;
white-space: pre; }
/* Tables
*/
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #E1E1E1; }
th:first-child,
td:first-child {
padding-left: 0; }
th:last-child,
td:last-child {
padding-right: 0; }
/* Spacing
*/
button,
.button {
margin-bottom: 1rem; }
input,
textarea,
select,
fieldset {
margin-bottom: 1.5rem; }
pre,
blockquote,
dl,
figure,
table,
p,
ul,
ol,
form {
margin-bottom: 2.5rem; }
/* Utilities
*/
.u-full-width {
width: 100%;
box-sizing: border-box; }
.u-max-full-width {
max-width: 100%;
box-sizing: border-box; }
.u-pull-right {
float: right; }
.u-pull-left {
float: left; }
/* Misc
*/
hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
border-top: 1px solid #E1E1E1; }
/* Clearing
*/
/* Self Clearing Goodness */
.container:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both; }
/* Media Queries
*/
/*
Note: The best way to structure the use of media queries is to create the queries
near the relevant code. For example, if you wanted to change the styles for buttons
on small devices, paste the mobile query code up in the buttons section and style it
there.
*/
/* Larger than mobile */
@media (min-width: 400px) {}
/* Larger than phablet (also point when grid becomes active) */
@media (min-width: 550px) {}
/* Larger than tablet */
@media (min-width: 750px) {}
/* Larger than desktop */
@media (min-width: 1000px) {}
/* Larger than Desktop HD */
@media (min-width: 1200px) {}

View file

@ -0,0 +1,67 @@
/* COLORPICKER 3 EXAMPLE */
.colorPicker
{
width: 30px;
height: 30px;
position: relative;
clear: both;
margin: 0 auto 20px;
}
.colorPicker .track {
background: #EFEFEF url(../text-color.png) no-repeat 50% 50%;
height: 150px;
width: 150px;
padding: 10px;
position: absolute;
cursor: crosshair;
float: left;
left: -71px;
top: -71px;
display: none;
border: 1px solid #ccc;
z-index: 10;
-webkit-border-radius: 150px;
-moz-border-radius: 150px;
border-radius: 150px;
}
.colorPicker .color {
width: 25px;
height: 25px;
padding: 1px;
border: 1px solid #ccc;
display: block;
position: relative;
z-index: 11;
background-color: #EFEFEF;
-webkit-border-radius: 27px;
-moz-border-radius: 27px;
border-radius: 27px;
cursor: pointer;
}
.colorPicker .colorInner {
width: 25px;
height: 25px;
-webkit-border-radius: 27px;
-moz-border-radius: 27px;
border-radius: 27px;
}
.colorPicker .dropdown {
list-style: none;
display: none;
width: 27px;
position: absolute;
top: 28px;
border: 1px solid #ccc;
left: 0;
z-index: 1000;
}
.colorPicker .dropdown li{
height: 25px;
cursor: pointer;
}

BIN
resources/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

136
resources/index.html Normal file
View file

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Basic Page Needs
-->
<meta charset="utf-8">
<title>Password Card Generator</title>
<meta name="description" content="Password Card Generator">
<meta name="author" content="Raphael Zimmermann">
<!-- Mobile Specific Metas
-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- FONT
-->
<link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
<!-- CSS
-->
<link rel="stylesheet" href="resources/css/normalize.css">
<link rel="stylesheet" href="resources/css/skeleton.css">
<link rel="stylesheet" href="resources/css/tinycolorpicker.css">
<link rel="stylesheet" href="resources/css/main.css">
<!-- Favicon
-->
<link rel="icon" type="image/png" href="resources/favicon.png">
</head>
<body>
<!-- Primary Page Layout
-->
<div class="container">
<div class="header">
<h1>Password Card Generator</h1>
<p>
This website allows you to generate customized password cards in
the spirit of <a href="https://www.qwertycards.com" target="_blank">Qwertycards.com</a>
</p>
<p>
If you like the concept, please buy a card from their <a href="https://www.qwertycards.com" target="_blank">website</a>
</p>
<img src="resources/cards.png" width="100%">
</div>
<div class="configure">
<h1>Generate a new card</h1>
<p>Pick the characters you want to use</p>
<form action="" method="POST" accept-charset="ISO-8859-15">
<div class="row">
<input type="checkbox" name="with-numbers" id="with-numbers" class="hidden" autocomplete="off" checked>
<label for="with-numbers" class="label-with-numbers button tooltip" title="0123456789">numbers</label>
<input type="checkbox" name="with-lower" id="with-lower" class="hidden" autocomplete="off" checked>
<label for="with-lower" class="label-with-lower button tooltip" title="abcdefghijklmnopqrstuvwxyz">lower case</label>
<input type="checkbox" name="with-upper" id="with-upper" class="hidden" autocomplete="off" checked>
<label for="with-upper" class="label-with-upper button tooltip" title="ABCDEFGHIJKLMNOPQRSTUVWXYZ">upper case</label>
<input type="checkbox" name="with-symbols" id="with-symbols" class="hidden" autocomplete="off" checked>
<label for="with-symbols" class="label-with-symbols button tooltip" title="!&quot;#$%&amp;\&apos;()*+,-./:;&lt;=&gt;?@[\]^_`{|}~">symbols</label>
<input type="checkbox" name="with-space" id="with-space" class="hidden" autocomplete="off" >
<label for="with-space" class="label-with-space button">space</label>
<input type="checkbox" name="with-other" id="with-other" class="hidden" autocomplete="off" >
<label for="with-other" class="label-with-other button tooltip" title="anything else?">other</label>
</div>
<div class="row" style="display: none; ">
<input type="text" id="other-chars" name="other-chars" placeholder="Place other symbols in here" maxlength="20" autocomplete="off"/>
</div>
<div class="row" style="margin-top: 30px;">
<p>What's your Keyboard layout?</p>
<select type="text" name="keyboardlayout">
<option value="qwerty">QWERTY</option>
<option value="qwertz">QWERTZ</option>
</select>
</div>
<div class="row" style="margin-top: 30px;">
<p>This text goes on the back of your card</p>
<input type="text" name="msg" placeholder="For Work" maxlength="20" autocomplete="off">
</div>
<div class="row" style="margin-top: 30px;">
<p>What colors shall we use?</p>
<label for="primaryColor">Primary</label>
<div id="primary" class="colorPicker">
<a class="color"><div class="colorInner"></div></a>
<div class="track"></div>
<ul class="dropdown"><li></li></ul>
<input type="hidden" class="colorInput" value='#1ABC9C' name='primaryColor' id='primaryColor' autocomplete="off"/>
</div>
<label for="secondaryColor">Secondary</label>
<div id="secondary" class="colorPicker">
<a class="color"><div class="colorInner"></div></a>
<div class="track"></div>
<ul class="dropdown"><li></li></ul>
<input type="hidden" class="colorInput" value='#ffff' name='secondaryColor' id='secondaryColor' autocomplete="off"/>
</div>
</div>
<div class="row" style="margin-top: 30px; text-align: center;">
<p id="advancedOptionsLabel"><span id="showMoreOptions">Show</span><span id="hideMoreOptions">Hide</span> more options....</p>
<div id="advancedOptions">
<label for="seed" class="tooltip" title="using seeds allows you to reproduce cards">Seed</label><br>
<input type="text" id="seed" name="seed" placeholder="Seed" maxlength="10" autocomplete="off">
<label for="space-size">Spacebar size</label>
<input type="number" min="1" max="8" style="width: 15em;" name="space-length" id="space-size" placeholder="Spacebar size (Default: 8)" maxlength="10" autocomplete="off">
</div>
</div>
<input type="hidden" value='{{sessiontoken}}'/>
<div class="row" style="margin-top: 30px; text-align: center;">
<input type="submit" value="Generate PDF!"/><br>
</div>
</form>
<p>Wanna buy a card? Checkout <a href="https://www.qwertycards.com" target="_blank">qwertycards.com</a>!</p>
<p>Fork me on <a href="https://github.com/raphiz/passwordcards" target="_blank">Github</a>!</p>
</div>
</div>
<script type="text/javascript" src="resources/js/jquery-2.1.3.js"></script>
<script type="text/javascript" src="resources/js/jquery.tinycolorpicker.js"></script>
<script type="text/javascript" src="resources/js/index.js"></script>
<!-- End Document
-->
</body>
</html>

22
resources/js/index.js Normal file
View file

@ -0,0 +1,22 @@
(function($) {
$('#advancedOptionsLabel').click(function() {
$('#showMoreOptions').toggle();
$('#hideMoreOptions').toggle();
$('#advancedOptions').slideToggle();
});
$('#with-other').click(function() {
$('#other-chars').parent().slideToggle();
});
$("#primary").tinycolorpicker();
var picker = $('#primary').data("plugin_tinycolorpicker");
picker.setColor("#1ABC9C");
$("#secondary").tinycolorpicker();
var picker = $('#secondary').data("plugin_tinycolorpicker");
picker.setColor("#ffffff");
})(jQuery);

9205
resources/js/jquery-2.1.3.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,303 @@
;(function(factory) {
if(typeof define === 'function' && define.amd) {
define(['jquery'], factory);
}
else if(typeof exports === 'object') {
module.exports = factory(require("jquery"));
}
else {
factory(jQuery);
}
}
(function($) {
var pluginName = "tinycolorpicker"
, defaults = {
colors : ["#ffffff", "#A7194B","#FE2712","#FB9902","#FABC02","#FEFE33","#D0EA2B","#66B032","#0391CE","#0247FE","#3D01A5","#8601AF"]
, backgroundUrl : null
}
;
function Plugin($container, options) {
/**
* The options of the colorpicker extended with the defaults.
*
* @property options
* @type Object
*/
this.options = $.extend({}, defaults, options);
/**
* @property _defaults
* @type Object
* @private
* @default defaults
*/
this._defaults = defaults;
/**
* @property _name
* @type String
* @private
* @final
* @default 'tinycolorpicker'
*/
this._name = pluginName;
var self = this
, $track = $container.find(".track")
, $color = $container.find(".color")
, $canvas = null
, $colorInput = $container.find(".colorInput")
, $dropdown = $container.find(".dropdown")
, $dropdownItem = $dropdown.find("li").remove()
, context = null
, mouseIsDown = false
, hasCanvas = !!document.createElement("canvas").getContext
, touchEvents = "ontouchstart" in document.documentElement
;
/**
* The current active color in hex.
*
* @property colorHex
* @type String
* @default ""
*/
this.colorHex = "";
/**
* The current active color in rgb.
*
* @property colorRGB
* @type String
* @default ""
*/
this.colorRGB = "";
/**
* @method _initialize
* @private
*/
function _initialize() {
if(hasCanvas) {
$canvas = $("<canvas></canvas>");
$track.append($canvas);
context = $canvas[0].getContext( "2d" );
_setImage();
}
else {
$.each(self.options.colors, function(index, color) {
var $clone = $dropdownItem.clone();
$clone.css("backgroundColor", color);
$clone.attr("data-color", color);
$dropdown.append($clone);
});
}
_setEvents();
return self;
}
/**
* @method _setImage
* @private
*/
function _setImage() {
var colorPicker = new Image()
, backgroundUrl = $track.css("background-image").replace(/"/g, "").replace(/url\(|\)$/ig, "")
;
$track.css("background-image", "none");
$(colorPicker).load(function() {
$canvas.attr("width", this.width);
$canvas.attr("height", this.height);
context.drawImage(colorPicker, 0, 0, this.width, this.height);
});
colorPicker.src = self.options.backgroundUrl || backgroundUrl;
}
/**
* @method _setEvents
* @private
*/
function _setEvents() {
var eventType = touchEvents ? "touchstart" : "mousedown";
if(hasCanvas) {
$color.bind(eventType, function(event) {
event.preventDefault();
event.stopPropagation();
$track.toggle();
$(document).bind("mousedown.colorpicker", function(event) {
$(document).unbind(".colorpicker");
$track.hide();
});
});
if(!touchEvents) {
$canvas.mousedown(function(event) {
mouseIsDown = true;
_getColorCanvas(event);
$(document).bind("mouseup.colorpicker", function(event) {
mouseIsDown = false;
$(document).unbind(".colorpicker");
$track.hide();
return false;
});
return false;
});
$canvas.mousemove(_getColorCanvas);
}
else {
$canvas.bind("touchstart", function(event) {
mouseIsDown = true;
_getColorCanvas(event.originalEvent.touches[0]);
return false;
});
$canvas.bind("touchmove", function(event) {
_getColorCanvas(event.originalEvent.touches[0]);
return false;
});
$canvas.bind("touchend", function(event) {
mouseIsDown = false;
$track.hide();
return false;
});
}
}
else {
$color.bind("mousedown", function(event) {
event.preventDefault();
event.stopPropagation();
$dropdown.toggle();
});
$dropdown.delegate("li", "mousedown", function(event) {
event.preventDefault();
event.stopImmediatePropagation();
var color = $(this).attr("data-color");
self.setColor(color);
$dropdown.hide();
});
}
}
/**
* @method _getColorCanvas
* @private
*/
function _getColorCanvas(event) {
if(mouseIsDown) {
var $target = $(event.target)
, offset = $target.offset()
, colorData = context.getImageData(event.pageX - offset.left, event.pageY - offset.top, 1, 1).data
;
self.setColor("rgb(" + colorData[0] + "," + colorData[1] + "," + colorData[2] + ")");
/**
* The change event will trigger when a new color is set.
*
* @event change
*/
$container.trigger("change", [self.colorHex, self.colorRGB]);
}
}
/**
* Set the color to a given hex or rgb color.
*
* @method setColor
* @chainable
*/
this.setColor = function(color) {
if(color.indexOf("#") >= 0) {
self.colorHex = color;
self.colorRGB = self.hexToRgb(self.colorHex);
}
else {
self.colorRGB = color;
self.colorHex = self.rgbToHex(self.colorRGB);
}
$color.find(".colorInner").css("backgroundColor", self.colorHex);
$colorInput.val(self.colorHex);
};
/**
* Convert hex to rgb
*
* @method hexToRgb
* @chainable
*/
this.hexToRgb = function(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return "rgb(" + parseInt(result[1], 16) + "," + parseInt(result[2], 16) + "," + parseInt(result[3], 16) + ")";
};
/**
* Convert rgb to hex
*
* @method rgbToHex
* @chainable
*/
this.rgbToHex = function(rgb) {
var result = rgb.match(/\d+/g);
function hex(x) {
var digits = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F");
return isNaN(x) ? "00" : digits[(x - x % 16 ) / 16] + digits[x % 16];
}
return "#" + hex(result[0]) + hex(result[1]) + hex(result[2]);
};
return _initialize();
}
/**
* @class tinycolorpicker
* @constructor
* @param {Object} options
@param {Array} [options.colors=[]] fallback colors for old browsers (ie8-).
@param {String} [options.backgroundUrl=''] It will look for a css image on the track div. If not found it will look if there's a url in this property.
*/
$.fn[pluginName] = function(options) {
return this.each(function() {
if(!$.data(this, "plugin_" + pluginName)) {
$.data(this, "plugin_" + pluginName, new Plugin($(this), options));
}
});
};
}));

BIN
resources/text-color.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

90
src/CardCreator.php Normal file
View file

@ -0,0 +1,90 @@
<?php
namespace raphiz\passwordcards;
class CardCreator
{
private $configration = null;
public function __construct($configration)
{
if ($configration == null) {
throw new \Exception('The given $configuration is null!');
}
if ($configration instanceof Configuration == false) {
throw new \Exception(
'The given $configuration is not a valid ' .
'Configuration object.'
);
}
$this->configration = $configration;
}
public function getSvgFilePath($template_name)
{
return __DIR__ . "/../templates/$template_name.svg";
}
public function getSvgTemplate($template_name)
{
return file_get_contents($this->getSvgFilePath($template_name));
}
public function render($svg)
{
// Get and count available characters
$chars = $this->configration->getPatternCharacters();
$char_count = count($chars);
// set seed
$seed = $this->configration->seed;
mt_srand($seed);
for ($i = 0; $i < strlen($this->configration->keys); $i++) {
$prefix = '$' . ($i+1);
$equivalent = $chars[mt_rand(0, $char_count-1)];
$equivalent = $this->escape($equivalent);
// Replace the equivalent on the "keyboard"
$svg = str_replace('$' . ($i+1) . '$', $equivalent, $svg);
$svg = str_replace('$k' . ($i+1) . '$', $this->configration->keys[$i], $svg);
}
$space_lenght = $this->configration->spaceBarSize;
$space = '';
for ($i = 0; $i < $space_lenght; $i++) {
$space .= $this->escape($chars[mt_rand(0, $char_count-1)]);
}
$svg = str_replace('$SPACE$', $space, $svg);
$svg = str_replace('$SEED$', $seed, $svg);
$svg = str_replace('$PRIMARY$', $this->configration->primaryColor, $svg);
$svg = str_replace('$SECONDARY$', $this->configration->secondaryColor, $svg);
$svg = str_replace('$TEXT$', $this->escape($this->configration->text), $svg);
$svg = str_replace('$PATTERN$', $this->escape($this->configration->pattern), $svg);
return $svg;
}
private function escape($str)
{
return htmlentities(utf8_encode($str), ENT_XML1);
}
public function renderIntoTempfile($svg)
{
$uri = tempnam("/tmp", "php");
file_put_contents($uri, $this->render($svg));
return $uri;
}
}

90
src/Configuration.php Normal file
View file

@ -0,0 +1,90 @@
<?php
namespace raphiz\passwordcards;
class Configuration
{
const DEFAULT_PATTERN = 'a-zA-Z0-9~*-*';
const LOWER = "abcdefghijklmnopqrstuvwxyz";
const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const NUMBERS = "0123456789";
const SYMBOLS = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~';
public $seed = null;
public $pattern = null;
public $primaryColor = null;
public $secondaryColor = null;
public $text = null;
public $keys = null;
public function __construct($seed, $pattern, $keys, $spaceBarSize, $text, $primaryColor, $secondaryColor)
{
$this->seed = self::evalSeed($seed);
$this->pattern = self::evalPattern($pattern);
$this->keys = self::evalKeys($keys);
$this->primaryColor = $primaryColor;
$this->text = $text;
$this->spaceBarSize = $spaceBarSize;
$this->secondaryColor = $secondaryColor;
}
public function getPatternCharacters()
{
return str_split(self::completePattern($this->pattern));
}
public static function completePattern($pattern)
{
$pattern = str_replace('a-z', self::LOWER, $pattern);
$pattern = str_replace('A-Z', self::UPPER, $pattern);
$pattern = str_replace('0-9', self::NUMBERS, $pattern);
$pattern = str_replace('*-*', self::SYMBOLS, $pattern);
return $pattern;
}
/**
* If no (numeric) seed is provided, generate one.
*/
public static function evalSeed($seed)
{
if ($seed == null || !is_numeric($seed)) {
list($usec, $sec) = explode(' ', microtime());
$seed = (float) $sec + ((float) $usec * 100000);
}
return $seed;
}
/**
* If no pattern is provided, return the default.
*/
public static function evalPattern($pattern)
{
if ($pattern == null) {
$pattern = self::DEFAULT_PATTERN;
}
return $pattern;
}
/**
* If no keys are provided, use qwerty.
*/
public static function evalKeys($keys)
{
// Return qwertz
if (strtolower($keys) === 'qwertz') {
return 'QWERTZUIOPASDFGHJKLYXCVBNM';
}
// Return qwerty
return 'QWERTYUIOPASDFGHJKLZXCVBNM';
}
}

49
src/PDFRenderer.php Normal file
View file

@ -0,0 +1,49 @@
<?php
namespace raphiz\passwordcards;
class PDFRenderer
{
public static function render($front, $back)
{
// create new PDF document
$pdf = new \TCPDF(PDF_PAGE_ORIENTATION, 'mm', 'A4', true, 'UTF-8', false);
// set document information
$pdf->SetAuthor('Raphael Zimmermann');
$pdf->SetTitle('Password Card');
$pdf->SetSubject('Password Card');
$pdf->SetPrintHeader(false);
$pdf->SetPrintFooter(false);
// add a page
$pdf->AddPage();
// Mark the position to fold...
$pdf->Line(95, 10, 95, 13);
$pdf->Line(95, 72, 95, 75);
// Add the front svg
$pdf->ImageSVG(
$file = $front,
$x = 10,
$y = 15,
$w = '85',
$h = '55'
);
// Add the back svg
$pdf->ImageSVG(
$file = $back,
$x = 95,
$y = 15,
$w = '85',
$h = '55'
);
//Close and output PDF document
return $pdf->Output('generated.pdf', 'S');
}
}

136
src/RequestUtils.php Normal file
View file

@ -0,0 +1,136 @@
<?php
namespace raphiz\passwordcards;
class RequestUtils
{
public static function isPost()
{
return $_SERVER['REQUEST_METHOD'] == "POST";
}
public static function parseSeed()
{
if (
isset($_POST['seed']) &&
is_numeric($_POST['seed'])
) {
return $_POST['seed'];
}
return null;
}
public static function parseSpacebarSize()
{
if (
isset($_POST['space-length']) &&
is_numeric($_POST['space-length']) &&
$_POST['space-length'] < 8 &&
$_POST['space-length'] > 0
) {
return $_POST['space-length'];
}
return 8;
}
public static function parseText()
{
if (isset($_POST['msg'])) {
return substr($_POST['msg'], 0, 20);
}
return '';
}
public static function parsePrimaryColor()
{
if (
isset($_POST['primaryColor']) &&
preg_match("/#[0-9a-zA-Z]{6}/", $_POST['primaryColor'])
) {
return $_POST['primaryColor'];
}
return '#1ABC9C';
}
public static function parseSecondaryColor()
{
if (
isset($_POST['secondaryColor']) &&
preg_match("/#[0-9a-zA-Z]{6}/", $_POST['secondaryColor'])
) {
return $_POST['secondaryColor'];
}
return '#ffffff';
}
public static function parseKeyboardLayout()
{
if (
isset($_POST['keyboardlayout']) &&
preg_match("/qwerty|qwertz/", $_POST['keyboardlayout'])
) {
return strtolower($_POST['keyboardlayout']);
}
return 'qwerty';
}
public static function parsePattern()
{
$pattern = "";
// With numbers?
if (self::isChecked('with-numbers')) {
$pattern .= '0-9';
}
// With lower?
if (self::isChecked('with-lower')) {
$pattern .= 'a-z';
}
// With upper?
if (self::isChecked('with-upper')) {
$pattern .= 'A-Z';
}
// With symbols?
if (self::isChecked('with-symbols')) {
$pattern .= '*-*';
}
// With space?
if (self::isChecked('with-space')) {
$pattern .= ' ';
}
// With others?
if (self::isChecked('with-other')) {
if (isset($_POST['other-chars'])) {
$pattern .= substr($_POST['other-chars'], 0, 20);
}
}
return $pattern;
}
private static function isChecked($parameter)
{
if (
isset($_POST[$parameter]) &&
$_POST[$parameter] === "on"
) {
return true;
}
return false;
}
public static function preparePdfHeader($length)
{
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=passwordcard.pdf');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $length);
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
header('Pragma: public');
}
}

119
templates/simple_back.svg Normal file
View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="301.18109"
height="194.88188"
id="svg3889"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="simple_back_en.svg">
<defs
id="defs3891" />
<sodipodi:namedview
id="base"
pagecolor="$PRIMARY$"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="69.231099"
inkscape:cy="112.82896"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="mm"
inkscape:window-width="1600"
inkscape:window-height="839"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3817"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata3894">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-857.4803)">
<rect
style="fill:$PRIMARY$;fill-opacity:1;stroke:none"
id="rect3204"
x="0"
y="857.48029"
height="194.8819"
width="301.18109" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:$SECONDARY$;fill-opacity:1;stroke:none;font-family:Open Sans;-inkscape-font-specification:Open Sans"
x="9.9902725"
y="1041.1512"
id="text3210"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3212"
x="9.9902725"
y="1041.1512"
style="font-size:10px;text-align:start;text-anchor:start;fill:$SECONDARY$;fill-opacity:1">/$SEED$/$PATTERN$/</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3207"
y="1029.4025"
x="242.0994"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:$SECONDARY$;fill-opacity:1;stroke:none;font-family:Open Sans;-inkscape-font-specification:Open Sans"
xml:space="preserve"><tspan
style="text-align:center;text-anchor:middle;fill:$SECONDARY$;fill-opacity:1"
y="1029.4025"
x="242.0994"
id="tspan3209"
sodipodi:role="line" /></text>
<text
sodipodi:linespacing="125%"
id="text3766"
y="1039.33"
x="236.00528"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:$SECONDARY$;fill-opacity:1;stroke:none;font-family:Open Sans;-inkscape-font-specification:Open Sans"
xml:space="preserve"><tspan
style="font-size:10px;text-align:center;text-anchor:middle;fill:$SECONDARY$;fill-opacity:1"
y="1039.33"
x="236.00528"
id="tspan3768"
sodipodi:role="line">http://raphael.li/pwcard</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3808"
y="947.46991"
x="150.53195"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:'Open Sans';-inkscape-font-specification:'Open Sans';letter-spacing:0px;word-spacing:0px;fill:$PRIMARY$;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
id="tspan3812"
style="font-weight:bold;-inkscape-font-specification:'Open Sans Bold';text-align:center;text-anchor:middle;fill:$SECONDARY$;fill-opacity:1"
y="947.46991"
x="150.53195"
sodipodi:role="line">$TEXT$</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

1349
templates/simple_front.svg Normal file

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 58 KiB

43
tests/CardCreatorTest.php Normal file
View file

@ -0,0 +1,43 @@
<?php
namespace raphiz\passwordcards;
class CardCreatorTest extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException \Exception
* @expectedExceptionMessage The given $configuration is null!
*/
public function testConstructorDeclinesNull()
{
new CardCreator(null);
}
/**
* @expectedException \Exception
* @expectedExceptionMessage The given $configuration is not a valid Configuration object.
*/
public function testConstructorDeclinesNonConfigurationInstances()
{
new CardCreator('fooBaa');
}
public function testGetSvgFilePath()
{
$creator = new CardCreator($this->testConfiguration);
$file = $creator->getSvgFilePath('abc');
$this->assertStringEndsWith('/templates/abc.svg', $file);
}
public function testCreateHappyPath()
{
$creator = new CardCreator($this->testConfiguration);
$creator->render($creator->getSvgFilePath('simple_front'));
}
public function setUp()
{
$this->testConfiguration = new Configuration(10, null, null, 8, '', '#000000', '#ffffff');
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace raphiz\passwordcards;
class ConfigurationTest extends \PHPUnit_Framework_TestCase
{
public function testEvalSeedReturnsGivenNonNullSeed()
{
// Verify that if a seed is given, it's returned properly
$this->assertEquals(1, Configuration::evalSeed(1));
$this->assertEquals(99, Configuration::evalSeed(99));
}
public function testEvalSeedGeneratesSeedIfNullGiven()
{
$seed1 = Configuration::evalSeed(null);
$seed2 = Configuration::evalSeed(null);
// Not null?
$this->assertNotNull($seed1);
$this->assertNotNull($seed2);
// Is float?
$this->assertInternalType('float', $seed1);
$this->assertInternalType('float', $seed2);
// The seeds shall not be equal!
$this->assertFalse($seed1 == $seed2);
}
public function testEvalPatternReturnsGivenNonNullPattern()
{
$this->assertEquals('a-zA-Z', Configuration::evalPattern('a-zA-Z'));
$this->assertEquals('a-z', Configuration::evalPattern('a-z'));
}
public function testEvalPatternReturnsDefaultIfNull()
{
$this->assertEquals('a-zA-Z0-9~*-*', Configuration::evalPattern(null));
}
public function testCompletePatterAcceptsShortForms()
{
$this->assertEquals(
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
Configuration::completePattern('A-Z')
);
$this->assertEquals(
'0123456789',
Configuration::completePattern('0-9')
);
$this->assertEquals(
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~',
Configuration::completePattern('*-*')
);
# Combination
$this->assertEquals(
'abcdefghijklmnopqrstuvwxyz0123456789',
Configuration::completePattern('a-z0-9')
);
}
public function testGetPatternCharactersHappyPath()
{
//$seed, $pattern, $keys, $spaceBarSize, $text, $primaryColor, $secondaryColor
$cfg = new Configuration(null, 'a0-', null, 8, '', '#000000', '#ffffff');
$chars = $cfg->getPatternCharacters();
$this->assertEquals('a', $chars[0]);
$this->assertEquals('0', $chars[1]);
$this->assertEquals('-', $chars[2]);
}
}