├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── Bug-Report.yml │ ├── Feature-Request.yml │ ├── Improvement.yml │ └── config.yml ├── ci │ ├── files │ │ └── .env │ └── scripts │ │ └── setup-pimcore-environment.sh └── workflows │ ├── cla-check.yaml │ ├── php-cs-fixer.yaml │ ├── stale.yml │ └── static-analysis.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── LICENSE.md ├── README.md ├── SECURITY.md ├── composer.json ├── doc ├── 01_Upgrade │ └── README.md └── img │ ├── favorite-output-channels.png │ └── outputputcanneltable.png ├── phpstan-baseline.neon ├── phpstan-bootstrap.php ├── phpstan.neon └── src ├── Controller └── AdminController.php ├── DependencyInjection └── Web2PrintToolsExtension.php ├── FavoriteOutputDefinition.php ├── FavoriteOutputDefinition ├── Dao.php ├── Listing.php └── Listing │ └── Dao.php ├── Migrations ├── PimcoreX │ ├── Version20210305134111.php │ ├── Version20220914092411.php │ └── Version20230124103907.php └── Version20210305134111.php ├── Model └── Document │ └── Editable │ ├── Outputchanneltable.php │ └── Outputchanneltable │ ├── MetaEntry.php │ └── MetaEntry │ ├── Defaultentry.php │ └── Table.php ├── Resources ├── config │ ├── doctrine_migrations.yml │ ├── pimcore │ │ ├── config.yml │ │ └── routing.yml │ └── services.yml ├── public │ ├── css │ │ └── admin.css │ ├── examples │ │ └── toc.js │ ├── js │ │ ├── Web2Print │ │ │ ├── bundle.js │ │ │ ├── favoriteOutputDefinitions.js │ │ │ └── saveAsFavouriteOutputDefinitionDialog.js │ │ └── pimcore │ │ │ └── document │ │ │ └── editables │ │ │ ├── metaentry │ │ │ ├── abstract.js │ │ │ ├── defaultentry.js │ │ │ └── table.js │ │ │ └── outputchanneltable.js │ └── vendor │ │ ├── css │ │ └── awesomizr.css │ │ └── js │ │ └── awesomizr.js └── translations │ ├── admin.de.yml │ └── admin.en.yml ├── Tools ├── Installer.php └── Tool.php ├── Twig └── OutputChannelExtension.php ├── Web2PrintToolsBundle.php └── cli └── license-updater.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 4 8 | 9 | [*.php] 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.yml] 17 | indent_size = 4 18 | 19 | [composer.json] 20 | indent_style = space 21 | indent_size = 2 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | * -text -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug-Report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [Bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## Important notice 10 | As an open core project we love to work together with our community to improve and develop our products. 11 | It's also important for us to make clear that **we're not working for you or your company**, 12 | but we enjoy to work together to solve existing bugs. 13 | So we would love to see PRs with bugfixes, discuss them and we are happy to merge them when they are ready. 14 | For details see also our [contributing guidelines](https://github.com/pimcore/pimcore/blob/10.x/CONTRIBUTING.md). 15 | 16 | Bug reports that do not meet the conditions listed below will be closed/deleted without comment. 17 | 18 | - Bug was verified on the latest supported version. 19 | - This is not a security issue -> see [our security policy](https://github.com/pimcore/pimcore/security/policy) instead. 20 | - You are not able to provide a pull request that fixes the issue. 21 | - There's no existing ticket for the same issue. 22 | 23 | - type: textarea 24 | attributes: 25 | label: Expected behavior 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: Actual behavior 31 | validations: 32 | required: true 33 | - type: textarea 34 | attributes: 35 | label: Steps to reproduce 36 | validations: 37 | required: true 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature-Request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request or propose a new feature 3 | title: "[Feature]: " 4 | labels: ["New Feature"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## Important notice 10 | As an open core project we love to work together with our community to improve and develop our products. 11 | It's also important for us to make clear that **we're not working for you or your company**, 12 | but we enjoy to work together to improve or add new features to the product. 13 | So we are always ready to discuss features and improvements with our community. 14 | Especially for bigger topics, please [start a discussion](https://github.com/pimcore/pimcore/discussions) first to aviod unnecessary efforts. 15 | 16 | As soon as a topic is more specific, feel free to create issues for it or even better provide a corresponding PR as we love to 17 | review and merge contributions. 18 | 19 | Feature requests that do not meet the conditions listed below will be closed/deleted without comment. 20 | - There's no existing ticket for the same topic 21 | - This is already a specific ready-to-work-on feature request 22 | 23 | - type: textarea 24 | attributes: 25 | label: Feature description 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Improvement.yml: -------------------------------------------------------------------------------- 1 | name: Improvement 2 | description: Request or propose an improvement 3 | title: "[Improvement]: " 4 | labels: ["Improvement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## Important notice 10 | As an open core project we love to work together with our community to improve and develop our products. 11 | It's also important for us to make clear that **we're not working for you or your company**, 12 | but we enjoy to work together to improve or add new features to the product. 13 | So we are always ready to discuss features and improvements with our community. 14 | Especially for bigger topics, please [start a discussion](https://github.com/pimcore/pimcore/discussions) first to aviod unnecessary efforts. 15 | 16 | As soon as a topic is more specific, feel free to create issues for it or even better provide a corresponding PR as we love to 17 | review and merge contributions. 18 | 19 | Feature requests that do not meet the conditions listed below will be closed/deleted without comment. 20 | - There's no existing ticket for the same topic 21 | - This is already a specific ready-to-work-on feature request 22 | 23 | - type: textarea 24 | attributes: 25 | label: Improvement description 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: We are hiring! 4 | url: https://pimcore.com/en/careers?utm_source=github&utm_medium=issue-template-web2print-tools&utm_campaign=careers 5 | about: Enjoy working with Pimcore? Join us on our mission! 6 | - name: Community Support 7 | url: https://github.com/pimcore/pimcore/discussions 8 | about: Please ask and answer questions here. 9 | -------------------------------------------------------------------------------- /.github/ci/files/.env: -------------------------------------------------------------------------------- 1 | APP_ENV=test -------------------------------------------------------------------------------- /.github/ci/scripts/setup-pimcore-environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu -o xtrace 4 | 5 | cp .github/ci/files/.env . 6 | -------------------------------------------------------------------------------- /.github/workflows/cla-check.yaml: -------------------------------------------------------------------------------- 1 | name: CLA check 2 | 3 | on: 4 | issue_comment: 5 | types: [created] 6 | pull_request_target: 7 | types: [opened, closed, synchronize] 8 | 9 | jobs: 10 | cla-workflow: 11 | uses: pimcore/workflows-collection-public/.github/workflows/reusable-cla-check.yaml@v1.3.0 12 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 13 | secrets: 14 | CLA_ACTION_ACCESS_TOKEN: ${{ secrets.CLA_ACTION_ACCESS_TOKEN }} 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/php-cs-fixer.yaml: -------------------------------------------------------------------------------- 1 | name: "PHP-CS-Fixer" 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - "[0-9]+.[0-9]+" 7 | - "[0-9]+.x" 8 | - "feature-*" 9 | push: 10 | branches: 11 | - "[0-9]+.[0-9]+" 12 | - "[0-9]+.x" 13 | - "*_actions" 14 | - "feature-*" 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | php-cs-fixer: 21 | permissions: 22 | contents: write # for stefanzweifel/git-auto-commit-action to push code in repo 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | with: 27 | ref: ${{ github.event.pull_request.head.ref }} 28 | repository: ${{ github.event.pull_request.head.repo.full_name }} 29 | 30 | - name: PHP-CS-Fixer 31 | uses: docker://oskarstark/php-cs-fixer-ga:latest 32 | 33 | - uses: stefanzweifel/git-auto-commit-action@v5 34 | with: 35 | commit_message: Apply php-cs-fixer changes 36 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Handle stale issues 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '37 7 * * *' 7 | 8 | jobs: 9 | call-stale-workflow: 10 | uses: pimcore/workflows-collection-public/.github/workflows/stale.yml@v1.1.0 11 | -------------------------------------------------------------------------------- /.github/workflows/static-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Static analysis centralised" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 3 * * 1,3,5' 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - "[0-9]+.[0-9]+" 10 | - "[0-9]+.x" 11 | - "feature-*" 12 | pull_request: 13 | types: [ opened, synchronize, reopened ] 14 | 15 | 16 | env: 17 | PIMCORE_PROJECT_ROOT: ${{ github.workspace }} 18 | PRIVATE_REPO: ${{ github.event.repository.private }} 19 | 20 | jobs: 21 | setup-matrix: 22 | runs-on: ubuntu-latest 23 | outputs: 24 | php_versions: ${{ steps.parse-php-versions.outputs.php_versions }} 25 | matrix: ${{ steps.set-matrix.outputs.matrix }} 26 | private_repo: ${{ env.PRIVATE_REPO }} 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v4 30 | 31 | - name: Checkout reusable workflow repo 32 | uses: actions/checkout@v4 33 | with: 34 | repository: pimcore/workflows-collection-public 35 | ref: main 36 | path: reusable-workflows 37 | 38 | - name: Parse PHP versions from composer.json 39 | id: parse-php-versions 40 | run: | 41 | if [ -f composer.json ]; then 42 | php_versions=$(jq -r '.require.php' composer.json | grep -oP '\d+\.\d+' | tr '\n' ',' | sed 's/,$//') 43 | if [ -z "$php_versions" ]; then 44 | echo "No PHP versions found in composer.json" 45 | echo "Setting default PHP value" 46 | echo "php_versions=default" >> $GITHUB_OUTPUT 47 | else 48 | echo "php_versions=$php_versions" >> $GITHUB_OUTPUT 49 | echo "#### php versions #### : $php_versions" 50 | fi 51 | else 52 | echo "composer.json not found" 53 | exit 1 54 | fi 55 | 56 | - name: Set up matrix 57 | id: set-matrix 58 | run: | 59 | php_versions="${{ steps.parse-php-versions.outputs.php_versions }}" 60 | 61 | MATRIX_JSON=$(cat reusable-workflows/phpstan-configuration/matrix-config.json) 62 | 63 | IFS=',' read -ra VERSIONS_ARRAY <<< "$php_versions" 64 | 65 | FILTERED_MATRIX_JSON=$(echo $MATRIX_JSON | jq --arg php_versions "$php_versions" ' 66 | { 67 | matrix: [ 68 | .configs[] | 69 | select(.php_version == $php_versions) | 70 | .matrix[] 71 | ] 72 | }') 73 | 74 | ENCODED_MATRIX_JSON=$(echo $FILTERED_MATRIX_JSON | jq -c .) 75 | 76 | echo "matrix=${ENCODED_MATRIX_JSON}" >> $GITHUB_OUTPUT 77 | 78 | static-analysis: 79 | needs: setup-matrix 80 | strategy: 81 | matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} 82 | uses: pimcore/workflows-collection-public/.github/workflows/reusable-static-analysis-centralized.yaml@main 83 | with: 84 | APP_ENV: test 85 | PIMCORE_TEST: 1 86 | PRIVATE_REPO: ${{ needs.setup-matrix.outputs.private_repo}} 87 | PHP_VERSION: ${{ matrix.matrix.php-version }} 88 | SYMFONY: ${{ matrix.matrix.symfony }} 89 | DEPENDENCIES: ${{ matrix.matrix.dependencies }} 90 | EXPERIMENTAL: ${{ matrix.matrix.experimental }} 91 | PIMCORE_VERSION: ${{ matrix.matrix.pimcore_version }} 92 | COMPOSER_OPTIONS: ${{ matrix.matrix.composer_options }} 93 | secrets: 94 | SSH_PRIVATE_KEY_PIMCORE_DEPLOYMENTS_USER: ${{ secrets.SSH_PRIVATE_KEY_PIMCORE_DEPLOYMENTS_USER }} 95 | COMPOSER_PIMCORE_REPO_PACKAGIST_TOKEN: ${{ secrets.COMPOSER_PIMCORE_REPO_PACKAGIST_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | *.log 4 | 5 | /phpunit.xml 6 | 7 | # PHP-CS-Fixer 8 | /.php_cs 9 | /.php-cs-fixer.cache 10 | 11 | # composer 12 | /composer.lock 13 | !/vendor 14 | /vendor/* 15 | !/vendor/.gitkeep 16 | 17 | # PhpStorm / IDEA 18 | .idea 19 | # NetBeans 20 | nbproject 21 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | in([ 5 | __DIR__ . '/src', 6 | ]) 7 | ; 8 | 9 | // do not enable self_accessor as it breaks pimcore models relying on get_called_class() 10 | $config = new PhpCsFixer\Config(); 11 | $config->setRules([ 12 | '@PSR1' => true, 13 | '@PSR2' => true, 14 | 'array_syntax' => ['syntax' => 'short'], 15 | 'list_syntax' => ['syntax' => 'short'], 16 | 17 | 'header_comment' => [ 18 | 'comment_type' => 'PHPDoc', 19 | 'header' => 20 | 'This source file is available under the terms of the' . PHP_EOL . 21 | 'Pimcore Open Core License (POCL)' . PHP_EOL . 22 | 'Full copyright and license information is available in' . PHP_EOL . 23 | 'LICENSE.md which is distributed with this source code.' . PHP_EOL . 24 | PHP_EOL . 25 | ' @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)' . PHP_EOL . 26 | ' @license Pimcore Open Core License (POCL)' 27 | ], 28 | 29 | 'blank_line_before_statement' => true, 30 | 'encoding' => true, 31 | 'function_typehint_space' => true, 32 | 'single_line_comment_style' => true, 33 | 'lowercase_cast' => true, 34 | 'magic_constant_casing' => true, 35 | 'method_argument_space' => ['on_multiline' => 'ignore'], 36 | 'class_attributes_separation' => true, 37 | 'native_function_casing' => true, 38 | 'no_blank_lines_after_class_opening' => true, 39 | 'no_blank_lines_after_phpdoc' => true, 40 | 'no_empty_comment' => true, 41 | 'no_empty_phpdoc' => true, 42 | 'no_empty_statement' => true, 43 | 'no_extra_blank_lines' => true, 44 | 'no_leading_import_slash' => true, 45 | 'no_leading_namespace_whitespace' => true, 46 | 'no_short_bool_cast' => true, 47 | 'no_spaces_around_offset' => true, 48 | 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'remove_inheritdoc' => true], 49 | 'no_unneeded_control_parentheses' => true, 50 | 'no_unused_imports' => true, 51 | 'no_whitespace_before_comma_in_array' => true, 52 | 'no_whitespace_in_blank_line' => true, 53 | 'object_operator_without_whitespace' => true, 54 | 'ordered_imports' => true, 55 | 'phpdoc_indent' => true, 56 | 'phpdoc_no_useless_inheritdoc' => true, 57 | 'phpdoc_scalar' => true, 58 | 'phpdoc_separation' => true, 59 | 'phpdoc_single_line_var_spacing' => true, 60 | 'return_type_declaration' => true, 61 | 'short_scalar_cast' => true, 62 | 'single_blank_line_before_namespace' => true, 63 | 'single_quote' => true, 64 | 'space_after_semicolon' => true, 65 | 'standardize_not_equals' => true, 66 | 'ternary_operator_spaces' => true, 67 | 'trailing_comma_in_multiline' => true, 68 | 'whitespace_after_comma_in_array' => true, 69 | ]); 70 | 71 | $config->setFinder($finder); 72 | return $config; 73 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | Copyright (C) Pimcore GmbH (http://www.pimcore.com) 3 | 4 | This software is available under the terms of the 5 | following Pimcore Open Core License (POCL) 6 | 7 | 8 | **PIMCORE OPEN CORE LICENSE AGREEMENT (POCL)** 9 | 10 | **Last Update: April 2025** 11 | 12 | This Open Core License Agreement ("**Agreement**" or “**POCL**”), effective as of the day of the first installation or use by Customer (the "**Effective Date**"), is by and between Pimcore GmbH, Söllheimer Straße 16, AT-5020 Salzburg, Republic of Austria (hereinafter "**Licensor**" or “**Pimcore**”) and the user of the Software, as defined herein, (hereinafter "**Licensee**" or "**Customer**"). Licensor and Licensee may be referred to herein collectively as the "**Parties**" or individually as a "**Party**." 13 | 14 | **WHEREAS** Licensor desires to license out certain Pimcore Software (“**Software**“). 15 | 16 | **WHEREAS** (a) Software for which the source code is publicly available but which is not licensed out as open source software is "**Open Core Software**" and 17 | (b) Software for which the source code is not publicly available is "**Proprietary Software**", 18 | both covered by this Agreement. 19 | 20 | **WHEREAS** the exact products that are available under this Agreement are defined in the additional contractual documents or by inclusion of, or referral to, this Agreement within the source code or within the source code repositories; if not provided for otherwise, a software element is Proprietary Software. 21 | 22 | **WHEREAS** the Software is protected by copyright world- wide; and 23 | 24 | **WHEREAS** Licensee desires to obtain a license to use the Software for its internal business purposes, subject to the terms and conditions of this Agreement. 25 | 26 | **NOW, THEREFORE**, in consideration of the mutual covenants, terms, and conditions set forth herein, and for other good and valuable consideration, the receipt and sufficiency of which are hereby acknowledged, the Parties agree as follows. 27 | 28 | ### 1. LICENSE 29 | 1.1 PLEASE READ THIS PIMCORE SOFTWARE LICENSE AGREEMENT CAREFULLY AS IT CONSTITUTES A LEGALLY BINDING AGREEMENT. BY INSTALLING OR USING THE SOFTWARE, YOU ACCEPT AND AGREE TO ALL TERMS AND CONDITIONS OF THIS AGREEMENT, AND CONFIRM THAT YOUR STATEMENT – IF APPLICABLE – ON THE RELEVANT GLOBAL REVENUE IS CORRECT AND COMPLETE. IF YOU REPRESENT A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU HAVE FULL LEGAL AUTHORITY TO ENTER INTO THIS AGREEMENT TO BIND THAT LEGAL ENTITY. IF YOU DO NOT AGREE TO THESE TERMS AND CONDITIONS, YOU MAY NOT INSTALL OR USE THE SOFTWARE. 30 | 31 | 1.2 Pimcore grants the Customer a non-exclusive, non-transferable, non-sublicensable, geographically unlimited right, limited in time to the term of the Agreement, to use the Software and to customize, modify or adapt it for its own purposes. Unless if required by Pimcore for compliance with applicable laws or any order of a governmental authority, the Customer is not obliged to share these modifications, adaptations, and customizations (“**Derivatives**”) with Pimcore or anyone else. 32 | 33 | 1.2.1 Solution Development and Production Use (Open Core Software) 34 | 35 | “**Production Use**” means the usage of a software for development of solutions and productions within a business operation. 36 | 37 | a) An organization with total global revenue not exceeding €5 million (€5M) or equivalent amount in other currency annually (“**Threshold**”) may qualify for a free license for Production Use of the Open Core Software, provided such organization is not a part, subsidiary, affiliate, or shell company to another organization, entity, or company group whose total combined revenue exceeds the Threshold. Eligibility must be self-certified by the Customer when starting the use of the Open Core Software and is subject to periodic review and audit by Pimcore. If at any time the Customer’s revenue exceeds the Threshold, a paid commercial license will be required for continued Production Use of the software. The Customer is obliged to inform Pimcore about relevant changes in revenues. Pimcore is entitled to charge license fees retroactively from the date on which Customer exceeded the Threshold. 38 | 39 | b) Non-profit and educational organizations are eligible for a free license for Production Use of the Open Core Software, subject to Pimcore’s non-profit criteria. 40 | 41 | Pimcore shall decide at its own reasonable discretion whether (a) the Threshold is exceeded or (b) the requirements for non-profit or educational usage are met. Legal recourse is excluded with regard to such decision of Pimcore. 42 | 43 | 1.2.2 Non-Production Use and Transition to Production Use (Open Core Software) 44 | 45 | For non-production purposes, such as demonstrations, designing of prototypes, proofs of concept, and sales presentations (such and comparable usages of a software “**Non-Production Use**”), the Pimcore Developer License (PDLA) must be purchased. 46 | 47 | If the Customer or a Partner or any other third person acting on the Customer’s behalf initiates development of a solution with the intention or foreseeable or actual effect of deploying it into production, such use from its beginning shall be deemed Production Use of the Open Core Software for which the Threshold applies from the outset. Individual transition periods to Production Use may be agreed between Pimcore and Customer in writing. 48 | 49 | Pimcore reserves the right to audit, verify and enforce compliance with these terms, including restricting or terminating access to the Open Core Software. 50 | 51 | 1.2.3 The use of Proprietary Software is never free of charge. Sect. 1.2.1 and 1.2.2 do not apply to Proprietary Software. 52 | 53 | 1.3 Restrictions on Use 54 | 55 | 1.3.1 The Customer may not offer the Software as a hosted or managed service by granting third parties access to a significant part of its features or functions. Additionally, the Customer may not fork, modify, or redistribute the Software, or any Derivative, in a manner that results in a competing or functionally comparable product that is offered as a free or commercial alternative to Pimcore’s official offerings. 56 | 57 | 1.3.2 The Customer shall also refrain from incorporating the Software, or any Derivative, into a commercial product or service offering materially deriving its economic value from the Software, even if it is not directly exposed or obvious. 58 | 59 | 1.3.3 The Customer is also prohibited from representing, implying, or otherwise suggesting that its use, distribution, or customization of the Software is endorsed, certified, or supported by Pimcore, unless such authorization has been explicitly granted in writing. 60 | 61 | 1.3.4 The Customer may only use the Software for its own enterprise. The Customer may not use the Software simultaneously in more instances than Customer has acquired usage licences for. The Customer is only permitted to copy the Software to the extent that this is necessary for the intended use, including the correction of errors. The creation of a backup copy is permitted if it is necessary to secure the contractual use. 62 | 63 | 1.3.5 The Customer must not, at any time, (i) rent, lease, lend, sell, license, assign, distribute, publish, transfer, or otherwise make available the Software; (ii) reverse engineer, disassemble, decompile, decode, adapt, or otherwise attempt to derive or gain access to source code of the Proprietary Software, in whole or in part; (iii) use the Software in any manner or for any purpose that infringes, misappropriate, or otherwise wireless any intellectual property ride or other ride of any person, or that violates any applicable law. 64 | 65 | 1.4 If the Customer violates any of the provisions Sect. 1.2 and 1.3, all rights of usage granted under the POCL shall immediately become invalid and shall automatically revert to Pimcore. In this case, the Customer must immediately and completely cease using the Software, delete all copies of the Software installed on its systems and delete any backup copies made or hand them over to Pimcore. In addition, Pimcore reserves the right to take all legal steps. 66 | 67 | 1.5 Sect. 1.4 applies accordingly if a Derivative of the Customer infringe upon patents. 68 | 69 | 1.6 The parties may agree on expanded usage rights, arrangements for enterprise customers, and special OEM provisions separately. 70 | 71 | 1.7 Upon request, the Customer shall enable Pimcore to verify the proper use of the Software, in particular whether the Customer is using the Software as agreed. For this purpose, the Customer shall provide Pimcore with information, grant access to relevant documents and records and enable an audit of the hardware and software environment by Pimcore or an auditing company named by Pimcore and acceptable to the Customer. Pimcore may carry out the audit on the Customer's premises during the Customer's regular business hours or have it carried out by third parties bound to secrecy. Pimcore shall ensure that the Customer's business operations are disturbed as little as possible by the on-site audit. If the inspection reveals a licence violation by the Customer that is not merely minimal, the Customer shall bear the costs of the inspection, otherwise Pimcore shall bear them. Pimcore reserves all other rights. 72 | 73 | 1.8 Licensee acknowledges that, as between Licensee and Licensor, Licensor owns all right, title, and interest, including all intellectual property rights, in and to the Software and, with respect to third-party products, the applicable third-party licensors own all right, title and interest, including all intellectual property rights, in and to the third-party products. 74 | 75 | 1.9 Licensor reserves all rights not expressly granted to Licensee in this Agreement. Except for the limited rights and licenses expressly granted under this Agreement, nothing in this Agreement grants, by implication, waiver, estoppel, or otherwise, to Licensee or any third party any intellectual property rights or other right, title, or interest in or to the Software. 76 | 77 | ### 2. CONTRIBUTIONS OF DERIVATIVES 78 | 2.1 If the Customer wishes to contribute to the Software or to distribute a Derivative, both must be made in accordance with the Pimcore Contributors License Agreement (“PCLA”), available at . The PCLA stipulates the terms under which intellectual contributions are managed, ensuring that all parties' rights are protected. Acceptance of the PCLA is mandatory for all contributors and can be reviewed on the source-code repository. Contributions without adherence to the PCLA will not be accepted. 79 | 80 | 2.2 Any contribution to the Software by a Derivative must be clearly documented, in order to maintain transparency and integrity of the source code. 81 | 82 | 2.3. Any Derivative distributed must prominently be specified as “Derivative”, comply with the terms of the POCL, include copyright notices, and be licensed as a whole under the terms of the POCL, with the proviso that the recipient (licensee) of the out-licensed Derivative gets the role of the “Customer” regarding rights and obligations. Upon distribution of any Derivative, recipient must be provided with a copy of this POCL. 83 | 84 | ### 3. COLLATERAL OBLIGATIONS OF THE CUSTOMER 85 | 86 | 3.1 The Customer shall not manipulate, in particular modify, move, remove, suppress, switch off or circumvent licence keys and technical protection mechanisms in the Software, e. g. directly, or through the use of intermediaries, white-labelling, or segmentation of services designed to avoid licensing obligations. 87 | 88 | 3.2 The Customer shall not alter or obfuscate any of the Pimcore's licensing, copyright, or other proprietary notices within the Software. Any use of Pimcore’s trademarks must comply with applicable laws. 89 | 90 | 3.3 The Customer shall not modify, relocate, disable, or bypass any functionalities associated with the Pimcore Store. 91 | 92 | 3.4 The Customer shall not (a) use GPLv3-licensed Pimcore software alongside POCL licensed Software, and shall not (b) revert from POCL to GPLv3, to protect the Customer’s rights in Derivatives. 93 | 94 | 3.5 The Customer must ensure that the access data to the user accounts is not passed on to unauthorised third parties and is protected against unauthorised access by third parties. The authorised users shall be instructed accordingly. The Customer shall inform Pimcore immediately if there is a suspicion of misuse of the Software. 95 | 96 | 3.6 If Customer infringes upon one of the provisions set up by Sect. 3.1 through 3.5, Sect. 1.4 sentence 1 applies accordingly. 97 | 98 | ### 4. CONFIDENTIALITY 99 | 100 | From time to time during the Term, either Party may disclose or make available to the other Party information about its business affairs, products, confidential intellectual property, trade secrets, third-party confidential information, and other sensitive or proprietary information, whether orally or in written, electronic, or other form or media, and whether or not marked, designated or otherwise identified as "confidential" (collectively, "**Confidential Information**"). Confidential Information does not include information that, at the time of disclosure is: (a) in the public domain; (b) known to the receiving Party at the time of disclosure; (c) rightfully obtained by the receiving Party on a non-confidential basis from a third party; or (d) independently developed by the receiving Party. The receiving Party shall not disclose the disclosing Party's Confidential Information to any person or entity, except to the receiving Party's employees who have a need to know the Confidential Information for the receiving Party to exercise its rights or perform its obligations hereunder. Notwithstanding the foregoing, each Party may disclose Confidential Information to the limited extent required (i) in order to comply with the order of a court or other governmental body, or as otherwise necessary to comply with applicable law, provided that the Party making the disclosure pursuant to the order shall first have given written notice to the other Party and made a reasonable effort to obtain a protective order; or (ii) to establish a Party's rights under this Agreement, including to make required court filings. On the expiration or termination of this Agreement, the receiving Party shall promptly return to the disclosing Party all copies, whether in written, electronic, or other form or media, of the disclosing Party's Confidential Information, or destroy all such copies and certify in writing to the disclosing Party that such Confidential Information has been destroyed. Each Party's obligations of non­disclosure with regard to Confidential Information are effective as of the Effective Date and will expire five years from the date first disclosed to the receiving Party; provided, however, with respect to any Confidential Information that constitutes a trade secret (as determined under applicable law), such obligations of non-disclosure will survive the termination or expiration of this Agreement for as long as such Confidential Information remains subject to trade secret protection under applicable law. 101 | 102 | ### 5. LIMITED WARRANTY AND WARRANTY DISCLAIMER 103 | 104 | Pimcore warrants that, at the time of delivery, the Software does not contain any virus or other malicious code that would cause the Software to become inoperable or incapable of being used in accordance with its documentation. The warranties set forth herein do not apply and become null and void if Licensee breaches any material provision of this Agreement or any instrument related hereto, or if Licensee, or any person provided access to the Software by Licensee whether or not in violation of this Agreement: (i) installs or uses the Software on or in connection with any hardware or software not specified in the documentation or expressly authorized by Licensor in writing; (ii) illicitly modifies or damages the Software; or (iii) misuses the Software, including any use of the Software other than as specified in the documentation or expressly authorized by Licensor in writing. If any Software fails to comply with the warranty set forth hereinbefore, and such failure is not excluded from warranty pursuant to this provision, Licensor shall, subject to Licensee's promptly notifying Licensor in writing of such failure, at its sole option, either: (i) repair or replace the Software, provided that Licensee provides Licensor with all information Licensor reasonably requests to resolve the reported failure, including sufficient information to enable the Licensor to recreate such failure; or (ii) refund the fees paid for such Software, subject to Licensee's ceasing all use of and, if requested by Licensor, returning to Licensor all copies of the Software. If Licensor repairs or replaces the Software, the warranty will continue to run from the Effective Date and not from Licensee's receipt of the repair or replacement. The remedies set forth in this Section 5 are Licensee's sole remedies and Licensor's sole liability under the limited warranty set forth in this Section 5. 105 | 106 | EXCEPT FOR THE LIMITED WARRANTY SET FORTH IN THIS SECTION 5, THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" AND LICENSOR HEREBY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE. LICENSOR SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT, AND ALL WARRANTIES ARISING FROM COURSE OF DEALING, USAGE, OR TRADE PRACTICE. LICENSOR MAKES NO WARRANTY OF ANY KIND THAT THE SOFTWARE AND DOCUMENTATION, OR ANY PRODUCTS OR RESULTS OF THE USE THEREOF, WILL MEET LICENSEE'S OR ANY OTHER PERSON'S REQUIREMENTS, OPERATE WITHOUT INTERRUPTION, ACHIEVE ANY INTENDED RESULT, BE COMPATIBLE OR WORK WITH ANY SOFTWARE, SYSTEM OR OTHER SERVICES, OR BE SECURE, ACCURATE, COMPLETE, FREE OF HARMFUL CODE, OR ERROR FREE. 107 | 108 | ### 6. DEFECTS 109 | 110 | 6.1 The Customer is obliged to notify Pimcore of any defect or error in the Software immediately after its occurrence. 111 | 112 | 6.2 Before reporting any defect or error, the Customer must carry out an analysis of the system environment as far as possible to ensure that the defect or error is not due to system components that are not covered by this Agreement. 113 | 114 | 6.3 The Customer shall immediately install or carry out updates or other troubleshooting measures provided by Pimcore. 115 | 116 | 6.4 Violations of the obligations to co-operate may result in additional costs for Pimcore. The Customer must reimburse Pimcore for such costs, unless it is not responsible for them. 117 | 118 | ### 7. LIMITATION OF LIABILITY 119 | 120 | IN NO EVENT WILL LICENSOR BE LIABLE UNDER OR IN CONNECTION WITH THIS AGREEMENT UNDER ANY LEGAL OR EQUITABLE THEORY, INCLUDING BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, AND OTHERWISE, FOR ANY: (a) CONSEQUENTIAL, INCIDENTAL, INDIRECT, EXEMPLARY, SPECIAL, ENHANCED, OR PUNITIVE DAMAGES; (b) INCREASED COSTS, DIMINUTION IN VALUE OR LOST BUSINESS, PRODUCTION, REVENUES, OR PROFITS; (c) LOSS OF GOODWILL OR REPUTATION; (d) USE, INABILITY TO USE, LOSS, INTERRUPTION, DELAY OR RECOVERY OF ANY DATA, OR BREACH OF DATA OR SYSTEM SECURITY; OR (e) COST OF REPLACEMENT GOODS OR SERVICES, IN EACH CASE REGARDLESS OF WHETHER LICENSOR WAS ADVISED OF THE POSSIBILITY OF SUCH LOSSES OR DAMAGES OR SUCH LOSSES OR DAMAGES WERE OTHERWISE FORESEEABLE. 121 | 122 | IN NO EVENT WILL LICENSOR'S AGGREGATE LIABILITY ARISING OUT OF OR RELATED TO THIS AGREEMENT UNDER ANY LEGAL OR EQUITABLE THEORY, INCLUDING BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, AND OTHERWISE EXCEED THE TOTAL AMOUNTS PAID TO LICENSOR UNDER THIS AGREEMENT IN THE TWELVE (12) MONTH PERIOD PRECEDING THE EVENT GIVING RISE TO THE CLAIM. 123 | 124 | ### 8. INDEMNIFICATION 125 | 126 | The Customer shall indemnify Pimcore and its affiliates, officers, directors, employees, agents, and assigns, from and against all claims, losses, damages, liabilities, costs, and expenses (including reasonable attorney’s fees and costs) against Pimcore arising out of or relating to the Customer’s use of the Software or Derivatives. 127 | 128 | ### 9. TERMINATION 129 | 130 | Term and termination will be regulated separately. If Customer uses the Software in violation of this Agreement or otherwise violates the use rights or prohibitions contained in this Agreement, Customer’s License shall automatically terminate. Upon termination of this Agreement, the Customer shall uninstall the Software, including all copies, and delete any remaining Software residues from its IT system. The Customer must destroy any backup copies made. At Pimcore's request, the Customer must confirm that it has fulfilled these obligations. 131 | 132 | ### 10. REMUNERATION 133 | 134 | The remuneration for the use of the software shall be agreed separately. 135 | 136 | ### 11. MISCELLANEOUS 137 | 138 | 11.1 The Software may automatically collect and transmit non-personal statistical data related to its installation and use, including but not limited to the number of records in the database, installed modules, system configuration, and usage metrics ("Usage Data"). Such data is collected solely for the purposes of product improvement, support, and analytics. Licensee agrees not to interfere with the collection and transmission of Usage Data. 139 | 140 | 11.2 Licensee may not assign or transfer any of its rights or delegate any of its obligations hereunder, in each case whether voluntarily, involuntarily, by operation of law or otherwise, without the prior written consent of Licensor. Any purported assignment, transfer, or delegation in violation of this Section is null and void. No assignment, transfer, or delegation will relieve the assigning or delegating Party of any of its obligations hereunder. This Agreement is binding upon and inures to the benefit of the Parties hereto and their respective permitted successors and assigns. 141 | 142 | 11.3 Each Party acknowledges and agrees that a breach or threatened breach by such Party of any of its contractual obligations may cause the other Party irreparable harm for which monetary damages would not be an adequate remedy and agrees that, in the event of such breach or threatened breach, the other Party will be entitled to equitable relief, including a restraining order, an injunction, specific performance, and any other relief that may be available from any court, without any requirement to post a bond or other security, or to prove actual damages or that monetary damages are not an adequate remedy. Such remedies are not exclusive and are in addition to all other remedies that may be available at law, in equity, or otherwise. 143 | 144 | 11.4 No amendment to or modification of this Agreement is effective unless it is in writing and signed by an authorized representative of each Party. No waiver by any Party of any of the provisions hereof will be effective unless explicitly set forth in writing and signed by the Party so waiving. Except as otherwise set forth in this Agreement, (i) no failure to exercise, or delay in exercising, any rights, remedy, power, or privilege arising from this Agreement will operate or be construed as a waiver thereof, and (ii) no single or partial exercise of any right, remedy, power, or privilege hereunder will preclude any other or further exercise thereof or the exercise of any other right, remedy, power, or privilege. 145 | 146 | 11.5 If any provision of this Agreement is invalid, illegal, or unenforceable in any jurisdiction, such invalidity, illegality, or unenforceability will not affect any other term or provision of this Agreement or invalidate or render unenforceable such term or provision in any other jurisdiction. Upon such determination that any term or other provision is invalid, illegal, or unenforceable, the Parties hereto shall negotiate in good faith to modify this Agreement so as to effect the original intent of the Parties as closely as possible in a mutually acceptable manner in order that the transactions contemplated hereby be consummated as originally contemplated to the greatest extent possible. 147 | 148 | 11.6 In all relevant respects that are not regulated by this Agreement, the following documents shall apply, as far as applicable: 149 | 150 | - Pimcore Terms & Conditions, available at [] 151 | - Pimcore Privacy Statement (PPS) 152 | - Pimcore Data Processing Agreement (PDPA) 153 | - Pimcore PaaS Terms & Conditions 154 | 155 | 11.7 Specifications originating from the Customer regarding the service content and legal elements, such as GTC or contractual clauses, do not apply. 156 | 157 | 11.8 Support, maintenance, and other services remain subject to separate agreements. 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web2Print Tools Bundle for Pimcore 2 | 3 | Adds following additional features to the web2print functionality of Pimcore. 4 | - Favorite Output Channels 5 | - Document Editable for Configurable Tables 6 | - PDFreactor controller tool 7 | - table of contents creation (awesomizr.js) 8 | 9 | 10 | ## Favorite Output Channels 11 | Based on the OutputDataConfigToolkit (https://github.com/pimcore/output-data-config-toolkit) this feature allows 12 | to save output channels centrally and reuse them in several places. 13 | 14 | ![favorite-output-channels](doc/img/favorite-output-channels.png) 15 | 16 | 17 | ## Document Editable for Configurable Tables 18 | Editable for adding tables with configurable columns based on the output channels to documents. 19 | 20 | ![outputputcanneltable](doc/img/outputputcanneltable.png) 21 | 22 | Usage: 23 | ```twig 24 | {{ pimcore_outputchanneltable('tableconfig')|raw }} 25 | ``` 26 | Also see included sample areas PrintColumnAttributeTable and PrintRowAttributeTable. 27 | 28 | 29 | ## PDFreactor PDF Creation Helper 30 | PDFreactor PDF Creation Helper allows you to integrate web2print functionality directly into the controller and 31 | creates a pdf response from a given HTML. 32 | 33 | Usage in controller action: 34 | ```php 35 | createPDFResponse($renderedHTML); 43 | 44 | } 45 | ``` 46 | 47 | With url parameter html=1 pdf rendering is deactivated. 48 | 49 | 50 | ## Create table of contents 51 | 52 | ```js 53 | 54 | /** 55 | * - include bundles/web2printtools/vendor/js/awesomizr.js 56 | * - include bundles/web2printtools/vendor/css/awesomizr.css 57 | */ 58 | 59 | Awesomizr.createTableOfContents({ 60 | insertiontarget: '#toc-wrapper', 61 | elements: ['h1','h2'], 62 | container: {tag: 'ul', addClass: 'toc'}, 63 | line: {tag: 'li'}, 64 | disabledocumenttitle: true, 65 | toctitle: ' ', 66 | text: function (elem) { 67 | return elem.textContent; 68 | } 69 | }); 70 | 71 | ``` 72 | See example: [toc.js](https://github.com/pimcore/web2print-tools/blob/master/src/Resources/public/examples/toc.js) 73 | See full documentation and examples: [pdfreactor-manual](http://www.pdfreactor.com/product/doc/manual.pdf) 74 | 75 | ## Running with Pimcore >= 11.0 76 | If you are using or upgrading to pimcore 11, then please make sure that the `pimcore/admin-ui-classic-bundle` bundle is installed. 77 | 78 | ```bash 79 | composer require pimcore/admin-ui-classic-bundle 80 | ``` 81 | 82 | ## Running with Pimcore < 5.4 83 | With Pimcore 5.4 the location of static Pimcore files like icons has changed. In order to make this bundle work 84 | with Pimcore < 5.4, please add following rewrite rule to your `.htaccess`. 85 | ``` 86 | # rewrite rule for pre pimcore 5.4 core static files 87 | RewriteRule ^bundles/pimcoreadmin/(.*) /pimcore/static6/$1 [PT,L] 88 | ``` 89 | 90 | # Migration from Pimcore 4 91 | - change table name from `plugin_web2print_favorite_outputdefinitions` to `bundle_web2print_favorite_outputdefinitions` 92 | ```sql 93 | RENAME TABLE plugin_web2print_favorite_outputdefinitions TO bundle_web2print_favorite_outputdefinitions; 94 | ``` 95 | - changed namespace from `Web2Print` to `Web2PrintToolsBundle` 96 | - renamed permission from `plugin_web2print_favourite_output_channels' to `web2print_web2print_favourite_output_channels` 97 | ```sql 98 | UPDATE users_permission_definitions SET `key` = REPLACE(`key`, 'plugin_web2print_favourite_output_channels', 'web2print_web2print_favourite_output_channels'); 99 | UPDATE users SET permissions = REPLACE(`permissions`, 'plugin_web2print_favourite_output_channels', 'web2print_web2print_favourite_output_channels'); 100 | ``` 101 | - removed Custom Document Areas 102 | - PDFReactor controller plugin replaced by PDFreactor PDF Creation Helper 103 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you think that you have found a security issue, 6 | don’t use the bug tracker and don’t publish it publicly. 7 | Instead, all security issues must be reported via a private vulnerability report. 8 | 9 | Please follow the [instructions](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) to submit a private report. 10 | 11 | 12 | ## Resolving Process 13 | Every submitted security issue is handled with top priority by following these steps: 14 | 15 | 1. Confirm the vulnerability 16 | 2. Determine the severity 17 | 3. Contact reporter 18 | 4. Work on a patch 19 | 5. Get a CVE identification number (may be done by the reporter or a security service provider) 20 | 6. Patch reviewing 21 | 7. Tagging a new release for supported versions 22 | 8. Publish security announcement 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pimcore/web2print-tools-bundle", 3 | "type": "pimcore-bundle", 4 | "license": "proprietary", 5 | "config": { 6 | "sort-packages": true 7 | }, 8 | "prefer-stable": true, 9 | "minimum-stability": "dev", 10 | "require": { 11 | "php": "~8.3.0 || ~8.4.0", 12 | "pimcore/admin-ui-classic-bundle": "^2.0", 13 | "pimcore/pimcore": "^12.0", 14 | "pimcore/output-data-config-toolkit-bundle": "^6.0", 15 | "symfony/http-foundation": "^6.3" 16 | }, 17 | "require-dev": { 18 | "phpstan/phpstan": "^1.12.15" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Web2PrintToolsBundle\\": "src/" 23 | } 24 | }, 25 | "extra": { 26 | "pimcore": { 27 | "bundles": [ 28 | "Web2PrintToolsBundle\\Web2PrintToolsBundle" 29 | ] 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /doc/01_Upgrade/README.md: -------------------------------------------------------------------------------- 1 | # Update Notes 2 | 3 | ## Update to Version 5.2 4 | ### General 5 | - Dropped support of Pimcore 10, bumped minimum requirement of `pimcore/pimcore` to `^11.2`. Replaced all `$request->get()` with their explicit input source. -------------------------------------------------------------------------------- /doc/img/favorite-output-channels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pimcore/web2print-tools/1c5251df27db352ca6178093d2355d60f79ac5ff/doc/img/favorite-output-channels.png -------------------------------------------------------------------------------- /doc/img/outputputcanneltable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pimcore/web2print-tools/1c5251df27db352ca6178093d2355d60f79ac5ff/doc/img/outputputcanneltable.png -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | -------------------------------------------------------------------------------- /phpstan-bootstrap.php: -------------------------------------------------------------------------------- 1 | request->getString('data')) { 35 | $data = json_decode($request->request->getString('data'), true); 36 | if ($request->query->getString('xaction') === 'destroy') { 37 | $idValue = $data['id'] ?? ''; 38 | if (!empty($idValue)) { 39 | $def = FavoriteOutputDefinition::getById($idValue); 40 | if (!empty($def)) { 41 | $def->delete(); 42 | 43 | return $this->jsonResponse(['data' => [], 'success' => true]); 44 | } 45 | } 46 | 47 | throw new Exception('OutputDefinition with id ' . $idValue . ' not found.'); 48 | } elseif ($request->query->getString('xaction') === 'update') { 49 | $def = FavoriteOutputDefinition::getById($data['id']); 50 | if (!empty($def)) { 51 | $def->setValues($data); 52 | $def->save(); 53 | 54 | return $this->jsonResponse(['data' => get_object_vars($def), 'success' => true]); 55 | } 56 | 57 | throw new Exception('Definition with id ' . $data['id'] . ' not found.'); 58 | } elseif ($request->query->getString('xaction') === 'create') { 59 | unset($data['id']); 60 | $def = new FavoriteOutputDefinition(); 61 | $def->setValues($data); 62 | $def->save(); 63 | 64 | return $this->jsonResponse(['data' => get_object_vars($def), 'success' => true]); 65 | } 66 | } 67 | 68 | $list = new FavoriteOutputDefinition\Listing(); 69 | $list->setOrder('asc'); 70 | $list->setOrderKey('description'); 71 | 72 | if ($request->request->getString('sort')) { 73 | $sortConfig = json_decode($request->request->getString('sort'), true); 74 | $sortConfig = $sortConfig[0]; 75 | if ($sortConfig['property']) { 76 | $list->setOrderKey($sortConfig['property']); 77 | } 78 | if ($sortConfig['direction']) { 79 | $list->setOrder($sortConfig['direction']); 80 | } 81 | } 82 | 83 | $list->setLimit($request->request->getInt('limit')); 84 | $list->setOffset($request->request->getInt('start')); 85 | 86 | $condition = '1 = 1'; 87 | if ($request->request->getString('filter')) { 88 | $filterString = $request->request->getString('filter'); 89 | $filters = json_decode($filterString, true); 90 | 91 | $db = Db::get(); 92 | 93 | foreach ($filters as $f) { 94 | if ($f['type'] === 'string') { 95 | $condition .= ' AND ' . $db->quoteIdentifier($f['property']) . ' LIKE ' . $db->quote('%' . $f['value'] . '%'); 96 | } 97 | } 98 | $list->setCondition($condition); 99 | } 100 | 101 | $definitions = []; 102 | foreach ($list->getOutputDefinitions() as $u) { 103 | $definitions[] = get_object_vars($u); 104 | } 105 | 106 | return $this->jsonResponse(['data' => $definitions, 'success' => true, 'total' => $list->getTotalCount()]); 107 | 108 | } 109 | 110 | #[Route('/favorite-output-definitions')] 111 | public function favoriteOutputDefinitionsAction(Request $request) 112 | { 113 | $list = new FavoriteOutputDefinition\Listing(); 114 | $list->setOrder('asc'); 115 | $list->setOrderKey('description'); 116 | $condition = 'classId = ' . $list->quote($request->query->getString('classId')); 117 | $list->setCondition($condition); 118 | 119 | $definitions = []; 120 | foreach ($list->getOutputDefinitions() as $u) { 121 | $definitions[] = get_object_vars($u); 122 | } 123 | 124 | return $this->jsonResponse(['data' => $definitions, 'success' => true, 'total' => $list->getTotalCount()]); 125 | } 126 | 127 | #[Route('/save-or-update-favorite-output-definition')] 128 | public function saveOrUpdateFavoriteOutputDefinitionAction(Request $request) 129 | { 130 | 131 | $configuration = $request->request->getString('configuration'); 132 | $id = $request->request->getInt('existing'); 133 | $newName = strip_tags($request->request->getString('text')); 134 | $savedConfig = FavoriteOutputDefinition::getById($id); 135 | 136 | if ($id && $savedConfig) { 137 | $savedConfig->setConfiguration($configuration); 138 | $savedConfig->save(); 139 | 140 | return $this->jsonResponse(['success' => true]); 141 | } 142 | 143 | if ($newName) { 144 | $db = Db::get(); 145 | $list = new FavoriteOutputDefinition\Listing(); 146 | $classId = $request->request->getString('classId'); 147 | $list->setCondition('classId = ' . $list->quote($classId) . ' AND ' . $db->quoteIdentifier('description') . ' = ' . $list->quote($newName)); 148 | $existingOnes = $list->load(); 149 | if (!empty($existingOnes) && !$request->request->getBoolean('force')) { 150 | return $this->jsonResponse(['success' => false, 'nameexists' => true, 'id' => $existingOnes[0]->getId()]); 151 | } 152 | 153 | $newConfiguration = new FavoriteOutputDefinition(); 154 | $newConfiguration->setClassId($request->request->getString('classId')); 155 | $newConfiguration->setDescription($newName); 156 | $newConfiguration->setConfiguration($configuration); 157 | $newConfiguration->save(); 158 | 159 | return $this->jsonResponse(['success' => true]); 160 | } 161 | 162 | return $this->jsonResponse(['success' => false]); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/DependencyInjection/Web2PrintToolsExtension.php: -------------------------------------------------------------------------------- 1 | hasExtension('doctrine_migrations')) { 26 | $loader = new YamlFileLoader( 27 | $container, 28 | new FileLocator(__DIR__ . '/../Resources/config') 29 | ); 30 | 31 | $loader->load('doctrine_migrations.yml'); 32 | } 33 | } 34 | 35 | public function load(array $configs, ContainerBuilder $container): void 36 | { 37 | // use this to load your custom configurations 38 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); 39 | $loader->load('services.yml'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/FavoriteOutputDefinition.php: -------------------------------------------------------------------------------- 1 | getDao()->getById($id); 39 | 40 | return $config; 41 | } catch (\Exception $ex) { 42 | Logger::debug($ex->getMessage()); 43 | 44 | return null; 45 | } 46 | } 47 | 48 | /** 49 | * @param array $values 50 | * 51 | * @return FavoriteOutputDefinition 52 | */ 53 | public static function create($values = []) 54 | { 55 | $config = new self(); 56 | $config->setValues($values); 57 | 58 | return $config; 59 | } 60 | 61 | /** 62 | * @return void 63 | */ 64 | public function save() 65 | { 66 | $this->getDao()->save(); 67 | } 68 | 69 | /** 70 | * @return void 71 | */ 72 | public function delete() 73 | { 74 | $this->getDao()->delete(); 75 | } 76 | 77 | public function setConfiguration($configuration) 78 | { 79 | $this->configuration = $configuration; 80 | } 81 | 82 | public function getConfiguration() 83 | { 84 | return $this->configuration; 85 | } 86 | 87 | public function setClassId($classId) 88 | { 89 | $this->classId = $classId; 90 | } 91 | 92 | public function getClassId() 93 | { 94 | return $this->classId; 95 | } 96 | 97 | public function setId($id) 98 | { 99 | $this->id = $id; 100 | } 101 | 102 | public function getId() 103 | { 104 | return $this->id; 105 | } 106 | 107 | public function setDescription($description) 108 | { 109 | $this->description = $description; 110 | } 111 | 112 | public function getDescription() 113 | { 114 | return $this->description; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/FavoriteOutputDefinition/Dao.php: -------------------------------------------------------------------------------- 1 | validColumns = $this->getValidTableColumns(self::TABLE_NAME); 40 | } 41 | 42 | /** 43 | * @return void 44 | */ 45 | public function getById($id) 46 | { 47 | $outputDefinitionRaw = $this->db->fetchAssociative('SELECT * FROM ' . self::TABLE_NAME . ' WHERE id=?', [$id]); 48 | if (empty($outputDefinitionRaw)) { 49 | throw new \Exception('OutputDefinition-Id ' . $id . ' not found.'); 50 | } 51 | $this->assignVariablesToModel($outputDefinitionRaw); 52 | } 53 | 54 | /** 55 | * Create a new record for the object in database 56 | * 57 | * @return void 58 | */ 59 | public function create() 60 | { 61 | $this->db->insert(self::TABLE_NAME, []); 62 | $this->model->setId($this->db->lastInsertId()); 63 | 64 | $this->save(); 65 | } 66 | 67 | /** 68 | * @return void 69 | */ 70 | public function save() 71 | { 72 | if ($this->model->getId()) { 73 | $this->update(); 74 | } else { 75 | $this->create(); 76 | } 77 | } 78 | 79 | /** 80 | * @return void 81 | */ 82 | public function update() 83 | { 84 | $class = get_object_vars($this->model); 85 | 86 | $data = []; 87 | 88 | foreach ($class as $key => $value) { 89 | if (in_array($key, $this->validColumns)) { 90 | if (is_array($value) || is_object($value)) { 91 | $value = serialize($value); 92 | } elseif (is_bool($value)) { 93 | $value = (int)$value; 94 | } 95 | $data[$key] = $value; 96 | } 97 | } 98 | 99 | Helper::upsert($this->db, self::TABLE_NAME, $data, $this->getPrimaryKey(self::TABLE_NAME)); 100 | } 101 | 102 | /** 103 | * Deletes object from database 104 | * 105 | * @return void 106 | */ 107 | public function delete() 108 | { 109 | $this->db->executeStatement('DELETE FROM ' . $this->db->quoteIdentifier(self::TABLE_NAME) . ' where id = ?', [$this->model->getId()]); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/FavoriteOutputDefinition/Listing.php: -------------------------------------------------------------------------------- 1 | outputDefinitions)) { 45 | $this->load(); 46 | } 47 | 48 | return $this->outputDefinitions; 49 | } 50 | 51 | /** 52 | * @param array $outputDefinitions 53 | * 54 | * @return void 55 | */ 56 | public function setOutputDefinitions($outputDefinitions) 57 | { 58 | $this->outputDefinitions = $outputDefinitions; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/FavoriteOutputDefinition/Listing/Dao.php: -------------------------------------------------------------------------------- 1 | db->fetchAllAssociative('SELECT id FROM ' . \Web2PrintToolsBundle\FavoriteOutputDefinition\Dao::TABLE_NAME . 27 | $this->getCondition() . $this->getOrder() . $this->getOffsetLimit()); 28 | 29 | foreach ($unitIds as $row) { 30 | $configs[] = FavoriteOutputDefinition::getById($row['id']); 31 | } 32 | 33 | $this->model->setOutputDefinitions($configs); 34 | 35 | return $configs; 36 | } 37 | 38 | public function getTotalCount(): int 39 | { 40 | $amount = $this->db->fetchAssociative('SELECT COUNT(*) as amount FROM `' . \Web2PrintToolsBundle\FavoriteOutputDefinition\Dao::TABLE_NAME . '`' . $this->getCondition()); 41 | 42 | return $amount['amount']; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Migrations/PimcoreX/Version20210305134111.php: -------------------------------------------------------------------------------- 1 | getClassIdColumn($schema)) { 35 | $this->addSql('alter table ' . $db->quoteIdentifier(DAO::TABLE_NAME) . ' modify ' . $column .' varchar(50) null'); 36 | } 37 | $this->addSql('alter table ' . $db->quoteIdentifier(DAO::TABLE_NAME) . ' modify ' . $db->quoteIdentifier('description') . ' varchar(255) null'); 38 | } 39 | 40 | public function down(Schema $schema): void 41 | { 42 | $db = Db::get(); 43 | if ($column = $this->getClassIdColumn($schema)) { 44 | $this->addSql('alter table ' . $db->quoteIdentifier(DAO::TABLE_NAME) . ' modify ' . $column .' varchar(50) not null'); 45 | } 46 | $this->addSql('alter table ' . $db->quoteIdentifier(DAO::TABLE_NAME) . ' modify ' . $db->quoteIdentifier('description') . ' varchar(255) not null;'); 47 | } 48 | 49 | private function getClassIdColumn(Schema $schema): ?string 50 | { 51 | $table = $schema->getTable(DAO::TABLE_NAME); 52 | if ($table->hasColumn('o_classid')) { 53 | return 'o_classid'; 54 | } elseif ($table->hasColumn('classId')) { 55 | return 'classId'; 56 | } 57 | 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Migrations/PimcoreX/Version20230124103907.php: -------------------------------------------------------------------------------- 1 | getTable(Dao::TABLE_NAME); 34 | 35 | if ($table->hasColumn('o_classId')) { 36 | $this->addSql(sprintf( 37 | 'ALTER TABLE `%s` CHANGE COLUMN `%s` `%s` varchar(50) NULL', 38 | $table->getName(), 39 | 'o_classId', 40 | 'classId' 41 | )); 42 | } 43 | } 44 | 45 | public function down(Schema $schema): void 46 | { 47 | $table = $schema->getTable(Dao::TABLE_NAME); 48 | 49 | if ($table->hasColumn('classId')) { 50 | $this->addSql(sprintf( 51 | 'ALTER TABLE `%s` CHANGE COLUMN `%s` `%s` varchar(50) NULL', 52 | $table->getName(), 53 | 'classId', 54 | 'o_classId' 55 | )); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Migrations/Version20210305134111.php: -------------------------------------------------------------------------------- 1 | fetchAssociative("SHOW TABLES LIKE '" . Dao::TABLE_NAME . "';"); 38 | } 39 | } catch (\Exception $e) { 40 | } 41 | $installed = !empty($result); 42 | 43 | SettingsStore::set('BUNDLE_INSTALLED__Web2PrintToolsBundle\\Web2PrintToolsBundle', $installed, 'bool', 'pimcore'); 44 | } 45 | 46 | public function down(Schema $schema): void 47 | { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Model/Document/Editable/Outputchanneltable.php: -------------------------------------------------------------------------------- 1 | elements)) { 60 | $this->elements = []; 61 | foreach ($this->elementIds as $elementId) { 62 | if ($elementId['type'] == 'meta') { 63 | $subType = $elementId['subtype'] == 'default' ? 'defaultentry' : $elementId['subtype']; 64 | $classname = '\\Web2PrintToolsBundle\\Model\\Document\\Editable\\Outputchanneltable\\MetaEntry\\' . ucfirst($subType); 65 | 66 | if ($subType && class_exists($classname)) { 67 | $this->elements[] = new $classname($elementId['path'], $elementId['config']); 68 | } 69 | } else { 70 | $el = \Pimcore\Model\Element\Service::getElementById($elementId['type'], $elementId['id']); 71 | if ($el instanceof \Pimcore\Model\Element\ElementInterface) { 72 | $this->elements[] = $el; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | /** 80 | * @see EditableInterface::getData 81 | */ 82 | public function getData(): array 83 | { 84 | $this->setElements(); 85 | 86 | return [ 87 | 'selectedClass' => $this->selectedClass, 88 | 'elements' => $this->elements, 89 | 'outputChannel' => $this->outputChannel, 90 | 'selectedFavouriteOutputChannel' => $this->selectedFavouriteOutputChannel, 91 | ]; 92 | } 93 | 94 | public function getDataForResource(): array 95 | { 96 | return [ 97 | 'selectedClass' => $this->selectedClass, 98 | 'elements' => $this->elementIds, 99 | 'outputChannel' => $this->outputChannel, 100 | 'selectedFavouriteOutputChannel' => $this->selectedFavouriteOutputChannel, 101 | ]; 102 | } 103 | 104 | /** 105 | * Converts the data so it's suitable for the editmode 106 | */ 107 | public function getDataEditmode(): array 108 | { 109 | $this->setElements(); 110 | $return = []; 111 | 112 | if (is_array($this->elements) && count($this->elements) > 0) { 113 | foreach ($this->elements as $index => $element) { 114 | if ($element instanceof \Pimcore\Model\DataObject\Concrete) { 115 | $return[] = [$element->getId(), $element->getFullPath(), 'object', $element->getClassName()]; 116 | } elseif ($element instanceof \Pimcore\Model\DataObject\AbstractObject) { 117 | $return[] = [$element->getId(), $element->getFullPath(), 'object', 'folder']; 118 | } elseif ($element instanceof MetaEntry) { 119 | $subtype = str_replace('Web2PrintToolsBundle\\Model\\Document\\Editable\\Outputchanneltable\\MetaEntry\\', '', get_class($element)); 120 | 121 | //old namespace for compatibility 122 | $subtype = str_replace('Web2PrintToolsBundle\\Model\\Document\\Tag\\Outputchanneltable\\MetaEntry\\', '', $subtype); 123 | $subtype = str_replace('Pimcore\\Model\\Document\\Tag\\Outputchanneltable\\MetaEntry\\', '', $subtype); 124 | 125 | $return[] = ['a' . $index, $element->getName(), 'meta', strtolower($subtype), $element->getConfig()]; 126 | } 127 | } 128 | } 129 | 130 | return [ 131 | 'selectedClass' => $this->selectedClass, 132 | 'selectedFavouriteOutputChannel' => $this->selectedFavouriteOutputChannel, 133 | 'elements' => $return, 134 | 'outputChannel' => $this->outputChannel, 135 | 'documentId' => $this->getDocumentId(), 136 | ]; 137 | } 138 | 139 | public function frontend(): string 140 | { 141 | $this->setElements(); 142 | $return = ''; 143 | 144 | if (is_array($this->elements) && count($this->elements) > 0) { 145 | foreach ($this->elements as $element) { 146 | if ($element instanceof MetaEntry) { 147 | $return .= $element->__toString() . '
'; 148 | } else { 149 | if ($element instanceof ElementDescriptor) { 150 | $element = \Pimcore\Model\Element\Service::getElementById($element->getType(), $element->getId()); 151 | } 152 | 153 | $return .= \Pimcore\Model\Element\Service::getElementType($element) . ': ' . $element->getFullPath() . '
'; 154 | } 155 | } 156 | } 157 | 158 | return $return; 159 | } 160 | 161 | /** 162 | * @see EditableInterface::setDataFromResource 163 | * 164 | * @param mixed $data 165 | * 166 | * @return $this 167 | */ 168 | public function setDataFromResource($data): static 169 | { 170 | if ($data = \Pimcore\Tool\Serialize::unserialize($data)) { 171 | $this->setDataFromEditmode($data); 172 | } 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * @see EditableInterface::setDataFromEditmode 179 | * 180 | * @param mixed $data 181 | * 182 | * @return $this 183 | */ 184 | public function setDataFromEditmode($data): static 185 | { 186 | if (is_array($data['elements'])) { 187 | $this->elementIds = $data['elements']; 188 | } 189 | $this->outputChannel = $data['outputChannel']; 190 | $this->selectedClass = $data['selectedClass']; 191 | $this->selectedFavouriteOutputChannel = $data['selectedFavouriteOutputChannel']; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * @return \Pimcore\Model\Element\ElementInterface[] 198 | */ 199 | public function getElements() 200 | { 201 | $this->setElements(); 202 | 203 | return $this->elements; 204 | } 205 | 206 | public function isEmpty(): bool 207 | { 208 | $this->setElements(); 209 | 210 | return count($this->elements) === 0; 211 | } 212 | 213 | public function resolveDependencies(): array 214 | { 215 | $this->setElements(); 216 | $dependencies = []; 217 | 218 | if (is_array($this->elements) && count($this->elements) > 0) { 219 | foreach ($this->elements as $element) { 220 | if ($element instanceof \Pimcore\Model\DataObject\AbstractObject) { 221 | $key = 'object_' . $element->getId(); 222 | 223 | $dependencies[$key] = [ 224 | 'id' => $element->getId(), 225 | 'type' => 'object', 226 | ]; 227 | } 228 | } 229 | } 230 | 231 | return $dependencies; 232 | } 233 | 234 | public function getFromWebserviceImport($wsElement, $document = null, $params = [], $idMapper = null) 235 | { 236 | // currently unsupported 237 | return []; 238 | } 239 | 240 | public function __sleep(): array 241 | { 242 | $finalVars = []; 243 | $parentVars = parent::__sleep(); 244 | $blockedVars = ['elements']; 245 | foreach ($parentVars as $key) { 246 | if (!in_array($key, $blockedVars)) { 247 | $finalVars[] = $key; 248 | } 249 | } 250 | 251 | return $finalVars; 252 | } 253 | 254 | public function load() 255 | { 256 | $this->setElements(); 257 | } 258 | 259 | public function rewind(): void 260 | { 261 | $this->setElements(); 262 | reset($this->elements); 263 | } 264 | 265 | public function current(): mixed 266 | { 267 | $this->setElements(); 268 | 269 | return current($this->elements); 270 | } 271 | 272 | public function key(): mixed 273 | { 274 | $this->setElements(); 275 | 276 | return key($this->elements); 277 | } 278 | 279 | public function next(): void 280 | { 281 | $this->setElements(); 282 | 283 | next($this->elements); 284 | } 285 | 286 | public function valid(): bool 287 | { 288 | $this->setElements(); 289 | 290 | return $this->current() !== false; 291 | } 292 | 293 | /** 294 | * @return OutputDefinition 295 | */ 296 | public function getOutputChannel() 297 | { 298 | $tmpClass = json_decode($this->outputChannel); 299 | $config = new OutputDefinition(); 300 | $config->setId($tmpClass->id); 301 | $config->setChannel($tmpClass->channel); 302 | $config->setClassId($tmpClass->classId); 303 | 304 | $config->setConfiguration(json_encode($tmpClass->configuration)); 305 | 306 | return $config; 307 | } 308 | 309 | /** 310 | * @return string 311 | */ 312 | public function getSelectedClass() 313 | { 314 | return $this->selectedClass; 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/Model/Document/Editable/Outputchanneltable/MetaEntry.php: -------------------------------------------------------------------------------- 1 | setName($name); 34 | $this->setConfig($config); 35 | } 36 | 37 | /** 38 | * @param string $name 39 | */ 40 | public function setName($name) 41 | { 42 | $this->name = $name; 43 | } 44 | 45 | /** 46 | * @return string|null 47 | */ 48 | public function getName() 49 | { 50 | return $this->name; 51 | } 52 | 53 | /** 54 | * @param array $config 55 | */ 56 | public function setConfig($config) 57 | { 58 | $this->config = $config; 59 | } 60 | 61 | /** 62 | * @return array 63 | */ 64 | public function getConfig() 65 | { 66 | return $this->config; 67 | } 68 | 69 | public function __toString() 70 | { 71 | return $this->getName(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Model/Document/Editable/Outputchanneltable/MetaEntry/Defaultentry.php: -------------------------------------------------------------------------------- 1 | setValue($config['value']); 38 | $this->setSpan($config['span']); 39 | } 40 | 41 | /** 42 | * @param string $value 43 | * 44 | * @return void 45 | */ 46 | public function setValue($value) 47 | { 48 | $this->value = $value; 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getValue() 55 | { 56 | return $this->value; 57 | } 58 | 59 | /** 60 | * @param bool $span 61 | */ 62 | public function setSpan($span) 63 | { 64 | $this->span = $span; 65 | } 66 | 67 | /** 68 | * @return bool 69 | */ 70 | public function getSpan() 71 | { 72 | return $this->span; 73 | } 74 | 75 | public function __toString() 76 | { 77 | return $this->getName() . ': ' . $this->getValue(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Model/Document/Editable/Outputchanneltable/MetaEntry/Table.php: -------------------------------------------------------------------------------- 1 | setValues($config['values']); 48 | } 49 | 50 | public function setValues($values) 51 | { 52 | $this->values = $values; 53 | 54 | $this->spanCleanedValues = []; 55 | if ($values) { 56 | foreach ($values as $v) { 57 | for ($i = 0; $i < $v['span']; $i++) { 58 | $this->spanCleanedValues[] = $v['value']; 59 | } 60 | } 61 | } 62 | } 63 | 64 | public function getValues() 65 | { 66 | return $this->values; 67 | } 68 | 69 | public function getValue($index) 70 | { 71 | return $this->spanCleanedValues[$index]; 72 | } 73 | 74 | public function resetNextValue() 75 | { 76 | $this->nextValue = -1; 77 | } 78 | 79 | public function getNextSpanCleanedValue() 80 | { 81 | $this->nextValue++; 82 | 83 | return $this->spanCleanedValues[$this->nextValue]; 84 | } 85 | 86 | public function getNextValue() 87 | { 88 | $this->nextValue++; 89 | 90 | return $this->values[$this->nextValue]; 91 | } 92 | 93 | /** 94 | * @param bool $span 95 | */ 96 | public function setSpan($span) 97 | { 98 | $this->span = $span; 99 | } 100 | 101 | /** 102 | * @return bool 103 | */ 104 | public function getSpan() 105 | { 106 | return $this->span; 107 | } 108 | 109 | public function __toString() 110 | { 111 | return $this->getName() . ': ' . $this->getValue(0); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Resources/config/doctrine_migrations.yml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | 'Web2PrintToolsBundle\Migrations\PimcoreX': '@Web2PrintToolsBundle/Migrations/PimcoreX' 4 | -------------------------------------------------------------------------------- /src/Resources/config/pimcore/config.yml: -------------------------------------------------------------------------------- 1 | pimcore: 2 | documents: 3 | editables: 4 | map: 5 | outputchanneltable: Web2PrintToolsBundle\Model\Document\Editable\Outputchanneltable -------------------------------------------------------------------------------- /src/Resources/config/pimcore/routing.yml: -------------------------------------------------------------------------------- 1 | _pimcore_web2printtools_backend: 2 | resource: "@Web2PrintToolsBundle/Controller/" 3 | type: attribute 4 | prefix: /admin/web2printtools 5 | -------------------------------------------------------------------------------- /src/Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | _defaults: 4 | autowire: true 5 | autoconfigure: true 6 | public: false 7 | 8 | Web2PrintToolsBundle\Twig\OutputChannelExtension: 9 | tags: ['twig.extension'] 10 | 11 | 12 | Web2PrintToolsBundle\Controller\: 13 | resource: '../../Controller' 14 | tags: ['controller.service_arguments'] 15 | 16 | 17 | Web2PrintToolsBundle\Tools\Installer: 18 | public: true 19 | arguments: 20 | # fetch the bundle via expression language 21 | $bundle: "@=service('kernel').getBundle('Web2PrintToolsBundle')" -------------------------------------------------------------------------------- /src/Resources/public/css/admin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | .bundle_outputdataconfig_icon { 13 | background: url("/bundles/pimcoreadmin/img/flat-color-icons/grid.svg") center center no-repeat !important; 14 | } 15 | 16 | .bundle_outputdataconfig_nav_icon { 17 | background: url("/bundles/pimcoreadmin/img/flat-white-icons/grid.svg") center center no-repeat; 18 | } 19 | 20 | .pimcore_version_6 .bundle_outputdataconfig_nav_icon { 21 | background: url("/bundles/pimcoreadmin/img/flat-white-icons/grid.svg") center center no-repeat; 22 | } 23 | 24 | #pimcore_menu_web2print { 25 | background-image: url("/bundles/pimcoreadmin/img/flat-color-icons/news.svg") !important; 26 | } 27 | 28 | .bundle_web2print_custom_areas { 29 | background: url("/bundles/pimcoreadmin/img/flat-color-icons/area.svg") center center no-repeat !important; 30 | } 31 | .bundle_web2print_custom_areas_overlay:before { 32 | position: absolute; 33 | width: 12px; 34 | height: 12px; 35 | bottom: 0px; 36 | right: 0px; 37 | content: ""; 38 | background: url(/bundles/pimcoreadmin/img/flat-color-icons/star.svg) center center no-repeat !important; 39 | } 40 | 41 | 42 | 43 | 44 | .bundle_web2print_customAreas_config, .bundle_web2print_custom_area_icon { 45 | background: url(/bundles/pimcoreadmin/img/icon/layout_content.png) left center no-repeat !important; 46 | } 47 | 48 | 49 | 50 | 51 | .bundle_web2print_customarea_file_css { 52 | background: url(/bundles/pimcoreadmin/img/icon/css.png) left center no-repeat !important; 53 | } 54 | 55 | .bundle_web2print_customarea_file_php { 56 | background: url(/bundles/pimcoreadmin/img/icon/html.png) left center no-repeat !important; 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/Resources/public/examples/toc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | /** 13 | * Creates a table of contents into the passed 'insertiontarget' container tag. 14 | * 15 | * - include bundles/web2printtools/vendor/js/awesomizr.js 16 | * - include bundles/web2printtools/vendor/css/awesomizr.css 17 | * 18 | * Make sure css variable "page" is set in your script to get the page numbers 19 | * for the toc. 20 | * 21 | * 22 | */ 23 | 24 | Awesomizr.createTableOfContents({ 25 | /* toc container */ 26 | insertiontarget: '#toc-wrapper', 27 | /* levels to look for and link to in toc*/ 28 | elements: ['.toc-level-1','.toc-level-2'], 29 | /* container element for the toc */ 30 | container: {tag: 'ul', addClass: 'toc'}, 31 | /* container element for one line in the toc */ 32 | line: {tag: 'li'}, 33 | disabledocumenttitle: true, 34 | toctitle: ' ', 35 | /* method of getting the text for the toc lines */ 36 | text: function (elem) { 37 | var txt = elem.title; 38 | 39 | if (txt) { 40 | return txt; 41 | } 42 | 43 | return elem.textContent; 44 | } 45 | }); -------------------------------------------------------------------------------- /src/Resources/public/js/Web2Print/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | pimcore.registerNS('pimcore.plugin.web2print'); 12 | 13 | pimcore.plugin.web2print = Class.create({ 14 | getClassName: function () { 15 | return 'pimcore.plugin.web2print'; 16 | }, 17 | 18 | initialize: function () { 19 | // if the new event exists, we use this 20 | if (pimcore.events.preMenuBuild) { 21 | document.addEventListener(pimcore.events.preMenuBuild, this.createNavigationEntry.bind(this)); 22 | } else { 23 | document.addEventListener(pimcore.events.pimcoreReady, this.createNavigationEntry.bind(this)); 24 | } 25 | }, 26 | 27 | createNavigationEntry: function (e) { 28 | const perspectiveCfg = pimcore.globalmanager.get('perspective'); 29 | 30 | if(!perspectiveCfg.inToolbar('settings.favorite_outputdefinitions')){ 31 | return; 32 | } 33 | 34 | const user = pimcore.globalmanager.get('user'); 35 | if (user.isAllowed('web2print_web2print_favourite_output_channels')) { 36 | const navigationItem = { 37 | text: t('web2print_favorite_outputdefinitions'), 38 | iconCls: 'bundle_outputdataconfig_nav_icon', 39 | handler: this.openFavouriteOutputChannel 40 | }; 41 | 42 | if(e.type === pimcore.events.preMenuBuild){ 43 | let menu = e.detail.menu.settings; 44 | 45 | menu.items.push(navigationItem); 46 | } 47 | 48 | if(e.type === pimcore.events.pimcoreReady){ 49 | let menu = pimcore.globalmanager.get('layout_toolbar').settingsMenu; 50 | 51 | menu.add(navigationItem); 52 | } 53 | } 54 | }, 55 | 56 | openFavouriteOutputChannel: function () { 57 | try { 58 | pimcore.globalmanager.get('web2print.favorite_outputdefinitions').activate(); 59 | } 60 | catch (e) { 61 | pimcore.globalmanager.add('web2print.favorite_outputdefinitions', new pimcore.bundle.web2print.favoriteOutputDefinitionsTable()); 62 | } 63 | } 64 | }); 65 | 66 | const web2PrintBundle = new pimcore.plugin.web2print(); -------------------------------------------------------------------------------- /src/Resources/public/js/Web2Print/favoriteOutputDefinitions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.bundle.web2print.favoriteOutputDefinitionsTable"); 13 | pimcore.bundle.web2print.favoriteOutputDefinitionsTable = Class.create({ 14 | 15 | dataUrl: '/admin/web2printtools/admin/favorite-output-definitions-table-proxy', 16 | 17 | initialize: function () { 18 | this.getTabPanel(); 19 | }, 20 | 21 | activate: function (filter) { 22 | var tabPanel = Ext.getCmp("pimcore_panel_tabs"); 23 | tabPanel.setActiveItem("bundle_web2print_favorite_outputdefinitions"); 24 | }, 25 | 26 | getHint: function () { 27 | return ""; 28 | }, 29 | 30 | getTabPanel: function () { 31 | if (!this.panel) { 32 | this.panel = new Ext.Panel({ 33 | id: "bundle_web2print_favorite_outputdefinitions", 34 | iconCls: "bundle_outputdataconfig_icon", 35 | title: t("web2print_favorite_outputdefinitions"), 36 | border: false, 37 | layout: "fit", 38 | closable: true, 39 | items: [this.createGrid()] 40 | }); 41 | 42 | var tabPanel = Ext.getCmp("pimcore_panel_tabs"); 43 | tabPanel.add(this.panel); 44 | tabPanel.setActiveItem("bundle_web2print_favorite_outputdefinitions"); 45 | 46 | this.panel.on("destroy", function () { 47 | pimcore.globalmanager.remove("web2print.favorite_outputdefinitions"); 48 | }.bind(this)); 49 | 50 | pimcore.layout.refresh(); 51 | } 52 | 53 | return this.panel; 54 | }, 55 | 56 | createGrid: function (response) { 57 | var itemsPerPage = pimcore.helpers.grid.getDefaultPageSize(); 58 | 59 | this.store = pimcore.helpers.grid.buildDefaultStore( 60 | this.dataUrl, 61 | [ 62 | {name: 'id'}, 63 | {name: 'description'}, 64 | {name: 'classId'}, 65 | {name: 'configuration'} 66 | ], 67 | itemsPerPage 68 | ); 69 | this.pagingtoolbar = pimcore.helpers.grid.buildDefaultPagingToolbar(this.store); 70 | 71 | var gridColumns = []; 72 | 73 | gridColumns.push({header: "ID", width: 40, sortable: true, dataIndex: 'id'}); 74 | gridColumns.push({ 75 | header: t("description"), 76 | flex: 200, 77 | sortable: true, 78 | dataIndex: 'description', 79 | renderer: Ext.util.Format.htmlEncode, 80 | filter: 'string', 81 | editor: new Ext.form.TextField({}) 82 | }); 83 | gridColumns.push({ 84 | header: t("class"), width: 200, sortable: true, dataIndex: 'classId', 85 | editor: new Ext.form.ComboBox({ 86 | triggerAction: 'all', 87 | editable: false, 88 | valueField: 'id', 89 | displayField: 'text', 90 | store: pimcore.globalmanager.get("object_types_store") 91 | }), 92 | renderer: function (value) { 93 | var store = pimcore.globalmanager.get("object_types_store"); 94 | var classObject = store.getById(value); 95 | if (classObject) { 96 | return classObject.data.text; 97 | } 98 | } 99 | }); 100 | 101 | gridColumns.push({ 102 | hideable: false, 103 | xtype: 'actioncolumn', 104 | width: 30, 105 | items: [ 106 | { 107 | tooltip: t("web2print_web2print_favourite_output_channel_configuration"), 108 | iconCls: "bundle_outputdataconfig_icon", 109 | handler: function (grid, rowIndex) { 110 | 111 | var data = grid.getStore().getAt(rowIndex); 112 | 113 | if (empty(data.data.classId)) { 114 | Ext.MessageBox.alert(t("error"), t("web2print_web2print_favourite_output_channel_select_class")); 115 | return; 116 | } 117 | 118 | var channel = { 119 | id: "SOME-ID", 120 | channel: Ext.util.Format.htmlEncode(data.data.description), 121 | classId: data.data.classId, 122 | configuration: Ext.decode(data.data.configuration) 123 | }; 124 | 125 | var dialog = new pimcore.bundle.outputDataConfigToolkit.OutputDataConfigDialog( 126 | channel, 127 | this.saveConfigDialog.bind(this, grid, rowIndex) 128 | ); 129 | 130 | }.bind(this) 131 | } 132 | ] 133 | }); 134 | 135 | gridColumns.push({ 136 | hideable: false, 137 | xtype: 'actioncolumn', 138 | width: 40, 139 | items: [ 140 | { 141 | tooltip: t('remove'), 142 | icon: "/bundles/pimcoreadmin/img/flat-color-icons/delete.svg", 143 | handler: function (grid, rowIndex) { 144 | grid.getStore().removeAt(rowIndex); 145 | }.bind(this) 146 | } 147 | ] 148 | }); 149 | 150 | this.grid = Ext.create('Ext.grid.Panel', { 151 | frame: false, 152 | store: this.store, 153 | border: true, 154 | columns: gridColumns, 155 | loadMask: true, 156 | bodyCls: "pimcore_editable_grid", 157 | stripeRows: true, 158 | trackMouseOver: true, 159 | viewConfig: { 160 | forceFit: false 161 | }, 162 | plugins: [ 163 | Ext.create('Ext.grid.plugin.CellEditing', { 164 | clicksToEdit: 1 165 | }), 166 | 'pimcore.gridfilters' 167 | ], 168 | selModel: Ext.create('Ext.selection.RowModel', {}), 169 | bbar: this.pagingtoolbar, 170 | tbar: [ 171 | { 172 | text: t('add'), 173 | handler: this.onAdd.bind(this), 174 | iconCls: "pimcore_icon_add" 175 | } 176 | ] 177 | }); 178 | 179 | this.store.load(); 180 | 181 | return this.grid; 182 | }, 183 | 184 | onAdd: function (btn, ev) { 185 | var u = {}; 186 | this.grid.store.insert(0, u); 187 | }, 188 | 189 | saveConfigDialog: function (grid, rowIndex, configData) { 190 | 191 | var data = grid.getStore().getAt(rowIndex); 192 | Ext.Ajax.request({ 193 | url: '/admin/outputdataconfig/admin/get-attribute-labels', 194 | method: 'POST', 195 | params: { 196 | classId: data.data.classId, 197 | configuration: Ext.encode(configData.config) 198 | }, 199 | success: function (response) { 200 | var responseObject = Ext.decode(response.responseText); 201 | data.set("configuration", Ext.encode(responseObject.configuration)); 202 | }.bind(this) 203 | }); 204 | } 205 | 206 | }); 207 | 208 | -------------------------------------------------------------------------------- /src/Resources/public/js/Web2Print/saveAsFavouriteOutputDefinitionDialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.bundle.web2print.SaveAsFavouriteOutputDefinitionDialog"); 13 | pimcore.bundle.web2print.SaveAsFavouriteOutputDefinitionDialog = Class.create({ 14 | 15 | 16 | initialize: function (currentClassId, callback) { 17 | 18 | var nameText = new Ext.form.TextField({ 19 | name: "text", 20 | length: 255, 21 | width: 200 22 | }); 23 | 24 | var configSelector = new Ext.form.ComboBox({ 25 | name: "existing", 26 | width: 200, 27 | disabled: true, 28 | store: new Ext.data.JsonStore({ 29 | proxy: { 30 | url: '/admin/web2printtools/admin/favorite-output-definitions', 31 | type: 'ajax', 32 | reader: { 33 | type: 'json', 34 | rootProperty: "data", 35 | idProperty: 'id' 36 | }, 37 | extraParams: {classId: currentClassId} 38 | }, 39 | fields: ['id', 'description'] 40 | }), 41 | valueField: 'id', 42 | displayField: 'description', 43 | triggerAction: "all", 44 | forceSelection: true 45 | }); 46 | 47 | 48 | var radioNew = new Ext.form.Radio({ 49 | name: "selection", 50 | checked: true, 51 | value: "new", 52 | listeners: { 53 | change: function(element, checked) { 54 | nameText.setDisabled(!checked); 55 | configSelector.setDisabled(checked); 56 | } 57 | } 58 | }); 59 | 60 | var compositeNew = { 61 | xtype: 'fieldcontainer', 62 | layout: 'hbox', 63 | hideLabel: true, 64 | items: [ 65 | radioNew, 66 | {xtype: 'label', text: t("web2print_outputchanneltable_save_favorite_name"), width: 150, style: 'margin-top: 6px; margin-left: 25px'}, 67 | nameText 68 | ] 69 | }; 70 | 71 | var radioExisting = new Ext.form.Radio({ 72 | name: "selection", 73 | value: "existing", 74 | listeners: { 75 | change: function(element, checked) { 76 | nameText.setDisabled(checked); 77 | configSelector.setDisabled(!checked); 78 | } 79 | } 80 | }); 81 | 82 | var compositeExisting = { 83 | xtype: 'fieldcontainer', 84 | layout: 'hbox', 85 | hideLabel: true, 86 | items: [ 87 | radioExisting, 88 | {xtype: 'label', text: t("web2print_outputchanneltable_overwrite_favorite"), width: 150, style: 'margin-top: 6px; margin-left: 25px'}, 89 | configSelector 90 | ] 91 | }; 92 | 93 | var configPanel = new Ext.form.FormPanel({ 94 | layout: "form", 95 | bodyStyle: "padding: 10px;", 96 | labelWidth: 0, 97 | items: [compositeNew, compositeExisting], 98 | buttons: [{ 99 | text: t("apply"), 100 | iconCls: "pimcore_icon_apply", 101 | handler: function() { 102 | callback(configPanel.getForm().getFieldValues()); 103 | }.bind(this) 104 | }] 105 | }); 106 | 107 | this.dialog = new window.parent.Ext.Window({ 108 | width: 450, 109 | height: 200, 110 | modal: true, 111 | title: t('web2print_outputchanneltable_save_favorite'), 112 | layout: "fit", 113 | items: [configPanel] 114 | }); 115 | 116 | this.dialog.show(); 117 | }, 118 | 119 | close: function() { 120 | this.dialog.close(); 121 | } 122 | }); -------------------------------------------------------------------------------- /src/Resources/public/js/pimcore/document/editables/metaentry/abstract.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.document.editables.metaentry.abstract"); 13 | pimcore.document.editables.metaentry.abstract = Class.create({ 14 | 15 | type: "meta", 16 | subtype: "abstract", 17 | 18 | getInitData: function(id) { 19 | 20 | var initData = { 21 | id: id, 22 | type: this.type, 23 | subtype: this.subtype 24 | }; 25 | 26 | return initData; 27 | } 28 | 29 | }); -------------------------------------------------------------------------------- /src/Resources/public/js/pimcore/document/editables/metaentry/defaultentry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.document.editables.metaentry.defaultentry"); 13 | pimcore.document.editables.metaentry.defaultentry = Class.create(pimcore.document.editables.metaentry.abstract, { 14 | 15 | subtype: "defaultentry", 16 | 17 | openDialog: function(record) { 18 | var nameText = new Ext.form.TextField({ 19 | name: "name", 20 | fieldLabel: t('web2print_outputchanneltable_name'), 21 | length: 255, 22 | width: 200, 23 | value: record.data.path 24 | }); 25 | var valueText = new Ext.form.TextArea({ 26 | name: "value", 27 | fieldLabel: t('web2print_outputchanneltable_value'), 28 | length: 255, 29 | width: 200, 30 | height: 50, 31 | value: record.data.config ? record.data.config.value : '' 32 | }); 33 | var spanCheck = new Ext.form.field.Checkbox({ 34 | name: "span", 35 | boxLabel: t('web2print_outputchanneltable_span'), 36 | checked: record.data.config ? record.data.config.span : '' 37 | }); 38 | 39 | 40 | var configPanel = new Ext.form.FormPanel({ 41 | layout: "form", 42 | bodyStyle: "padding: 10px;", 43 | items: [nameText, valueText, spanCheck], 44 | buttons: [{ 45 | text: t("apply"), 46 | iconCls: "pimcore_icon_apply", 47 | handler: function () { 48 | this.updateMetaEntry(record, configPanel.getForm().getFieldValues()); 49 | }.bind(this) 50 | }] 51 | }); 52 | 53 | this.metaEntryWindow = new Ext.Window({ 54 | width: 450, 55 | height: 350, 56 | modal: true, 57 | title: t('web2print_outputchanneltable_metaentry'), 58 | layout: "fit", 59 | items: [configPanel] 60 | }); 61 | 62 | this.metaEntryWindow.show(); 63 | }, 64 | 65 | updateMetaEntry: function(record, values) { 66 | record.set("path", values.name); 67 | record.set("config", {'value': values.value, 'span': values.span}); 68 | this.metaEntryWindow.close(); 69 | } 70 | 71 | }); -------------------------------------------------------------------------------- /src/Resources/public/js/pimcore/document/editables/metaentry/table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.document.editables.metaentry.table"); 13 | pimcore.document.editables.metaentry.table = Class.create(pimcore.document.editables.metaentry.abstract, { 14 | 15 | subtype: "table", 16 | 17 | openDialog: function(record) { 18 | var nameText = new Ext.form.TextField({ 19 | name: "name", 20 | fieldLabel: t('web2print_outputchanneltable_name'), 21 | length: 255, 22 | width: 400, 23 | value: record.data.path 24 | }); 25 | 26 | this.valueStore = new Ext.data.JsonStore({ 27 | fields: ["value", "span"], 28 | data: record.data.config ? record.data.config.values : [] 29 | }); 30 | 31 | var valueGrid = Ext.create('Ext.grid.Panel', { 32 | bodyCls: "pimcore_editable_grid", 33 | tbar: [{ 34 | xtype: "tbtext", 35 | text: t("web2print_outputchanneltable_values") 36 | }, "-", { 37 | xtype: "button", 38 | iconCls: "pimcore_icon_add", 39 | handler: function () { 40 | var u = { 41 | value: "", 42 | span: 1 43 | }; 44 | this.valueStore.insert(0, u); 45 | }.bind(this) 46 | }], 47 | plugins: [ 48 | Ext.create('Ext.grid.plugin.CellEditing', { 49 | clicksToEdit: 1 50 | }) 51 | ], 52 | viewConfig: { 53 | plugins: { 54 | ptype: 'gridviewdragdrop', 55 | dragroup: 'element' 56 | } 57 | }, 58 | style: "margin-top: 10px", 59 | store: this.valueStore, 60 | width: "100%", 61 | selModel: Ext.create('Ext.selection.RowModel', {}), 62 | columnLines: true, 63 | columns: [ 64 | {header: t("web2print_outputchanneltable_value"), sortable: false, dataIndex: 'value', editor: new Ext.form.TextField({}), flex: 320}, 65 | {header: t("web2print_outputchanneltable_span"), sortable: false, dataIndex: 'span', editor: new Ext.form.NumberField({}), width: 180}, 66 | { 67 | xtype: 'actioncolumn', 68 | width: 40, 69 | items: [ 70 | { 71 | tooltip: t('remove'), 72 | icon: "/bundles/pimcoreadmin/img/flat-color-icons/delete.svg", 73 | handler: function (grid, rowIndex) { 74 | grid.getStore().removeAt(rowIndex); 75 | }.bind(this) 76 | } 77 | ] 78 | } 79 | ], 80 | autoHeight: true 81 | }); 82 | 83 | var configPanel = new Ext.form.FormPanel({ 84 | bodyStyle: "padding: 10px;", 85 | autoScroll: true, 86 | items: [nameText, valueGrid], 87 | buttons: [{ 88 | text: t("apply"), 89 | iconCls: "pimcore_icon_apply", 90 | handler: function () { 91 | this.updateMetaEntry(record, configPanel.getForm().getFieldValues()); 92 | }.bind(this) 93 | }] 94 | }); 95 | 96 | this.metaEntryWindow = new Ext.Window({ 97 | width: 650, 98 | height: 300, 99 | modal: true, 100 | title: t('web2print_outputchanneltable_metaentry'), 101 | layout: "fit", 102 | items: [configPanel] 103 | }); 104 | 105 | this.metaEntryWindow.show(); 106 | }, 107 | 108 | updateMetaEntry: function(record, values) { 109 | record.set("path", values.name); 110 | 111 | 112 | var options = []; 113 | this.valueStore.commitChanges(); 114 | this.valueStore.each(function (rec) { 115 | options.push({ 116 | value: rec.get("value"), 117 | span: rec.get("span") 118 | }); 119 | }); 120 | 121 | record.set("config", {'values': options}); 122 | this.metaEntryWindow.close(); 123 | } 124 | 125 | }); 126 | -------------------------------------------------------------------------------- /src/Resources/public/js/pimcore/document/editables/outputchanneltable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This source file is available under the terms of the 3 | * Pimcore Open Core License (POCL) 4 | * Full copyright and license information is available in 5 | * LICENSE.md which is distributed with this source code. 6 | * 7 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 8 | * @license Pimcore Open Core License (POCL) 9 | */ 10 | 11 | 12 | pimcore.registerNS("pimcore.document.editables.outputchanneltable"); 13 | pimcore.document.editables.outputchanneltable = Class.create(pimcore.document.editable, { 14 | 15 | selectedClass: null, 16 | selectedFavouriteOutputChannel: null, 17 | outputChannel: null, 18 | documentId: null, 19 | outputChannelName: null, 20 | allowedOperators: ['Concatenator','Group','Text'], 21 | 22 | 23 | initialize: function(id, name, options, data, inherited) { 24 | 25 | this.id = id; 26 | this.name = name; 27 | 28 | if (!options) { 29 | options = {}; 30 | } 31 | 32 | 33 | this.options = options; 34 | this.data = data.elements; 35 | 36 | if(options.selectedClass){ 37 | this.selectedClass = options.selectedClass; 38 | }else{ 39 | this.selectedClass = data.selectedClass; 40 | } 41 | 42 | this.selectedFavouriteOutputChannel = data.selectedFavouriteOutputChannel ? data.selectedFavouriteOutputChannel : ""; 43 | this.documentId = data.documentId; 44 | this.outputChannelName = "web2print_" + this.name; 45 | 46 | if(data.outputChannel) { 47 | this.outputChannel = Ext.decode(data.outputChannel); 48 | } 49 | 50 | this.setupWrapper(); 51 | 52 | 53 | this.store = new Ext.data.ArrayStore({ 54 | data: this.data, 55 | fields: [ 56 | "id", 57 | "path", 58 | "type", 59 | "subtype", 60 | "config" 61 | ], 62 | listeners: { 63 | 'add': function(store, records, index) { 64 | var record = store.getAt(index); 65 | if(record.data.type == "meta" && record.data.newRecord == true) { 66 | this.openMetaInfoDialog(record); 67 | record.data.newRecord = false; 68 | } 69 | 70 | }.bind(this) 71 | } 72 | }); 73 | 74 | 75 | var elementConfig = { 76 | disabled: !this.selectedClass, 77 | store: this.store, 78 | selModel: Ext.create('Ext.selection.RowModel', {}), 79 | viewConfig: { 80 | plugins: { 81 | ptype: 'gridviewdragdrop', 82 | dragroup: 'element' 83 | }, 84 | listeners: { 85 | drop: function(node, data, dropRec, dropPosition) { 86 | var dropOn = dropRec ? ' ' + dropPosition + ' ' + dropRec.get('name') : ' on empty view'; 87 | //Ext.example.msg('Drag from left to right', 'Dropped ' + data.records[0].get('name') + dropOn); 88 | } 89 | } 90 | }, 91 | border: false, 92 | cls: "outputchanneltable", 93 | frame: true, 94 | columns: { 95 | defaults: { 96 | sortable: false 97 | }, 98 | items: [ 99 | {header: 'ID', dataIndex: 'id', width: 50}, 100 | {header: t("path"), dataIndex: 'path', flex: 250}, 101 | {header: t("type"), dataIndex: 'type', width: 100}, 102 | {header: t("subtype"), dataIndex: 'subtype', width: 100}, 103 | { 104 | xtype: 'actioncolumn', 105 | width: 40, 106 | items: [{ 107 | tooltip: t('open'), 108 | icon: "/bundles/pimcoreadmin/img/flat-color-icons/cursor.svg", 109 | handler: function (grid, rowIndex) { 110 | var data = grid.getStore().getAt(rowIndex); 111 | 112 | if(data.data.type == "meta") { 113 | this.openMetaInfoDialog(data); 114 | } else { 115 | var subtype = data.data.subtype; 116 | if (data.data.type == "object" && data.data.subtype != "folder") { 117 | subtype = "object"; 118 | } 119 | pimcore.helpers.openElement(data.data.id, data.data.type, subtype); 120 | } 121 | }.bind(this) 122 | }] 123 | }, 124 | { 125 | xtype: 'actioncolumn', 126 | width: 40, 127 | items: [{ 128 | tooltip: t('remove'), 129 | icon: "/bundles/pimcoreadmin/img/flat-color-icons/delete.svg", 130 | handler: function (grid, rowIndex) { 131 | grid.getStore().removeAt(rowIndex); 132 | }.bind(this) 133 | }] 134 | } 135 | ] 136 | }, 137 | tbar: { 138 | items: [ 139 | { 140 | xtype: "tbspacer", 141 | width: 20, 142 | height: 16, 143 | cls: "pimcore_icon_droptarget" 144 | }, 145 | { 146 | xtype: "tbtext", 147 | text: "" + (this.options.title ? this.options.title : "") + "" 148 | }, 149 | "->", 150 | this.getAddMetaInfoControl(), 151 | { 152 | xtype: "button", 153 | iconCls: "pimcore_icon_delete", 154 | handler: this.empty.bind(this) 155 | }, 156 | { 157 | xtype: "button", 158 | iconCls: "pimcore_icon_search", 159 | handler: this.openSearchEditor.bind(this) 160 | } 161 | ] 162 | }, 163 | 164 | ddGroup: 'element' 165 | }; 166 | 167 | // height specifics 168 | if(typeof this.options.height != "undefined") { 169 | elementConfig.height = this.options.height; 170 | } else { 171 | elementConfig.autoHeight = true; 172 | } 173 | 174 | // width specifics 175 | if(typeof this.options.width != "undefined") { 176 | elementConfig.width = this.options.width; 177 | } 178 | 179 | this.gridElement = Ext.create("Ext.grid.Panel", elementConfig); 180 | 181 | this.gridElement.on("rowcontextmenu", this.onRowContextmenu.bind(this)); 182 | 183 | this.gridElement.on("afterrender", function (el) { 184 | // register at global DnD manager 185 | dndManager.addDropTarget(this.gridElement.getEl(), 186 | this.onNodeOver.bind(this), 187 | this.onNodeDrop.bind(this) 188 | ); 189 | 190 | }.bind(this)); 191 | 192 | var classStore = pimcore.globalmanager.get("object_types_store"); 193 | var possibleClasses = []; 194 | classStore.each(function (rec) { 195 | possibleClasses.push([rec.data.text, rec.data.translatedText]); 196 | }); 197 | 198 | this.classSelector = new Ext.form.ComboBox({ 199 | xtype: "combo", 200 | store: possibleClasses, 201 | mode: "local", 202 | name: "class", 203 | cls : "web2print-outputchannel-class-selector", 204 | triggerAction: "all", 205 | forceSelection: true, 206 | value: this.selectedClass, 207 | fieldLabel: t("class"), 208 | style: 'margin-bottom: 10px', 209 | listeners: { 210 | select: this.changeClass.bind(this) 211 | } 212 | }); 213 | 214 | this.configSelector = new Ext.form.ComboBox({ 215 | xtype: "combo", 216 | store: new Ext.data.JsonStore({ 217 | proxy: { 218 | url: '/admin/web2printtools/admin/favorite-output-definitions', 219 | type: 'ajax', 220 | reader: { 221 | type: 'json', 222 | rootProperty: "data", 223 | idProperty: 'id' 224 | }, 225 | extraParams: {classId: this.getCurrentClassId()} 226 | }, 227 | fields: ['id', 'description', 'configuration'] 228 | }), 229 | valueField: 'id', 230 | displayField: 'description', 231 | triggerAction: "all", 232 | forceSelection: true 233 | }); 234 | 235 | this.loadConfigButton = new Ext.Button({ 236 | text: t("web2print_area_load"), 237 | disabled: !this.selectedClass, 238 | iconCls: "pimcore_icon_reload", 239 | style: "margin-left: 6px; margin-right: 6px", 240 | handler: this.loadConfig.bind(this) 241 | }); 242 | this.currentConfigLabel = new Ext.form.Label({ 243 | text: t("web2print_outputchanneltable_last_loaded_favorite") + ": " + this.selectedFavouriteOutputChannel, 244 | style: "padding: 8px 8px 6px 0" 245 | }); 246 | 247 | this.configSelection = { 248 | xtype: 'fieldset', 249 | layout: 'hbox', 250 | border: false, 251 | fieldLabel: t("web2print_area_outputchannel"), 252 | style: "margin-top: 10px; border: none !important; padding-left: 0", 253 | items: [ 254 | this.configSelector, 255 | this.loadConfigButton, 256 | this.currentConfigLabel 257 | ] 258 | }; 259 | 260 | this.editOutputChannelButton = new Ext.Button({ 261 | text: t("web2print_area_edit_outputchannel"), 262 | disabled: !this.selectedClass, 263 | style: "margin-top: 10px; float:left;", 264 | iconCls: "bundle_outputdataconfig_icon", 265 | handler: this.openConfigDialog.bind(this) 266 | }); 267 | 268 | 269 | this.saveOutputChannelButton = new Ext.Button({ 270 | text: t("web2print_area_save_outputchannel"), 271 | disabled: !this.selectedClass, 272 | style: "margin: 10px 0 10px 10px;", 273 | iconCls: "pimcore_icon_publish", 274 | handler: this.saveFavoriteConfig.bind(this) 275 | }); 276 | 277 | var items = this.getFormItems(); 278 | this.element = new Ext.form.FormPanel({ 279 | bodyStyle: "padding: 10px;", 280 | border: false, 281 | items: items 282 | }); 283 | 284 | 285 | this.element.render(id); 286 | }, 287 | 288 | getFormItems : function(){ 289 | var items = []; 290 | if(!this.options.disableClassSelection){ 291 | items.push(this.classSelector); 292 | } 293 | items.push(this.gridElement, this.editOutputChannelButton); 294 | 295 | if(!this.options.disableFavoriteOutputChannel){ 296 | items.push(this.saveOutputChannelButton, this.configSelection) 297 | } 298 | 299 | return items; 300 | }, 301 | 302 | getAddMetaInfoControl: function () { 303 | 304 | var typeMenu = []; 305 | for(var type in window.parent.pimcore.document.editables.metaentry) { 306 | if(type != "abstract") { 307 | typeMenu.push({ 308 | text: type, 309 | handler: this.addMetaInfo.bind(this, type), 310 | iconCls: "pimcore_icon_fieldcollections" 311 | }); 312 | } 313 | } 314 | 315 | var items = []; 316 | 317 | if (typeMenu.length == 1) { 318 | items.push({ 319 | cls: "pimcore_block_button_plus", 320 | iconCls: "pimcore_icon_plus", 321 | handler: typeMenu[0].handler 322 | }); 323 | } else if (typeMenu.length > 1) { 324 | items.push({ 325 | cls: "pimcore_block_button_plus", 326 | iconCls: "pimcore_icon_plus", 327 | menu: typeMenu 328 | }); 329 | } else { 330 | items.push({ 331 | xtype: "tbtext", 332 | text: t("no_collections_allowed") 333 | }); 334 | } 335 | 336 | return items[0]; 337 | }, 338 | 339 | addMetaInfo: function(type) { 340 | 341 | var entry = new window.parent.pimcore.document.editables.metaentry[type](); 342 | var initData = entry.getInitData("meta" + (this.store.getCount() + 1)); 343 | initData.newRecord = true; 344 | 345 | // check for existing element 346 | if (!this.elementAlreadyExists(initData.id, initData.type)) { 347 | this.store.add(initData); 348 | } 349 | }, 350 | 351 | openMetaInfoDialog: function(record) { 352 | var entry = new window.parent.pimcore.document.editables.metaentry[record.data.subtype](); 353 | entry.openDialog(record); 354 | }, 355 | 356 | changeClass: function(value) { 357 | this.selectedClass = value.getValue(); 358 | this.outputChannel = null; 359 | this.empty(); 360 | this.gridElement.setDisabled(false); 361 | this.configSelector.setDisabled(false); 362 | 363 | var proxy = this.configSelector.store.getProxy(); 364 | proxy.extraParams.classId = this.getCurrentClassId(); 365 | 366 | this.configSelector.store.load({params: {classId: this.getCurrentClassId()}}); 367 | this.loadConfigButton.setDisabled(false); 368 | this.editOutputChannelButton.setDisabled(false); 369 | this.createOrGetOutputChannel(); 370 | }, 371 | 372 | loadConfig: function() { 373 | var store = this.configSelector.store; 374 | var entry = store.getById(this.configSelector.getValue()); 375 | if(entry) { 376 | var outputChannel = this.createOrGetOutputChannel(); 377 | var config = Ext.decode(entry.data.configuration); 378 | outputChannel.configuration = config; 379 | this.updateSelectedFavouriteOutputChannelLabel(entry.data.description); 380 | pimcore.helpers.showNotification(t('web2print_outputchanneltable_load_favorite'), t('web2print_outputchanneltable_load_favorite_success'), "success"); 381 | } 382 | }, 383 | 384 | saveFavoriteConfig: function() { 385 | this.saveFavoriteConfigDialog = new window.parent.pimcore.bundle.web2print.SaveAsFavouriteOutputDefinitionDialog(this.getCurrentClassId(), this.doSaveFavoriteConfig.bind(this)); 386 | }, 387 | 388 | doSaveFavoriteConfig: function(params, force) { 389 | Ext.Ajax.request({ 390 | url: '/admin/web2printtools/admin/save-or-update-favorite-output-definition', 391 | method: 'POST', 392 | params: { 393 | text: params.text, 394 | existing: params.existing, 395 | classId: this.outputChannel.classId, 396 | configuration: Ext.encode(this.outputChannel.configuration), 397 | force: force ? true : '' 398 | }, 399 | success: function(response) { 400 | var data = Ext.decode(response.responseText); 401 | if(data.success) { 402 | this.updateSelectedFavouriteOutputChannelLabel(params.text); 403 | pimcore.helpers.showNotification(t('web2print_outputchanneltable_save_favorite'), t('web2print_outputchanneltable_save_favorite_success'), "success"); 404 | this.saveFavoriteConfigDialog.close(); 405 | } else if(data.nameexists) { 406 | window.parent.Ext.MessageBox.confirm(t('web2print_outputchanneltable_save_favorite_name_exists'), t('web2print_outputchanneltable_overwrite_existing'), function(answer) { 407 | if(answer == "yes") { 408 | params.existing = data.id; 409 | this.doSaveFavoriteConfig(params, force); 410 | } 411 | }.bind(this)); 412 | } else { 413 | pimcore.helpers.showNotification(t('web2print_outputchanneltable_save_favorite'), t('web2print_outputchanneltable_save_favorite_error'), "error"); 414 | } 415 | }.bind(this) 416 | }); 417 | 418 | 419 | }, 420 | 421 | createOrGetOutputChannel: function() { 422 | if(!this.outputChannel) { 423 | this.outputChannel = { 424 | id: "SOME-ID", 425 | channel: this.outputChannelName, 426 | classId: this.getCurrentClassId(), 427 | configuration: [] 428 | }; 429 | this.saveOutputChannelButton.setDisabled(false); 430 | } 431 | return this.outputChannel; 432 | }, 433 | 434 | openConfigDialog: function() { 435 | var outputChannel = this.createOrGetOutputChannel(); 436 | var dialog = new window.parent.pimcore.bundle.outputDataConfigToolkit.OutputDataConfigDialog(outputChannel, this.saveConfigDialog.bind(this), this.allowedOperators); 437 | }, 438 | 439 | saveConfigDialog: function(data) { 440 | this.outputChannel.id = data.id; 441 | var oldConfigString = Ext.encode(this.outputChannel.configuration); 442 | this.outputChannel.configuration = data.config; 443 | 444 | this.configSelector.setValue(""); 445 | 446 | Ext.Ajax.request({ 447 | url: '/admin/outputdataconfig/admin/get-attribute-labels', 448 | method: 'POST', 449 | params: { 450 | classId: this.outputChannel.classId, 451 | configuration: Ext.encode(data.config) 452 | }, 453 | success: function(response) { 454 | var data = Ext.decode(response.responseText); 455 | 456 | if(oldConfigString != Ext.encode(data.configuration)) { 457 | this.updateSelectedFavouriteOutputChannelLabel(""); 458 | } 459 | 460 | this.outputChannel.configuration = data.configuration; 461 | }.bind(this) 462 | }); 463 | }, 464 | 465 | getCurrentClassId: function() { 466 | var classStore = pimcore.globalmanager.get("object_types_store"); 467 | var index = classStore.find("text", this.selectedClass, 0, false, false, true); 468 | if(typeof index !== 'undefined' && classStore.getAt(index)) { 469 | return classStore.getAt(index).id; 470 | } 471 | }, 472 | 473 | onNodeOver: function(target, dd, e, data) { 474 | var record = data.records[0]; 475 | var data = record.data; 476 | 477 | if(data.elementType == "object" && data.className == this.selectedClass) { 478 | return Ext.dd.DropZone.prototype.dropAllowed; 479 | } else { 480 | return Ext.dd.DropZone.prototype.dropNotAllowed; 481 | } 482 | }, 483 | 484 | onNodeDrop: function (target, dd, e, data) { 485 | var record = data.records[0]; 486 | var data = record.data; 487 | 488 | //data = this.getCustomPimcoreDropData(data); 489 | if(data.elementType == "object" && data.className == this.selectedClass) { 490 | var initData = { 491 | id: data.id, 492 | path: data.path, 493 | type: data.elementType 494 | }; 495 | 496 | if (initData.type == "object") { 497 | if (data.className) { 498 | initData.subtype = data.className; 499 | } 500 | else { 501 | initData.subtype = "folder"; 502 | } 503 | } 504 | 505 | if (initData.type == "document" || initData.type == "asset") { 506 | initData.subtype = data.type; 507 | } 508 | 509 | // check for existing element 510 | if (!this.elementAlreadyExists(initData.id, initData.type)) { 511 | this.store.add(initData); 512 | return true; 513 | } 514 | } 515 | 516 | return false; 517 | 518 | }, 519 | 520 | onRowContextmenu: function (grid, record, tr, rowIndex, e, eOpts) { 521 | 522 | var menu = new Ext.menu.Menu(); 523 | var data = grid.getStore().getAt(rowIndex); 524 | 525 | menu.add(new Ext.menu.Item({ 526 | text: t('remove'), 527 | iconCls: "pimcore_icon_delete", 528 | handler: this.removeElement.bind(this, rowIndex) 529 | })); 530 | 531 | menu.add(new Ext.menu.Item({ 532 | text: t('open'), 533 | iconCls: "pimcore_icon_open", 534 | handler: function (data, item) { 535 | 536 | item.parentMenu.destroy(); 537 | 538 | if(data.data.type == 'meta') { 539 | 540 | this.openMetaInfoDialog(data); 541 | 542 | } else if(data.data.type == "object" ) { 543 | 544 | var subtype = data.data.subtype; 545 | if (data.data.type == "object" && data.data.subtype != "folder") { 546 | subtype = "object"; 547 | } 548 | pimcore.helpers.openElement(data.data.id, data.data.type, subtype); 549 | } 550 | 551 | }.bind(this, data) 552 | })); 553 | 554 | menu.add(new Ext.menu.Item({ 555 | text: t('search'), 556 | iconCls: "pimcore_icon_search", 557 | handler: function (item) { 558 | item.parentMenu.destroy(); 559 | this.openSearchEditor(); 560 | }.bind(this) 561 | })); 562 | 563 | e.stopEvent(); 564 | menu.showAt(e.pageX, e.pageY); 565 | }, 566 | 567 | openSearchEditor: function () { 568 | 569 | var restrictions = { 570 | type: ["object"], 571 | subtype: { 572 | object: ["object", "folder", "variant"] 573 | }, 574 | forceSubtypeFilter: true, 575 | specific: { 576 | classes: [this.selectedClass] 577 | } 578 | }; 579 | pimcore.helpers.itemselector(true, this.addDataFromSelector.bind(this), restrictions); 580 | 581 | }, 582 | 583 | elementAlreadyExists: function (id, type) { 584 | 585 | // check for existing element 586 | var result = this.store.queryBy(function (id, type, record, rid) { 587 | if (record.data.id == id && record.data.type == type) { 588 | return true; 589 | } 590 | return false; 591 | }.bind(this, id, type)); 592 | 593 | if (result.length < 1) { 594 | return false; 595 | } 596 | return true; 597 | }, 598 | 599 | addDataFromSelector: function (items) { 600 | if (items.length > 0) { 601 | for (var i = 0; i < items.length; i++) { 602 | if (!this.elementAlreadyExists(items[i].id, items[i].type)) { 603 | 604 | var subtype = items[i].subtype; 605 | if (items[i].type == "object") { 606 | if (items[i].subtype == "object") { 607 | if (items[i].classname) { 608 | subtype = items[i].classname; 609 | } 610 | } 611 | } 612 | 613 | this.store.add({ 614 | id: items[i].id, 615 | path: items[i].fullpath, 616 | type: items[i].type, 617 | subtype: subtype 618 | }); 619 | } 620 | } 621 | } 622 | }, 623 | 624 | empty: function () { 625 | this.store.removeAll(); 626 | }, 627 | 628 | removeElement: function (index, item) { 629 | this.store.removeAt(index); 630 | item.parentMenu.destroy(); 631 | }, 632 | 633 | getValue: function () { 634 | var tmData = []; 635 | 636 | var data = this.store.queryBy(function(record, id) { 637 | return true; 638 | }); 639 | 640 | 641 | for (var i = 0; i < data.items.length; i++) { 642 | tmData.push(data.items[i].data); 643 | } 644 | 645 | return { 646 | elements: tmData, 647 | outputChannel: Ext.encode(this.outputChannel), 648 | selectedClass: this.selectedClass, 649 | selectedFavouriteOutputChannel: this.selectedFavouriteOutputChannel 650 | }; 651 | }, 652 | 653 | updateSelectedFavouriteOutputChannelLabel: function(description) { 654 | this.currentConfigLabel.setText(t("web2print_outputchanneltable_last_loaded_favorite") + ": " + description); 655 | this.selectedFavouriteOutputChannel = description; 656 | }, 657 | 658 | getType: function () { 659 | return "outputchanneltable"; 660 | } 661 | }); 662 | -------------------------------------------------------------------------------- /src/Resources/public/vendor/css/awesomizr.css: -------------------------------------------------------------------------------- 1 | /* Table of Contents */ 2 | 3 | @media screen { 4 | .ro-toc a { 5 | color: gray; 6 | } 7 | } 8 | 9 | @media print { 10 | .ro-toc a { 11 | color: black; 12 | text-decoration: none; 13 | } 14 | 15 | /* leader can be dotted/space/... 16 | /* page is counter variable for page number in toc 17 | */ 18 | .ro-toc a::after { 19 | content: leader(space) target-counter(-ro-attr(href url), page); 20 | } 21 | 22 | } 23 | 24 | .ro-toc-heading::before { 25 | content: ""; 26 | } 27 | 28 | .ro-toc { 29 | break-after: always; 30 | } 31 | 32 | .ro-toc > h2 { 33 | break-before: avoid; 34 | } 35 | 36 | .ro-toc div { 37 | margin-bottom: 1.5pt; 38 | } 39 | -------------------------------------------------------------------------------- /src/Resources/public/vendor/js/awesomizr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Awesomizr 2014 RealObjects GmbH 3 | */ 4 | var Awesomizr = {}; 5 | 6 | Awesomizr.matchesSelector = function(ele, selector) { 7 | var matchesList = ['matches', 'webkitMatches', 'mozMatches', 'msMatches', 'oMatches', 'matchesSelector', 'webkitMatchesSelector', 'mozMatchesSelector', 'msMatchesSelector', 'oMatchesSelector']; 8 | 9 | for (var i = 0; i < matchesList.length; i++) { 10 | if (typeof ele[matchesList[i]] == 'function') { 11 | Awesomizr.matchesSelector = function(e, s) { 12 | return e[matchesList[i]](s); 13 | }; 14 | 15 | return Awesomizr.matchesSelector(ele, selector); 16 | } 17 | } 18 | 19 | return true; 20 | }; 21 | 22 | Awesomizr.getNextId = function(startValue, prefix, suffix) { 23 | if (startValue === undefined || (typeof startValue).toLowerCase() != "number") { 24 | startValue = 0; 25 | } 26 | 27 | if (prefix === undefined || (typeof prefix).toLowerCase() != "string" || !prefix) { 28 | prefix = "ro-id"; 29 | } 30 | 31 | if (suffix === undefined || (typeof suffix).toLowerCase() != "string") { 32 | suffix = ""; 33 | } 34 | 35 | var index = startValue; 36 | 37 | while (document.getElementById(prefix + index + suffix)) { 38 | index++; 39 | } 40 | 41 | return { index: index, prefix: prefix, value: prefix + index + suffix }; 42 | }; 43 | 44 | Awesomizr.rotateTableHeader = function(table, params) { 45 | var tablizrDefaultParams = { 46 | angle: 45, 47 | width: "auto", 48 | firstCol: false, 49 | lastCol: false, 50 | footer: false 51 | }; 52 | 53 | var angle = params.angle || tablizrDefaultParams.angle, 54 | width = params.width || "auto"; 55 | 56 | // normalize the angle 57 | while (angle > 90) { 58 | angle = angle - 180; 59 | } 60 | 61 | while (angle < -90) { 62 | angle = 180 + angle; 63 | } 64 | 65 | // there is currently an issue if the angle is exactly -90 or 90 66 | if (angle == 90) { 67 | angle = 89.9; 68 | } 69 | 70 | if (angle == -90) { 71 | angle = -89.9; 72 | } 73 | 74 | if (!table) { 75 | return; 76 | } 77 | 78 | var elems, 79 | maxWidth = 0, 80 | startCol = 0, 81 | endCol = 0; 82 | 83 | if (params.firstCol) { 84 | startCol = 1; 85 | } 86 | 87 | if (params.lastCol) { 88 | endCol = -1; 89 | } 90 | 91 | table.classList.add("awesomizr-table-table"); 92 | 93 | elems = table.querySelectorAll("thead td, thead th"); 94 | 95 | var hasNoTHead = elems.length == 0; 96 | var needsTFoot = params.footer; 97 | 98 | // if we dont have a thead then create one from the first row 99 | // also create a tfoot when necessary from the last row 100 | if (hasNoTHead || (needsTFoot = params.footer 101 | && (table.querySelectorAll("tfoot td").length == 0))) { 102 | 103 | var firstRow = table.querySelector("tr"); 104 | 105 | if (!firstRow) { 106 | return; 107 | } 108 | 109 | // Build a new valid table with thead, tbody and optional tfoot 110 | var thead = document.createElement("thead"), 111 | tbody = document.createElement("tbody"), 112 | tfoot = document.createElement("tfoot"); 113 | 114 | var rows; 115 | 116 | // get the rows of the body 117 | if (hasNoTHead) { 118 | rows = firstRow.parentNode.children; 119 | } else { 120 | // rows inside tbody 121 | rows = table.querySelectorAll("tbody > tr"); 122 | if (rows.lenght == 0) { 123 | // rows might be direct children of table 124 | var hadId = true; 125 | if (!table.id) { 126 | hadID = false; 127 | table.id = "awesomizr-table-no-tbody-helper-id"; 128 | } 129 | 130 | rows = table.parentNode.querySelectorAll("#" + table.id + " > tr"); 131 | 132 | if (!hadId) { 133 | table.id = ""; 134 | } 135 | } 136 | } 137 | 138 | var length = rows.length; 139 | 140 | if (hasNoTHead) { 141 | // use first row as head 142 | thead.appendChild(firstRow.cloneNode(true)); 143 | } else { 144 | // continue to use thead 145 | thead = table.querySelector("thead").cloneNode(true); 146 | } 147 | if (params.footer && needsTFoot && length > 0) { 148 | // use the last row as tfoot 149 | tfoot.appendChild(rows[--length].cloneNode(true)); 150 | } 151 | 152 | // use the remaining rows for tbody 153 | for (var i = (hasNoTHead ? 1 : 0); i < length; i++) { 154 | tbody.appendChild(rows[i].cloneNode(true)); 155 | } 156 | table.innerHTML = ""; 157 | 158 | table.appendChild(thead); 159 | table.appendChild(tbody); 160 | table.appendChild(tfoot); 161 | elems = table.querySelectorAll("thead td, thead th"); 162 | } 163 | 164 | for (var i = 0; i < elems.length; i++) { 165 | elems[i].innerHTML = "
" + elems[i].innerHTML + "
"; 166 | elems[i].firstElementChild.style.overflow = "visible"; 167 | elems[i].firstElementChild.style.whitespace = "nowrap"; 168 | elems[i].style.verticalAlign = "bottom"; 169 | 170 | elems[i].style.MozTransformOrigin = "50% 100%"; 171 | elems[i].style.WebkitTransformOrigin = "50% 100%"; 172 | elems[i].style.MsTransformOrigin = "50% 100%"; 173 | elems[i].style.OTransformOrigin = "50% 100%"; 174 | elems[i].style.transformOrigin = "50% 100%"; 175 | 176 | 177 | 178 | if (i >= startCol && i < elems.length + endCol) { 179 | elems[i].style.textAlign = "center"; 180 | elems[i].style.width = width; 181 | 182 | // some styles to extract the width 183 | elems[i].firstElementChild.style.position = "absolute"; 184 | elems[i].firstElementChild.style["float"] = "left"; 185 | elems[i].firstElementChild.style.maxWidth = "none"; 186 | 187 | var curWidth = elems[i].firstElementChild.clientWidth; 188 | maxWidth = Math.max(maxWidth,curWidth); 189 | 190 | // style back 191 | elems[i].firstElementChild.style.maxWidth = "0pt"; 192 | elems[i].firstElementChild.style.position = "static"; 193 | elems[i].firstElementChild.style["float"] = "none"; 194 | elems[i].firstElementChild.style.marginLeft = "50%"; 195 | 196 | // flip the direction of the text if the angle is less than 0 197 | if (angle < 0 || angle >= 180) { 198 | elems[i].firstElementChild.firstElementChild.style.MozTransform = "translateX(" + (-curWidth) + "px)"; 199 | elems[i].firstElementChild.firstElementChild.style.WebkitTransform = "translateX(" + (-curWidth) + "px)"; 200 | elems[i].firstElementChild.firstElementChild.style.MsTransform = "translateX(" + (-curWidth) + "px)"; 201 | elems[i].firstElementChild.firstElementChild.style.OTransform = "translateX(" + (-curWidth) + "px)"; 202 | elems[i].firstElementChild.firstElementChild.style.transform = "translateX(" + (-curWidth) + "px)"; 203 | } 204 | 205 | elems[i].style.MozTransform = "skewX(" + (-angle) + "deg)"; 206 | elems[i].style.WebkitTransform = "skewX(" + (-angle) + "deg)"; 207 | elems[i].style.MsTransform = "skewX(" + (-angle) + "deg)"; 208 | elems[i].style.OTransform = "skewX(" + (-angle) + "deg)"; 209 | elems[i].style.transform = "skewX(" + (-angle) + "deg)"; 210 | } 211 | } 212 | 213 | maxWidth = maxWidth * 1.5; 214 | table.querySelector("thead tr").style.height = (maxWidth * Math.abs(Math.cos(angle/180 * Math.PI))) + "px"; 215 | elems = table.querySelectorAll("thead td > div"); 216 | 217 | for (var i = startCol; i < elems.length + endCol; i++) { 218 | var rotation = angle - 90; 219 | 220 | if (angle < 0 || angle > 180) { 221 | rotation = angle - 270; 222 | } 223 | 224 | elems[i].style.MozTransform = "skewX(" + angle + "deg) rotate(" + rotation + "deg)"; 225 | elems[i].style.WebkitTransform = "skewX(" + angle + "deg) rotate(" + rotation + "deg)"; 226 | elems[i].style.MsTransform = "skewX(" + angle + "deg) rotate(" + rotation + "deg)"; 227 | elems[i].style.OTransform = "skewX(" + angle + "deg) rotate(" + rotation + "deg)"; 228 | elems[i].style.transform = "skewX(" + angle + "deg) rotate(" + rotation + "deg)"; 229 | } 230 | 231 | // if we have a special first column and/or last column 232 | var specialCols = []; 233 | 234 | if (startCol > 0) specialCols.push(0); 235 | if (endCol < 0) specialCols.push(elems.length - 1); 236 | 237 | if (specialCols.length > 0) { 238 | for (var i = 0; i < specialCols.length; i++) { 239 | elems[specialCols[i]].parentNode.className = specialCols[i] === 0 ? 240 | "awesomizr-table-first-column" : "awesomizr-table-last-column"; 241 | elems[specialCols[i]].style.width = "auto"; 242 | } 243 | } 244 | }; 245 | 246 | Awesomizr.createTableOfContents = function(params) { 247 | var tocDefaultParams = { 248 | insertiontarget: "body", 249 | insertiontype: "afterbegin", 250 | elements: ["h1", "h2"], 251 | toctitle: "Table of Contents", 252 | disabledocumenttitle: false, 253 | text: null, 254 | }; 255 | 256 | var toc = ''; 257 | 258 | var lineTag = params.line.tag || 'div'; 259 | var lineClass = params.line.addClass || ''; 260 | 261 | var containerTag = params.container.tag || 'div'; 262 | var containerClass = params.container.addClass || ''; 263 | 264 | var tocHeadingClass = "ro-toc-heading"; 265 | var tocClass = "ro-toc"; 266 | 267 | if (!params) { 268 | params = {}; 269 | } 270 | 271 | var elementStrings = params.elements || tocDefaultParams.elements; 272 | var target = params.insertiontarget || tocDefaultParams.insertiontarget; 273 | var insertionType = params.insertiontype || tocDefaultParams.insertiontype; 274 | var tocTitle = params.toctitle || tocDefaultParams.toctitle; 275 | var disableDocumentTitle = params.disabledocumenttitle !== undefined 276 | ? params.disabledocumenttitle 277 | : tocDefaultParams.disabledocumenttitle; 278 | var textContent = params.text || tocDefaultParams.text; 279 | 280 | // Check whether the elements parameter is a string instead of an array 281 | if ((typeof elementStrings).toLowerCase() == "string") { 282 | elementStrings = [elementStrings]; 283 | } 284 | 285 | // Get an selector for all heading elements that should be added to the toc 286 | var selector = elementStrings[0]; 287 | for (var i = 1; i < elementStrings.length; i++) { 288 | selector += ", " + elementStrings[i]; 289 | } 290 | 291 | // Create the TOC HTML 292 | var elements = document.querySelectorAll(selector); 293 | var idNumber = 0; 294 | for (var i = 0; i < elements.length; i++) { 295 | var ele = elements[i]; 296 | var id = ele.id; 297 | var text = null; 298 | 299 | if ((typeof textContent).toLowerCase() === "function") { 300 | text = params.text(ele); 301 | 302 | if (text === false) { 303 | continue; 304 | } else if (text === true) { 305 | text = null; 306 | } else { 307 | text += ""; 308 | } 309 | } 310 | 311 | if (text === null) { 312 | text = ele.textContent; 313 | } 314 | 315 | if (!id) { 316 | var nextId = Awesomizr.getNextId(idNumber, "ro-toc-heading"); 317 | 318 | idNumber = nextId.index; 319 | id = nextId.value; 320 | 321 | ele.id = id; 322 | } 323 | var tocLevel = 0; 324 | for (var k = 0; k < elementStrings.length; k++) { 325 | if (Awesomizr.matchesSelector(ele, elementStrings[k])) { 326 | tocLevel = k+1; 327 | break; 328 | } 329 | } 330 | 331 | // Add line to TOC HTML 332 | toc += '<'+lineTag+' class="'+lineClass+' ro-toc-level-' + tocLevel + '">' 333 | + text + ''; 334 | } 335 | 336 | // Prepare to wrap TOC HTML into container 337 | var tocContainer = '<'+containerTag+' class="'+containerClass+' ' + tocClass + '">'; 338 | 339 | if (!disableDocumentTitle) { 340 | // Add the document title as a heading 341 | tocContainer += '

' + document.title + '

'; 342 | } 343 | 344 | // Only insert a heading for the toc if it has not been set to empty string 345 | if (tocTitle.length > 0) { 346 | tocContainer += '

' + tocTitle + '

'; 347 | } 348 | tocContainer += toc + ''; 349 | 350 | // Insert TOC HTML before the content of target (body by default) 351 | document.querySelector(target).insertAdjacentHTML(insertionType, tocContainer); 352 | }; 353 | 354 | Awesomizr.applyAdaptivePageBreaks = function(selector, threshold) { 355 | // Check whether the required JS API exists 356 | if (ro === undefined || ro.layout === undefined) { 357 | return; 358 | } 359 | 360 | // Default selector value 361 | if (selector === undefined) { 362 | selector = "h1, h2"; 363 | } 364 | // Default threshold 365 | if (threshold === undefined) { 366 | threshold = 67; 367 | } 368 | 369 | // If the threshold is a string (e.g. "50%") convert it to a 0-100 value. 370 | if ((typeof threshold).toLowerCase() == "string") { 371 | threshold = Math.max(Math.min(parseInt(threshold), 100), 0); 372 | } 373 | 374 | // 50% -> 0.5 375 | threshold = threshold / 100; 376 | 377 | // Iterate over the selected elements 378 | var elements = document.querySelectorAll(selector); 379 | for (var i = 0; i < elements.length; i++) { 380 | var element = elements[i]; 381 | var boxDescList; 382 | var compStyle; 383 | var compDisplayStyle; 384 | // if necessary move up until we find an element that is not inline, has at least one box and is not the body or root element 385 | while(element && ( 386 | element == document 387 | || element == document.documentElement 388 | || element == document.body 389 | || !(compStyle = window.getComputedStyle(element)) 390 | || !(compDisplayStyle = compStyle.display) 391 | || (compDisplayStyle == "inline") 392 | || (compDisplayStyle == "none") 393 | || (compDisplayStyle == "table-cell") 394 | || !(boxDescList = ro.layout.getBoxDescriptions(element)) 395 | || (boxDescList.length == 0))) { 396 | element = element.parentNode; 397 | } 398 | if(element) { 399 | var boxDesc = boxDescList[0]; 400 | var boxRect = boxDesc.borderRectInPage; 401 | var pageMarginRect = boxDesc.pageDescription.marginRect; 402 | // Check whether the box starts below the threshold value 403 | if (boxRect.top > pageMarginRect.height * threshold) { 404 | // Add a page break 405 | element.style.breakBefore = "page"; 406 | } 407 | } 408 | } 409 | }; 410 | -------------------------------------------------------------------------------- /src/Resources/translations/admin.de.yml: -------------------------------------------------------------------------------- 1 | web2print_area_outputchannel: Ausgabekanal 2 | web2print_outputchanneltable_name: Name 3 | web2print_outputchanneltable_value: Wert 4 | web2print_outputchanneltable_span: Tablespan entlang aller Einträge (funktioniert nur ohne Untergruppen) 5 | web2print_outputchanneltable_metaentry: Meta-Eintrag 6 | web2print_outputchanneltable_values: Werte 7 | web2print_area_load: Laden 8 | web2print_area_edit_outputchannel: Aktuelle Ausgabekanaldefinition bearbeiten 9 | web2print_area_save_outputchannel: Aktuelle Ausgabekanaldefinition speichern 10 | web2print_favorite_outputdefinitions: Gespeicherte Ausgabekanaldefinitionen 11 | web2print_outputchanneltable_save_favorite: Aktuelle Ausgabekanaldefinition speichern als 12 | web2print_outputchanneltable_save_favorite_name: neue 13 | web2print_outputchanneltable_overwrite_favorite: bestehende ersetzen 14 | web2print_outputchanneltable_save_favorite_success: Speichern der Ausgabekanaldefinition erfolgreich 15 | web2print_outputchanneltable_save_favorite_error: Fehler beim Speichern der Ausgabekanaldefinition 16 | web2print_outputchanneltable_load_favorite: Laden 17 | web2print_outputchanneltable_load_favorite_success: Laden der Ausgabekanaldefinition erfolgreich 18 | web2print_outputchanneltable_last_loaded_favorite: zuletzt geladen 19 | web2print_outputchanneltable_save_favorite_name_exists: Name existiert bereits. 20 | web2print_outputchanneltable_overwrite_existing: Soll die bestehende Definition überschrieben werden? 21 | web2print_web2print_mainmenu: Web2Print-Tools 22 | web2print_web2print_favourite_output_channels: Web2Print gespeicherte Ausgabekanaldefinitionen -------------------------------------------------------------------------------- /src/Resources/translations/admin.en.yml: -------------------------------------------------------------------------------- 1 | web2print_area_outputchannel: Output Channel 2 | web2print_outputchanneltable_name: Name 3 | web2print_outputchanneltable_value: Value 4 | web2print_outputchanneltable_span: Table span along all entries (works only without subgroups) 5 | web2print_outputchanneltable_values: Values 6 | web2print_outputchanneltable_metaentry: Meta Entry 7 | web2print_area_load: Load 8 | web2print_area_edit_outputchannel: Edit current Output Channel Config 9 | web2print_area_save_outputchannel: Save as Favorite Output Channel Config 10 | web2print_favorite_outputdefinitions: Favorite Output Channel Configurations 11 | web2print_outputchanneltable_save_favorite: Save Output Channel Config as Favorite 12 | web2print_outputchanneltable_save_favorite_name: save new 13 | web2print_outputchanneltable_overwrite_favorite: overwrite existing 14 | web2print_outputchanneltable_save_favorite_success: Saving Output Channel Config as Favorite successful 15 | web2print_outputchanneltable_save_favorite_error: Saving Output Channel Config as Favorite failed 16 | web2print_outputchanneltable_load_favorite: Loading 17 | web2print_outputchanneltable_load_favorite_success: Loading saved Output Channel Config successful 18 | web2print_outputchanneltable_last_loaded_favorite: last loaded 19 | web2print_outputchanneltable_save_favorite_name_exists: Name exists already 20 | web2print_outputchanneltable_overwrite_existing: Do you want to overwrite the existing Output Channel Config? 21 | web2print_web2print_mainmenu: Web2Print-Tools 22 | web2print_web2print_favourite_output_channels: Web2Print Favorite Outputchannels 23 | web2print_web2print_favourite_output_channel_configuration: Output Channel Configuration 24 | web2print_web2print_favourite_output_channel_select_class: Select Class for this configuration -------------------------------------------------------------------------------- /src/Tools/Installer.php: -------------------------------------------------------------------------------- 1 | executeQuery("INSERT IGNORE INTO users_permission_definitions(`key`) VALUES ('web2print_web2print_favourite_output_channels')"); 26 | 27 | $db->executeQuery(' 28 | CREATE TABLE IF NOT EXISTS `' . Dao::TABLE_NAME . '` ( 29 | `id` int(11) NOT NULL AUTO_INCREMENT, 30 | `classId` varchar(50) NULL, 31 | `description` varchar(255) COLLATE utf8_bin NULL, 32 | `configuration` longtext CHARACTER SET latin1, 33 | PRIMARY KEY (`id`) 34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 35 | '); 36 | 37 | parent::install(); 38 | } 39 | 40 | public function needsReloadAfterInstall(): bool 41 | { 42 | return true; 43 | } 44 | 45 | public function uninstall(): void 46 | { 47 | $db = \Pimcore\Db::get(); 48 | $db->executeQuery('DROP TABLE IF EXISTS `' . Dao::TABLE_NAME . '`'); 49 | 50 | parent::uninstall(); 51 | 52 | if (self::isInstalled()) { 53 | throw new InstallationException('Could not be uninstalled.'); 54 | } 55 | } 56 | 57 | public function getLastMigrationVersionClassName(): ?string 58 | { 59 | return Version20230124103907::class; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Tools/Tool.php: -------------------------------------------------------------------------------- 1 | getChilds(), $level); 30 | 31 | if ($subLevel > $level) { 32 | $level = $subLevel; 33 | } 34 | } 35 | } 36 | 37 | return $level; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Twig/OutputChannelExtension.php: -------------------------------------------------------------------------------- 1 | getLabeledValue(new $classname())->label; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Web2PrintToolsBundle.php: -------------------------------------------------------------------------------- 1 | container->get(Installer::class); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/cli/license-updater.php: -------------------------------------------------------------------------------- 1 | \n\n" . $fileContent; 40 | } 41 | 42 | return $fileContent; 43 | } 44 | 45 | function processTEXTContent($fileContent, $license) 46 | { 47 | //remove phpstorm header 48 | $regex = '#^\s*\/\**\*\s*\**\s*Created by JetBrains PhpStorm\.\s*\**\s*User[\s\S]*\*\/\s*#U'; 49 | 50 | if (preg_match($regex, $fileContent) === 1) { 51 | $fileContent = preg_replace($regex, '', $fileContent, 1); 52 | } 53 | 54 | //remove old license 55 | $regex = '#^\s*\/\**\*\s*\**\s*Pimcore[\s\S]*\*\/#U'; 56 | 57 | if (preg_match($regex, $fileContent) === 1) { 58 | $fileContent = preg_replace($regex, '', $fileContent, 1); 59 | } 60 | 61 | //apply new license 62 | $regex = '#^[\n\s]*#'; 63 | if (preg_match($regex, $fileContent) === 1) { 64 | $fileContent = preg_replace($regex, $license . "\n\n", $fileContent, 1); 65 | } else { 66 | $fileContent = $license . "\n\n" . $fileContent; 67 | } 68 | 69 | return $fileContent; 70 | } 71 | 72 | $rootPath = '../'; 73 | $excludedDirectories = [ 74 | '../install', 75 | '../doc', 76 | '../Resources/config', 77 | '../Resources/public/vendor', 78 | ]; 79 | 80 | $license = 81 | '/** 82 | * This source file is available under the terms of the 83 | * Pimcore Open Core License (POCL) 84 | * Full copyright and license information is available in 85 | * LICENSE.md which is distributed with this source code. 86 | * 87 | * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.com) 88 | * @license Pimcore Open Core License (POCL) 89 | */ 90 | '; 91 | 92 | $excludePatterns = []; 93 | foreach ($excludedDirectories as $dir) { 94 | $excludePatterns[] = '(^' . str_replace('/', '\/', $dir) . ')'; 95 | } 96 | $excludePatterns_flattened = '/'. implode('|', $excludePatterns) .'/'; 97 | echo $excludePatterns_flattened; 98 | 99 | $files = []; 100 | 101 | $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::SELF_FIRST); 102 | 103 | foreach ($iterator as $path) { 104 | 105 | /** 106 | * @var $path SplFileInfo 107 | */ 108 | if (preg_match($excludePatterns_flattened, $path, $matches) === 1) { 109 | print $path->__toString() . ' -> exclude' . PHP_EOL; 110 | } else { 111 | print $path->__toString() . ' -> include' . PHP_EOL; 112 | if (!$path->isDir()) { 113 | $files[$path->getExtension()][] = $path->getPath() . '/' . $path->getFilename(); 114 | } 115 | } 116 | } 117 | 118 | //php files 119 | foreach ($files['php'] as $file) { 120 | echo 'process file ' . $file . '...'; 121 | $fileContent = file_get_contents($file); 122 | $fileContent = processPHPContent($fileContent, $license); 123 | // echo $fileContent; die(); 124 | file_put_contents($file, $fileContent); 125 | echo "done\n"; 126 | } 127 | 128 | //css files 129 | foreach ($files['css'] as $file) { 130 | echo 'process file ' . $file . '...'; 131 | $fileContent = file_get_contents($file); 132 | $fileContent = processTEXTContent($fileContent, $license); 133 | // echo $fileContent; die(); 134 | file_put_contents($file, $fileContent); 135 | echo "done\n"; 136 | } 137 | 138 | //txt files 139 | foreach ($files['txt'] as $file) { 140 | echo 'process file ' . $file . '...'; 141 | $fileContent = file_get_contents($file); 142 | $fileContent = processTEXTContent($fileContent, $license); 143 | // echo $fileContent; die(); 144 | file_put_contents($file, $fileContent); 145 | echo "done\n"; 146 | } 147 | 148 | //js files 149 | foreach ($files['js'] as $file) { 150 | echo 'process file ' . $file . '...'; 151 | $fileContent = file_get_contents($file); 152 | $fileContent = processTEXTContent($fileContent, $license); 153 | // echo $fileContent; die(); 154 | file_put_contents($file, $fileContent); 155 | echo "done\n"; 156 | } 157 | 158 | die("done.\n\n"); 159 | --------------------------------------------------------------------------------