├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Doxyfile
├── README.md
├── build.xml
├── build
├── Doxyfile
├── phpdox.xml
├── phpmd.xml
└── screenshots
│ └── .keep
├── cache
└── .keep
├── composer.json
├── db
└── config
│ ├── database.sample.yml
│ └── database.testing.yml
├── examples
├── login.php
└── runner.php
├── phprelease.ini
├── phpunit.xml
├── phpunit.xml.dist
├── src
├── Action.php
├── ActionDescriptor.php
├── ActionGenerator.php
├── ActionGeneratorTest.php
├── ActionLogger.php
├── ActionRequest.php
├── ActionRequestTest.php
├── ActionRunner.php
├── ActionRunnerTest.php
├── ActionTemplate
│ ├── ActionTemplate.php
│ ├── ActionTemplateTest.php
│ ├── CodeGenActionTemplate.php
│ ├── CodeGenActionTemplateTest.php
│ ├── FileBasedActionTemplateTest.php
│ ├── RecordActionTemplate.php
│ ├── SampleActionTemplate.php
│ ├── SampleActionTemplateTest.php
│ ├── TwigActionTemplate.php
│ ├── UpdateOrderingRecordActionTemplate.php
│ └── UpdateOrderingRecordActionTemplateTest.php
├── ActionTest.php
├── ActionTrait
│ └── RoleChecker.php
├── ColumnConvert.php
├── ColumnConvertTest.php
├── Csrf
│ ├── CsrfArrayStorage.php
│ ├── CsrfSessionStorage.php
│ ├── CsrfStorage.php
│ ├── CsrfToken.php
│ ├── CsrfTokenProvider.php
│ └── CsrfTokenProviderTest.php
├── EmailAction.php
├── Exception
│ ├── ActionException.php
│ ├── ActionNotFoundException.php
│ ├── InvalidActionNameException.php
│ ├── RequiredConfigKeyException.php
│ ├── UnableToCreateActionException.php
│ ├── UnableToWriteCacheException.php
│ └── UndefinedTemplateException.php
├── FieldView
│ ├── BootstrapFieldView.php
│ ├── BootstrapFieldViewTest.php
│ ├── DivFieldView.php
│ └── DivFieldViewTest.php
├── GeneratedAction.php
├── Loggable.php
├── Messages
│ ├── en.php
│ └── zh_TW.php
├── MixinAction.php
├── Param
│ ├── FileParam.php
│ ├── Image
│ │ ├── CropAndScaleResize.php
│ │ ├── MaxHeightResize.php
│ │ ├── MaxWidthResize.php
│ │ └── ScaleResize.php
│ ├── ImageParam.php
│ ├── ImageParamTest.php
│ ├── ImageResizer.php
│ ├── Param.php
│ └── ParamTest.php
├── RecordAction
│ ├── BaseRecordAction.php
│ ├── BulkCopyRecordAction.php
│ ├── BulkDeleteRecordAction.php
│ ├── BulkRecordAction.php
│ ├── BulkZhConvertRecordAction.php
│ ├── CreateRecordAction.php
│ ├── DeleteRecordAction.php
│ ├── UpdateOrderingRecordAction.php
│ └── UpdateRecordAction.php
├── Result.php
├── ResultTest.php
├── ServiceContainer.php
├── Storage
│ ├── FilePath.php
│ ├── FilePathTest.php
│ ├── FileRename
│ │ └── Md5Rename.php
│ ├── FileRenameMethods.php
│ └── FileRenameMethodsTest.php
├── Template.php
├── Templates
│ └── RecordAction.html.twig
├── Testing
│ ├── ActionTestAssertions.php
│ └── ActionTestCase.php
├── Utils.php
├── ValueType
│ ├── BaseType.php
│ ├── BoolType.php
│ ├── BoolTypeTest.php
│ ├── DateTimeType.php
│ ├── DateTimeTypeTest.php
│ ├── DirType.php
│ ├── EmailType.php
│ ├── FileType.php
│ ├── IntType.php
│ ├── IntTypeTest.php
│ ├── IpType.php
│ ├── IpTypeTest.php
│ ├── Ipv4Type.php
│ ├── Ipv6Type.php
│ ├── JsonType.php
│ ├── JsonTypeTest.php
│ ├── NumType.php
│ ├── PathType.php
│ ├── RegexType.php
│ ├── StrType.php
│ ├── TimestampType.php
│ ├── UrlType.php
│ └── ValueTypeTest.php
└── View
│ ├── BaseView.php
│ ├── ManyToManyCheckboxView.php
│ ├── StackView.php
│ ├── StackViewTest.php
│ ├── TemplateView.php
│ └── TemplateViewTest.php
└── tests
├── ActionTrait
└── RoleCheckerTest.php
├── ActionWithUserTest.php
├── EmailFieldActionTest.php
├── FooTemplateView.php
├── IntFieldActionTest.php
├── Model
└── CRUDTest
│ ├── FooUser.php
│ ├── FooUserCollection.php
│ └── FooUserCollectionBase.php
├── OrderBundle
├── Model
│ ├── Order.php
│ ├── OrderBase.php
│ ├── OrderCollection.php
│ ├── OrderCollectionBase.php
│ ├── OrderItem.php
│ ├── OrderItemBase.php
│ ├── OrderItemCollection.php
│ ├── OrderItemCollectionBase.php
│ ├── OrderItemRepo.php
│ ├── OrderItemSchema.php
│ ├── OrderRepo.php
│ └── OrderSchema.php
└── Tests
│ └── OrderItemTest.php
├── ProductBundle
├── Action
│ ├── CreateProduct.php
│ ├── CreateProductFile.php
│ ├── CreateProductImage.php
│ ├── ProductBaseMixin.php
│ └── UpdateProduct.php
├── Model
│ ├── Category.php
│ ├── CategoryBase.php
│ ├── CategoryCollection.php
│ ├── CategoryCollectionBase.php
│ ├── CategoryRepo.php
│ ├── CategorySchema.php
│ ├── Feature.php
│ ├── FeatureBase.php
│ ├── FeatureCollection.php
│ ├── FeatureCollectionBase.php
│ ├── FeatureRepo.php
│ ├── FeatureSchema.php
│ ├── Product.php
│ ├── ProductBase.php
│ ├── ProductCategory.php
│ ├── ProductCategoryBase.php
│ ├── ProductCategoryCollection.php
│ ├── ProductCategoryCollectionBase.php
│ ├── ProductCategoryRepo.php
│ ├── ProductCategorySchema.php
│ ├── ProductCollection.php
│ ├── ProductCollectionBase.php
│ ├── ProductFeature.php
│ ├── ProductFeatureBase.php
│ ├── ProductFeatureCollection.php
│ ├── ProductFeatureCollectionBase.php
│ ├── ProductFeatureRepo.php
│ ├── ProductFeatureSchema.php
│ ├── ProductFile.php
│ ├── ProductFileBase.php
│ ├── ProductFileCollection.php
│ ├── ProductFileCollectionBase.php
│ ├── ProductFileRepo.php
│ ├── ProductFileSchema.php
│ ├── ProductImage.php
│ ├── ProductImageBase.php
│ ├── ProductImageCollection.php
│ ├── ProductImageCollectionBase.php
│ ├── ProductImageRepo.php
│ ├── ProductImageSchema.php
│ ├── ProductLink.php
│ ├── ProductLinkBase.php
│ ├── ProductLinkCollection.php
│ ├── ProductLinkCollectionBase.php
│ ├── ProductLinkRepo.php
│ ├── ProductLinkSchema.php
│ ├── ProductProduct.php
│ ├── ProductProductBase.php
│ ├── ProductProductCollection.php
│ ├── ProductProductCollectionBase.php
│ ├── ProductProductRepo.php
│ ├── ProductProductSchema.php
│ ├── ProductProperty.php
│ ├── ProductPropertyBase.php
│ ├── ProductPropertyCollection.php
│ ├── ProductPropertyCollectionBase.php
│ ├── ProductPropertyRepo.php
│ ├── ProductPropertySchema.php
│ ├── ProductRecipe.php
│ ├── ProductRecipeCollection.php
│ ├── ProductRepo.php
│ ├── ProductSchema.php
│ ├── ProductSubsection.php
│ ├── ProductSubsectionBase.php
│ ├── ProductSubsectionCollection.php
│ ├── ProductSubsectionCollectionBase.php
│ ├── ProductSubsectionRepo.php
│ ├── ProductSubsectionSchema.php
│ ├── ProductType.php
│ ├── ProductTypeBase.php
│ ├── ProductTypeCollection.php
│ ├── ProductTypeCollectionBase.php
│ ├── ProductTypeRepo.php
│ ├── ProductTypeSchema.php
│ ├── ProductUseCase.php
│ ├── ProductUseCaseCollection.php
│ ├── Resource.php
│ ├── ResourceBase.php
│ ├── ResourceCollection.php
│ ├── ResourceCollectionBase.php
│ ├── ResourceRepo.php
│ └── ResourceSchema.php
├── ProductActionTest.php
└── Tests
│ └── ProductTest.php
├── Templates
└── foo.html
├── User
└── Model
│ ├── User.php
│ ├── UserBase.php
│ ├── UserCollection.php
│ ├── UserCollectionBase.php
│ ├── UserRepo.php
│ └── UserSchema.php
├── bootstrap.php
├── config
├── database.yml
├── mysql.yml
├── mysql_configserver.yml
├── pgsql.yml
├── sqlite.yml
└── tmp.yml
├── data
└── 404.png
├── fixture
├── bulk_update_user4.php
└── handle_with_result.json
└── index.php
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | *.rar
3 | *.tar
4 | *.tar.bz2
5 | *.tbz
6 | *.zip
7 | *.jar
8 | .sass-cache
9 | .lazy.yml
10 | .DS_Store
11 | .gitmodules
12 | .hg
13 | .svn
14 | cache/
15 | db/config/*.php
16 | coverage/
17 | .lazy.php
18 | vendor/
19 | route
20 | .lazy.php
21 | cache/
22 | config/application.yml
23 | config/database.yml
24 | config/framework.yml
25 | utils/Selenium/
26 | cache.properties
27 | *SchemaProxy.php
28 | *CollectionBase.php
29 | *RepoBase.php
30 | *.swp
31 | /build/
32 | .maghead-cli.yml
33 | .php_cs.cache
34 | composer.lock
35 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 |
2 | # get mysql 5.6
3 | dist: trusty
4 |
5 | language: php
6 | php:
7 | # - 7.0 # trusty has some problem with phpunit
8 | - 7.1
9 | - hhvm
10 | env:
11 | - DB=sqlite
12 | - DB=mysql
13 | matrix:
14 | fast_finish: true
15 | allow_failures:
16 | - php: "hhvm"
17 | install:
18 | - travis_retry composer require satooshi/php-coveralls "^1" --no-update --dev --prefer-dist
19 | - travis_retry composer install
20 | before_script:
21 | - rm -rf *.sqlite
22 | - if [[ "$DB" == "pgsql" ]]; then vendor/maghead/maghead/travis/setup-pgsql ; fi
23 | - if [[ "$DB" == "mysql" ]]; then vendor/maghead/maghead/travis/setup-mysql ; fi
24 | - phpenv rehash
25 | - php vendor/bin/maghead use tests/config/sqlite.yml
26 | - php vendor/bin/maghead schema build -f
27 | script:
28 | - phpunit -c phpunit.xml.dist
29 | after_success:
30 | - php vendor/bin/coveralls -v
31 | cache:
32 | apt: true
33 | directories:
34 | - vendor
35 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOGS
2 | ===================
3 |
4 | Version x - Sun Nov 15 13:07:53 2015
5 |
6 | 1. CSRFTokenProvider now must be instancialized.
7 | 2. CSRF token verification is now enabled only when csrf token provider is given.
8 |
9 | Version 2.1 - Sat Aug 29 13:55:39 2015
10 |
11 | 1. Added `cache_dir` to ServiceContainer.
12 | 2. Provided an option for customizing field view class.
13 | 3. Added BootstrapFieldView class
14 |
15 | Version 2.0.0 - Tue Jun 30 14:23:00 2015
16 |
17 | 1. Improved action generator to use action template to generate action.
18 | 2. Added action templates.
19 | - File-based action template
20 | - Record action template
21 | - Update ordering record action template
22 | 2. Added ActionRunner:handleWith method to run action with $_Request directly.
23 | 3. Added CSRF token support.
24 | 4. Added service container.
25 | 5. Added image process.
26 | 6. Renamed SortRecordAction to UpdateOrderingRecordAction.
27 | 7. Refactored RecordAction options
28 | - Added options for 'request', 'parent', 'files'
29 | - Added ActionRequest for managing $_REQUEST, $_FILES parameters.
30 | 8. Raised test coverage to 70%
31 |
32 | ### Deprecation
33 |
34 | - ActionGenerator:generate2
35 | - ActionGenerator:generateRecordAction
36 | - ActionRunner:registerCRUD
37 | - ActionKit/View
38 | - ActionKit/CRUD
39 |
40 |
41 | Version 1.4 - Fri Apr 25 20:10:02 2014
42 |
43 | 1. Added SortRecordAction for sorting records.
44 | 2. Refactored BulkCopyAction with contentType attribute
45 | 3. Added contentType attribute support, currently for "ImageFile" and "File"
46 |
47 | Version 1.2 - Sat Dec 7 21:56:54 2013
48 |
49 | 1. Refactored ManyToManyCheckboxView to support hierarchical data.
50 | 2. Improved interface for StackView.
51 |
52 |
--------------------------------------------------------------------------------
/build/phpdox.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/build/phpmd.xml:
--------------------------------------------------------------------------------
1 |
7 | Description of your coding standard
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build/screenshots/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corneltek/ActionKit/a85f04c48bdaeff4c30d35246be9545508911ada/build/screenshots/.keep
--------------------------------------------------------------------------------
/cache/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corneltek/ActionKit/a85f04c48bdaeff4c30d35246be9545508911ada/cache/.keep
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "corneltek/actionkit",
3 | "authors": [
4 | { "name": "Yo-An Lin", "email": "yoanlin93+composer@gmail.com" }
5 | ],
6 | "require": {
7 | "php": ">=5.6",
8 | "corneltek/cascading-attribute": "^1",
9 | "corneltek/fileutil": "^1.7",
10 | "universal/universal": "2.0.x-dev",
11 | "corneltek/imagekit": "^1",
12 | "corneltek/codegen": "@dev",
13 | "corneltek/cliframework": "@dev",
14 | "corneltek/formkit": "^1",
15 | "phifty/locale": "^3",
16 | "twig/twig": "^2",
17 | "pekkis/mime-types": "^1",
18 | "pimple/pimple": "^3.0"
19 | },
20 | "require-dev": {
21 | "maghead/maghead": "4.0.x-dev",
22 | "maghead/magsql": "@dev",
23 | "corneltek/webserver-runner": "dev-master",
24 | "corneltek/kendo": "4.0.x-dev"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "ActionKit\\": "src/"
29 | }
30 | },
31 | "autoload-dev": {
32 | "classmap": ["tests"],
33 | "psr-4": {
34 | "ProductBundle\\": "tests/ProductBundle/",
35 | "OrderBundle\\": "tests/OrderBundle/",
36 | "User\\": "tests/User/"
37 | }
38 | },
39 | "extra": { "branch-alias": { "dev-master": "4.0.x-dev" } },
40 | "license": "MIT"
41 | }
42 |
--------------------------------------------------------------------------------
/db/config/database.sample.yml:
--------------------------------------------------------------------------------
1 | ---
2 | bootstrap:
3 | schema:
4 | auto_id: true
5 | paths:
6 | - tests
7 | data_source:
8 | default: sqlite
9 | nodes:
10 | mysql:
11 | dsn: "mysql:host=localhost;dbname=actionkit_test"
12 | # database: actionkit_test
13 | # user: root
14 | # pass:
15 | sqlite:
16 | dsn: "sqlite:tests.db"
17 |
--------------------------------------------------------------------------------
/db/config/database.testing.yml:
--------------------------------------------------------------------------------
1 | ---
2 | bootstrap:
3 | schema:
4 | auto_id: true
5 | paths:
6 | - tests
7 | data_source:
8 | default: sqlite
9 | nodes:
10 | mysql:
11 | dsn: "mysql:host=localhost;dbname=actionkit_test"
12 | # database: actionkit_test
13 | # user: root
14 | # pass:
15 | sqlite:
16 | dsn: "sqlite:tests.db"
17 |
--------------------------------------------------------------------------------
/examples/login.php:
--------------------------------------------------------------------------------
1 | param('email')->renderAs('TextInput');
15 | $this->param('password')->renderAs('PasswordInput');
16 | }
17 |
18 | public function run() {
19 |
20 | if ( $this->arg('email') == 'test@test.com' &&
21 | $this->arg('password') == 'test') {
22 | return $this->success('登入成功');
23 | } else {
24 | if( $this->arg('email') != 'test@test.com') {
25 | return $this->error('無此帳號');
26 | } else if($this->arg('password') != 'test') {
27 | return $this->error('密碼錯誤');
28 | }
29 | }
30 | }
31 | }
32 |
33 | $container = new ActionKit\ServiceContainer;
34 | $runner = new ActionKit\ActionRunner($container);
35 |
36 | // you can also run action directly
37 | // $result = $runner->run('MyLoginAction',array( 'email' => '...', 'password' => '...' ));
38 |
39 | if (isset($_POST['action'])) {
40 | $sig = $_POST['action'];
41 | unset($_POST['action']);
42 | $result = $runner->run($sig, $_POST);
43 | //var_dump($result);
44 | echo $result->getMessage();
45 | } else {
46 | $action = new MyLoginAction;
47 | echo $action->asView()->render(); // implies view class ActionKit\View\StackView
48 | }
49 |
--------------------------------------------------------------------------------
/examples/runner.php:
--------------------------------------------------------------------------------
1 | handleWith(STDOUT, $_REQUEST)) {
8 | var_dump( $result );
9 | }
10 |
--------------------------------------------------------------------------------
/phprelease.ini:
--------------------------------------------------------------------------------
1 | ; Steps = PHPUnit, BumpVersion, GitCommit, GitTag, GitPush, GitPushTags
2 | Steps = PHPUnit, BumpVersion, GitCommit, GitTag, GitPush, GitPushTags
3 | ; VersionFrom = src/PHPRelease/Console.php
4 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | src
24 |
25 |
26 |
27 | tests
28 |
29 |
30 |
31 | tests/ProductBundle
32 |
33 |
34 |
35 | tests/OrderBundle
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | src
23 |
24 | src
25 | src/Cache
26 | src/Testing
27 | tests
28 |
29 |
30 |
31 |
32 |
33 |
34 | src
35 |
36 |
37 |
38 | tests
39 |
40 |
41 |
42 | tests/ProductBundle
43 |
44 |
45 |
46 | tests/OrderBundle
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/ActionDescriptor.php:
--------------------------------------------------------------------------------
1 | arguments = array_merge($this->parameters, array());
28 |
29 | if (isset($this->arguments['__ajax_request'])) {
30 | unset($this->arguments['__ajax_request']);
31 | $this->ajax = true;
32 | }
33 |
34 | unset($this->arguments['__action']);
35 | unset($this->arguments['action']);
36 |
37 | // handle actionName
38 | $actionKey = isset($requestParameters['__action']) ? '__action' : 'action';
39 | if (isset($requestParameters[$actionKey])) {
40 | $this->actionName = $requestParameters[$actionKey];
41 | }
42 | }
43 |
44 | public function arg($field)
45 | {
46 | if (isset($this->arguments[$field])) {
47 | return $this->arguments[$field];
48 | }
49 | return null;
50 | }
51 |
52 |
53 |
54 |
55 | /**
56 | * isInvalidActionName returns int
57 | *
58 | * @return integer matched count.
59 | */
60 | public function isInvalidActionName()
61 | {
62 | return preg_match('/[^A-Za-z0-9:]/i', $this->actionName);
63 | }
64 |
65 | public function isFullQualifiedName()
66 | {
67 | return strpos($this->actionName, '::') !== false;
68 | }
69 |
70 |
71 | public function isAjax()
72 | {
73 | return $this->ajax;
74 | }
75 |
76 | public function getActionName()
77 | {
78 | return $this->actionName;
79 | }
80 |
81 | public function getArguments()
82 | {
83 | return $this->arguments;
84 | }
85 |
86 | public static function hasAction(array $requestParameters = array())
87 | {
88 | return isset($requestParameters['__action']);
89 | }
90 |
91 |
92 |
93 |
94 | // XXX: the uploadedFile methods should be not used.
95 | /**
96 | * This is a simple uploaded file storage, it doesn't support multiple files
97 | */
98 | public function uploadedFile($fieldName, $index = 0)
99 | {
100 | if (isset($this->uploadedFiles[$fieldName][$index])) {
101 | return $this->uploadedFiles[$fieldName][$index];
102 | }
103 | }
104 |
105 | public function saveUploadedFile($fieldName, $index, $file)
106 | {
107 | return $this->uploadedFiles[$fieldName][$index] = $file;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/ActionRequestTest.php:
--------------------------------------------------------------------------------
1 | 'MyApp::Action::CreateUser',
13 | '__ajax_request' => true,
14 | 'account' => 'user@gmail.com',
15 | 'password' => md5('qwer1234'),
16 | ));
17 | $this->assertTrue($request->isAjax());
18 | $this->assertEquals('MyApp::Action::CreateUser',$request->getActionName());
19 | $this->assertSame([
20 | 'account' => 'user@gmail.com',
21 | 'password' => '5d93ceb70e2bf5daa84ec3d0cd2c731a',
22 | ], $request->getArguments());
23 | }
24 |
25 | public function testFullQualifiedName()
26 | {
27 | $request = new ActionRequest(array(
28 | '__action' => 'MyApp::Action::CreateUser',
29 | '__ajax_request' => true,
30 | 'account' => 'user@gmail.com',
31 | 'password' => md5('qwer1234'),
32 | ));
33 | $this->assertTrue($request->isFullQualifiedName());
34 | }
35 |
36 | public function testNonFullQualifiedName()
37 | {
38 | $request = new ActionRequest(array(
39 | '__action' => 'CreateUser',
40 | '__ajax_request' => true,
41 | 'account' => 'user@gmail.com',
42 | 'password' => md5('qwer1234'),
43 | ));
44 | $this->assertFalse($request->isFullQualifiedName());
45 | }
46 |
47 | public function testArg()
48 | {
49 | $request = new ActionRequest([
50 | '__action' => 'MyApp::Action::CreateProduct',
51 | '__ajax_request' => true,
52 | 'account' => 'user@gmail.com',
53 | 'password' => md5('qwer1234'),
54 | ]);
55 | $this->assertEquals('user@gmail.com',$request->arg('account'));
56 | }
57 |
58 | public function testFiles()
59 | {
60 | $files = [
61 | 'download' => [
62 | 'name' => array(
63 | 'file1' => 'MyFile.txt',
64 | 'file2' => 'MyFile.jpg',
65 | ),
66 | 'type' => array(
67 | 'file1' => 'text/plain',
68 | 'file2' => 'image/jpeg',
69 | ),
70 | 'tmp_name' => array (
71 | 'file1' => '/tmp/php/php1h4j1o',
72 | 'file2' => '/tmp/php/php6hst32',
73 | ),
74 | 'error' => array(
75 | 'file1' => UPLOAD_ERR_OK,
76 | 'file2' => UPLOAD_ERR_OK,
77 | ),
78 | 'size' => array(
79 | 'file1' => 123,
80 | 'file2' => 98174
81 | ),
82 | ],
83 | ];
84 | $request = new ActionRequest([
85 | '__action' => 'MyApp::Action::CreateProduct',
86 | '__ajax_request' => true,
87 | 'account' => 'user@gmail.com',
88 | 'password' => md5('qwer1234'),
89 | ], $files);
90 | $fileStash = $request->file('download');
91 | $this->assertNotNull($fileStash);
92 | $this->assertCount(2, $fileStash);
93 | }
94 |
95 | public function testInvalidActionName()
96 | {
97 | $request = new ActionRequest(array(
98 | '__action' => 'bb_fjeijfe',
99 | '__ajax_request' => true,
100 | 'account' => 'user@gmail.com',
101 | 'password' => md5('qwer1234'),
102 | ));
103 | $this->assertEquals(1, $request->isInvalidActionName());
104 | }
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/src/ActionTemplate/ActionTemplate.php:
--------------------------------------------------------------------------------
1 | 'test2',
17 | ] ],
18 | [ [
19 | 'namespace' => 'test2',
20 | 'model' => 'test2Model', // model's name
21 | ] ],
22 | ];
23 | }
24 |
25 | /**
26 | * @dataProvider failingArgumentProvider
27 | * @expectedException ActionKit\Exception\RequiredConfigKeyException
28 | */
29 | public function testRecordActionTemplateFailingArguments($arguments)
30 | {
31 | $actionTemplate = new RecordActionTemplate();
32 | $runner = new ActionRunner;
33 | $actionTemplate->register($runner, 'RecordActionTemplate', $arguments);
34 | }
35 |
36 | public function testRecordActionTemplate()
37 | {
38 | $actionTemplate = new RecordActionTemplate();
39 | $runner = new ActionRunner;
40 | $actionTemplate->register($runner, 'RecordActionTemplate', array(
41 | 'namespace' => 'test2',
42 | 'model' => 'test2Model', // model's name
43 | 'types' => array(
44 | [ 'prefix' => 'Create'],
45 | [ 'prefix' => 'Update'],
46 | [ 'prefix' => 'Delete'],
47 | [ 'prefix' => 'BulkDelete']
48 | )
49 | ));
50 |
51 | $className = 'test2\Action\Updatetest2Model';
52 | $this->assertCount(4, $runner->getPretreatments());
53 | $this->assertNotNull($pretreatment = $runner->getActionPretreatment($className));
54 |
55 | $generatedAction = $actionTemplate->generate($className, $pretreatment);
56 | $this->assertRequireGeneratedAction($className, $generatedAction);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/ActionTemplate/CodeGenActionTemplate.php:
--------------------------------------------------------------------------------
1 | register($runner, 'CodeGenActionTemplate', array(
16 | * 'namespace' => 'test2',
17 | * 'model' => 'test2Model', // model's name
18 | * 'types' => array('Create','Update','Delete','BulkDelete')
19 | * ));
20 | *
21 | * $className = 'test2\Action\UpdatetestModel';
22 | * $generatedAction = $actionTemplate->generate($className, [
23 | * 'extends' => "\\ActionKit\\RecordAction\\CreateRecordAction",
24 | * 'properties' => [
25 | * 'recordClass' => "test2\\Model\\testModel",
26 | * ],
27 | * ]);
28 | *
29 | * $generatedAction->requireAt($cacheCodePath);
30 | *
31 | */
32 | class CodeGenActionTemplate implements ActionTemplate
33 | {
34 | /**
35 | * @synopsis
36 | *
37 | * $template->register($runner, [
38 | * 'action_class' => 'FooAction',
39 | * 'extends' => "\\ActionKit\\RecordAction\\{$type}RecordAction",
40 | * 'properties' => [
41 | * 'recordClass' => $options['namespace'] . "\\Model\\" . $options['model'],
42 | * ],
43 | * ]);
44 | */
45 | public function register(ActionRunner $runner, $asTemplate, array $options = array())
46 | {
47 | if (isset($options['use'])) {
48 | array_unshift($options['use'], '\\ActionKit\\Action');
49 | } else {
50 | $options['use'] = ['\\ActionKit\\Action'];
51 | }
52 | $runner->register($options['action_class'], $asTemplate, $options);
53 | }
54 |
55 | public function createActionClassFile($actionClass, array $options = array())
56 | {
57 | $class = new ClassFile($actionClass);
58 | if (isset($options['use'])) {
59 | foreach ($options['use'] as $use) {
60 | $class->useClass($use);
61 | }
62 | }
63 | if (isset($options['extends'])) {
64 | $class->extendClass($options['extends']);
65 | }
66 | if (isset($options['properties'])) {
67 | foreach ($options['properties'] as $name => $value) {
68 | $class->addProperty($name, $value);
69 | }
70 | }
71 | if (isset($options['constants'])) {
72 | foreach ($options['constants'] as $name => $value) {
73 | $class->addConst($name, $value);
74 | }
75 | }
76 | if (isset($options['traits'])) {
77 | foreach ($options['traits'] as $traitClass) {
78 | $class->useTrait($traitClass);
79 | }
80 | }
81 |
82 | return $class;
83 | }
84 |
85 | public function generate($actionClass, array $options = array())
86 | {
87 | $class = $this->createActionClassFile($actionClass, $options);
88 | $code = $class->render();
89 | return new GeneratedAction($actionClass, $code, $class);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/ActionTemplate/CodeGenActionTemplateTest.php:
--------------------------------------------------------------------------------
1 | register($runner, 'CodeGenActionTemplate', array(
22 | 'action_class' => $className,
23 | 'use' => ['TestApp\Database'],
24 | 'extends' => 'Action',
25 | 'constants' => [
26 | 'foo' => 123
27 | ],
28 | ));
29 | $this->assertCount(1, $runner->getPretreatments());
30 | $this->assertNotNull($pretreatment = $runner->getActionPretreatment($className));
31 |
32 | $generatedAction = $actionTemplate->generate($className, $pretreatment['arguments']);
33 | $this->assertRequireGeneratedAction($className, $generatedAction);
34 | }
35 |
36 |
37 | /**
38 | * @dataProvider classNameProvider
39 | */
40 | public function testCodeGenTemplateActionSuccessfulGeneration($className)
41 | {
42 | $actionTemplate = new CodeGenActionTemplate();
43 | $runner = new ActionRunner;
44 |
45 | $actionTemplate->register($runner, 'CodeGenActionTemplate', array(
46 | 'action_class' => $className,
47 | 'extends' => 'Action',
48 | ));
49 | $this->assertCount(1, $runner->getPretreatments());
50 | $this->assertNotNull($pretreatment = $runner->getActionPretreatment($className));
51 |
52 | $generatedAction = $actionTemplate->generate($className, $pretreatment['arguments']);
53 | $this->assertRequireGeneratedAction($className, $generatedAction);
54 | }
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/src/ActionTemplate/RecordActionTemplate.php:
--------------------------------------------------------------------------------
1 | register($runner, array(
16 | * 'namespace' => 'test',
17 | * 'model' => 'testModel', // model's name
18 | * 'allowed_roles' => array('admin', 'manager'),
19 | * 'types' => [
20 | * ['prefix' => 'Create', 'allowed_roles' => ['user', 'admin'] ],
21 | * ['prefix' => 'Update'],
22 | * ['prefix' => 'Delete']
23 | * ]
24 | * ));
25 | */
26 | public function register(ActionRunner $runner, $asTemplate, array $options = array())
27 | {
28 | if (isset($options['use'])) {
29 | array_unshift($options['use'], '\\ActionKit\\Action', '\\ActionKit\\RecordAction\\BaseRecordAction');
30 | } else {
31 | $options['use'] = ['\\ActionKit\\Action', '\\ActionKit\\RecordAction\\BaseRecordAction'];
32 | }
33 |
34 | if (!isset($options['namespace'])) {
35 | throw new RequiredConfigKeyException('namespace', 'namespace of the generated action');
36 | }
37 | if (!isset($options['model'])) {
38 | throw new RequiredConfigKeyException('model', 'required for creating record actions');
39 | }
40 | if (! isset($options['types'])) {
41 | throw new RequiredConfigKeyException('types', 'types is an array of operation names for CRUD');
42 | }
43 |
44 | foreach ((array) $options['types'] as $type) {
45 | // re-define type
46 | if (is_string($type)) {
47 | $type = [ 'prefix' => $type ];
48 | }
49 |
50 |
51 | $actionClass = $options['namespace'] . '\\Action\\' . $type['prefix'] . $options['model'];
52 | $properties = ['recordClass' => $options['namespace'] . "\\Model\\" . $options['model']];
53 | $traits = array();
54 | if (isset($options['allowed_roles']) || isset($type['allowed_roles'])) {
55 | $properties['allowedRoles'] = isset($type['allowed_roles']) ? $type['allowed_roles'] : $options['allowed_roles'];
56 | $traits = ['ActionKit\\ActionTrait\\RoleChecker'];
57 | }
58 | $configs = [
59 | 'extends' => "\\ActionKit\\RecordAction\\{$type['prefix']}RecordAction",
60 | 'properties' => $properties,
61 | 'traits' => $traits,
62 | 'use' => $options['use']
63 | ];
64 |
65 | $runner->register($actionClass, $asTemplate, $configs);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/ActionTemplate/SampleActionTemplate.php:
--------------------------------------------------------------------------------
1 | generate('', array(
12 | * 'namespace' => 'Core',
13 | * 'action_name' => 'GrantAccess'
14 | * ));
15 | *
16 | * $generatedAction->requireAt($cacheFilePath);
17 | */
18 | class SampleActionTemplate extends CodeGenActionTemplate
19 | {
20 | public function generate($actionClass, array $options = array())
21 | {
22 | if (! isset($options['namespace'])) {
23 | throw new RequiredConfigKeyException('action_name');
24 | }
25 |
26 | if (!isset($options['action_name'])) {
27 | throw new RequiredConfigKeyException('action_name');
28 | }
29 |
30 | $namespace = $options['namespace'];
31 | $actionName = $options['action_name'];
32 |
33 | $actionClass = "$namespace\\Action\\$actionName";
34 | $options = [ 'extends' => 'Action', ];
35 |
36 | $class = $this->createActionClassFile($actionClass, $options);
37 |
38 | // General use statement
39 | $class->useClass('\\ActionKit\\Action');
40 | $class->useClass('\\ActionKit\\RecordAction\\BaseRecordAction');
41 |
42 |
43 | $class->addMethod('public', 'schema', [], '');
44 | $class->addMethod('public', 'run', [], 'return $this->success("Success!");');
45 |
46 | $code = $class->render();
47 | return new GeneratedAction($actionClass, $code, $class);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ActionTemplate/SampleActionTemplateTest.php:
--------------------------------------------------------------------------------
1 | 'FooBar'] ],
17 | [ ['action_name' => 'CreateSample'] ],
18 | [ [] ]
19 | ];
20 | }
21 |
22 | /**
23 | * @dataProvider failingArgumentProvider
24 | * @expectedException ActionKit\Exception\RequiredConfigKeyException
25 | */
26 | public function testSampleActionTemplateWithException($arguments)
27 | {
28 | $generator = new ActionGenerator();
29 | $generator->registerTemplate('SampleActionTemplate', new SampleActionTemplate());
30 | $generator->generate('SampleActionTemplate', 'SampleAction', $arguments);
31 | }
32 |
33 | public function testSampleActionTemplate()
34 | {
35 | $generator = new ActionGenerator();
36 | $generator->registerTemplate('SampleActionTemplate', new SampleActionTemplate());
37 | $runner = new ActionRunner([ 'generator' => $generator ]);
38 | // $runner->registerAction('SampleActionTemplate', array('action_class' => 'SampleAction'));
39 | $action = $runner->getGenerator()->generate('SampleActionTemplate', 'SampleAction', [
40 | 'namespace' => 'FooBar',
41 | 'action_name' => 'CreateSample'
42 | ]);
43 | $this->assertInstanceOf(GeneratedAction::class, $action);
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/ActionTemplate/UpdateOrderingRecordActionTemplate.php:
--------------------------------------------------------------------------------
1 | register($runner, 'UpdateOrderingRecordActionTemplate', array(
13 | * 'namespace' => 'test2',
14 | * 'model' => 'Test2Model', // model's name
15 | * ));
16 | *
17 | * $className = 'test2\Action\SortTest2Model';
18 | * $actionArgs = $runner->pretreatments[$className]['actionArgs'];
19 | * $generatedAction = $actionTemplate->generate($className, $actionArgs);
20 | *
21 | * $generatedAction->load();
22 | */
23 | class UpdateOrderingRecordActionTemplate extends RecordActionTemplate
24 | {
25 | public function register(ActionRunner $runner, $asTemplate, array $options = array())
26 | {
27 | if (isset($options['use'])) {
28 | array_unshift($options['use'], '\\ActionKit\\Action', '\\ActionKit\\RecordAction\\BaseRecordAction');
29 | } else {
30 | $options['use'] = ['\\ActionKit\\Action', '\\ActionKit\\RecordAction\\BaseRecordAction'];
31 | }
32 |
33 |
34 | if (!isset($options['model'])) {
35 | if (isset($options['record_class'])) {
36 | $nslist = explode("\\Model\\", $options['record_class']);
37 | $options['model'] = $nslist[1];
38 |
39 | if (!isset($options['namespace'])) {
40 | $options['namespace'] = $nslist[0];
41 | }
42 | } else {
43 | throw new RequiredConfigKeyException('model', 'required for creating record actions');
44 | }
45 | }
46 |
47 | if (!isset($options['namespace'])) {
48 | throw new RequiredConfigKeyException('namespace', 'namespace');
49 | }
50 |
51 | $actionClass = $options['namespace'] . '\\Action\\Update' . $options['model'] . 'Ordering';
52 | $runner->register($actionClass, $asTemplate, [
53 | 'extends' => "\\ActionKit\\RecordAction\\UpdateOrderingRecordAction",
54 | 'properties' => [
55 | 'recordClass' => $options['namespace'] . "\\Model\\" . $options['model'],
56 | ]
57 | ]);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/ActionTemplate/UpdateOrderingRecordActionTemplateTest.php:
--------------------------------------------------------------------------------
1 | ['OrderingTest\SomeProvider']
17 | ] ],
18 | [ [
19 | 'namespace' => 'OrderingTest',
20 | ] ],
21 | [ [
22 | 'model' => 'Bar', // model's name
23 | ] ],
24 | ];
25 | }
26 |
27 |
28 | /**
29 | * @dataProvider failingArgumentProvider
30 | * @expectedException ActionKit\Exception\RequiredConfigKeyException
31 | */
32 | public function testUpdateOrderingRecordActionTemplateWithFailingArguments($arguments)
33 | {
34 | $recordClass = 'OrderingTest\Model\Foo';
35 | $className = 'OrderingTest\Action\UpdateFooOrdering';
36 |
37 | $actionTemplate = new UpdateOrderingRecordActionTemplate;
38 | $runner = new ActionRunner;
39 | $actionTemplate->register($runner, 'UpdateOrderingRecordActionTemplate', $arguments);
40 | }
41 |
42 |
43 | public function testGenerationWithRecordClassOption()
44 | {
45 | $recordClass = 'OrderingTest\Model\Foo';
46 | $className = 'OrderingTest\Action\UpdateFooOrdering';
47 |
48 | $actionTemplate = new UpdateOrderingRecordActionTemplate;
49 | $runner = new ActionRunner;
50 | $actionTemplate->register($runner, 'UpdateOrderingRecordActionTemplate', array(
51 | 'record_class' => $recordClass,
52 | ));
53 | $this->assertCount(1, $runner->getPretreatments());
54 | $this->assertNotNull($pretreatment = $runner->getActionPretreatment($className));
55 | $generatedAction = $actionTemplate->generate($className, $pretreatment);
56 | $this->assertRequireGeneratedAction($className, $generatedAction);
57 | }
58 |
59 |
60 | public function testUpdateOrderingRecordActionTemplate()
61 | {
62 | $actionTemplate = new UpdateOrderingRecordActionTemplate;
63 | $runner = new ActionRunner;
64 | $actionTemplate->register($runner, 'UpdateOrderingRecordActionTemplate', array(
65 | 'namespace' => 'OrderingTest',
66 | 'model' => 'Test2Model' // model's name
67 | ));
68 |
69 | $className = 'OrderingTest\Action\UpdateTest2ModelOrdering';
70 |
71 | $this->assertCount(1, $runner->getPretreatments());
72 | $this->assertNotNull($pretreatment = $runner->getActionPretreatment($className));
73 |
74 | $generatedAction = $actionTemplate->generate($className, $pretreatment);
75 | $this->assertRequireGeneratedAction($className, $generatedAction);
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/src/ActionTrait/RoleChecker.php:
--------------------------------------------------------------------------------
1 | deny("Anonymous user is not allowed.");
13 | }
14 |
15 | if (is_string($user)) {
16 | if (in_array($user, $this->allowedRoles)) {
17 | return $this->allow();
18 | } else {
19 | return $this->deny();
20 | }
21 | } elseif ($user instanceof MultiRoleInterface || method_exists($user, 'getRoles')) {
22 | foreach ($user->getRoles() as $role) {
23 | if (in_array($role, $this->allowedRoles)) {
24 | return $this->allow();
25 | }
26 | }
27 | return $this->deny();
28 | } else {
29 | throw new Exception("Unsupported current user object");
30 | }
31 | return $this->deny();
32 | }
33 |
34 | public function allow($message = null)
35 | {
36 | return array(true, $message ?: $this->permissionAllowedMessage());
37 | }
38 |
39 | public function deny($message = null)
40 | {
41 | return array(false, $message ?: $this->permissionDeniedMessage());
42 | }
43 |
44 | public function getAllowedRoles()
45 | {
46 | return $this->allowedRoles;
47 | }
48 |
49 | public function permissionDeniedMessage()
50 | {
51 | return 'Permission denied.';
52 | }
53 |
54 | public function permissionAllowedMessage()
55 | {
56 | return 'Permission allowed.';
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/ColumnConvertTest.php:
--------------------------------------------------------------------------------
1 | getColumn('created_at');
40 | $this->assertInstanceOf(RuntimeColumn::class, $column);
41 |
42 | // assert the input
43 | $this->assertInstanceOf(Closure::class, $column->default);
44 |
45 | $param = ColumnConvert::toParam($column, new CreateOrder);
46 | $this->assertInstanceOf(Param::class, $param);
47 | $this->assertInstanceOf(DateTime::class, $param->getDefaultValue());
48 | }
49 |
50 | public function testConvertColumnNotNull()
51 | {
52 | $schema = Order::getSchema();
53 | $column = $schema->getColumn('amount');
54 | $this->assertInstanceOf(RuntimeColumn::class, $column);
55 |
56 | $param = ColumnConvert::toParam($column, new CreateOrder);
57 | $this->assertInstanceOf(Param::class, $param);
58 |
59 | $this->assertTrue($param->required);
60 | }
61 |
62 | public function testConvertCurrentTimestampIntoPHPDateTimeObject()
63 | {
64 | $schema = Order::getSchema();
65 | $column = $schema->getColumn('updated_at');
66 | $this->assertInstanceOf(RuntimeColumn::class, $column);
67 |
68 | // assert the input
69 | $this->assertInstanceOf(Raw::class, $column->default);
70 | $this->assertEquals('CURRENT_TIMESTAMP', $column->default->__toString());
71 |
72 |
73 | $param = ColumnConvert::toParam($column, new CreateOrder);
74 | $this->assertInstanceOf(Param::class, $param);
75 | $this->assertEquals('DateTime', $param->isa);
76 | $this->assertNull($param->getDefaultValue());
77 | }
78 |
79 | public function testColumnConvert()
80 | {
81 | $schema = Order::getSchema();
82 | $this->assertNotNull($schema);
83 |
84 | $order = new Order;
85 | $action = ColumnConvert::convertSchemaToAction($schema, $order);
86 | $this->assertNotNull($action);
87 | $this->assertInstanceOf(Action::class, $action);
88 | $this->assertInstanceOf(RecordAction\BaseRecordAction::class, $action);
89 |
90 | $view = $action->asView(View\StackView::class);
91 | $this->assertNotNull($view);
92 | $this->assertInstanceOf(View\StackView::class, $view);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Csrf/CsrfArrayStorage.php:
--------------------------------------------------------------------------------
1 | tokenKey = $key;
19 | $this->array = array();
20 | }
21 |
22 | public function getTokenKey()
23 | {
24 | return $this->key;
25 | }
26 |
27 | public function store(CsrfToken $token, $key = null)
28 | {
29 | $this->array[$key ?: $this->tokenKey] = serialize($token);
30 | }
31 |
32 | public function exists($key = null)
33 | {
34 | return isset($this->array[$key ?: $this->tokenKey]);
35 | }
36 |
37 | /**
38 | * Load a CSRF token from session by specific session key
39 | *
40 | * @param string $key
41 | * @return CsrfToken
42 | */
43 | public function load($key = null)
44 | {
45 | if (isset($this->array[$key ?: $this->tokenKey])) {
46 | return unserialize($this->array[$key ?: $this->tokenKey]);
47 | }
48 | }
49 |
50 | public function drop($key = null)
51 | {
52 | unset($this->array[$key ?: $this->tokenKey]);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Csrf/CsrfSessionStorage.php:
--------------------------------------------------------------------------------
1 | sessionKey = $sessionKey;
17 | }
18 |
19 | public function getSessionKey()
20 | {
21 | return $this->sessionKey;
22 | }
23 |
24 | public function store(CsrfToken $token, $key = null)
25 | {
26 | $_SESSION[$key ?: $this->sessionKey] = serialize($token);
27 | }
28 |
29 | public function exists($key = null)
30 | {
31 | return isset($_SESSION[$key ?: $this->sessionKey]);
32 | }
33 |
34 | /**
35 | * Load a CSRF token from session by specific session key
36 | *
37 | * @param string $key
38 | * @return CsrfToken
39 | */
40 | public function load($key = null)
41 | {
42 | if (isset($_SESSION[$key ?: $this->sessionKey])) {
43 | return unserialize($_SESSION[$key ?: $this->sessionKey]);
44 | }
45 | }
46 |
47 | public function drop($key = null)
48 | {
49 | unset($_SESSION[$key ?: $this->sessionKey]);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Csrf/CsrfStorage.php:
--------------------------------------------------------------------------------
1 | id = $id;
41 | $this->ttl = $ttl;
42 | $this->timestamp = time(); // created_at
43 | $this->salt = $this->randomString(32);
44 | $this->extra = $extra;
45 | $this->hash = $this->generateChecksum();
46 | }
47 |
48 | public function isExpired($now)
49 | {
50 | if ($this->ttl != 0) {
51 | return ($now - $this->timestamp) > $this->ttl;
52 | }
53 | return false;
54 | }
55 |
56 | public function toPublicArray()
57 | {
58 | return [
59 | 'hash' => $this->hash,
60 | 'ttl' => $this->ttl,
61 | 'timestamp' => $this->timestamp,
62 | 'created_at' => date('c', $this->timestamp),
63 | 'expired_at' => date('c', $this->timestamp + $this->ttl),
64 | ];
65 | }
66 |
67 | protected function randomString($len = 10)
68 | {
69 | $rString = '';
70 | $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
71 | $charsTotal = strlen($chars);
72 | for ($i = 0; $i < $len; $i++) {
73 | $rInt = (integer) mt_rand(0, $charsTotal);
74 | $rString .= substr($chars, $rInt, 1);
75 | }
76 | return $rString;
77 | }
78 |
79 | /**
80 | * Output token hash
81 | *
82 | * @return string
83 | */
84 | public function __toString()
85 | {
86 | return $this->hash;
87 | }
88 |
89 | /**
90 | * generateChecksum generates sha1 checksum and stored in base64 format string
91 | *
92 | * @return string
93 | */
94 | protected function generateChecksum()
95 | {
96 | return base64_encode(sha1(serialize($this)));
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Csrf/CsrfTokenProvider.php:
--------------------------------------------------------------------------------
1 | storage = $storage;
17 | }
18 |
19 | public function setTtl($seconds)
20 | {
21 | $this->ttl = $seconds;
22 | }
23 |
24 | /**
25 | * Generate a CSRF token and save the token into the session with the
26 | * current session key.
27 | *
28 | * @param integer $ttl time to live seconds
29 | *
30 | * @return CsrfToken
31 | */
32 | public function generateToken()
33 | {
34 | $token = new CsrfToken(session_id(), $this->ttl, [
35 | 'session_id' => session_id(),
36 | 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0',
37 | ]);
38 | return $token;
39 | }
40 |
41 | public function loadCurrentToken($refresh = false)
42 | {
43 | if ($refresh) {
44 | $token = $this->generateToken();
45 | $this->storage->store($token);
46 | }
47 | if ($token = $this->storage->load()) {
48 | return $token;
49 | }
50 | $token = $this->generateToken();
51 | $this->storage->store($token);
52 | return $token;
53 | }
54 |
55 | public function getStorage()
56 | {
57 | return $this->storage;
58 | }
59 |
60 | /**
61 | * Verify incoming csrf token
62 | *
63 | * @param string $insecureTokenHash
64 | * @param integer $requestTime
65 | * @return boolean
66 | */
67 | public function isValidToken($insecureTokenHash, $requestTime)
68 | {
69 | // Load the token object from the storage (session storage)
70 | $token = $this->storage->load();
71 | if ($token) {
72 | if ($token->isExpired($requestTime)) {
73 | return false;
74 | }
75 | $generatedHash = $token->hash;
76 | if ($insecureTokenHash !== null && $generatedHash !== null) {
77 | return $insecureTokenHash === $generatedHash;
78 | }
79 | }
80 | return false;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Csrf/CsrfTokenProviderTest.php:
--------------------------------------------------------------------------------
1 | provider = new CsrfTokenProvider($storage);
17 | $this->provider->setTtl(1800);
18 | $this->token = $this->provider->loadCurrentToken(true);
19 | }
20 |
21 | public function testArrayStorage()
22 | {
23 | $storage = new CsrfArrayStorage('__csrf_token');
24 | $provider = new CsrfTokenProvider($storage);
25 | $token = $provider->loadCurrentToken();
26 | $this->assertTrue($storage->exists(), '__csrf_token should exists');
27 | }
28 |
29 | public function testGenerateToken()
30 | {
31 | $token = $this->token;
32 | $this->assertNotNull($token);
33 | $this->assertEquals(1800, $token->ttl);
34 | $this->assertNotNull($token->hash);
35 | $this->assertNotNull($this->provider->loadCurrentToken());
36 | }
37 |
38 | public function testVerifyToken()
39 | {
40 | $ret = $this->provider->isValidToken($this->token->hash, time());
41 | $this->assertTrue($ret);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Exception/ActionException.php:
--------------------------------------------------------------------------------
1 | action = $action;
14 | parent::__construct($msg);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Exception/ActionNotFoundException.php:
--------------------------------------------------------------------------------
1 | actionClass = $actionClass;
13 | parent::__construct("Action class '$actionClass' not found, you might need to setup action autoloader or register the action template");
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Exception/InvalidActionNameException.php:
--------------------------------------------------------------------------------
1 | configKey = $key;
20 | $this->configDesc = $desc;
21 | parent::__construct("Config '$key' is required. [$desc]");
22 | }
23 |
24 | public function getConfigKey()
25 | {
26 | return $this->configKey;
27 | }
28 |
29 | public function getConfigDesc()
30 | {
31 | return $this->configDesc;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Exception/UnableToCreateActionException.php:
--------------------------------------------------------------------------------
1 | column = $column;
33 |
34 | if (isset($options['wrapperClass'])) {
35 | $this->wrapperClass = $options['wrapperClass'];
36 | }
37 |
38 | if (isset($options['labelClass'])) {
39 | $this->labelClass = $options['labelClass'];
40 | }
41 |
42 | if (isset($options['inputWrapperClass'])) {
43 | $this->inputWrapperClass = $options['inputWrapperClass'];
44 | }
45 | }
46 |
47 | public function setWidgetAttributes($attrs)
48 | {
49 | $this->widgetAttributes = $attrs;
50 | }
51 |
52 | /**
53 | *
54 | * Build a div structure like this:
55 |
56 |
62 | */
63 | public function build()
64 | {
65 | $wrapper = new Div(array(
66 | 'class' => $this->wrapperClass,
67 | ));
68 |
69 | $widget = $this->column->createWidget(null, $this->widgetAttributes);
70 | $widget->addClass('form-control');
71 | $widgetId = $widget->getSerialId();
72 |
73 | $wrapper->addClass($widget->convertClassToCssClassName());
74 |
75 | /*
76 | if ( $widget instanceof CheckboxInput ) {
77 | $label = $this->column->createLabelWidget();
78 | $label->prepend($widget);
79 | $wrapper->append($label);
80 | */
81 | if ($widget instanceof HiddenInput) {
82 |
83 | // $wrapper->append( $widget );
84 | return $widget;
85 | } else {
86 | $inputDiv = new Div(array('class' => $this->inputWrapperClass));
87 | $inputDiv->append($widget);
88 |
89 | $label = $this->column->createLabelWidget();
90 | $label->setAttributes(array('class' => $this->labelClass, 'for' => $widgetId));
91 |
92 | $wrapper->append($label);
93 | $wrapper->append($inputDiv);
94 |
95 | if ($this->column->hint) {
96 | $hintEl = new Span(array('class' => $this->hintClass ));
97 | $hintEl->append($this->column->hint);
98 | $inputDiv->append($hintEl);
99 | }
100 | }
101 | return $wrapper;
102 | }
103 |
104 | public function render()
105 | {
106 | return $this->build()->render();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/FieldView/BootstrapFieldViewTest.php:
--------------------------------------------------------------------------------
1 | required(1);
14 |
15 | $field = new BootstrapFieldView($column, array(
16 | 'labelClass' => 'col-lg-2',
17 | 'inputWrapperClass' => 'col-lg-10'
18 | ));
19 | $field->setWidgetAttributes(array(
20 | 'placeholder' => "ariel123",
21 | 'readoly' => "",
22 | 'autocomplete' => "off",
23 | ));
24 | $html = $field->render();
25 |
26 | $xml = simplexml_load_string($html);
27 |
28 | $this->assertEquals('form-group formkit-widget-textinput', (string)$xml->attributes()['class']);
29 | $label = $xml->label;
30 | $this->assertEquals('col-lg-2', (string)$label->attributes()['class']);
31 | $this->assertEquals('* Account', (string)$label[0]);
32 |
33 | $div = $xml->div;
34 | $this->assertEquals('col-lg-10', (string)$div->attributes()['class']);
35 | $input = $div->input->attributes();
36 | $this->assertEquals('formkit-widget formkit-widget-text form-control', $input->class);
37 | $this->assertEquals('account', $input->name);
38 | $this->assertEquals('ariel123', $input->placeholder);
39 | $this->assertEquals('off', $input->autocomplete);
40 | }
41 | }
42 |
43 | /*
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | */
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/FieldView/DivFieldView.php:
--------------------------------------------------------------------------------
1 | column = $column;
33 | }
34 |
35 | public function setWidgetAttributes($attrs)
36 | {
37 | $this->widgetAttributes = $attrs;
38 | }
39 |
40 | /**
41 | *
42 | * Build a div structure like this:
43 |
44 |
45 |
{% trans 'Role' %}
46 |
47 |
51 |
52 |
53 | */
54 | public function build()
55 | {
56 | $wrapper = new Div(array(
57 | 'class' => $this->wrapperClass,
58 | ));
59 |
60 |
61 | $widget = $this->column->createWidget(null, $this->widgetAttributes);
62 | $wrapper->addClass($widget->convertClassToCssClassName());
63 |
64 | if ($widget instanceof CheckboxInput) {
65 | $label = $this->column->createLabelWidget();
66 | $label->prepend($widget);
67 | $wrapper->append($label);
68 | } elseif ($widget instanceof HiddenInput) {
69 | $wrapper->append($widget);
70 | } else {
71 | $labelDiv = new Div(array( 'class' => $this->labelClass ));
72 | $inputDiv = new Div(array( 'class' => $this->inputClass ));
73 | $inputDiv->append($widget);
74 | $label = $this->column->createLabelWidget();
75 | $labelDiv->append($label);
76 | $wrapper->append($labelDiv);
77 | $wrapper->append($inputDiv);
78 | if ($this->column->hint) {
79 | $hintEl = new Span(array( 'class' => $this->hintClass ));
80 | $hintEl->append($this->column->hint);
81 | $wrapper->append($hintEl);
82 | }
83 | }
84 | return $wrapper;
85 | }
86 |
87 | public function render()
88 | {
89 | return $this->build()->render();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/FieldView/DivFieldViewTest.php:
--------------------------------------------------------------------------------
1 | required(1);
14 | $column->default('John');
15 |
16 | $field = new DivFieldView($column);
17 | $html = $field->render();
18 |
19 | $xml = simplexml_load_string($html);
20 | $this->assertEquals('v-field formkit-widget-textinput', (string)$xml->attributes()['class']);
21 | $div = $xml->div[0];
22 | $this->assertEquals('label', (string)$div->attributes()['class']);
23 | $this->assertEquals('* Name', (string)$div->label);
24 |
25 | $label = $div->label;
26 | $this->assertEquals('formkit-widget formkit-label formkit-widget-label', (string)$label->attributes()['class']);
27 |
28 | $div = $xml->div[1];
29 | $this->assertEquals('input', (string)$div->attributes()['class']);
30 | $input = $div->input->attributes();
31 | $this->assertEquals('formkit-widget formkit-widget-text', $input->class);
32 | $this->assertEquals('name', $input->name);
33 | $this->assertEquals('text', $input->type);
34 | $this->assertEquals('John', $input->value);
35 | }
36 | }
37 |
38 | /*
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | */
48 |
49 |
--------------------------------------------------------------------------------
/src/GeneratedAction.php:
--------------------------------------------------------------------------------
1 | className = $className;
19 | $this->code = $code;
20 | $this->object = $object;
21 | }
22 |
23 | public function requireAt($path)
24 | {
25 | $this->writeTo($path);
26 | require $path;
27 | }
28 |
29 | public function writeTo($path)
30 | {
31 | if (false === file_put_contents($path, $this->code)) {
32 | throw new UnableToWriteCacheException("Can not write action class cache file: $cacheFile");
33 | }
34 | }
35 |
36 | public function getRequiredPath()
37 | {
38 | return $this->requiredPath;
39 | }
40 |
41 | public function load()
42 | {
43 | $this->requiredPath = $tmpname = tempnam('/tmp', md5($this->className));
44 | $this->requireAt($tmpname);
45 | return $tmpname;
46 | }
47 |
48 | public function getPsr4ClassPath($namespacePrefix)
49 | {
50 | $class = ltrim($this->className, '\\');
51 | $class = str_replace($namespacePrefix, '', $class);
52 | return str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
53 | }
54 |
55 | public function getPsrClassPath()
56 | {
57 | return str_replace('\\', DIRECTORY_SEPARATOR, ltrim($this->className, '\\')) . '.php';
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Loggable.php:
--------------------------------------------------------------------------------
1 | 'File field %1 is required.',
7 | 'param.required' => 'Field %1 is required.',
8 | 'validation.error' => 'Please check your input.',
9 | 'csrf.token_expired' => 'CSRF token is expired, please refresh your page.',
10 | 'csrf.token_mismatch' => 'CSRF Token mismatched.',
11 | 'csrf.token_invalid' => 'CSRF token invalid.',
12 |
13 | // record action messages
14 | 'record_action.primary_key_is_required' => 'Updating record requires primary key value.',
15 | 'record_action.load_failed' => 'Can not load record.',
16 | 'record_action.record_not_found' => '%1 record not found.',
17 | 'record_action.validation_error' => '%1 validation failed.',
18 | 'record_action.successful_update' => '%1 record is updated successfully.',
19 | 'record_action.failed_update' => '%1 record update failed.',
20 |
21 | 'bulk_delete.successful_delete' => '%1 items were deleted successfully.',
22 |
23 | 'record_action.successful_create' => "%1 record is created successfully.",
24 | 'record_action.failed_create' => "%1 record create failed.",
25 |
26 | 'record_action.successful_delete' => "%1 record is deleted successfully.",
27 | 'record_action.failed_delete' => "%1 record delete failed.",
28 | ];
29 |
--------------------------------------------------------------------------------
/src/Messages/zh_TW.php:
--------------------------------------------------------------------------------
1 | '請上傳檔案欄位 %1',
7 | 'param.required' => '請輸入欄位 %1',
8 | 'validation.error' => '請檢查表單欄位是否正確。',
9 | 'csrf.token_expired' => '跨站請求認證碼逾期,請重新載入頁面',
10 | 'csrf.token_mismatch' => '跨站請求認證碼不符合,請重試',
11 | 'csrf.token_invalid' => '跨站請求認證碼不合法',
12 |
13 | 'record_action.primary_key_is_required' => '需要鍵值',
14 | 'record_action.load_failed' => '無法載入資料',
15 | 'record_action.record_not_found' => '%1 找不到資料',
16 | 'record_action.validation_error' => '%1 表單資料驗證失敗',
17 | 'record_action.successful_update' => '%1 成功更新',
18 | 'record_action.failed_update' => '%1 更新失敗.',
19 |
20 | /* bulk delete action */
21 | 'bulk_delete.successful_delete' => '%1 個項目已成功刪除',
22 |
23 |
24 | // record action messages
25 | 'record_action.successful_create' => "%1 成功建立",
26 | 'record_action.failed_create' => "%1 無法建立",
27 |
28 | 'record_action.successful_delete' => "%1 成功刪除",
29 | 'record_action.failed_delete' => "%1 無法刪除",
30 | ];
31 |
--------------------------------------------------------------------------------
/src/MixinAction.php:
--------------------------------------------------------------------------------
1 | _action = $action;
16 | }
17 |
18 | public function preinit()
19 | {
20 | }
21 |
22 | public function postinit()
23 | {
24 | }
25 |
26 | public function beforeRun()
27 | {
28 | return true;
29 | }
30 |
31 | public function afterRun()
32 | {
33 | return true;
34 | }
35 |
36 | public function run()
37 | {
38 | return true;
39 | }
40 |
41 | public function schema()
42 | {
43 | /*
44 | $this->param('...');
45 | */
46 | }
47 |
48 | public function __get($k)
49 | {
50 | return $this->_action->$k;
51 | }
52 |
53 | public function __set($k, $v)
54 | {
55 | return $this->_action->$k = $v;
56 | }
57 |
58 | public function __call($m, $args)
59 | {
60 | if (method_exists($this->_action, $m)) {
61 | return call_user_func_array(array($this->_action,$m), $args);
62 | } else {
63 | throw new RuntimeException("Method $m is not defined in " . get_class($this));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Param/Image/CropAndScaleResize.php:
--------------------------------------------------------------------------------
1 | param = $param;
13 | }
14 |
15 | public function label()
16 | {
17 | return _('Crop Then Scale');
18 | }
19 |
20 | public function resize($targetPath)
21 | {
22 | if (isset($this->param->size['height'])
23 | && isset($this->param->size['width'])) {
24 | $h = intval($this->param->size['height']);
25 | $w = intval($this->param->size['width']);
26 | $image = new ImageProcessor;
27 | $image->load($targetPath);
28 |
29 | $size = getimagesize($targetPath);
30 | if ($size[0] > $w || $size[1] > $h) {
31 | $image->cropOuterAndScale($w, $h);
32 | }
33 | $image->save($targetPath, null, $this->param->compression);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Param/Image/MaxHeightResize.php:
--------------------------------------------------------------------------------
1 | param = $param;
13 | }
14 |
15 | public function label()
16 | {
17 | return _('Fit To Height');
18 | }
19 |
20 | public function resize($targetPath)
21 | {
22 | if ($this->param->resizeHeight) {
23 | $maxHeight = $this->param->resizeHeight;
24 | } elseif (isset($this->param->size['height'])) {
25 | $maxHeight = $this->param->size['height'];
26 | }
27 |
28 |
29 | if ($maxHeight) {
30 | $image = new ImageProcessor;
31 | $image->load($targetPath);
32 |
33 | // we should only resize image file only when size is changed.
34 | if ($image->getHeight() > $maxHeight) {
35 | $image->resizeToHeight($maxHeight);
36 | // (filename, image type, jpeg compression, permissions);
37 | $image->save($targetPath, null, $this->param->compression);
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Param/Image/MaxWidthResize.php:
--------------------------------------------------------------------------------
1 | param = $param;
14 | }
15 |
16 | public function label()
17 | {
18 | return _('Fit To Width');
19 | }
20 |
21 | public function resize($targetPath)
22 | {
23 | if ($this->param->resizeWidth) {
24 | $maxWidth = $this->param->resizeWidth;
25 | } elseif (isset($this->param->size['width'])) {
26 | $maxWidth = $this->param->size['width'];
27 | }
28 |
29 |
30 | if ($maxWidth) {
31 | $image = new ImageProcessor;
32 | $image->load($targetPath);
33 |
34 | // we should only resize image file only when size is changed.
35 | if ($image->getWidth() > $maxWidth) {
36 | $image->resizeToWidth($maxWidth);
37 | // (filename, image type, jpeg compression, permissions);
38 | $image->save($targetPath, null, $this->param->compression);
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Param/Image/ScaleResize.php:
--------------------------------------------------------------------------------
1 | param = $param;
13 | }
14 |
15 | public function label()
16 | {
17 | return _('Scale');
18 | }
19 |
20 | public function resize($targetPath)
21 | {
22 | if (isset($this->param->size['height'])
23 | && isset($this->param->size['width'])) {
24 | $h = $this->param->size['height'];
25 | $w = $this->param->size['width'];
26 |
27 | $image = new ImageProcessor;
28 | $image->load($targetPath);
29 | $image->resize($w, $h);
30 |
31 | // (filename, image type, jpeg compression, permissions);
32 | $image->save($targetPath, null, $this->param->compression);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Param/ImageParamTest.php:
--------------------------------------------------------------------------------
1 | assertNotNull($image->size(['width' => 100, 'height' => 200]));
13 | $this->assertNotNull($image->autoResize(false));
14 | $this->assertNotNull($image->autoResize(true));
15 | }
16 |
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/src/Param/ImageResizer.php:
--------------------------------------------------------------------------------
1 | param('image','Image')
21 | * ->validExtensions('jpg','png');
22 | * }
23 | *
24 | */
25 |
26 | class ImageResizer
27 | {
28 | public static $classes = array(
29 | 'max_width' => 'ActionKit\\Param\\Image\\MaxWidthResize',
30 | 'max_height' => 'ActionKit\\Param\\Image\\MaxHeightResize',
31 | 'scale' => 'ActionKit\\Param\\Image\\ScaleResize',
32 | 'crop_and_scale' => 'ActionKit\\Param\\Image\\CropAndScaleResize',
33 | );
34 |
35 | public static function create($type, Param $param)
36 | {
37 | if (!isset(self::$classes[$type])) {
38 | throw new Exception("Image Resize Type '$type' is undefined.");
39 | }
40 | $c = self::$classes[$type];
41 | return new $c($param);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Param/ParamTest.php:
--------------------------------------------------------------------------------
1 | required();
14 | $this->assertNotNull($p->required);
15 | }
16 |
17 | public function testDefaultValueByScalar()
18 | {
19 | $p = new Param('name', new Action);
20 | $p->default('John');
21 | $this->assertEquals('John', $p->getDefaultValue());
22 | }
23 |
24 | public function testDefaultValueByClosure()
25 | {
26 | $p = new Param('created_at', new Action);
27 | $p->default(function() {
28 | return new DateTime;
29 | });
30 | $this->assertInstanceOf(DateTime::class, $p->getDefaultValue());
31 | }
32 |
33 | public function testValidValuesByArray()
34 | {
35 | $p = new Param('type', new Action);
36 | $p->validValues([
37 | 'user',
38 | 'admin',
39 | 'guest',
40 | ]);
41 | $validValues = $p->getValidValues();
42 | $this->assertEquals([
43 | 'user',
44 | 'admin',
45 | 'guest',
46 | ], $validValues);
47 | }
48 |
49 |
50 | public function testValidValuesByClosure()
51 | {
52 | $p = new Param('type', new Action);
53 | $p->validValues(function() {
54 | return [ 'user', 'admin', 'guest' ];
55 | });
56 | $this->assertTrue($p->isValidValue('user'));
57 | $this->assertFalse($p->isValidValue('foo'));
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/src/RecordAction/BulkDeleteRecordAction.php:
--------------------------------------------------------------------------------
1 | loadRecords();
16 | foreach ($records as $record) {
17 | $delete = $record->asDeleteAction();
18 | $delete->run();
19 | }
20 | $msg = $this->messagePool->translate('bulk_delete.successful_delete', count($records));
21 | return $this->success($msg);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/RecordAction/BulkRecordAction.php:
--------------------------------------------------------------------------------
1 | param('items');
20 | }
21 |
22 | public function runValidate()
23 | {
24 | if (isset($this->args['items'])) {
25 | return true; // no error
26 | }
27 | return false;
28 | }
29 |
30 | // TODO: we should use
31 | // collection and where id in (1,2,3) to improve performance.
32 | public function loadRecords()
33 | {
34 | $itemIds = $this->arg('items');
35 | $records = array();
36 | foreach ($itemIds as $id) {
37 | $record = new $this->recordClass;
38 | $record->load((int) $id);
39 | if ($record->id) {
40 | $records[] = $record;
41 | }
42 | }
43 | return $records;
44 | }
45 |
46 | public function run()
47 | {
48 | $records = $this->loadRecords();
49 | foreach ($records as $record) {
50 | $ret = $record->delete();
51 | }
52 |
53 | return $this->deleteSuccess($ret);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/RecordAction/BulkZhConvertRecordAction.php:
--------------------------------------------------------------------------------
1 | getData();
19 | $newArgs = array();
20 | foreach ($this->convertionKeys as $key) {
21 | if (! isset($args[$key])) {
22 | continue;
23 | }
24 | $newArgs[ $key ] = call_user_func($convertion, $args[ $key ]);
25 | }
26 | $record->update($newArgs);
27 | }
28 | }
29 |
30 | public function run()
31 | {
32 | kernel()->library->load('han-convert');
33 | $convertion = $this->arg('convertion');
34 | if (! in_array($convertion, $this->convertionFunctions)) {
35 | return $this->error('Invalid convertion method.');
36 | }
37 |
38 | $records = $this->loadRecords();
39 | $this->convertRecords($convertion, $records);
40 |
41 | return $this->success(count($records) . '個項目轉換成功');
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/RecordAction/CreateRecordAction.php:
--------------------------------------------------------------------------------
1 | recordResult = $ret = $this->recordClass::create($args);
11 | if ($ret->error) {
12 | $this->convertRecordValidation($ret);
13 | return $this->createError($ret);
14 | }
15 | $this->record = $this->recordClass::findByPrimaryKey($ret->key);
16 | $this->result->data($this->record->getData());
17 | return $this->createSuccess($ret);
18 | }
19 |
20 | protected function filterArguments(array $args)
21 | {
22 | if ($this->takeFields) {
23 | // take these fields only
24 | return array_intersect_key($args, array_fill_keys($this->takeFields, 1));
25 | } elseif ($this->filterOutFields) {
26 | return array_diff_key($args, array_fill_keys($this->filterOutFields, 1));
27 | }
28 | return $args;
29 | }
30 |
31 | /**
32 | * runValidate inherited from parent class.
33 | * */
34 | public function run()
35 | {
36 | $ret = $this->create($this->args);
37 | if ($ret === false) {
38 | return $ret;
39 | }
40 | if ($this->nested && ! empty($this->relationships)) {
41 | return $this->processSubActions();
42 | }
43 | return $ret;
44 | }
45 |
46 | public function successMessage($ret)
47 | {
48 | return $this->messagePool->translate('record_action.successful_create', $this->record->getLabel());
49 | }
50 |
51 | public function errorMessage($ret)
52 | {
53 | // XXX: should show exception message when error is found.
54 | if ($ret->exception) {
55 | return __('Can not create %1 record: %2', $this->record->getLabel(), $ret->exception->getMessage());
56 | }
57 | return $this->messagePool->translate('record_action.failed_create', $this->record->getLabel());
58 | }
59 |
60 | public function createSuccess($ret)
61 | {
62 | return $this->success($this->successMessage($ret), array(
63 | 'id' => $this->record->id
64 | ));
65 | }
66 |
67 | public function createError($ret)
68 | {
69 | return $this->error($this->errorMessage($ret));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/RecordAction/DeleteRecordAction.php:
--------------------------------------------------------------------------------
1 | record;
13 | $schema = $record->getSchema();
14 | $data = $record->getData();
15 | foreach ($data as $name => $val) {
16 | if ($val == null) {
17 | continue;
18 | }
19 | $column = $schema->getColumn($name);
20 | switch ($column->contentType) {
21 | case "ImageFile":
22 | case "File":
23 | if ($this->unlink && file_exists($val)) {
24 | unlink($val);
25 | }
26 | break;
27 | }
28 | }
29 |
30 | return $this->doDelete($this->args);
31 | }
32 |
33 | public function doDelete($args)
34 | {
35 | $backup = clone $this->record;
36 | $ret = $this->record->delete();
37 | if ($ret->success) {
38 | $this->record = $backup;
39 | return $this->deleteSuccess($ret);
40 | } else {
41 | return $this->deleteError($ret);
42 | }
43 | }
44 |
45 | /**
46 | * @inherit
47 | */
48 | public function runValidate()
49 | {
50 | if (isset($this->args['id'])) {
51 | return true;
52 | }
53 | return false;
54 | }
55 |
56 | public function successMessage($ret)
57 | {
58 | return $this->messagePool->translate('record_action.successful_delete', $this->record->getLabel());
59 | }
60 |
61 | public function errorMessage($ret)
62 | {
63 | return $this->messagePool->translate('record_action.failed_delete', $this->record->getLabel());
64 | }
65 |
66 | public function deleteSuccess($ret)
67 | {
68 | return $this->success($this->successMessage($ret), array( 'id' => $this->record->id));
69 | }
70 |
71 | public function deleteError($ret)
72 | {
73 | return $this->error($this->errorMessage($ret));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/RecordAction/UpdateOrderingRecordAction.php:
--------------------------------------------------------------------------------
1 | param('list')->isa('str');
30 | }
31 |
32 |
33 | public function loadRecord($key)
34 | {
35 | return $this->recordClass::findByPrimaryKey($key);
36 | }
37 |
38 | public function runUpdateList()
39 | {
40 | if ($this->mode !== self::MODE_INCREMENTALLY) {
41 | throw new Exception("Unsupported sort mode");
42 | }
43 | if ($orderingList = json_decode($this->arg('list'))) {
44 | foreach ($orderingList as $ordering) {
45 | $record = $this->loadRecord($ordering->record);
46 | $ret = $record->update(array( $this->targetColumn => $ordering->ordering ));
47 | if ($ret->error) {
48 | throw new Exception("Record update failed: {$ret->message}");
49 | }
50 | }
51 | }
52 | }
53 |
54 | public function run()
55 | {
56 | try {
57 | $this->runUpdateList();
58 | } catch (Exception $e) {
59 | return $this->error("Ordering Update Failed: {$e->getMessage()}");
60 | }
61 | return $this->success('排列順序已更新');
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/ResultTest.php:
--------------------------------------------------------------------------------
1 | assertNotNull($result);
11 |
12 | $result->success('Success Tset');
13 | $this->assertEquals('success', $result->type);
14 | $this->assertEquals(true, $result->isSuccess());
15 | $this->assertNotNull($result->message);
16 |
17 | $this->assertNotNull($result->error('Error Tset'));
18 | $this->assertEquals('error', $result->type);
19 | $this->assertEquals(true, $result->isError());
20 | $this->assertNotNull($result->getMessage());
21 |
22 | $this->assertNotNull($result->valid('Valid Tset'));
23 | $this->assertEquals('valid', $result->type);
24 | $this->assertEquals(true, $result->isValidation());
25 |
26 | $this->assertNotNull($result->invalid('Valid Tset'));
27 | $this->assertEquals('invalid', $result->type);
28 | $this->assertEquals(true, $result->isValidation());
29 |
30 | $this->assertNotNull($result->completion('country', 'list', ['tw', 'jp', 'us']));
31 | $this->assertEquals('completion', $result->type);
32 | $this->assertEquals(true, $result->isCompletion());
33 |
34 | $this->assertNotNull($result->desc('description'));
35 | $this->assertNotNull($result->debug('debug'));
36 |
37 | $this->assertNotNull($result->toArray());
38 | $this->assertNotNull($result->__toString());
39 | }
40 |
41 | public function testResultData()
42 | {
43 | $result = new Result;
44 | $this->assertNotNull($result->data(['data1' => 'value1', 'data2' => 'value2']));
45 | $this->assertNotNull($result->data('data1', 'value1'));
46 | $this->assertNotNull($result->addData('data2', 'value2'));
47 | $this->assertNotNull($result->mergeData(['data3' => 'value3', 'data4' => 'value4']));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/ServiceContainer.php:
--------------------------------------------------------------------------------
1 | preset();
33 | }
34 |
35 | protected function preset()
36 | {
37 | $self = $this;
38 |
39 | // the default parameter
40 | $this['locale'] = 'en';
41 |
42 | // the default cache dir
43 | $this['cache_dir'] = __DIR__ . DIRECTORY_SEPARATOR . 'Cache';
44 |
45 | $this['message_directory'] = __DIR__ . DIRECTORY_SEPARATOR . 'Messages';
46 |
47 | $this['message_pool'] = function ($c) {
48 | return new MessagePool($c['locale'], $c['message_directory']);
49 | };
50 |
51 | $this['csrf'] = function ($c) {
52 | return new CsrfTokenProvider(new CsrfSessionStorage('__csrf_token'));
53 | };
54 |
55 | // This factory will always generate new csrf token
56 | $this['csrf_token_new'] = $this->factory(function ($c) {
57 | return $c['csrf']->loadCurrentToken($refresh = true);
58 | });
59 |
60 | // Create csrf token on demain
61 | $this['csrf_token'] = $this->factory(function ($c) {
62 | $provider = $c['csrf'];
63 | // try to load csrf token in the current session
64 | $token = $provider->loadCurrentToken();
65 | if ($token == null || $token->isExpired($_SERVER['REQUEST_TIME'])) {
66 | // generate a new token
67 | return $provider->loadCurrentToken(true);
68 | }
69 | return $token;
70 | });
71 |
72 | // The default twig loader
73 | $this['twig_loader'] = function ($c) {
74 | $refClass = new ReflectionClass('ActionKit\\ActionGenerator');
75 | $templateDirectory = dirname($refClass->getFilename()) . DIRECTORY_SEPARATOR . 'Templates';
76 |
77 | // add ActionKit built-in template path
78 | $loader = new Twig_Loader_Filesystem([]);
79 | $loader->addPath($templateDirectory, 'ActionKit');
80 | return $loader;
81 | };
82 |
83 | $this['generator'] = function ($c) {
84 | return new ActionGenerator;
85 | };
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Storage/FilePath.php:
--------------------------------------------------------------------------------
1 | dirname = $info['dirname'];
23 | $this->extension = $info['extension'];
24 | $this->filename = $info['filename'];
25 | $this->basename = $info['basename'];
26 | }
27 |
28 | /**
29 | * Append a suffix to the current filename.
30 | */
31 | public function appendFilenameSuffix($suffix)
32 | {
33 | $this->filename = "{$this->filename}{$suffix}";
34 | }
35 |
36 | public function exists()
37 | {
38 | $p = $this->__toString();
39 | return file_exists($p);
40 | }
41 |
42 | public function appendFilenameTimestamp()
43 | {
44 | $timestamp = time();
45 | $this->filename = "{$this->filename}_{$timestamp}";
46 | }
47 |
48 | public function appendFilenameUniqid($prefix = null)
49 | {
50 | $uniqid = uniqid($prefix);
51 | $this->filename = "{$this->filename}_{$uniqid}";
52 | }
53 |
54 |
55 | /**
56 | * strip special charactor
57 | */
58 | public function strip()
59 | {
60 | $this->filename = preg_replace('/\W+/', '_', $this->filename);
61 | $this->filename = preg_replace('/_{2,}/', '_', $this->filename);
62 | $this->filename = preg_replace('/_+$/', '', $this->filename);
63 | }
64 |
65 |
66 | /**
67 | * The rename method returns a new FilePath to copy the instance.
68 | *
69 | * @return FilePath
70 | */
71 | public function renameAs($newfilename)
72 | {
73 | $newp = clone $this;
74 | $newp->filename = $newfilename;
75 | return $newp;
76 | }
77 |
78 | public function __toString()
79 | {
80 | return "{$this->dirname}/{$this->filename}.{$this->extension}";
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Storage/FilePathTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($p, $path->__toString());
27 | }
28 |
29 | public function testFilenameRename()
30 | {
31 | $p = new FilePath('upload/test_中文.jpg');
32 | $p2 = $p->renameAs('foo');
33 | $this->assertEquals('upload/foo.jpg', $p2->__toString());
34 | }
35 |
36 | public function testUniqid()
37 | {
38 | $p = new FilePath('upload/foo.jpg');
39 | $p->appendFilenameUniqid();
40 | $this->assertRegExp('!upload/foo_\w+.jpg!', $p->__toString());
41 | }
42 |
43 | public function testStrip()
44 | {
45 | $p = new FilePath('upload/test_(1200x300)_中文.jpg');
46 | $p->strip();
47 | $this->assertEquals('upload/test_1200x300.jpg', $p->__toString());
48 | }
49 |
50 |
51 | public function testSuffix()
52 | {
53 | $p = new FilePath('upload/test.jpg');
54 | $p->appendFilenameSuffix('_foo');
55 | $this->assertEquals('upload/test_foo.jpg', $p->__toString());
56 | }
57 |
58 | public function testExists()
59 | {
60 | $p = new FilePath('src/Action.php');
61 | $this->assertTrue($p->exists());
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/src/Storage/FileRename/Md5Rename.php:
--------------------------------------------------------------------------------
1 | renameAs($md5);
19 | return $p2->__toString();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Storage/FileRenameMethodsTest.php:
--------------------------------------------------------------------------------
1 | assertEquals('/tmp/upload/8b1a9953c4611296a827abf8c47804d7.jpg', $newp);
16 | }
17 | }
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Template.php:
--------------------------------------------------------------------------------
1 | config = $config;
23 | }
24 |
25 | public function init()
26 | {
27 | $dir = $this->getTemplateDir();
28 | if (! file_exists($dir)) {
29 | throw RuntimeException("Directory $dir for TemplateView does not exist.");
30 | }
31 | $this->loader = new Twig_Loader_Filesystem($dir);
32 | $this->environment = new Twig_Environment($this->loader, $this->config);
33 | }
34 |
35 | public function setClassDirFrom($object)
36 | {
37 | $ref = new ReflectionObject($object);
38 | return $this->_classDir = dirname($ref->getFilename());
39 | }
40 |
41 | public function getClassDir()
42 | {
43 | if ($this->_classDir) {
44 | return $this->_classDir;
45 | }
46 | return $this->setClassDirFrom($this);
47 | }
48 |
49 | public function getTemplateDir()
50 | {
51 | return $this->getClassDir() . DIRECTORY_SEPARATOR . 'Templates';
52 | }
53 |
54 |
55 | /**
56 | * $template->render('@ActionKit/index.html', array('the' => 'variables', 'go' => 'here'));
57 | */
58 | public function render($templateFile, $arguments = array())
59 | {
60 | $template = $this->environment->loadTemplate($templateFile);
61 | return $template->render($arguments);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Templates/RecordAction.html.twig:
--------------------------------------------------------------------------------
1 | assertNotNull($generatedAction);
16 | $generatedAction->load();
17 | $this->assertTrue(class_exists($className), "$className exists");
18 | }
19 |
20 | public function assertActionInvokeSuccess(Action $action)
21 | {
22 | $ret = $action->invoke();
23 | $result = $action->getResult();
24 | $this->assertTrue($ret, $result->message);
25 | $this->assertEquals('success', $result->type, $result->message);
26 | return $result;
27 | }
28 |
29 | public function assertActionInvokeFail(Action $action)
30 | {
31 | $ret = $action->invoke();
32 | $result = $action->getResult();
33 | $this->assertFalse($ret, $result->message);
34 | $this->assertEquals('error', $result->type, $result->message);
35 | return $result;
36 | }
37 |
38 | public static function assertStringEqualsFile($expectedFile, $actualString, $message = '', $canonicalize = false, $ignoreCase = false)
39 | {
40 | if (!file_exists($expectedFile)) {
41 | file_put_contents($expectedFile, $actualString);
42 | echo PHP_EOL, "Added expected file: ", $expectedFile, PHP_EOL;
43 | echo "=========================================", PHP_EOL;
44 | echo $actualString, PHP_EOL;
45 | echo "=========================================", PHP_EOL;
46 | }
47 | return parent::assertStringEqualsFile($expectedFile, $actualString, $message, $canonicalize, $ignoreCase);
48 | }
49 |
50 | public static function assertFileEquals($expectedFile, $actualFile, $message = '', $canonicalize = false, $ignoreCase = false)
51 | {
52 | if (!file_exists($expectedFile)) {
53 | copy($actualFile, $expectedFile);
54 | echo PHP_EOL, "Added expected file: ", $expectedFile, PHP_EOL;
55 | echo "=========================================", PHP_EOL;
56 | echo file_get_contents($expectedFile), PHP_EOL;
57 | echo "=========================================", PHP_EOL;
58 | }
59 | return parent::assertFileEquals($expectedFile, $actualFile, $message, $canonicalize, $ignoreCase);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Testing/ActionTestCase.php:
--------------------------------------------------------------------------------
1 | getType() : null;
57 | if (!$filetype) {
58 | $mt = new MimeTypes;
59 | $filetype = $mt->resolveMimeType($path);
60 | }
61 | $pathinfo = pathinfo($path);
62 | $file = array(
63 | 'name' => $pathinfo['basename'],
64 | 'tmp_name' => $path,
65 | 'type' => $filetype,
66 | 'saved_path' => $path,
67 | 'size' => filesize($path)
68 | );
69 | return $file;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/ValueType/BaseType.php:
--------------------------------------------------------------------------------
1 | assertSame($success, $bool->test($input));
34 | $this->assertSame($expected, $bool->parse($input));
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/ValueType/DateTimeType.php:
--------------------------------------------------------------------------------
1 | format(DateTime::ATOM);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/ValueType/DateTimeTypeTest.php:
--------------------------------------------------------------------------------
1 | assertSame($expected, $type->test($input));
26 | }
27 |
28 | public function testDateTimeTypeParse()
29 | {
30 | $bool = new DateTimeType;
31 | $this->assertNotNull($bool->parse('2015-01-01'));
32 | $this->assertNotNull($bool->parse(date('c')));
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/ValueType/DirType.php:
--------------------------------------------------------------------------------
1 | getPathname();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ValueType/EmailType.php:
--------------------------------------------------------------------------------
1 | getPathname();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ValueType/IntType.php:
--------------------------------------------------------------------------------
1 | assertSame($success, $bool->test($input));
32 | $this->assertSame($expect, $bool->parse($input));
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/src/ValueType/IpType.php:
--------------------------------------------------------------------------------
1 | assertNotNull(new IpType );
10 | $this->assertNotNull(new Ipv4Type );
11 | $this->assertNotNull(new Ipv6Type );
12 | }
13 |
14 | public function testIpType()
15 | {
16 | $ip = new IpType;
17 | $this->assertTrue( $ip->test('192.168.25.58') );
18 | $this->assertTrue( $ip->test('2607:f0d0:1002:51::4') );
19 | $this->assertTrue( $ip->test('::1') );
20 | $this->assertFalse($ip->test('10.10.15.10/16'));
21 | }
22 |
23 | public function testIpv4Type()
24 | {
25 | $ipv4 = new Ipv4Type;
26 | $this->assertTrue( $ipv4->test('192.168.25.58') );
27 | $this->assertTrue( $ipv4->test('8.8.8.8') );
28 | $this->assertFalse($ipv4->test('2607:f0d0:1002:51::4'));
29 | }
30 |
31 | public function testIpv6Type()
32 | {
33 | $ipv6 = new Ipv6Type;
34 | $this->assertTrue($ipv6->test('2607:f0d0:1002:51::4') );
35 | $this->assertTrue($ipv6->test('2607:f0d0:1002:0051:0000:0000:0000:0004') );
36 | $this->assertFalse($ipv6->test('192.168.25.58'));
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/src/ValueType/Ipv4Type.php:
--------------------------------------------------------------------------------
1 | "bar" ], true],
14 | ["", null, true],
15 | ];
16 | }
17 |
18 | /**
19 | * @dataProvider jsonDataProvider
20 | */
21 | public function testJsonType($input, $expected, $success)
22 | {
23 | $type = new JsonType;
24 | $this->assertSame($success, $type->test($input));
25 |
26 | if ($expected instanceof \Object) {
27 | $this->assertSame($expected, $type->parse($input));
28 | } else {
29 | $this->assertEquals($expected, $type->parse($input));
30 | }
31 | }
32 | }
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/ValueType/NumType.php:
--------------------------------------------------------------------------------
1 | getPathname();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/ValueType/RegexType.php:
--------------------------------------------------------------------------------
1 | option)) {
12 | return false;
13 | }
14 | $pm = preg_match($this->option, $value);
15 | if ($pm == 0) {
16 | $pm = false;
17 | }
18 |
19 | return $pm;
20 | }
21 |
22 | public function parse($value)
23 | {
24 | return strval($value);
25 | }
26 |
27 | public function deflate($value)
28 | {
29 | return $value;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ValueType/StrType.php:
--------------------------------------------------------------------------------
1 | assertNotNull(new BoolType);
10 | $this->assertNotNull(new StrType);
11 | $this->assertNotNull(new FileType );
12 | $this->assertNotNull(new NumType );
13 | $this->assertNotNull(new UrlType );
14 | $this->assertNotNull(new IpType );
15 | $this->assertNotNull(new Ipv4Type);
16 | $this->assertNotNull(new Ipv6Type);
17 | $this->assertNotNull(new EmailType);
18 | $this->assertNotNull(new PathType);
19 | }
20 |
21 | public function testBoolType()
22 | {
23 | $bool = new BoolType;
24 | $this->assertTrue( $bool->test('true') );
25 | $this->assertTrue( $bool->test('false') );
26 | $this->assertTrue( $bool->test('0') );
27 | $this->assertTrue( $bool->test('1') );
28 | $this->assertFalse( $bool->test('foo') );
29 | $this->assertFalse( $bool->test('123') );
30 | }
31 |
32 | public function testPathType()
33 | {
34 | $url = new PathType;
35 | $this->assertTrue( $url->test('tests') );
36 | $this->assertTrue($url->test('composer.json') );
37 | $this->assertFalse($url->test('foo/bar'));
38 | }
39 |
40 | public function testUrlType()
41 | {
42 | $url = new UrlType;
43 | $this->assertTrue($url->test('http://t'));
44 | $this->assertTrue($url->test('http://t.c'));
45 | $this->assertFalse($url->test('t.c'));
46 | }
47 |
48 | public function testIpType()
49 | {
50 | $ip = new IpType;
51 | $this->assertTrue( $ip->test('192.168.25.58') );
52 | $this->assertTrue( $ip->test('2607:f0d0:1002:51::4') );
53 | $this->assertTrue( $ip->test('::1') );
54 | $this->assertFalse($ip->test('10.10.15.10/16'));
55 | }
56 |
57 | public function testIpv4Type()
58 | {
59 | $ipv4 = new Ipv4Type;
60 | $this->assertTrue( $ipv4->test('192.168.25.58') );
61 | $this->assertTrue( $ipv4->test('8.8.8.8') );
62 | $this->assertFalse($ipv4->test('2607:f0d0:1002:51::4'));
63 | }
64 |
65 | public function testIpv6Type()
66 | {
67 | $ipv6 = new Ipv6Type;
68 | $this->assertTrue($ipv6->test('2607:f0d0:1002:51::4') );
69 | $this->assertTrue($ipv6->test('2607:f0d0:1002:0051:0000:0000:0000:0004') );
70 | $this->assertFalse($ipv6->test('192.168.25.58'));
71 | }
72 |
73 | public function testEmailType()
74 | {
75 | $email = new EmailType;
76 | $this->assertTrue($email->test('test@gmail.com'));
77 | $this->assertFalse($email->test('test@test'));
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/src/View/StackViewTest.php:
--------------------------------------------------------------------------------
1 | param('first_name')
14 | ->label('First name')
15 | ->renderAs('TextInput');
16 |
17 | $this->param('last_name')
18 | ->label('Last name')
19 | ->renderAs('TextInput');
20 |
21 | $this->param('role')
22 | ->label('Role')
23 | ->validValues(array( 'Admin', 'User' ))
24 | ->renderAs('SelectInput');
25 | }
26 |
27 | public function run()
28 | {
29 | return $this->success('Created!');
30 | }
31 | }
32 |
33 | /**
34 | * @group maghead
35 | */
36 | use Maghead\Testing\ModelTestCase;
37 | use ProductBundle\Model\ProductSchema;
38 | use ProductBundle\Model\Category;
39 | use ProductBundle\Model\CategorySchema;
40 | use ProductBundle\Action\CreateProduct;
41 |
42 | class StackViewTest extends ModelTestCase
43 | {
44 | public function models()
45 | {
46 | return [new ProductSchema, new CategorySchema];
47 | }
48 |
49 | public function testNestedView()
50 | {
51 | $c = new Category;
52 | $c->create(array( 'name' => 'Foo' ));
53 |
54 | $action = new CreateProduct;
55 | $view = $action->asView(StackView::class, array(
56 | 'no_form' => true,
57 | 'no_layout' => true,
58 | ));
59 | $this->assertNotNull($view);
60 |
61 | $view->buildRelationalActionViewForExistingRecords('categories');
62 | $html = $view->getContainer()->render();
63 | $this->assertNotNull($html);
64 |
65 | # $dom = new DOMDocument;
66 | # $dom->load($html);
67 |
68 | $c->delete();
69 | }
70 |
71 | public function testBasicView()
72 | {
73 | $action = new CreateUserAction;
74 | $this->assertNotNull($action);
75 |
76 | $view = new StackView($action);
77 | $this->assertNotNull($view);
78 |
79 | $html = $view->render();
80 | $this->assertNotNull($html);
81 |
82 | $resultDom = new DOMDocument;
83 | $resultDom->loadXML($html);
84 |
85 | $finder = new DomXPath($resultDom);
86 |
87 | $nodes = $finder->query("//form");
88 | $this->assertEquals(1, $nodes->length);
89 |
90 | $nodes = $finder->query("//input");
91 | $this->assertEquals(4, $nodes->length);
92 |
93 | $nodes = $finder->query("//*[contains(@class, 'formkit-widget')]");
94 | $this->assertEquals(8, $nodes->length);
95 |
96 | $nodes = $finder->query("//*[contains(@class, 'formkit-widget-text')]");
97 | $this->assertEquals(2, $nodes->length);
98 |
99 | $nodes = $finder->query("//*[contains(@class, 'formkit-label')]");
100 | $this->assertEquals(3, $nodes->length);
101 |
102 | $nodes = $finder->query("//input[@name='last_name']");
103 | $this->assertEquals(1, $nodes->length);
104 |
105 | $nodes = $finder->query("//input[@name='first_name']");
106 | $this->assertEquals(1, $nodes->length);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/View/TemplateView.php:
--------------------------------------------------------------------------------
1 | action = $action;
20 | $this->template = new Template;
21 | $this->template->setClassDirFrom($this);
22 | $this->template->init();
23 | }
24 |
25 | /**
26 | * $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
27 | * */
28 | public function renderTemplateFile($templateFile, $arguments = array())
29 | {
30 | $arguments = array_merge(array(
31 | // the view object.
32 | 'View' => $this,
33 |
34 | // the action object.
35 | 'Action' => $this->action
36 | ), $arguments);
37 | return $this->template->render($templateFile, $arguments);
38 | }
39 |
40 | public function __toString()
41 | {
42 | return $this->render();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/View/TemplateViewTest.php:
--------------------------------------------------------------------------------
1 | assertNotNull($action);
13 |
14 | $view = new \FooTemplateView($action);
15 | $this->assertNotNull($view);
16 | $this->assertNotNull($view->render());
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/tests/ActionTrait/RoleCheckerTest.php:
--------------------------------------------------------------------------------
1 | roles = $roles;
12 | }
13 |
14 | public function getRoles()
15 | {
16 | return $this->roles;
17 | }
18 | }
19 |
20 | class MyRoleChecker
21 | {
22 |
23 | use RoleChecker;
24 |
25 | public $allowedRoles;
26 |
27 | public function __construct(array $allowedRoles)
28 | {
29 | $this->allowedRoles = $allowedRoles;
30 | }
31 |
32 | }
33 |
34 | class RoleCheckerTest extends \PHPUnit\Framework\TestCase
35 | {
36 | public function testRoleAllowedRoleByString()
37 | {
38 | $checker = new MyRoleChecker(['admin']);
39 | $ret = $checker->currentUserCan('admin', 'run', []);
40 | $this->assertTrue($ret[0]);
41 | }
42 |
43 | public function testRoleDisallowedByString()
44 | {
45 | $checker = new MyRoleChecker(['admin']);
46 | $ret = $checker->currentUserCan('foo', 'run', []);
47 | $this->assertFalse($ret[0]);
48 | }
49 |
50 | public function testRoleAnonymousUserWithNull()
51 | {
52 | $checker = new MyRoleChecker(['admin']);
53 | $ret = $checker->currentUserCan(null, 'run', []);
54 | $this->assertFalse($ret[0]);
55 | }
56 |
57 | public function testRoleAllowedByUserObject()
58 | {
59 | $user = new MyUser(['admin']);
60 | $checker = new MyRoleChecker(['admin']);
61 | $ret = $checker->currentUserCan($user, 'run', []);
62 | $this->assertTrue($ret[0]);
63 | }
64 |
65 | public function testRoleDisallowedByUserObject()
66 | {
67 | $user = new MyUser(['user']);
68 | $checker = new MyRoleChecker(['admin']);
69 | $ret = $checker->currentUserCan($user, 'run', []);
70 | $this->assertFalse($ret[0]);
71 | }
72 |
73 |
74 | public function testGetAllowedRoles()
75 | {
76 | $checker = new MyRoleChecker(['admin']);
77 | $this->assertSame(['admin'],$checker->getAllowedRoles());
78 | }
79 |
80 |
81 | /**
82 | * @expectedException Exception
83 | */
84 | public function testUnsupportedCurrentUser()
85 | {
86 | $checker = new MyRoleChecker(['admin']);
87 | $checker->currentUserCan(false, 'run', []);
88 | }
89 |
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/tests/ActionWithUserTest.php:
--------------------------------------------------------------------------------
1 | roles;
15 | }
16 | }
17 |
18 | /**
19 | * @group maghead
20 | */
21 | class ActionWithUserTest extends \Maghead\Testing\ModelTestCase
22 | {
23 | public function models()
24 | {
25 | return [ new OrderSchema ];
26 | }
27 |
28 | public function userProvider()
29 | {
30 | return array(
31 | array('memeber', 'error', true),
32 | array('admin', 'success', true),
33 | array('admin', 'error', false),
34 | );
35 | }
36 |
37 | /**
38 | * @dataProvider userProvider
39 | */
40 | public function testRunnerWithSimpleUser($roles, $resultType, $setUser)
41 | {
42 | $container = new ServiceContainer;
43 | $generator = $container['generator'];
44 | $generator->registerTemplate('RecordActionTemplate', new RecordActionTemplate);
45 | $runner = new ActionRunner($container);
46 | $runner->registerAutoloader();
47 | $runner->registerAction('RecordActionTemplate', array(
48 | 'namespace' => 'OrderBundle',
49 | 'model' => 'Order',
50 | 'types' => array(
51 | ['prefix' => 'Create', 'allowed_roles' => ['user', 'admin'] ],
52 | ['prefix' => 'Update'],
53 | ['prefix' => 'Delete']
54 | )
55 | ));
56 |
57 | if($setUser) {
58 | $runner->setCurrentUser($roles);
59 | }
60 | $result = $runner->run('OrderBundle::Action::CreateOrder',[
61 | 'quantity' => '1',
62 | 'amount' => 100,
63 | ]);
64 | $this->assertNotNull($result);
65 | $this->assertEquals($resultType, $result->type);
66 | }
67 |
68 |
69 | public function roleProvider()
70 | {
71 | return array(
72 | array(['member', 'manager'], 'error'),
73 | array(['member', 'user'], 'success'),
74 | );
75 | }
76 | /**
77 | * @dataProvider roleProvider
78 | */
79 | public function testRunnerWithMultiRoleInterface($roles, $resultType)
80 | {
81 | $container = new ServiceContainer;
82 | $generator = $container['generator'];
83 | $generator->registerTemplate('RecordActionTemplate', new RecordActionTemplate);
84 | $runner = new ActionRunner($container);
85 | $runner->registerAutoloader();
86 | $runner->registerAction('RecordActionTemplate', array(
87 | 'namespace' => 'OrderBundle',
88 | 'model' => 'Order',
89 | 'types' => array(
90 | ['prefix' => 'Create', 'allowed_roles' => ['user', 'admin'] ],
91 | ['prefix' => 'Update'],
92 | ['prefix' => 'Delete']
93 | )
94 | ));
95 |
96 | $user = new TestUser;
97 | $user->roles = $roles;
98 | $runner->setCurrentUser($user);
99 | $result = $runner->run('OrderBundle::Action::CreateOrder',[
100 | 'quantity' => '1',
101 | 'amount' => 100,
102 | ]);
103 | $this->assertNotNull($result);
104 | $this->assertEquals($resultType, $result->type);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/tests/EmailFieldActionTest.php:
--------------------------------------------------------------------------------
1 | param('email')
10 | ->isa('Email');
11 | }
12 | }
13 |
14 |
15 |
16 | class EmailFieldActionTest extends \PHPUnit\Framework\TestCase
17 | {
18 |
19 |
20 | public function testInvalidEmailFieldAction()
21 | {
22 | $action = new EmailFieldTestAction([ 'email' => 'yoanlin93' ]);
23 | $ret = $action->invoke();
24 | $this->assertFalse($ret);
25 | }
26 |
27 | public function testEmailFieldAction()
28 | {
29 | $action = new EmailFieldTestAction([ 'email' => 'yoanlin93@gmail.com' ]);
30 | $ret = $action->invoke();
31 | $this->assertTrue($ret);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/tests/FooTemplateView.php:
--------------------------------------------------------------------------------
1 | renderTemplateFile('foo.html',array( ));
8 | }
9 | }
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tests/IntFieldActionTest.php:
--------------------------------------------------------------------------------
1 | param('cnt')
11 | ->isa('Int');
12 | }
13 |
14 | }
15 |
16 |
17 | class IntFieldActionTest extends \PHPUnit\Framework\TestCase
18 | {
19 |
20 | public function testIntFieldAction()
21 | {
22 | $action = new IntFieldTestAction([ 'cnt' => 10 ]);
23 | $ret = $action->invoke();
24 | $this->assertTrue($ret);
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tests/Model/CRUDTest/FooUser.php:
--------------------------------------------------------------------------------
1 | column('username')->varchar(12);
8 | $schema->column('password')->varchar(12);
9 | }
10 | #boundary start 2d278467a6071e8ac2130d201b3510e1
11 | const schema_proxy_class = 'ActionKit\\Model\\CRUDTest\\FooUserSchemaProxy';
12 | const collection_class = 'ActionKit\\Model\\CRUDTest\\FooUserCollection';
13 | const model_class = 'ActionKit\\Model\\CRUDTest\\FooUser';
14 | const table = 'foo_users';
15 | #boundary end 2d278467a6071e8ac2130d201b3510e1
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/tests/Model/CRUDTest/FooUserCollection.php:
--------------------------------------------------------------------------------
1 | column('quantity')
9 | ->integer()
10 | ->required()
11 | ;
12 |
13 | $this->column('order_id')
14 | ->integer()
15 | ->required()
16 | ->unsigned()
17 | ->refer(OrderSchema::class);
18 |
19 | $this->column('subtotal')->integer();
20 |
21 | $this->belongsTo('order', OrderSchema::class, 'id', 'order_id');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/OrderBundle/Model/OrderRepo.php:
--------------------------------------------------------------------------------
1 | column('sum')
12 | ->integer();
13 |
14 | $this->column('quantity')
15 | ->integer();
16 |
17 | $this->column('amount')
18 | ->notNull()
19 | ->integer();
20 |
21 | $this->column('updated_at')
22 | ->timestamp()
23 | ->notNull()
24 | ->isa('DateTime')
25 | ->renderAs('DateTimeInput')
26 | ->default(new Raw('CURRENT_TIMESTAMP'))
27 | ->onUpdate(new Raw('CURRENT_TIMESTAMP'))
28 | ->label(_('更新時間'))
29 | ;
30 |
31 | $this->column('created_at')
32 | ->timestamp()
33 | ->isa('DateTime')
34 | ->null()
35 | ->renderAs('DateTimeInput')
36 | ->label( _('建立時間') )
37 | ->default(function() {
38 | return new \DateTime;
39 | })
40 | ;
41 |
42 | $this->hasMany('items', OrderItemSchema::class, 'order_id', 'id');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Action/CreateProduct.php:
--------------------------------------------------------------------------------
1 | mixin = new ProductBaseMixin($this);
19 | $this->mixin->preinit();
20 | }
21 |
22 | public function schema()
23 | {
24 | $this->mixin->schema();
25 | }
26 |
27 | public function successMessage($ret)
28 | {
29 | return '產品資料 ' . $this->record->name . ' 建立成功';
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Action/CreateProductFile.php:
--------------------------------------------------------------------------------
1 | useRecordSchema();
12 |
13 | $sizeLimit = 1024; // 1024kb
14 |
15 | $this->replaceParam('file','File')
16 | ->sizeLimit($sizeLimit)
17 | ->required()
18 | ->hint('product file hint')
19 | ->label('product file label')
20 | ->putIn('tests/upload')
21 | ;
22 |
23 | }
24 |
25 |
26 | }
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Action/CreateProductImage.php:
--------------------------------------------------------------------------------
1 | useRecordSchema();
12 |
13 | $imageSizeLimit = 1024; // 1024kb
14 | $imageSize = [
15 | 'width' => 200,
16 | 'height' => 200,
17 | ];
18 | $autoResize = true;
19 |
20 | $this->replaceParam('image','Image')
21 | ->sizeLimit($imageSizeLimit)
22 | ->size( $imageSize )
23 | ->autoResize($autoResize)
24 | ->sourceField( 'large' )
25 | ->required()
26 | ->hint('product image hint')
27 | ->hintFromSizeInfo($imageSize)
28 | ->hintFromSizeLimit()
29 | ->label('product image label')
30 | ->putIn('tests/upload')
31 | ;
32 |
33 | $this->replaceParam('large','Image')
34 | ->sizeLimit($imageSizeLimit)
35 | ->size( $imageSize )
36 | ->autoResize($autoResize)
37 | ->hint('product large image hint')
38 | ->hintFromSizeInfo()
39 | ->label('product large image label')
40 | ->putIn('tests/upload')
41 | ;
42 |
43 | }
44 |
45 |
46 | }
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Action/ProductBaseMixin.php:
--------------------------------------------------------------------------------
1 | object = $object;
16 | }
17 |
18 | public function preinit()
19 | {
20 | /**
21 | * TODO: Note that the self_key is pointing the related class currently.
22 | * We want to make self_key to point the action record itself.
23 | */
24 | $this->object->nested = true;
25 | $this->object->relationships['product_categories']['renderable'] = false;
26 | }
27 |
28 | public function schema()
29 | {
30 | $this->object->useRecordSchema();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Action/UpdateProduct.php:
--------------------------------------------------------------------------------
1 | mixin = new ProductBaseMixin($this);
18 | $this->mixin->preinit();
19 | }
20 |
21 | public function schema()
22 | {
23 | $this->mixin->schema();
24 | }
25 |
26 | public function successMessage($ret)
27 | {
28 | return "Product {$this->record->name} updated.";
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/Category.php:
--------------------------------------------------------------------------------
1 | parent_id )
13 | return $this->parent->dataLabel() . ' > ' . $this->name;
14 | return $this->name;
15 | }
16 |
17 | public function getParent()
18 | {
19 | if( $this->parent_id )
20 | return $this->parent;
21 | }
22 |
23 | public function getChilds()
24 | {
25 | $childs = new CategoryCollection;
26 | $childs->where(array( 'parent_id' => $this->id ));
27 | return $childs;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/CategoryCollection.php:
--------------------------------------------------------------------------------
1 | table('product_categories');
14 |
15 | $this->column( 'name' )
16 | ->varchar(130)
17 | ->label('產品類別名稱')
18 | ->required(1);
19 |
20 | $this->column('description')
21 | ->text()
22 | ->label('產品類別敘述')
23 | ->renderAs('TextareaInput',array(
24 | 'class' => '+=mceEditor',
25 | ));
26 |
27 | $this->column('parent_id')
28 | ->integer()
29 | ->unsigned()
30 | ->refer(CategorySchema::class)
31 | ->label( _('父類別') )
32 | ->default(NULL)
33 | ->renderAs('SelectInput', [
34 | 'allow_empty' => NULL,
35 | ]);
36 |
37 | // hide this category in front-end
38 | $this->column('hide')
39 | ->boolean()
40 | ->label(_('隱藏這個類別'));
41 |
42 | $this->column('thumb')
43 | ->varchar(128)
44 | ->label('縮圖')
45 | ;
46 |
47 | $this->column('image')
48 | ->varchar(128)
49 | ->label('圖片');
50 |
51 | $this->column('handle')
52 | ->varchar(32)
53 | ->label(_('程式用操作碼'));
54 |
55 |
56 | $this->many('subcategories','ProductBundle\\Model\\CategorySchema','parent_id','id');
57 | $this->belongsTo('parent','ProductBundle\\Model\\CategorySchema','id','parent_id');
58 |
59 | $this->many( 'category_products', 'ProductBundle\\Model\\ProductCategorySchema', 'category_id', 'id' );
60 | $this->manyToMany( 'products', 'category_products' , 'product');
61 | }
62 |
63 | public function bootstrap($record)
64 | {
65 | $record->create(array('identity' => 'c1', 'name' => 'Category 1','lang' => 'en'));
66 | $record->create(array('identity' => 'c2', 'name' => 'Category 2','lang' => 'en'));
67 | $record->create(array('identity' => 'c3', 'name' => 'Category 3','lang' => 'en'));
68 |
69 | $record->create(array('name' => '類別 1', 'lang' => 'zh_TW'));
70 | $record->create(array('name' => '類別 2', 'lang' => 'zh_TW'));
71 | $record->create(array('name' => '類別 3', 'lang' => 'zh_TW'));
72 | }
73 | }
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/Feature.php:
--------------------------------------------------------------------------------
1 | name;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/FeatureCollection.php:
--------------------------------------------------------------------------------
1 | column('name')->varchar(128)->label('產品功能名稱');
12 | $this->column('description')->text()->label( _('Description') );
13 | $this->column('image')
14 | ->varchar(250)
15 | ->label( '產品功能圖示' );
16 |
17 | }
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/Product.php:
--------------------------------------------------------------------------------
1 | lang ) {
17 | return '[' . _($this->lang) . '] ' . $this->name;
18 | }
19 | */
20 | return $this->name;
21 | }
22 |
23 | public function beforeUpdate($args) {
24 | $args['updated_on'] = date('c');
25 | return $args;
26 | }
27 |
28 | public function availableTypes() {
29 | return $this->types->filter(function($type) {
30 | return $type->quantity > 0;
31 | });
32 | }
33 |
34 | public function renderThumb($attrs = array()) {
35 | $html = "
thumb}\"" ;
36 | $attrs = array_merge(array(
37 | 'title' => $this->name,
38 | 'alt' => $this->name,
39 | ), $attrs);
40 | foreach( $attrs as $key => $val ) {
41 | $html .= " $key=\"" . addslashes($val) . "\"";
42 | }
43 | $html .= "/>";
44 | return $html;
45 | }
46 |
47 | public function renderImage($attrs = array()) {
48 | $html = "
image}\"" ;
49 | foreach( $attrs as $key => $val ) {
50 | $html .= " $key=\"" . addslashes($val) . "\"";
51 | }
52 | $html .= "/>";
53 | return $html;
54 | }
55 |
56 | public function getPageKeywords() { }
57 |
58 | public function getPageDescription() { }
59 |
60 | public function getPageTitle() {
61 | $title = $this->name;
62 | if ($this->sn) {
63 | $title .= ' - ' . $this->sn;
64 | }
65 | return $title;
66 | }
67 |
68 |
69 | /**
70 | * @return bool check price and sellable flag.
71 | */
72 | public function isSellable() {
73 | return $this->sellable && $this->price > 0;
74 | }
75 |
76 |
77 | protected $_allSoldOut;
78 |
79 | public function isAllSoldOut() {
80 | if ( $this->_allSoldOut !== null ) {
81 | return $this->_allSoldOut;
82 | }
83 | return $this->_allSoldOut = ! $this->types->quantityAvailable();
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductCategory.php:
--------------------------------------------------------------------------------
1 | table('product_category_junction');
14 |
15 | $this->column('product_id')
16 | ->integer()
17 | ->unsigned()
18 | ->required()
19 | ;
20 | $this->column('category_id')
21 | ->integer()
22 | ->unsigned()
23 | ->required()
24 | ;
25 |
26 | $this->belongsTo( 'category' , 'ProductBundle\\Model\\CategorySchema','id','category_id');
27 | $this->belongsTo( 'product' , 'ProductBundle\\Model\\ProductSchema','id','product_id');
28 | }
29 | }
30 |
31 |
32 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductCollection.php:
--------------------------------------------------------------------------------
1 | where()
14 | ->equal('status','publish');
15 | $items->order('created_on','desc');
16 | return $items;
17 | }
18 |
19 | public static function getCoverProducts() {
20 | $coverProducts = new self;
21 | $coverProducts->where(array(
22 | 'is_cover' => true,
23 | 'status' => 'publish'
24 | ));
25 | $coverProducts->order('created_on','desc');
26 | return $coverProducts;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductCollectionBase.php:
--------------------------------------------------------------------------------
1 | column('product_id')->label( _('Product Id') )->refer( 'ProductBundle\\Model\\Product' );
13 | $this->column('feature_id')->label( _('Feature Id') )->refer( 'ProductBundle\\Model\\Feature' );
14 | $this->belongsTo('product','ProductBundle\\Model\\ProductSchema','id','product_id');
15 | $this->belongsTo('feature','ProductBundle\\Model\\FeatureSchema','id','feature_id');
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductFile.php:
--------------------------------------------------------------------------------
1 | column( 'product_id' )
15 | ->integer()
16 | ->refer('ProductBundle\\Model\\Product')
17 | ->renderAs('SelectInput')
18 | ->label('產品');
19 |
20 | $this->column( 'title' )
21 | ->varchar(130)
22 | ->label('檔案標題');
23 |
24 | $this->column( 'file' )
25 | ->varchar(130)
26 | ->required()
27 | ->label('檔案');
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductImage.php:
--------------------------------------------------------------------------------
1 | column( 'product_id' )
15 | ->integer()
16 | ->refer('ProductBundle\\Model\\Product')
17 | ->renderAs('SelectInput')
18 | ->label('產品');
19 |
20 | $this->column( 'title' )
21 | ->varchar(130)
22 | ->label('圖片標題');
23 |
24 | $this->column('image')
25 | ->varchar(130)
26 | ->required()
27 | ->label('圖');
28 |
29 | $this->column('large')
30 | ->varchar(130)
31 | ->label('最大圖');
32 |
33 | }
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductLink.php:
--------------------------------------------------------------------------------
1 | column('label')->varchar(128);
10 | $this->column('url')->varchar(128);
11 | $this->column('product_id')
12 | ->integer()
13 | ->refer( 'ProductBundle\\Model\\ProductSchema')
14 | ;
15 | $this->belongsTo('product','ProductBundle\\Model\\ProductSchema','id','product_id');
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductProduct.php:
--------------------------------------------------------------------------------
1 | column('product_id')
9 | ->integer()
10 | ->refer('ProductBundle\\Model\\Product')
11 | ->renderAs('SelectInput')
12 | ->label('產品')
13 | ;
14 | $this->column('related_product_id')
15 | ->integer()
16 | ->refer('ProductBundle\\Model\\Product')
17 | ->renderAs('SelectInput')
18 | ->label('關連產品')
19 | ;
20 |
21 |
22 | $this->belongsTo('product','ProductBundle\\Model\\ProductSchema','id','product_id');
23 | $this->belongsTo('related_product','ProductBundle\\Model\\ProductSchema','id','related_product_id');
24 | }
25 | }
26 |
27 |
28 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductProperty.php:
--------------------------------------------------------------------------------
1 | column('name')->varchar(64);
10 | $this->column('val')->varchar(512);
11 | $this->column('product_id')
12 | ->integer()
13 | ->unsigned()
14 | ->refer(ProductSchema::class)
15 | ;
16 | $this->belongsTo('product', ProductSchema::class, 'id','product_id');
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductRecipe.php:
--------------------------------------------------------------------------------
1 | column('title')
10 | ->varchar(64)
11 | ->label('子區塊標題')
12 | ->renderAs('TextInput', [ 'size' => 50 ])
13 | ;
14 |
15 | $this->column('cover_image')
16 | ->varchar(64)
17 | ->label('子區塊封面圖')
18 | ->renderAs('ThumbImageFileInput')
19 | ;
20 |
21 | $this->column('content')
22 | ->text()
23 | ->label('子區塊內文')
24 | ->renderAs('TextareaInput', [
25 | 'class' => '+=mceEditor',
26 | 'rows' => 5,
27 | 'cols' => 50,
28 | ])
29 | ;
30 |
31 | $this->column('product_id')
32 | ->integer()
33 | ->refer( 'ProductBundle\\Model\\Product')
34 | ;
35 |
36 | $this->belongsTo('product','ProductBundle\\Model\\ProductSchema','id','product_id');
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductType.php:
--------------------------------------------------------------------------------
1 | name;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductTypeCollection.php:
--------------------------------------------------------------------------------
1 | quantity);
14 | }
15 | return $q > 0;
16 | }
17 |
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductTypeCollectionBase.php:
--------------------------------------------------------------------------------
1 | label('產品類型');
14 |
15 |
16 | $this->column('product_id')
17 | ->integer()
18 | ->label('產品')
19 | ->renderAs('SelectInput')
20 | ->refer('ProductBundle\\Model\\ProductSchema');
21 |
22 | $this->column('name')
23 | ->varchar(120)
24 | ->required()
25 | ->label(_('類型名稱'))
26 | ->renderAs('TextInput', [
27 | 'size' => 20,
28 | 'placeholder' => _('如: 綠色, 黑色, 羊毛, 大、中、小等等。'),
29 | ])
30 | ;
31 |
32 | $this->column('quantity')
33 | ->integer()
34 | ->default(0)
35 | ->label( _('數量') )
36 | // ->renderAs('SelectInput')
37 | ->renderAs('TextInput')
38 | // ->hint(_('設定成 -1 時為不限制數量'))
39 | ->validValues(range(-1,100))
40 | ;
41 |
42 | $this->column('comment')
43 | ->text()
44 | ->label(_('備註'))
45 | ->renderAs('TextareaInput')
46 | ;
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tests/ProductBundle/Model/ProductUseCase.php:
--------------------------------------------------------------------------------
1 | table('product_resources');
10 | $this->column('product_id')
11 | ->integer()
12 | ->refer('ProductBundle\\Model\\Product')
13 | ->label('產品')
14 | ;
15 |
16 | $this->column('url')
17 | ->varchar(256)
18 | ->label( '網址' )
19 | ;
20 |
21 | $this->column('html')
22 | ->varchar(512)
23 | ->label('內嵌 HTML')
24 | ->renderAs('TextareaInput')
25 | ;
26 |
27 | $this->belongsTo('product','ProductBundle\\Model\\ProductSchema','id','product_id');
28 | }
29 | }
30 |
31 |
32 |
--------------------------------------------------------------------------------
/tests/Templates/foo.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/User/Model/User.php:
--------------------------------------------------------------------------------
1 | 'id',
53 | 1 => 'name',
54 | 2 => 'email',
55 | 3 => 'password',
56 | );
57 |
58 | public static $mixin_classes = array (
59 | );
60 |
61 | protected $table = 'users';
62 |
63 | public $id;
64 |
65 | public $name;
66 |
67 | public $email;
68 |
69 | public $password;
70 |
71 | public static function getSchema()
72 | {
73 | static $schema;
74 | if ($schema) {
75 | return $schema;
76 | }
77 | return $schema = new \User\Model\UserSchemaProxy;
78 | }
79 |
80 | public static function createRepo($write, $read)
81 | {
82 | return new \User\Model\UserRepoBase($write, $read);
83 | }
84 |
85 | public function getKeyName()
86 | {
87 | return 'id';
88 | }
89 |
90 | public function getKey()
91 | {
92 | return $this->id;
93 | }
94 |
95 | public function hasKey()
96 | {
97 | return isset($this->id);
98 | }
99 |
100 | public function setKey($key)
101 | {
102 | return $this->id = $key;
103 | }
104 |
105 | public function removeLocalPrimaryKey()
106 | {
107 | $this->id = null;
108 | }
109 |
110 | public function getId()
111 | {
112 | return intval($this->id);
113 | }
114 |
115 | public function getName()
116 | {
117 | return $this->name;
118 | }
119 |
120 | public function getEmail()
121 | {
122 | return $this->email;
123 | }
124 |
125 | public function getPassword()
126 | {
127 | return $this->password;
128 | }
129 |
130 | public function getAlterableData()
131 | {
132 | return ["id" => $this->id, "name" => $this->name, "email" => $this->email, "password" => $this->password];
133 | }
134 |
135 | public function getData()
136 | {
137 | return ["id" => $this->id, "name" => $this->name, "email" => $this->email, "password" => $this->password];
138 | }
139 |
140 | public function setData(array $data)
141 | {
142 | if (array_key_exists("id", $data)) { $this->id = $data["id"]; }
143 | if (array_key_exists("name", $data)) { $this->name = $data["name"]; }
144 | if (array_key_exists("email", $data)) { $this->email = $data["email"]; }
145 | if (array_key_exists("password", $data)) { $this->password = $data["password"]; }
146 | }
147 |
148 | public function clear()
149 | {
150 | $this->id = NULL;
151 | $this->name = NULL;
152 | $this->email = NULL;
153 | $this->password = NULL;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/tests/User/Model/UserCollection.php:
--------------------------------------------------------------------------------
1 | column('name')
9 | ->varchar(30);
10 |
11 | $this->column('email')
12 | ->varchar(128);
13 |
14 | $this->column('password')
15 | ->varchar(128);
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | [ 'bootstrap' => ['tests/bootstrap.php'] ],
19 | 'schema' => [
20 | 'auto_id' => 1,
21 | 'paths' => ['tests'],
22 | ],
23 | 'databases' => [
24 | 'master' => [
25 | 'dsn' => 'sqlite::memory:',
26 | 'user' => NULL,
27 | 'password' => NULL,
28 | ],
29 | ]
30 | ]);
31 | Bootstrap::setup($config);
32 | */
33 |
34 | /*
35 | $loader = ComposerSchemaLoader::from('composer.json');
36 | $loader->load();
37 |
38 | $logger = new CLIFramework\Logger;
39 | // $logger->setQuiet();
40 | $logger->info("Updating schema class files...");
41 | $schemas = SchemaLoader::loadDeclaredSchemas();
42 | $g = new SchemaGenerator($config, $logger);
43 | $g->setForceUpdate(true);
44 | $g->generate($schemas);
45 | */
46 |
47 | /**
48 | * Clean up cache files
49 | */
50 | const CACHE_DIR = 'src/Cache';
51 | const UPLOAD_DIR = 'tests/upload';
52 | if (file_exists(CACHE_DIR)) {
53 | futil_rmtree(CACHE_DIR);
54 | mkdir(CACHE_DIR, 0755, true);
55 | } else {
56 | mkdir(CACHE_DIR, 0755, true);
57 | }
58 |
59 | if (file_exists(UPLOAD_DIR)) {
60 | futil_rmtree(UPLOAD_DIR);
61 | mkdir(UPLOAD_DIR, 0755, true);
62 | } else {
63 | mkdir(UPLOAD_DIR, 0755, true);
64 | }
65 |
66 | /*
67 | use WebServerRunner\WebServerRunner;
68 | if (defined('WEB_SERVER_HOST') && defined('WEB_SERVER_PORT')) {
69 | $runner = new WebServerRunner(WEB_SERVER_HOST, WEB_SERVER_PORT, WEB_SERVER_DOCROOT);
70 | $runner->setVerbose(true);
71 | $runner->execute();
72 | $runner->stopOnShutdown();
73 | }
74 | */
75 |
--------------------------------------------------------------------------------
/tests/config/database.yml:
--------------------------------------------------------------------------------
1 | ---
2 | schema:
3 | paths:
4 | - tests
5 | databases:
6 | master:
7 | dsn: 'sqlite::memory:'
8 | query_options: { quote_table: true }
9 | mysql:
10 | dsn: 'mysql:host=localhost;dbname=testing'
11 | user: root
12 | # create database testing charset utf8;
13 | # grant all privileges on testing.* to testing@localhost identified by 'testing';
14 | pgsql:
15 | dsn: 'pgsql:host=localhost;dbname=testing'
16 | user: postgres
17 |
--------------------------------------------------------------------------------
/tests/config/mysql.yml:
--------------------------------------------------------------------------------
1 | ---
2 | cli:
3 | bootstrap: tests/bootstrap.php
4 | schema:
5 | auto_id: true
6 | base_model: \Maghead\Runtime\Model
7 | base_collection: \Maghead\Runtime\Collection
8 | paths:
9 | - tests
10 | instance:
11 | local:
12 | dsn: 'mysql:host=localhost'
13 | user: root
14 | driver: mysql
15 | host: localhost
16 | password: null
17 | query_options: { }
18 | connection_options:
19 | 1002: 'SET NAMES utf8'
20 | databases:
21 | master:
22 | dsn: 'mysql:host=localhost;dbname=testing'
23 | host: localhost
24 | user: root
25 | driver: mysql
26 | database: testing
27 | password: null
28 | query_options: { }
29 | connection_options:
30 | 1002: 'SET NAMES utf8'
31 | node1:
32 | dsn: 'mysql:host=localhost;dbname=s1'
33 | host: localhost
34 | user: root
35 | driver: mysql
36 | database: s1
37 | password: null
38 | query_options: { }
39 | connection_options:
40 | 1002: 'SET NAMES utf8'
41 | node2:
42 | dsn: 'mysql:host=localhost;dbname=s2'
43 | host: localhost
44 | user: root
45 | driver: mysql
46 | database: s2
47 | password: null
48 | query_options: { }
49 | connection_options:
50 | 1002: 'SET NAMES utf8'
51 | node3:
52 | dsn: 'mysql:host=localhost;dbname=s3'
53 | host: localhost
54 | user: root
55 | driver: mysql
56 | database: s3
57 | password: null
58 | query_options: { }
59 | connection_options:
60 | 1002: 'SET NAMES utf8'
61 |
--------------------------------------------------------------------------------
/tests/config/mysql_configserver.yml:
--------------------------------------------------------------------------------
1 | ---
2 | appId: testapp
3 | configServer: "mongodb://localhost:27017"
4 | cli:
5 | bootstrap: tests/bootstrap.php
6 | schema:
7 | auto_id: true
8 | base_model: \Maghead\Runtime\Model
9 | base_collection: \Maghead\Runtime\Collection
10 | paths:
11 | - tests
12 | instance:
13 | local:
14 | dsn: 'mysql:host=localhost'
15 | user: root
16 | driver: mysql
17 | host: localhost
18 | password: null
19 | query_options: { }
20 | connection_options:
21 | 1002: 'SET NAMES utf8'
22 | databases:
23 | master:
24 | dsn: 'mysql:host=localhost;dbname=testing'
25 | host: localhost
26 | user: root
27 | driver: mysql
28 | database: testing
29 | password: null
30 | query_options: { }
31 | connection_options:
32 | 1002: 'SET NAMES utf8'
33 | node1:
34 | dsn: 'mysql:host=localhost;dbname=s1'
35 | host: localhost
36 | user: root
37 | driver: mysql
38 | database: s1
39 | password: null
40 | query_options: { }
41 | connection_options:
42 | 1002: 'SET NAMES utf8'
43 | node2:
44 | dsn: 'mysql:host=localhost;dbname=s2'
45 | host: localhost
46 | user: root
47 | driver: mysql
48 | database: s2
49 | password: null
50 | query_options: { }
51 | connection_options:
52 | 1002: 'SET NAMES utf8'
53 | node3:
54 | dsn: 'mysql:host=localhost;dbname=s3'
55 | host: localhost
56 | user: root
57 | driver: mysql
58 | database: s3
59 | password: null
60 | query_options: { }
61 | connection_options:
62 | 1002: 'SET NAMES utf8'
63 |
--------------------------------------------------------------------------------
/tests/config/pgsql.yml:
--------------------------------------------------------------------------------
1 | ---
2 | cli:
3 | bootstrap: tests/bootstrap.php
4 | schema:
5 | auto_id: true
6 | base_model: \Maghead\Runtime\Model
7 | base_collection: \Maghead\Runtime\Collection
8 | paths:
9 | - tests
10 | instance:
11 | local:
12 | dsn: 'pgsql:host=localhost'
13 | driver: pgsql
14 | host: localhost
15 | user: postgres
16 | databases:
17 | master:
18 | dsn: 'pgsql:host=localhost;dbname=testing'
19 | driver: pgsql
20 | host: localhost
21 | user: postgres
22 | node1:
23 | driver: pgsql
24 | dsn: 'pgsql:host=localhost'
25 | user: postgres
26 | host: localhost
27 | database: s1
28 | node2:
29 | driver: pgsql
30 | dsn: 'pgsql:host=localhost'
31 | user: postgres
32 | host: localhost
33 | database: s2
34 | node3:
35 | driver: pgsql
36 | dsn: 'pgsql:host=localhost'
37 | user: postgres
38 | host: localhost
39 | database: s3
40 |
--------------------------------------------------------------------------------
/tests/config/sqlite.yml:
--------------------------------------------------------------------------------
1 | ---
2 | cli:
3 | bootstrap: tests/bootstrap.php
4 | schema:
5 | auto_id: true
6 | base_model: \Maghead\Runtime\Model
7 | base_collection: \Maghead\Runtime\Collection
8 | paths:
9 | - tests
10 | instance:
11 | local:
12 | dsn: 'sqlite::memory:'
13 | databases:
14 | master:
15 | dsn: 'sqlite::memory:'
16 | query_options: { quote_table: true }
17 | node1:
18 | dsn: 'sqlite::memory:'
19 | query_options: { quote_table: true }
20 | node2:
21 | dsn: 'sqlite::memory:'
22 | query_options: { quote_table: true }
23 | node3:
24 | dsn: 'sqlite::memory:'
25 | query_options: { quote_table: true }
26 |
--------------------------------------------------------------------------------
/tests/config/tmp.yml:
--------------------------------------------------------------------------------
1 | ---
2 | cli:
3 | bootstrap: tests/bootstrap.php
4 | schema:
5 | auto_id: true
6 | base_model: \Maghead\Runtime\Model
7 | base_collection: \Maghead\Runtime\Collection
8 | paths:
9 | - tests
10 | instance:
11 | local:
12 | dsn: 'sqlite::memory:'
13 | databases:
14 | master:
15 | dsn: 'sqlite::memory:'
16 | query_options: { quote_table: true }
17 | node1:
18 | dsn: 'sqlite::memory:'
19 | query_options: { quote_table: true }
20 | node2:
21 | dsn: 'sqlite::memory:'
22 | query_options: { quote_table: true }
23 | node3:
24 | dsn: 'sqlite::memory:'
25 | query_options: { quote_table: true }
26 |
--------------------------------------------------------------------------------
/tests/data/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/corneltek/ActionKit/a85f04c48bdaeff4c30d35246be9545508911ada/tests/data/404.png
--------------------------------------------------------------------------------
/tests/fixture/bulk_update_user4.php:
--------------------------------------------------------------------------------
1 | load('../.lazy.yml');
6 | $config->init();
7 |
8 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\ProductCategory', 'Create');
9 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\ProductCategory', 'Update');
10 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\Category', 'Create');
11 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\Category', 'Update');
12 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\ProductType', 'Create');
13 | ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\ProductType', 'Update');
14 |
15 | // handle actions
16 | if ( isset($_REQUEST['action']) ) {
17 | try {
18 | $container = new ActionKit\ServiceContainer;
19 | $runner = new ActionKit\ActionRunner($container);
20 | $result = $runner->run( $_REQUEST['action'] );
21 | if ( $result && $runner->isAjax() ) {
22 | // Deprecated:
23 | // The text/plain seems work for IE8 (IE8 wraps the
24 | // content with a '' tag.
25 | header('Cache-Control: no-cache');
26 | header('Content-Type: text/plain; Charset=utf-8');
27 |
28 | // Since we are using "textContent" instead of "innerHTML" attributes
29 | // we should output the correct json mime type.
30 | // header('Content-Type: application/json; Charset=utf-8');
31 | echo $result->__toString();
32 | exit(0);
33 | }
34 | } catch ( Exception $e ) {
35 | /**
36 | * Return 403 status forbidden
37 | */
38 | header('HTTP/1.0 403');
39 | if ( $runner->isAjax() ) {
40 | die(json_encode(array(
41 | 'error' => 1,
42 | 'message' => $e->getMessage()
43 | )));
44 | } else {
45 | die( $e->getMessage() );
46 | }
47 | }
48 | }
49 |
50 |
51 | if ( isset($result) ) {
52 | var_dump($result->message);
53 | }
54 |
55 |
56 | $class = ActionKit\RecordAction\BaseRecordAction::createCRUDClass('Product\\Model\\Product', 'Create');
57 | $create = new $class;
58 | echo $create->asView()->render();
59 |
--------------------------------------------------------------------------------