├── .crowdin.yaml ├── .ddev ├── config.yaml └── homeadditions │ └── .bashrc.d │ └── path.sh ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── ci.yml │ ├── nightly-main.yml │ └── publish.yaml ├── .gitignore ├── Build ├── Scripts │ └── runTests.sh ├── php-cs-fixer │ └── config.php ├── phpstan │ ├── phpstan-baseline.neon │ ├── phpstan-constants.neon │ ├── phpstan-constants.php │ └── phpstan.neon └── phpunit │ ├── FunctionalTests.xml │ ├── FunctionalTestsBootstrap.php │ ├── UnitTests.xml │ └── UnitTestsBootstrap.php ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTING.rst ├── Classes ├── ContextMenu │ └── PasteReferenceItemProvider.php ├── DataHandler │ ├── AbstractDataHandler.php │ └── ProcessCmdmap.php ├── EventListener │ ├── AfterTcaCompilationEventListener.php │ └── ModifyPageLayoutContentListener.php ├── Helper │ └── Helper.php ├── Hooks │ ├── DataHandler.php │ └── PageLayoutController.php └── PageLayoutView │ └── ShortcutPreviewRenderer.php ├── Configuration ├── Icons.php ├── JavaScriptModules.php └── Services.yaml ├── Documentation ├── AdministratorManual │ ├── Configuration │ │ ├── ExtensionConfiguration │ │ │ └── Index.rst │ │ └── Index.rst │ ├── Index.rst │ ├── Installation │ │ └── Index.rst │ └── Update │ │ └── Index.rst ├── DeveloperManual │ ├── Contribute │ │ └── Index.rst │ └── Index.rst ├── Images │ ├── Administration │ │ └── extension-configuration.png │ └── Backend │ │ ├── copy-content-other-page.png │ │ ├── paste-copy-or-reference.png │ │ └── paste-into-column.png ├── Includes.txt ├── Index.rst ├── Introduction │ ├── About │ │ └── Index.rst │ ├── Index.rst │ ├── Support │ │ └── Index.rst │ └── Thanks │ │ └── Index.rst ├── Misc │ ├── Changelog │ │ ├── 0-1-0.rst │ │ ├── 1-0-0.rst │ │ ├── 2-0-0.rst │ │ ├── 2-0-1.rst │ │ ├── 2-0-2.rst │ │ ├── 2-0-3.rst │ │ ├── 2-0-4.rst │ │ ├── 2-0-5.rst │ │ ├── 3-0-0.rst │ │ ├── 3-0-1.rst │ │ ├── 3-0-2.rst │ │ ├── 4-0-0.rst │ │ ├── 4-0-1.rst │ │ ├── 4-0-2.rst │ │ └── Index.rst │ ├── DocumentationBestPractice │ │ ├── Examples │ │ │ └── Index.rst │ │ └── Index.rst │ ├── Index.rst │ └── MissingKnownErrors │ │ └── Index.rst ├── UsersManual │ ├── HowToStart │ │ └── Index.rst │ └── Index.rst └── guides.xml ├── LICENSE ├── README.md ├── Resources ├── Private │ └── Language │ │ ├── locallang.xlf │ │ └── locallang_db.xlf └── Public │ ├── Icons │ └── Extension.svg │ └── JavaScript │ ├── context-menu-actions.js │ ├── helper.js │ ├── paste-reference-drag-drop.js │ └── paste-reference.js ├── Tests ├── Functional │ └── DummyTest.php └── Unit │ └── DummyTest.php ├── composer.json ├── ext_conf_template.txt ├── ext_emconf.php └── ext_tables.php /.crowdin.yaml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /Resources/Private/Language/locallang*.xlf 3 | translation: /Resources/Private/Language/%two_letters_code%.%original_file_name% 4 | -------------------------------------------------------------------------------- /.ddev/config.yaml: -------------------------------------------------------------------------------- 1 | name: paste-reference 2 | type: typo3 3 | docroot: .Build/public/ 4 | php_version: "8.2" 5 | webserver_type: apache-fpm 6 | xdebug_enabled: false 7 | additional_hostnames: [] 8 | additional_fqdns: [] 9 | database: 10 | type: mariadb 11 | version: "10.4" 12 | use_dns_when_possible: true 13 | timezone: Europe/Berlin 14 | composer_version: stable 15 | web_environment: 16 | - TYPO3_CONTEXT=Development 17 | nodejs_version: "22" 18 | corepack_enable: false 19 | default_container_timeout: "240" 20 | 21 | # Key features of DDEV's config.yaml: 22 | 23 | # name: # Name of the project, automatically provides 24 | # http://projectname.ddev.site and https://projectname.ddev.site 25 | 26 | # type: # backdrop, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress 27 | # See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more 28 | # information on the different project types 29 | # "drupal" covers recent Drupal 8+ 30 | 31 | # docroot: # Relative path to the directory containing index.php. 32 | 33 | # php_version: "8.2" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4" 34 | 35 | # You can explicitly specify the webimage but this 36 | # is not recommended, as the images are often closely tied to DDEV's' behavior, 37 | # so this can break upgrades. 38 | 39 | # webimage: # nginx/php docker image. 40 | 41 | # database: 42 | # type: # mysql, mariadb, postgres 43 | # version: # database version, like "10.11" or "8.0" 44 | # MariaDB versions can be 5.5-10.8, 10.11, and 11.4. 45 | # MySQL versions can be 5.5-8.0. 46 | # PostgreSQL versions can be 9-16. 47 | 48 | # router_http_port: # Port to be used for http (defaults to global configuration, usually 80) 49 | # router_https_port: # Port for https (defaults to global configuration, usually 443) 50 | 51 | # xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart" 52 | # Note that for most people the commands 53 | # "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better, 54 | # as leaving Xdebug enabled all the time is a big performance hit. 55 | 56 | # xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart" 57 | # Note that for most people the commands 58 | # "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better, 59 | # as leaving Xhprof enabled all the time is a big performance hit. 60 | 61 | # webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn 62 | 63 | # timezone: Europe/Berlin 64 | # This is the timezone used in the containers and by PHP; 65 | # it can be set to any valid timezone, 66 | # see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 67 | # For example Europe/Dublin or MST7MDT 68 | 69 | # composer_root: 70 | # Relative path to the Composer root directory from the project root. This is 71 | # the directory which contains the composer.json and where all Composer related 72 | # commands are executed. 73 | 74 | # composer_version: "2" 75 | # You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 76 | # to use the latest major version available at the time your container is built. 77 | # It is also possible to use each other Composer version channel. This includes: 78 | # - 2.2 (latest Composer LTS version) 79 | # - stable 80 | # - preview 81 | # - snapshot 82 | # Alternatively, an explicit Composer version may be specified, for example "2.2.18". 83 | # To reinstall Composer after the image was built, run "ddev debug refresh". 84 | 85 | # nodejs_version: "20" 86 | # change from the default system Node.js version to any other version. 87 | # See https://ddev.readthedocs.io/en/stable/users/configuration/config/#nodejs_version for more information 88 | # and https://www.npmjs.com/package/n#specifying-nodejs-versions for the full documentation, 89 | # Note that using of 'ddev nvm' is discouraged because "nodejs_version" is much easier to use, 90 | # can specify any version, and is more robust than using 'nvm'. 91 | 92 | # corepack_enable: false 93 | # Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm 94 | 95 | # additional_hostnames: 96 | # - somename 97 | # - someothername 98 | # would provide http and https URLs for "somename.ddev.site" 99 | # and "someothername.ddev.site". 100 | 101 | # additional_fqdns: 102 | # - example.com 103 | # - sub1.example.com 104 | # would provide http and https URLs for "example.com" and "sub1.example.com" 105 | # Please take care with this because it can cause great confusion. 106 | 107 | # upload_dirs: "custom/upload/dir" 108 | # 109 | # upload_dirs: 110 | # - custom/upload/dir 111 | # - ../private 112 | # 113 | # would set the destination paths for ddev import-files to /custom/upload/dir 114 | # When Mutagen is enabled this path is bind-mounted so that all the files 115 | # in the upload_dirs don't have to be synced into Mutagen. 116 | 117 | # disable_upload_dirs_warning: false 118 | # If true, turns off the normal warning that says 119 | # "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" 120 | 121 | # ddev_version_constraint: "" 122 | # Example: 123 | # ddev_version_constraint: ">= 1.22.4" 124 | # This will enforce that the running ddev version is within this constraint. 125 | # See https://github.com/Masterminds/semver#checking-version-constraints for 126 | # supported constraint formats 127 | 128 | # working_dir: 129 | # web: /var/www/html 130 | # db: /home 131 | # would set the default working directory for the web and db services. 132 | # These values specify the destination directory for ddev ssh and the 133 | # directory in which commands passed into ddev exec are run. 134 | 135 | # omit_containers: [db, ddev-ssh-agent] 136 | # Currently only these containers are supported. Some containers can also be 137 | # omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit 138 | # the "db" container, several standard features of DDEV that access the 139 | # database container will be unusable. In the global configuration it is also 140 | # possible to omit ddev-router, but not here. 141 | 142 | # performance_mode: "global" 143 | # DDEV offers performance optimization strategies to improve the filesystem 144 | # performance depending on your host system. Should be configured globally. 145 | # 146 | # If set, will override the global config. Possible values are: 147 | # - "global": uses the value from the global config. 148 | # - "none": disables performance optimization for this project. 149 | # - "mutagen": enables Mutagen for this project. 150 | # - "nfs": enables NFS for this project. 151 | # 152 | # See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs 153 | # See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen 154 | 155 | # fail_on_hook_fail: False 156 | # Decide whether 'ddev start' should be interrupted by a failing hook 157 | 158 | # host_https_port: "59002" 159 | # The host port binding for https can be explicitly specified. It is 160 | # dynamic unless otherwise specified. 161 | # This is not used by most people, most people use the *router* instead 162 | # of the localhost port. 163 | 164 | # host_webserver_port: "59001" 165 | # The host port binding for the ddev-webserver can be explicitly specified. It is 166 | # dynamic unless otherwise specified. 167 | # This is not used by most people, most people use the *router* instead 168 | # of the localhost port. 169 | 170 | # host_db_port: "59002" 171 | # The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic 172 | # unless explicitly specified. 173 | 174 | # mailpit_http_port: "8025" 175 | # mailpit_https_port: "8026" 176 | # The Mailpit ports can be changed from the default 8025 and 8026 177 | 178 | # host_mailpit_port: "8025" 179 | # The mailpit port is not normally bound on the host at all, instead being routed 180 | # through ddev-router, but it can be bound directly to localhost if specified here. 181 | 182 | # webimage_extra_packages: [php7.4-tidy, php-bcmath] 183 | # Extra Debian packages that are needed in the webimage can be added here 184 | 185 | # dbimage_extra_packages: [telnet,netcat] 186 | # Extra Debian packages that are needed in the dbimage can be added here 187 | 188 | # use_dns_when_possible: true 189 | # If the host has internet access and the domain configured can 190 | # successfully be looked up, DNS will be used for hostname resolution 191 | # instead of editing /etc/hosts 192 | # Defaults to true 193 | 194 | # project_tld: ddev.site 195 | # The top-level domain used for project URLs 196 | # The default "ddev.site" allows DNS lookup via a wildcard 197 | # If you prefer you can change this to "ddev.local" to preserve 198 | # pre-v1.9 behavior. 199 | 200 | # ngrok_args: --basic-auth username:pass1234 201 | # Provide extra flags to the "ngrok http" command, see 202 | # https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" 203 | 204 | # disable_settings_management: false 205 | # If true, DDEV will not create CMS-specific settings files like 206 | # Drupal's settings.php/settings.ddev.php or TYPO3's additional.php 207 | # In this case the user must provide all such settings. 208 | 209 | # You can inject environment variables into the web container with: 210 | # web_environment: 211 | # - SOMEENV=somevalue 212 | # - SOMEOTHERENV=someothervalue 213 | 214 | # no_project_mount: false 215 | # (Experimental) If true, DDEV will not mount the project into the web container; 216 | # the user is responsible for mounting it manually or via a script. 217 | # This is to enable experimentation with alternate file mounting strategies. 218 | # For advanced users only! 219 | 220 | # bind_all_interfaces: false 221 | # If true, host ports will be bound on all network interfaces, 222 | # not the localhost interface only. This means that ports 223 | # will be available on the local network if the host firewall 224 | # allows it. 225 | 226 | # default_container_timeout: 120 227 | # The default time that DDEV waits for all containers to become ready can be increased from 228 | # the default 120. This helps in importing huge databases, for example. 229 | 230 | #web_extra_exposed_ports: 231 | #- name: nodejs 232 | # container_port: 3000 233 | # http_port: 2999 234 | # https_port: 3000 235 | #- name: something 236 | # container_port: 4000 237 | # https_port: 4000 238 | # http_port: 3999 239 | # Allows a set of extra ports to be exposed via ddev-router 240 | # Fill in all three fields even if you don’t intend to use the https_port! 241 | # If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. 242 | # 243 | # The port behavior on the ddev-webserver must be arranged separately, for example 244 | # using web_extra_daemons. 245 | # For example, with a web app on port 3000 inside the container, this config would 246 | # expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 247 | # web_extra_exposed_ports: 248 | # - name: myapp 249 | # container_port: 3000 250 | # http_port: 9998 251 | # https_port: 9999 252 | 253 | #web_extra_daemons: 254 | #- name: "http-1" 255 | # command: "/var/www/html/node_modules/.bin/http-server -p 3000" 256 | # directory: /var/www/html 257 | #- name: "http-2" 258 | # command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" 259 | # directory: /var/www/html 260 | 261 | # override_config: false 262 | # By default, config.*.yaml files are *merged* into the configuration 263 | # But this means that some things can't be overridden 264 | # For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge 265 | # and you can't erase existing hooks or all environment variables. 266 | # However, with "override_config: true" in a particular config.*.yaml file, 267 | # 'use_dns_when_possible: false' can override the existing values, and 268 | # hooks: 269 | # post-start: [] 270 | # or 271 | # web_environment: [] 272 | # or 273 | # additional_hostnames: [] 274 | # can have their intended affect. 'override_config' affects only behavior of the 275 | # config.*.yaml file it exists in. 276 | 277 | # Many DDEV commands can be extended to run tasks before or after the 278 | # DDEV command is executed, for example "post-start", "post-import-db", 279 | # "pre-composer", "post-composer" 280 | # See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more 281 | # information on the commands that can be extended and the tasks you can define 282 | # for them. Example: 283 | #hooks: 284 | # post-start: 285 | # - exec: composer install -d /var/www/html 286 | -------------------------------------------------------------------------------- /.ddev/homeadditions/.bashrc.d/path.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This detects if the composer binary folder has been reconfigured, 4 | # which is a casual practice for TYPO3 extension development setups. 5 | CHANGED_COMPOSER_FOLDER="$( jq -r '.config."bin-dir"' composer.json )" 6 | if [[ -n "${CHANGED_COMPOSER_FOLDER}" ]]; then 7 | PATH="${PATH}:$(pwd)/${CHANGED_COMPOSER_FOLDER}" 8 | fi 9 | 10 | # Detect legacy installation binary path 11 | if [[ -f "typo3/sysext/core/bin/typo3" ]]; then 12 | PATH="${PATH}:$(pwd)/typo3/sysext/core/bin" 13 | fi 14 | 15 | # Ensure to symlink additional.php ddev configuration to changed folder 16 | if [[ -f "config/system/additional.php" ]]; then 17 | if [[ ! -f "${DDEV_DOCROOT}/../config/system/additional.php" ]]; then 18 | mkdir -p "${DDEV_DOCROOT}/../config/system" 19 | CURRENT_FOLDER="$( pwd )" 20 | cd "${DDEV_DOCROOT}/../config/system" 21 | [[ -f "../../../config/system/additional.php" ]] && ln -s ../../../config/system/additional.php && echo ">> additional.php symlink created" 22 | cd "${CURRENT_FOLDER}" 23 | fi 24 | fi 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_style = space 11 | indent_size = 4 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | # TS/JS-Files 16 | [*.{ts,js}] 17 | indent_size = 2 18 | 19 | # JSON-Files 20 | [*.json] 21 | indent_style = tab 22 | 23 | # ReST-Files 24 | [*.rst] 25 | indent_size = 3 26 | max_line_length = 80 27 | 28 | # YAML-Files 29 | [*.{yaml,yml}] 30 | indent_size = 2 31 | 32 | # package.json 33 | # .travis.yml 34 | [{package.json,.travis.yml}] 35 | indent_size = 2 36 | 37 | # TypoScript 38 | [*.{typoscript,tsconfig}] 39 | indent_size = 2 40 | 41 | # XLF-Files 42 | [*.xlf] 43 | indent_style = tab 44 | 45 | # SQL-Files 46 | [*.sql] 47 | indent_style = tab 48 | indent_size = 2 49 | 50 | # .htaccess 51 | [{_.htaccess,.htaccess}] 52 | indent_style = tab 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help improving paste_reference 4 | title: "[BUG] Something does not work properly" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Summary / Description 11 | 12 | Short description of the bug. 13 | 14 | ## Version 15 | 16 | Version in which the bug occurs. 17 | 18 | ## Steps to reproduce 19 | 20 | How one can reproduce the issue - **this is very important** 21 | 22 | ## Expected behaviour 23 | 24 | What you expected to happen. 25 | 26 | ## Actual behavior 27 | 28 | What actually happens. 29 | 30 | ### Additional 31 | 32 | **Possible fix** 33 | 34 | If you can, link to the line of code that might be responsible 35 | for the problem or create a pull request if you have created 36 | the fix already. 37 | 38 | ***Miscellaneous*** 39 | 40 | Add any other information like screenshots. You can also paste 41 | any relevant logs. Please use code blocks (```) to format console 42 | output, logs, and code as it's very hard to read otherwise. 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea to help improving paste_reference 4 | title: "[FEATURE] Suggestion for some new feature" 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Summary / Description 11 | 12 | Summarize your idea for a new feature. 13 | 14 | ## Describe the solution you'd like 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | ## Describe alternatives you've considered 19 | 20 | A clear and concise description of any alternative solutions or 21 | features you've considered. 22 | 23 | ## Possible solution 24 | 25 | If you can provide the feature by yourself, please either 26 | include the corresponding code or even better create a 27 | pull request. 28 | 29 | If you paste code, please use code blocks (```) for formatting 30 | since it's otherwise very hard to read. 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | #======================================================================================================================= 2 | name: CI 3 | run-name: "[CI] requested by @${{ github.actor }} on ${{ github.event_name }}" 4 | #----------------------------------------------------------------------------------------------------------------------- 5 | # This workflow defines all required CI execution and test runs required 6 | # to ensure quality for this extension. This may be extended over time. 7 | # 8 | # Note that it is on purpose to not containing a `scheduled` run in here, 9 | # because this will done using `workflow_dispatch` by `scheduled-*.yml`. 10 | # It is absolute required to have for all dispatched branch execution 11 | # this ci.yaml file for the main ci checks in place. 12 | #======================================================================================================================= 13 | 14 | on: 15 | push: 16 | pull_request: 17 | types: 18 | - opened 19 | - edited 20 | - reopened 21 | - synchronize 22 | - ready_for_review 23 | workflow_dispatch: 24 | 25 | jobs: 26 | 27 | #--------------------------------------------------------------------------------------------------------------------- 28 | # Code quality provides low-level and quick executable checks to fail early before 29 | # executing more costly tool executions like unit, functional or acceptance tests. 30 | #--------------------------------------------------------------------------------------------------------------------- 31 | code-quality: 32 | name: "code quality insurance" 33 | runs-on: ubuntu-latest 34 | strategy: 35 | matrix: 36 | php: [ '8.2', '8.3' , '8.4' ] 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@v4 40 | 41 | - name: Composer install 42 | run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -s composer -- install 43 | 44 | - name: Lint PHP 45 | run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -s lintPhp 46 | 47 | - name: CGL 48 | if: ${{ matrix.php <= '8.3' }} 49 | run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -s cgl -n 50 | 51 | - name: Phpstan 52 | if: ${{ matrix.php <= '8.3' }} 53 | run: Build/Scripts/runTests.sh -p ${{ matrix.php }} -s phpstan 54 | 55 | documentation: 56 | name: "Extension documentation" 57 | runs-on: ubuntu-latest 58 | permissions: 59 | contents: write 60 | issues: write 61 | pull-requests: write 62 | steps: 63 | - name: Checkout 64 | uses: actions/checkout@v4 65 | 66 | - name: Render documentation 67 | run: Build/Scripts/runTests.sh -s renderDocumentation 68 | 69 | - uses: actions/upload-artifact@v4 70 | id: artifact-upload-step 71 | with: 72 | name: rendered-documentation-folder 73 | path: Documentation-GENERATED-temp/ 74 | compression-level: 9 75 | if-no-files-found: error 76 | retention-days: 90 77 | overwrite: true 78 | 79 | # @todo Add unit test execution after use-full tests has been added 80 | # @todo Add functional test execution after use-full tests has been added 81 | # @todo Add acceptance test execution after use-full tests has been added along with infrastructure and setup 82 | -------------------------------------------------------------------------------- /.github/workflows/nightly-main.yml: -------------------------------------------------------------------------------- 1 | name: "nightly-main" 2 | on: 3 | schedule: 4 | - cron: '42 5 * * *' 5 | workflow_dispatch: 6 | 7 | jobs: 8 | nightly-main: 9 | name: "dispatch-nightly-main" 10 | runs-on: ubuntu-latest 11 | permissions: write-all 12 | steps: 13 | - name: Checkout 'main' 14 | uses: actions/checkout@v4 15 | with: 16 | ref: 'main' 17 | 18 | - name: Execute 'ci.yml' on 'main' 19 | env: 20 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | run: | 22 | gh workflow run ci.yml --ref main 23 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | # @todo Consider to refactor this workflow to react on workflow_dispatch request instead of tag push events, 4 | # executing all ci tests first before automatically creating a tag and release after clean state is 5 | # ensured. 6 | 7 | on: 8 | push: 9 | tags: 10 | - "**" 11 | 12 | jobs: 13 | publish: 14 | name: Publish new version to TER 15 | if: startsWith(github.ref, 'refs/tags/') 16 | runs-on: ubuntu-20.04 17 | env: 18 | TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }} 19 | #TYPO3_EXCLUDE_FROM_PACKAGING: ${{ secrets.TYPO3_EXCLUDE_FROM_PACKAGING }} 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v2 24 | 25 | - name: Check tag 26 | run: | 27 | if ! [[ ${{ github.ref }} =~ ^refs/tags/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then 28 | exit 1 29 | fi 30 | 31 | - name: Get version 32 | id: get-version 33 | run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//} 34 | 35 | - name: Get comment 36 | id: get-comment 37 | run: | 38 | readonly local comment=$(git tag -n10 -l ${{ steps.get-version.outputs.version }} | sed "s/^[0-9.]*[ ]*//g") 39 | 40 | if [[ -z "${comment// }" ]]; then 41 | echo ::set-output name=comment::Released version ${{ steps.get-version.outputs.version }} of tailor_ext 42 | else 43 | echo ::set-output name=comment::$comment 44 | fi 45 | 46 | - name: Setup PHP 47 | uses: shivammathur/setup-php@v2 48 | with: 49 | php-version: 7.4 50 | extensions: intl, mbstring, json, zip, curl 51 | tools: composer:v2 52 | 53 | - name: Install tailor 54 | run: composer global require typo3/tailor --prefer-dist --no-progress 55 | 56 | - name: Publish to TER 57 | run: php ~/.composer/vendor/bin/tailor ter:publish --comment "${{ steps.get-comment.outputs.comment }}" ${{ steps.get-version.outputs.version }} 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ project files 2 | .idea 3 | *.iml 4 | out 5 | 6 | # temporary files 7 | *.*~ 8 | .~lock.* 9 | 10 | # Created by .ignore support plugin (hsz.mobi) 11 | ### Windows template 12 | # Windows thumbnail cache files 13 | Thumbs.db 14 | ehthumbs.db 15 | ehthumbs_vista.db 16 | 17 | # Dump file 18 | *.stackdump 19 | 20 | # Folder config file 21 | [Dd]esktop.ini 22 | 23 | # Recycle Bin used on file shares 24 | $RECYCLE.BIN/ 25 | 26 | # Windows Installer files 27 | *.cab 28 | *.msi 29 | *.msix 30 | *.msm 31 | *.msp 32 | 33 | # Windows shortcuts 34 | *.lnk 35 | 36 | # CMake 37 | cmake-build-*/ 38 | 39 | # Mongo Explorer plugin 40 | .idea/**/mongoSettings.xml 41 | 42 | # File-based project format 43 | *.iws 44 | 45 | # IntelliJ 46 | out/ 47 | 48 | # mpeltonen/sbt-idea plugin 49 | .idea_modules/ 50 | 51 | # JIRA plugin 52 | atlassian-ide-plugin.xml 53 | 54 | # Cursive Clojure plugin 55 | .idea/replstate.xml 56 | 57 | # Crashlytics plugin (for Android Studio and IntelliJ) 58 | com_crashlytics_export_strings.xml 59 | crashlytics.properties 60 | crashlytics-build.properties 61 | fabric.properties 62 | gen### NetBeans template 63 | nbproject/private/ 64 | build/ 65 | nbbuild/ 66 | dist/ 67 | nbdist/ 68 | .nb-gradle/ 69 | 70 | *.log 71 | vendor/ 72 | node_modules/ 73 | .sass_cache 74 | var/ 75 | ENABLE_INSTALL_TOOL 76 | 77 | # TYPO3 docs 78 | Documentation-GENERATED-temp 79 | 80 | # project 81 | public 82 | .build 83 | .Build 84 | 85 | # Unused TYPO3 composer mode configuration folder for additional.php 86 | /config 87 | 88 | # composer 89 | composer.lock 90 | # phpunit 91 | .phpunit.cache 92 | -------------------------------------------------------------------------------- /Build/Scripts/runTests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # EXT:examples test runner based on docker/podman. 5 | # 6 | 7 | trap 'cleanUp;exit 2' SIGINT 8 | 9 | waitFor() { 10 | local HOST=${1} 11 | local PORT=${2} 12 | local TESTCOMMAND=" 13 | COUNT=0; 14 | while ! nc -z ${HOST} ${PORT}; do 15 | if [ \"\${COUNT}\" -gt 10 ]; then 16 | echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; 17 | exit 1; 18 | fi; 19 | sleep 1; 20 | COUNT=\$((COUNT + 1)); 21 | done; 22 | " 23 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" 24 | if [[ $? -gt 0 ]]; then 25 | kill -SIGINT -$$ 26 | fi 27 | } 28 | 29 | cleanUp() { 30 | ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}') 31 | for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do 32 | ${CONTAINER_BIN} rm -f ${ATTACHED_CONTAINER} >/dev/null 33 | done 34 | ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null 35 | } 36 | 37 | cleanCacheFiles() { 38 | echo -n "Clean caches ... " 39 | rm -rf \ 40 | .cache \ 41 | .php-cs-fixer.cache 42 | echo "done" 43 | } 44 | 45 | cleanRenderedDocumentationFiles() { 46 | echo -n "Clean rendered documentation files ... " 47 | rm -rf \ 48 | Documentation-GENERATED-temp 49 | echo "done" 50 | } 51 | 52 | handleDbmsOptions() { 53 | # -a, -d, -i depend on each other. Validate input combinations and set defaults. 54 | case ${DBMS} in 55 | mariadb) 56 | [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" 57 | if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then 58 | echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 59 | echo >&2 60 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 61 | exit 1 62 | fi 63 | [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" 64 | if ! [[ ${DBMS_VERSION} =~ ^(10.3|10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then 65 | echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 66 | echo >&2 67 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 68 | exit 1 69 | fi 70 | ;; 71 | mysql) 72 | [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" 73 | if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then 74 | echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 75 | echo >&2 76 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 77 | exit 1 78 | fi 79 | [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" 80 | if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then 81 | echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 82 | echo >&2 83 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 84 | exit 1 85 | fi 86 | ;; 87 | postgres) 88 | if [ -n "${DATABASE_DRIVER}" ]; then 89 | echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 90 | echo >&2 91 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 92 | exit 1 93 | fi 94 | [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" 95 | if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then 96 | echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 97 | echo >&2 98 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 99 | exit 1 100 | fi 101 | ;; 102 | sqlite) 103 | if [ -n "${DATABASE_DRIVER}" ]; then 104 | echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 105 | echo >&2 106 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 107 | exit 1 108 | fi 109 | if [ -n "${DBMS_VERSION}" ]; then 110 | echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 111 | echo >&2 112 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 113 | exit 1 114 | fi 115 | ;; 116 | *) 117 | echo "Invalid option -d ${DBMS}" >&2 118 | echo >&2 119 | echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 120 | exit 1 121 | ;; 122 | esac 123 | } 124 | 125 | loadHelp() { 126 | # Load help text into $HELP 127 | read -r -d '' HELP < 134 | Specifies which test suite to run 135 | - cgl: test and fix all php files 136 | - clean: Clean temporary files 137 | - composer: "composer" with all remaining arguments dispatched. 138 | - functional: PHP functional tests 139 | - lintPhp: PHP linting 140 | - renderDocumentation: Render documentation locally 141 | - renderDocumentationCheck: Validation rendering documentation in minimal mode 142 | - phpstan: Execute static code analyzer PHPStan 143 | - phpstanGenerateBaseline: Regenerate PHPStan baseline, handy after PHPStan updates 144 | - unit: PHP unit tests 145 | 146 | -b 147 | Container environment: 148 | - docker 149 | - podman 150 | 151 | If not specified, podman will be used if available. Otherwise, docker is used. 152 | 153 | -a 154 | Only with -s functional|functionalDeprecated 155 | Specifies to use another driver, following combinations are available: 156 | - mysql 157 | - mysqli (default) 158 | - pdo_mysql 159 | - mariadb 160 | - mysqli (default) 161 | - pdo_mysql 162 | 163 | -d 164 | Only with -s functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall 165 | Specifies on which DBMS tests are performed 166 | - sqlite: (default): use sqlite 167 | - mariadb: use mariadb 168 | - mysql: use MySQL 169 | - postgres: use postgres 170 | 171 | -i version 172 | Specify a specific database version 173 | With "-d mariadb": 174 | - 10.3 short-term, maintained until 2023-05-25 (default) 175 | - 10.4 short-term, maintained until 2024-06-18 176 | - 10.5 short-term, maintained until 2025-06-24 177 | - 10.6 long-term, maintained until 2026-06 178 | - 10.7 short-term, no longer maintained 179 | - 10.8 short-term, maintained until 2023-05 180 | - 10.9 short-term, maintained until 2023-08 181 | - 10.10 short-term, maintained until 2023-11 182 | - 10.11 long-term, maintained until 2028-02 183 | - 11.0 development series 184 | - 11.1 short-term development series 185 | With "-d mysql": 186 | - 8.0 maintained until 2026-04 (default) LTS 187 | - 8.1 unmaintained since 2023-10 188 | - 8.2 unmaintained since 2024-01 189 | - 8.3 maintained until 2024-04 190 | With "-d postgres": 191 | - 10 unmaintained since 2022-11-10 (default) 192 | - 11 unmaintained since 2023-11-09 193 | - 12 maintained until 2024-11-14 194 | - 13 maintained until 2025-11-13 195 | - 14 maintained until 2026-11-12 196 | - 15 maintained until 2027-11-11 197 | - 16 maintained until 2028-11-09 198 | 199 | -p <8.1|8.2|8.3|8.4> 200 | Specifies the PHP minor version to be used 201 | - 8.1: use PHP 8.1 (default) 202 | - 8.2: use PHP 8.2 203 | - 8.3: use PHP 8.3 204 | - 8.4: use PHP 8.4 205 | 206 | -x 207 | Only with -s functional|unit 208 | Send information to host instance for test or system under test break points. This is especially 209 | useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port 210 | can be selected with -y 211 | 212 | -y 213 | Send xdebug information to a different port than default 9003 if an IDE like PhpStorm 214 | is not listening on default port. 215 | 216 | -n 217 | Only with -s cgl 218 | Activate dry-run in CGL check that does not actively change files and only prints broken ones. 219 | 220 | -u 221 | Update existing typo3/core-testing-*:latest container images and remove dangling local volumes. 222 | New images are published once in a while and only the latest ones are supported by core testing. 223 | Use this if weird test errors occur. Also removes obsolete image versions of typo3/core-testing-*. 224 | 225 | -h 226 | Show this help. 227 | 228 | Examples: 229 | # Run unit tests using PHP 8.1 (default) 230 | ./Build/Scripts/runTests.sh -s unit 231 | ./Build/Scripts/runTests.sh -p 8.1 -s unit 232 | 233 | # Run functional tests using PHP 8.3 and MariaDB 10.6 using pdo_mysql 234 | ./Build/Scripts/runTests.sh -p 8.3 -s functional -d mariadb -i 10.6 -a pdo_mysql 235 | 236 | # Run functional tests on postgres with xdebug, php 8.3 and execute a restricted set of tests 237 | ./Build/Scripts/runTests.sh -x -p 8.3 -s functional -d postgres -- Tests/Functional/DummyTest.php 238 | EOF 239 | } 240 | 241 | # Test if docker exists, else exit out with error 242 | if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then 243 | echo "This script relies on docker or podman. Please install" >&2 244 | exit 1 245 | fi 246 | 247 | # Option defaults 248 | TEST_SUITE="help" 249 | DATABASE_DRIVER="" 250 | DBMS="sqlite" 251 | DBMS_VERSION="" 252 | PHP_VERSION="8.1" 253 | PHP_XDEBUG_ON=0 254 | PHP_XDEBUG_PORT=9003 255 | CGLCHECK_DRY_RUN="" 256 | CI_PARAMS="${CI_PARAMS:-}" 257 | DOCS_PARAMS="${DOCS_PARAMS:=--pull always}" 258 | CONTAINER_BIN="" 259 | CONTAINER_HOST="host.docker.internal" 260 | 261 | # Option parsing updates above default vars 262 | # Reset in case getopts has been used previously in the shell 263 | OPTIND=1 264 | # Array for invalid options 265 | INVALID_OPTIONS=() 266 | # Simple option parsing based on getopts (! not getopt) 267 | while getopts "a:b:d:i:s:p:xy:nhu" OPT; do 268 | case ${OPT} in 269 | a) 270 | DATABASE_DRIVER=${OPTARG} 271 | ;; 272 | s) 273 | TEST_SUITE=${OPTARG} 274 | ;; 275 | b) 276 | if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then 277 | INVALID_OPTIONS+=("${OPTARG}") 278 | fi 279 | CONTAINER_BIN=${OPTARG} 280 | ;; 281 | d) 282 | DBMS=${OPTARG} 283 | ;; 284 | i) 285 | DBMS_VERSION=${OPTARG} 286 | ;; 287 | p) 288 | PHP_VERSION=${OPTARG} 289 | if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3|8.4)$ ]]; then 290 | INVALID_OPTIONS+=("p ${OPTARG}") 291 | fi 292 | ;; 293 | x) 294 | PHP_XDEBUG_ON=1 295 | ;; 296 | y) 297 | PHP_XDEBUG_PORT=${OPTARG} 298 | ;; 299 | n) 300 | CGLCHECK_DRY_RUN="-n" 301 | ;; 302 | h) 303 | loadHelp 304 | echo "${HELP}" 305 | exit 0 306 | ;; 307 | u) 308 | TEST_SUITE=update 309 | ;; 310 | \?) 311 | INVALID_OPTIONS+=("${OPTARG}") 312 | ;; 313 | :) 314 | INVALID_OPTIONS+=("${OPTARG}") 315 | ;; 316 | esac 317 | done 318 | 319 | # Exit on invalid options 320 | if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then 321 | echo "Invalid option(s):" >&2 322 | for I in "${INVALID_OPTIONS[@]}"; do 323 | echo "-"${I} >&2 324 | done 325 | echo >&2 326 | echo "call \".Build/Scripts/runTests.sh -h\" to display help and valid options" 327 | exit 1 328 | fi 329 | 330 | handleDbmsOptions 331 | 332 | COMPOSER_ROOT_VERSION="2.0.x-dev" 333 | HOST_UID=$(id -u) 334 | USERSET="" 335 | if [ $(uname) != "Darwin" ]; then 336 | USERSET="--user $HOST_UID" 337 | fi 338 | 339 | # Go to the directory this script is located, so everything else is relative 340 | # to this dir, no matter from where this script is called, then go up two dirs. 341 | THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" 342 | cd "$THIS_SCRIPT_DIR" || exit 1 343 | cd ../../ || exit 1 344 | ROOT_DIR="${PWD}" 345 | 346 | # Create .cache dir: composer need this. 347 | mkdir -p .Build/.cache 348 | mkdir -p .Build/public/typo3temp/var/tests 349 | 350 | IMAGE_PREFIX="docker.io/" 351 | # Non-CI fetches TYPO3 images (php and nodejs) from ghcr.io 352 | TYPO3_IMAGE_PREFIX="ghcr.io/typo3/" 353 | CONTAINER_INTERACTIVE="-it --init" 354 | 355 | IS_CORE_CI=0 356 | # ENV var "CI" is set by gitlab-ci. We use it here to distinct 'local' and 'CI' environment. 357 | if [ "${CI}" == "true" ]; then 358 | IS_CORE_CI=1 359 | IMAGE_PREFIX="" 360 | CONTAINER_INTERACTIVE="" 361 | fi 362 | 363 | # determine default container binary to use: 1. podman 2. docker 364 | if [[ -z "${CONTAINER_BIN}" ]]; then 365 | if type "podman" >/dev/null 2>&1; then 366 | CONTAINER_BIN="podman" 367 | elif type "docker" >/dev/null 2>&1; then 368 | CONTAINER_BIN="docker" 369 | fi 370 | fi 371 | 372 | IMAGE_PHP="${TYPO3_IMAGE_PREFIX}core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" 373 | IMAGE_ALPINE="${IMAGE_PREFIX}alpine:3.8" 374 | IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" 375 | IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" 376 | IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" 377 | 378 | # Set $1 to first mass argument, this is the optional test file or test directory to execute 379 | shift $((OPTIND - 1)) 380 | 381 | SUFFIX=$(echo $RANDOM) 382 | NETWORK="paste-reference-${SUFFIX}" 383 | ${CONTAINER_BIN} network create ${NETWORK} >/dev/null 384 | 385 | if [ ${CONTAINER_BIN} = "docker" ]; then 386 | # docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in 387 | CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" 388 | CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:/project" 389 | else 390 | # podman 391 | CONTAINER_HOST="host.containers.internal" 392 | CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}" 393 | CONTAINER_DOCS_PARAMS="${CONTAINER_INTERACTIVE} ${DOCS_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:/project" 394 | fi 395 | 396 | if [ ${PHP_XDEBUG_ON} -eq 0 ]; then 397 | XDEBUG_MODE="-e XDEBUG_MODE=off" 398 | XDEBUG_CONFIG=" " 399 | else 400 | XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" 401 | XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}" 402 | fi 403 | 404 | # Suite execution 405 | case ${TEST_SUITE} in 406 | cgl) 407 | # Active dry-run for cgl needs not "-n" but specific options 408 | if [[ ! -z ${CGLCHECK_DRY_RUN} ]]; then 409 | CGLCHECK_DRY_RUN="--dry-run --diff" 410 | fi 411 | COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --config=Build/php-cs-fixer/config.php" 412 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-${SUFFIX} ${IMAGE_PHP} ${COMMAND} 413 | SUITE_EXIT_CODE=$? 414 | ;; 415 | clean) 416 | cleanCacheFiles 417 | cleanRenderedDocumentationFiles 418 | SUITE_EXIT_CODE=$? 419 | ;; 420 | composer) 421 | COMMAND=(composer "$@") 422 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-command-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" 423 | SUITE_EXIT_CODE=$? 424 | ;; 425 | functional) 426 | CONTAINER_PARAMS="" 427 | COMMAND=(.Build/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") 428 | case ${DBMS} in 429 | mariadb) 430 | echo "Using driver: ${DATABASE_DRIVER}" 431 | ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null 432 | waitFor mariadb-func-${SUFFIX} 3306 433 | CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" 434 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" 435 | SUITE_EXIT_CODE=$? 436 | ;; 437 | mysql) 438 | echo "Using driver: ${DATABASE_DRIVER}" 439 | ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null 440 | waitFor mysql-func-${SUFFIX} 3306 441 | CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" 442 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" 443 | SUITE_EXIT_CODE=$? 444 | ;; 445 | postgres) 446 | ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null 447 | waitFor postgres-func-${SUFFIX} 5432 448 | CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" 449 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" 450 | SUITE_EXIT_CODE=$? 451 | ;; 452 | sqlite) 453 | # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues 454 | mkdir -p "${ROOT_DIR}/.Build/public/typo3temp/var/tests/functional-sqlite-dbs/" 455 | CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${ROOT_DIR}/.Build/public//typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" 456 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" 457 | SUITE_EXIT_CODE=$? 458 | ;; 459 | esac 460 | ;; 461 | lintPhp) 462 | COMMAND="php -v | grep '^PHP'; find . -name '*.php' ! -path './.Build/*' -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null" 463 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-php-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" 464 | SUITE_EXIT_CODE=$? 465 | ;; 466 | renderDocumentation) 467 | mkdir -p Documentation-GENERATED-temp 468 | ${CONTAINER_BIN} run --rm --pull always -v ./:/project/ ghcr.io/typo3-documentation/render-guides:latest --no-progress --config=Documentation Documentation 469 | SUITE_EXIT_CODE=$? 470 | ;; 471 | renderDocumentationCheck) 472 | mkdir -p Documentation-GENERATED-temp 473 | ${CONTAINER_BIN} run --rm --pull always -v ./:/project/ ghcr.io/typo3-documentation/render-guides:latest --no-progress --config=Documentation Documentation --minimal-test 474 | SUITE_EXIT_CODE=$? 475 | ;; 476 | phpstan) 477 | COMMAND=(php -dxdebug.mode=off .Build/bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G "$@") 478 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} ${IMAGE_PHP} "${COMMAND[@]}" 479 | SUITE_EXIT_CODE=$? 480 | ;; 481 | phpstanGenerateBaseline) 482 | COMMAND="php -dxdebug.mode=off .Build/bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G --generate-baseline=Build/phpstan/phpstan-baseline.neon" 483 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-baseline-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" 484 | SUITE_EXIT_CODE=$? 485 | ;; 486 | unit) 487 | COMMAND=(.Build/bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} "$@") 488 | ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} "${COMMAND[@]}" 489 | SUITE_EXIT_CODE=$? 490 | ;; 491 | update) 492 | # pull typo3/core-testing-* versions of those ones that exist locally 493 | echo "> pull ${TYPO3_IMAGE_PREFIX}core-testing-* versions of those ones that exist locally" 494 | ${CONTAINER_BIN} images "${TYPO3_IMAGE_PREFIX}core-testing-*" --format "{{.Repository}}:{{.Tag}}" | xargs -I {} ${CONTAINER_BIN} pull {} 495 | echo "" 496 | # remove "dangling" typo3/core-testing-* images (those tagged as ) 497 | echo "> remove \"dangling\" ${TYPO3_IMAGE_PREFIX}/core-testing-* images (those tagged as )" 498 | ${CONTAINER_BIN} images --filter "reference=${TYPO3_IMAGE_PREFIX}/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {} 499 | echo "" 500 | ;; 501 | help) 502 | loadHelp 503 | exit 0; 504 | ;; 505 | *) 506 | loadHelp 507 | echo "Invalid -s option argument ${TEST_SUITE}" >&2 508 | echo >&2 509 | echo "${HELP}" >&2 510 | exit 1 511 | ;; 512 | esac 513 | 514 | cleanUp 515 | 516 | # Print summary 517 | echo "" >&2 518 | echo "###########################################################################" >&2 519 | echo "Result of ${TEST_SUITE}" >&2 520 | echo "Container runtime: ${CONTAINER_BIN}" >&2 521 | if [[ ${IS_CORE_CI} -eq 1 ]]; then 522 | echo "Environment: CI" >&2 523 | else 524 | echo "Environment: local" >&2 525 | fi 526 | echo "PHP: ${PHP_VERSION}" >&2 527 | echo "TYPO3: ${CORE_VERSION}" >&2 528 | if [[ ${TEST_SUITE} =~ ^functional$ ]]; then 529 | case "${DBMS}" in 530 | mariadb|mysql) 531 | echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 532 | ;; 533 | postgres) 534 | echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver pdo_pgsql" >&2 535 | ;; 536 | sqlite) 537 | echo "DBMS: ${DBMS} driver pdo_sqlite" >&2 538 | ;; 539 | esac 540 | fi 541 | if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then 542 | echo "SUCCESS" >&2 543 | else 544 | echo "FAILURE" >&2 545 | fi 546 | echo "###########################################################################" >&2 547 | echo "" >&2 548 | 549 | # Exit with code of test suite - This script return non-zero if the executed test failed. 550 | exit $SUITE_EXIT_CODE 551 | -------------------------------------------------------------------------------- /Build/php-cs-fixer/config.php: -------------------------------------------------------------------------------- 1 | 11 | // * (c) 2013 Jo Hasenau 12 | // * All rights reserved 13 | // * This script is part of the TYPO3 project. The TYPO3 project is 14 | // * free software; you can redistribute it and/or modify 15 | // * it under the terms of the GNU General Public License as published by 16 | // * the Free Software Foundation; either version 2 of the License, or 17 | // * (at your option) any later version. 18 | // * The GNU General Public License can be found at 19 | // * http://www.gnu.org/copyleft/gpl.html. 20 | // * This script is distributed in the hope that it will be useful, 21 | // * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | // * GNU General Public License for more details. 24 | // * This copyright notice MUST APPEAR in all copies of the script! 25 | // ***************************************************************/ 26 | //EOL; 27 | 28 | $headerComment = << 33 | (c) 2013 Jo Hasenau 34 | 35 | ??? 36 | 37 | The TYPO3 project - inspiring people to share! 38 | EOF; 39 | 40 | return (new \PhpCsFixer\Config()) 41 | ->setFinder( 42 | (new PhpCsFixer\Finder()) 43 | ->ignoreVCSIgnored(true) 44 | ->in([ 45 | __DIR__ . '/../../Build/', 46 | __DIR__ . '/../../Classes/', 47 | __DIR__ . '/../../Resources/', 48 | __DIR__ . '/../../Tests/', 49 | ]) 50 | ) 51 | ->setUsingCache(false) 52 | ->setRiskyAllowed(true) 53 | ->setRules([ 54 | '@DoctrineAnnotation' => true, 55 | // @todo: Switch to @PER-CS2.0 once php-cs-fixer's todo list is done: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7247 56 | '@PER-CS1.0' => true, 57 | 'array_syntax' => ['syntax' => 'short'], 58 | 'cast_spaces' => ['space' => 'none'], 59 | // @todo: Can be dropped once we enable @PER-CS2.0 60 | 'concat_space' => ['spacing' => 'one'], 61 | 'declare_equal_normalize' => ['space' => 'none'], 62 | 'declare_parentheses' => true, 63 | 'dir_constant' => true, 64 | // @todo: Can be dropped once we enable @PER-CS2.0 65 | 'function_declaration' => [ 66 | 'closure_fn_spacing' => 'none', 67 | ], 68 | 'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']], 69 | 'type_declaration_spaces' => true, 70 | 'global_namespace_import' => ['import_classes' => false, 'import_constants' => false, 'import_functions' => false], 71 | 'list_syntax' => ['syntax' => 'short'], 72 | // @todo: Can be dropped once we enable @PER-CS2.0 73 | 'method_argument_space' => true, 74 | 'modernize_strpos' => true, 75 | 'modernize_types_casting' => true, 76 | 'native_function_casing' => true, 77 | 'no_alias_functions' => true, 78 | 'no_blank_lines_after_phpdoc' => true, 79 | 'no_empty_phpdoc' => true, 80 | 'no_empty_statement' => true, 81 | 'no_extra_blank_lines' => true, 82 | 'no_leading_namespace_whitespace' => true, 83 | 'no_null_property_initialization' => true, 84 | 'no_short_bool_cast' => true, 85 | 'no_singleline_whitespace_before_semicolons' => true, 86 | 'no_superfluous_elseif' => true, 87 | 'no_trailing_comma_in_singleline' => true, 88 | 'no_unneeded_control_parentheses' => true, 89 | 'no_unused_imports' => true, 90 | 'no_useless_else' => true, 91 | 'no_useless_nullsafe_operator' => true, 92 | 'nullable_type_declaration' => [ 93 | 'syntax' => 'question_mark', 94 | ], 95 | 'nullable_type_declaration_for_default_null_value' => true, 96 | 'ordered_imports' => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'], 97 | 'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']], 98 | 'php_unit_mock_short_will_return' => true, 99 | 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], 100 | 'phpdoc_no_access' => true, 101 | 'phpdoc_no_empty_return' => true, 102 | 'phpdoc_no_package' => true, 103 | 'phpdoc_scalar' => true, 104 | 'phpdoc_trim' => true, 105 | 'phpdoc_types' => true, 106 | 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], 107 | 'return_type_declaration' => ['space_before' => 'none'], 108 | 'single_quote' => true, 109 | 'single_space_around_construct' => true, 110 | 'single_line_comment_style' => ['comment_types' => ['hash']], 111 | // @todo: Can be dropped once we enable @PER-CS2.0 112 | 'single_line_empty_body' => true, 113 | 'trailing_comma_in_multiline' => ['elements' => ['arrays']], 114 | 'whitespace_after_comma_in_array' => ['ensure_single_space' => true], 115 | 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], 116 | // 'header_comment' => [ 117 | // 'header' => $headerComment, 118 | // 'comment_type' => 'comment', 119 | // 'location' => 'after_declare_strict', 120 | // 'separate' => 'both', 121 | // ], 122 | ]); 123 | -------------------------------------------------------------------------------- /Build/phpstan/phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | -------------------------------------------------------------------------------- /Build/phpstan/phpstan-constants.neon: -------------------------------------------------------------------------------- 1 | 404: Not Found -------------------------------------------------------------------------------- /Build/phpstan/phpstan-constants.php: -------------------------------------------------------------------------------- 1 | 2 | 16 | 34 | 35 | 36 | 40 | ../../Tests/Functional/ 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Build/phpunit/FunctionalTestsBootstrap.php: -------------------------------------------------------------------------------- 1 | defineOriginalRootPath(); 29 | $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests'); 30 | $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/transient'); 31 | })(); 32 | -------------------------------------------------------------------------------- /Build/phpunit/UnitTests.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 34 | 35 | 36 | 40 | ../../Tests/Unit/ 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Build/phpunit/UnitTestsBootstrap.php: -------------------------------------------------------------------------------- 1 | getWebRoot(), '/')); 42 | } 43 | if (!getenv('TYPO3_PATH_WEB')) { 44 | putenv('TYPO3_PATH_WEB=' . rtrim($testbase->getWebRoot(), '/')); 45 | } 46 | 47 | $testbase->defineSitePath(); 48 | 49 | // We can use the "typo3/cms-composer-installers" constant "TYPO3_COMPOSER_MODE" to determine composer mode. 50 | // This should be always true except for TYPO3 mono repository. 51 | $composerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE === true; 52 | 53 | // @todo: Remove else branch when dropping support for v12 54 | $hasConsolidatedHttpEntryPoint = class_exists(CoreHttpApplication::class); 55 | if ($hasConsolidatedHttpEntryPoint) { 56 | \TYPO3\TestingFramework\Core\SystemEnvironmentBuilder::run(0, \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI, $composerMode); 57 | } else { 58 | $requestType = \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE | \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI; 59 | \TYPO3\TestingFramework\Core\SystemEnvironmentBuilder::run(0, $requestType, $composerMode); 60 | } 61 | 62 | $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3conf/ext'); 63 | $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/assets'); 64 | $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/tests'); 65 | $testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/transient'); 66 | 67 | // Retrieve an instance of class loader and inject to core bootstrap 68 | $classLoader = require $testbase->getPackagesPath() . '/autoload.php'; 69 | \TYPO3\CMS\Core\Core\Bootstrap::initializeClassLoader($classLoader); 70 | 71 | // Initialize default TYPO3_CONF_VARS 72 | $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager(); 73 | $GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration(); 74 | 75 | $cache = new \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend( 76 | 'core', 77 | new \TYPO3\CMS\Core\Cache\Backend\NullBackend('production', []) 78 | ); 79 | $packageManager = \TYPO3\CMS\Core\Core\Bootstrap::createPackageManager( 80 | \TYPO3\CMS\Core\Package\UnitTestPackageManager::class, 81 | \TYPO3\CMS\Core\Core\Bootstrap::createPackageCache($cache) 82 | ); 83 | 84 | \TYPO3\CMS\Core\Utility\GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager); 85 | \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::setPackageManager($packageManager); 86 | 87 | $testbase->dumpClassLoadingInformation(); 88 | 89 | \TYPO3\CMS\Core\Utility\GeneralUtility::purgeInstances(); 90 | })(); 91 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct - paste_reference 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to a positive environment for our 15 | community include: 16 | 17 | * Demonstrating empathy and kindness toward other people 18 | * Being respectful of differing opinions, viewpoints, and experiences 19 | * Giving and gracefully accepting constructive feedback 20 | * Accepting responsibility and apologizing to those affected by our mistakes, 21 | and learning from the experience 22 | * Focusing on what is best not just for us as individuals, but for the 23 | overall community 24 | 25 | Examples of unacceptable behavior include: 26 | 27 | * The use of sexualized language or imagery, and sexual attention or 28 | advances 29 | * Trolling, insulting or derogatory comments, and personal or political attacks 30 | * Public or private harassment 31 | * Publishing others' private information, such as a physical or email 32 | address, without their explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | ## Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying and enforcing our standards of 39 | acceptable behavior and will take appropriate and fair corrective action in 40 | response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, or reject 43 | comments, commits, code, wiki edits, issues, and other contributions that are 44 | not aligned to this Code of Conduct, or to ban 45 | temporarily or permanently any contributor for other behaviors that they deem 46 | inappropriate, threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies within all community spaces, and also applies when 51 | an individual is officially representing the community in public spaces. 52 | Examples of representing our community include using an official e-mail address, 53 | posting via an official social media account, or acting as an appointed 54 | representative at an online or offline event. 55 | 56 | ## Enforcement 57 | 58 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 59 | reported to the community leaders responsible for enforcement at . 60 | All complaints will be reviewed and investigated promptly and fairly. 61 | 62 | All community leaders are obligated to respect the privacy and security of the 63 | reporter of any incident. 64 | 65 | ## Attribution 66 | 67 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version 68 | [1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and 69 | [2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md), 70 | and was generated by [contributing-gen](https://github.com/bttger/contributing-gen). -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to paste_reference 3 | 4 | First off, thanks for taking the time to contribute! ❤️ 5 | 6 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for 7 | different ways to help and details about how this project handles them. Please make sure to 8 | read the relevant section before making your contribution. It will make it a lot easier for 9 | us maintainers and smooth out the experience for all involved. The community looks forward 10 | to your contributions. 🎉 11 | 12 | > And if you like the project, but just don't have time to contribute, that's fine. There are 13 | > other easy ways to support the project and show your appreciation, which we would also be very 14 | > happy about: 15 | > - Star the project 16 | > - Tweet about it 17 | > - Refer this project in your project's readme 18 | > - Mention the project at local meetups and tell your friends/colleagues 19 | 20 | 21 | ## Table of Contents 22 | 23 | - [Code of Conduct](#code-of-conduct) 24 | - [I Have a Question](#i-have-a-question) 25 | - [I Want To Contribute](#i-want-to-contribute) 26 | - [Reporting Bugs](#reporting-bugs) 27 | - [Suggesting Enhancements](#suggesting-enhancements) 28 | - [Your First Code Contribution](#your-first-code-contribution) 29 | - [Improving The Documentation](#improving-the-documentation) 30 | - [Styleguides](#styleguides) 31 | - [Commit Messages](#commit-messages) 32 | - [Join The Project Team](#join-the-project-team) 33 | 34 | 35 | 36 | ## Code of Conduct 37 | 38 | This project and everyone participating in it is governed by the 39 | [paste_reference Code of Conduct](https://github.com/Kephson/paste_reference/blob/main/CODE_OF_CONDUCT.md). 40 | By participating, you are expected to uphold this code. Please report unacceptable behavior 41 | to . 42 | 43 | 44 | ## I Have a Question 45 | 46 | > If you want to ask a question, we assume that you have read the available [Documentation](https://docs.typo3.org/p/ehaerer/paste-reference/main/en-us/). 47 | 48 | Before you ask a question, it is best to search for existing [Issues](https://github.com/Kephson/paste_reference/issues) that might help you. In case 49 | you have found a suitable issue and still need clarification, you can write your question in this issue. 50 | It is also advisable to search the internet for answers first. 51 | 52 | If you then still feel the need to ask a question and need clarification, we recommend the following: 53 | 54 | - Open an [Issue](https://github.com/Kephson/paste_reference/issues/new). 55 | - Provide as much context as you can about what you're running into. 56 | - Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. 57 | 58 | We will then take care of the issue as soon as possible. 59 | 60 | 76 | 77 | ## I Want To Contribute 78 | 79 | > ### Legal Notice 80 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the 81 | > necessary rights to the content and that the content you contribute may be provided under the project license. 82 | 83 | ### Contributing code and documentation 84 | 85 | 1. Create a fork of the repository. 86 | You need a [GitHub account](https://github.com/join). 87 | 2. Create a new branch holding your changes. 88 | 3. Apply your changes. 89 | 4. Commit your changes following the [TYPO3 git commit conventions](https://docs.typo3.org/m/typo3/guide-contributionworkflow/main/en-us/Appendix/CommitMessage.html#commitmessage). 90 | The only relevant difference is that we do not use the `Releases` or `Change-Id` metadata. 91 | 5. Push your changes. 92 | 6. Open a pull request. 93 | 94 | Some pull requests can not be merged quickly. In cases where the changes cannot be merged quickly, 95 | we consider the original author responsible for keeping their PR branches up to by rebasing. 96 | 97 | When opening a pull request, automatic code quality checks and other tests are executed to ensure 98 | a working state including a defined coding style guideline (cgl) which may not be the style you are 99 | personally acknowledge on or using yourself. This may be discussed, but for your change to be merged 100 | the automatic checks needs to be clean. 101 | 102 | Please also have a look at the documented [contribution workflow for GitHub](https://docs.github.com/en/get-started/quickstart/contributing-to-projects). 103 | 104 | ### Reporting Bugs 105 | 106 | 107 | #### Before Submitting a Bug Report 108 | 109 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to 110 | investigate carefully, collect information and describe the issue in detail in your report. Please complete the 111 | following steps in advance to help us fix any potential bug as fast as possible. 112 | 113 | - Make sure that you are using the latest version. 114 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment 115 | components/versions (Make sure that you have read the [documentation](https://docs.typo3.org/p/ehaerer/paste-reference/main/en-us/). If you are looking for support, 116 | you might want to check [this section](#i-have-a-question)). 117 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check 118 | - if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/Kephson/paste_referenceissues?q=label%3Abug). 119 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community 120 | have discussed the issue. 121 | - Collect information about the bug: 122 | - Stack trace (Traceback) 123 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) 124 | - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. 125 | - Possibly your input and the output 126 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 127 | 128 | 129 | #### How Do I Submit a Good Bug Report? 130 | 131 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue 132 | > tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . 133 | 134 | 135 | We use GitHub issues to track bugs and errors. If you run into an issue with the project: 136 | 137 | - Open an [Issue](https://github.com/Kephson/paste_reference/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about 138 | - a bug yet and not to label the issue.) 139 | - Explain the behavior you would expect and the actual behavior. 140 | - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to 141 | - recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the 142 | - problem and create a reduced test case. 143 | - Provide the information you collected in the previous section. 144 | 145 | Once it's filed: 146 | 147 | - The project team will label the issue accordingly. 148 | - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no 149 | obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. 150 | Bugs with the `needs-repro` tag will not be addressed until they are reproduced. 151 | - If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags 152 | (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). 153 | 154 | 158 | 159 | 160 | ### Suggesting Enhancements 161 | 162 | This section guides you through submitting an enhancement suggestion for paste_reference, 163 | **including completely new features and minor improvements to existing functionality**. 164 | Following these guidelines will help maintainers and the community to understand your 165 | suggestion and find related suggestions. 166 | 167 | 168 | #### Before Submitting an Enhancement 169 | 170 | - Make sure that you are using the latest version. 171 | - Read the [documentation](https://docs.typo3.org/p/ehaerer/paste-reference/main/en-us/) carefully and find 172 | out if the functionality is already covered, maybe by an individual configuration. 173 | - Perform a [search](https://github.com/Kephson/paste_reference/issues) to see if the enhancement has already 174 | been suggested. If it has, add a comment to the existing issue instead of opening a new one. 175 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make 176 | a strong case to convince the project's developers of the merits of this feature. Keep in mind 177 | that we want features that will be useful to the majority of our users and not just a small subset. 178 | If you're just targeting a minority of users, consider writing an add-on/plugin library. 179 | 180 | 181 | #### How Do I Submit a Good Enhancement Suggestion? 182 | 183 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/Kephson/paste_reference/issues). 184 | 185 | - Use a **clear and descriptive title** for the issue to identify the suggestion. 186 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. 187 | - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. 188 | At this point you can also tell which alternatives do not work for you. 189 | - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point 190 | out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS 191 | and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. 193 | - **Explain why this enhancement would be useful** to most paste_reference users. You may also want to 194 | point out the other projects that solved it better and which could serve as inspiration. 195 | 196 | 197 | ## Attribution 198 | This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! 199 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contribute to TYPO3 Documentation 2 | ================================= 3 | 4 | Create Issues 5 | ============= 6 | 7 | * If you find something missing or something is wrong in this repository, you are welcome to write an 8 | `issue `__ 9 | describing the problem. 10 | * If you can, please try to fix the problem yourself. 11 | * For minor changes, it is not necessary to create an issue first. 12 | 13 | Make changes 14 | ============ 15 | 16 | In order to make changes on this repository, either directly edit on GitHub or fork and clone 17 | the repository, make the change and create a pull request. 18 | 19 | See the GitHub documentation `Create a pull request `__. 20 | 21 | Get help 22 | ======== 23 | 24 | If you need help with contributing to the docs, see 25 | `Help & Support `__. 26 | 27 | Contribute to TYPO3 Core 28 | ======================== 29 | 30 | See `TYPO3 Contribution Guide - Core Development `__. 31 | 32 | General TYPO3 Support 33 | ===================== 34 | 35 | If you have some general TYPO3 support questions or need help with TYPO3, please see https://typo3.org/help. 36 | -------------------------------------------------------------------------------- /Classes/ContextMenu/PasteReferenceItemProvider.php: -------------------------------------------------------------------------------- 1 | table === 'tt_content'; 35 | } 36 | 37 | /** 38 | * @return int 39 | */ 40 | public function getPriority(): int 41 | { 42 | return 45; 43 | } 44 | 45 | /** 46 | * @param string $itemName 47 | * @return string[] 48 | * @throws RouteNotFoundException 49 | */ 50 | protected function getAdditionalAttributes(string $itemName): array 51 | { 52 | $urlParameters = [ 53 | 'prErr' => 1, 54 | 'uPT' => 1, 55 | 'CB[paste]' => $this->table . '|' . -$this->record['uid'], 56 | 'CB[pad]' => 'normal', 57 | 'CB[update]' => [ 58 | 'colPos' => $this->record['colPos'], 59 | 'sys_language_uid' => $this->record['sys_language_uid'], 60 | ], 61 | 'reference' => 1, 62 | ]; 63 | 64 | // Add needed EXT:container information to reference into a container 65 | if (($this->record['tx_container_parent'] ?? 0) > 0) { 66 | $urlParameters['CB[update]']['tx_container_parent'] = $this->record['tx_container_parent']; 67 | } 68 | 69 | $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); 70 | $attributes = $this->getPasteAdditionalAttributes('after'); 71 | $attributes += [ 72 | 'data-callback-module' => '@ehaerer/paste-reference/context-menu-actions', 73 | 'data-action-url' => (string)$uriBuilder->buildUriFromRoute('tce_db', $urlParameters), 74 | 'data-title' => $this->languageService->sL('LLL:EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:newContentElementReference'), 75 | 'data-message' => 'my message here', 76 | ]; 77 | if ($this->backendUser->jsConfirmation(JsConfirmation::COPY_MOVE_PASTE)) { 78 | $selItem = $this->clipboard->getSelectedRecord(); 79 | $title = $this->languageService->sL('LLL:EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:newContentElementReference'); 80 | $confirmMessage = sprintf( 81 | $this->languageService->sL('LLL:EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:mess.reference_after'), 82 | GeneralUtility::fixed_lgd_cs($selItem['_RECORD_TITLE'], (int)$this->backendUser->uc['titleLen']), 83 | GeneralUtility::fixed_lgd_cs(BackendUtility::getRecordTitle($this->table, $this->record), (int)$this->backendUser->uc['titleLen']) 84 | ); 85 | $attributes ['data-title'] = htmlspecialchars($title); 86 | $attributes ['data-message'] = htmlspecialchars($confirmMessage); 87 | } 88 | 89 | return $attributes; 90 | } 91 | 92 | /** 93 | * This method adds custom item to list of items generated by item providers with higher priority value (PageProvider) 94 | * You could also modify existing items here. 95 | * The new item is added after the 'info' item. 96 | * @see https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ApiOverview/Backend/ContextualMenu.html 97 | * 98 | * @param array $items 99 | * @return array 100 | */ 101 | public function addItems(array $items): array 102 | { 103 | $itemsConfiguration = [ 104 | 'pasteReference' => [ 105 | 'type' => 'item', 106 | 'label' => 'LLL:EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:tx_paste_reference_clickmenu_pastereference', 107 | 'iconIdentifier' => 'actions-document-paste-after', 108 | 'callbackAction' => 'pasteReference', 109 | ], 110 | ]; 111 | 112 | $this->initialize(); 113 | $this->initDisabledItems(); 114 | $localItems = $this->prepareItems($itemsConfiguration); 115 | 116 | if (isset($items['pasteAfter'])) { 117 | // @todo Instead of simple typecasting to (int) non integer return values of 118 | // `array_search()` needs to be handled in a more appropriated way. 119 | $position = (int)array_search('pasteAfter', array_keys($items), true); 120 | $beginning = array_slice($items, 0, $position + 1, true); 121 | $end = array_slice($items, $position, null, true); 122 | $items = $beginning + $localItems + $end; 123 | } else { 124 | $items += $localItems; 125 | } 126 | return $items; 127 | } 128 | 129 | /** 130 | * @param string $itemName 131 | * @param string $type 132 | * @return bool 133 | */ 134 | protected function canRender(string $itemName, string $type): bool 135 | { 136 | $canRender = false; 137 | if ($itemName === 'pasteReference') { 138 | $canRender = $this->canBePastedAfter() 139 | && $this->clipboard->currentMode() === 'copy' 140 | && $this->backendUser->checkAuthMode( 141 | 'tt_content', 142 | 'CType', 143 | 'shortcut' 144 | ); 145 | } 146 | return $canRender; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Classes/DataHandler/AbstractDataHandler.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * All rights reserved 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * This script is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * This copyright notice MUST APPEAR in all copies of the script! 24 | ***************************************************************/ 25 | 26 | use Doctrine\DBAL\Driver\Exception as DBALDriverException; 27 | use Doctrine\DBAL\Exception as DBALException; 28 | use EHAERER\PasteReference\Helper\Helper; 29 | use TYPO3\CMS\Core\Database\Connection; 30 | use TYPO3\CMS\Core\Database\ConnectionPool; 31 | use TYPO3\CMS\Core\Database\Query\QueryBuilder; 32 | use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; 33 | use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; 34 | use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction; 35 | use TYPO3\CMS\Core\DataHandling\DataHandler; 36 | use TYPO3\CMS\Core\Utility\GeneralUtility; 37 | 38 | /** 39 | * Class/Function which offers TCE main hook functions. 40 | * 41 | * @author Jo Hasenau 42 | */ 43 | abstract class AbstractDataHandler 44 | { 45 | protected ?Connection $connection = null; 46 | protected string $table = ''; 47 | protected int $pageUid = 0; 48 | protected int $contentUid = 0; 49 | protected ?DataHandler $dataHandler = null; 50 | 51 | /** 52 | * initializes this class 53 | * 54 | * @param string $table The name of the table the data should be saved to 55 | * @param int $uidPid The uid of the record or page we are currently working on 56 | * @param DataHandler $dataHandler 57 | * @throws DBALException|DBALDriverException 58 | */ 59 | public function init(string $table, int $uidPid, DataHandler $dataHandler): void 60 | { 61 | $this->setTable($table); 62 | if ($table === 'tt_content' && $uidPid < 0) { 63 | $this->setContentUid(abs($uidPid)); 64 | $helper = GeneralUtility::makeInstance(Helper::class); 65 | $pageUid = $helper->getPidFromUid($this->getContentUid()); 66 | $this->setPageUid($pageUid); 67 | } else { 68 | $this->setPageUid($uidPid); 69 | } 70 | $this->setTceMain($dataHandler); 71 | } 72 | 73 | /** 74 | * Function to remove any remains of versioned records after finalizing a workspace action 75 | * via 'Discard' or 'Publish' commands 76 | */ 77 | public function cleanupWorkspacesAfterFinalizing(): void 78 | { 79 | $queryBuilder = $this->getQueryBuilder(); 80 | $queryBuilder 81 | ->delete('tt_content') 82 | ->where( 83 | $queryBuilder->expr()->eq( 84 | 'pid', 85 | $queryBuilder->createNamedParameter(-1, Connection::PARAM_INT) 86 | ), 87 | $queryBuilder->expr()->eq( 88 | 't3ver_wsid', 89 | $queryBuilder->createNamedParameter(0, Connection::PARAM_INT) 90 | ) 91 | ) 92 | ->executeStatement(); 93 | } 94 | 95 | /** 96 | * @param string $table 97 | * @return QueryBuilder 98 | */ 99 | public function getQueryBuilder(string $table = 'tt_content'): QueryBuilder 100 | { 101 | $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) 102 | ->getQueryBuilderForTable($table); 103 | $queryBuilder->getRestrictions() 104 | ->removeByType(HiddenRestriction::class) 105 | ->removeByType(StartTimeRestriction::class) 106 | ->removeByType(EndTimeRestriction::class); 107 | 108 | return $queryBuilder; 109 | } 110 | 111 | /** 112 | * @return Connection 113 | */ 114 | public function getConnection(): Connection 115 | { 116 | return GeneralUtility::makeInstance(ConnectionPool::class) 117 | ->getConnectionForTable('tt_content'); 118 | } 119 | 120 | /** 121 | * @return string 122 | */ 123 | public function getTable(): string 124 | { 125 | return $this->table; 126 | } 127 | 128 | /** 129 | * @param string $table 130 | */ 131 | public function setTable(string $table): void 132 | { 133 | $this->table = $table; 134 | } 135 | 136 | /** 137 | * @return int 138 | */ 139 | public function getPageUid(): int 140 | { 141 | return $this->pageUid; 142 | } 143 | 144 | /** 145 | * @param int $pageUid 146 | */ 147 | public function setPageUid(int $pageUid): void 148 | { 149 | $this->pageUid = $pageUid; 150 | } 151 | 152 | /** 153 | * @return int 154 | */ 155 | public function getContentUid(): int 156 | { 157 | return $this->contentUid; 158 | } 159 | 160 | /** 161 | * @param int $contentUid 162 | */ 163 | public function setContentUid(int $contentUid): void 164 | { 165 | $this->contentUid = $contentUid; 166 | } 167 | 168 | /** 169 | * @return DataHandler 170 | */ 171 | public function getTceMain(): DataHandler 172 | { 173 | return $this->dataHandler; 174 | } 175 | 176 | /** 177 | * @param DataHandler $dataHandler 178 | */ 179 | public function setTceMain(DataHandler $dataHandler): void 180 | { 181 | $this->dataHandler = $dataHandler; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Classes/DataHandler/ProcessCmdmap.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * (c) 2013 Stefan Froemken 12 | * All rights reserved 13 | * This script is part of the TYPO3 project. The TYPO3 project is 14 | * free software; you can redistribute it and/or modify 15 | * it under the terms of the GNU General Public License as published by 16 | * the Free Software Foundation; either version 2 of the License, or 17 | * (at your option) any later version. 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * This copyright notice MUST APPEAR in all copies of the script! 25 | ***************************************************************/ 26 | 27 | use Doctrine\DBAL\Driver\Exception as DBALDriverException; 28 | use Doctrine\DBAL\Exception as DBALException; 29 | use Psr\Http\Message\ServerRequestInterface; 30 | use TYPO3\CMS\Core\DataHandling\DataHandler; 31 | 32 | class ProcessCmdmap extends AbstractDataHandler 33 | { 34 | /** 35 | * Function to process the drag & drop copy action 36 | * 37 | * @param string $command The command to be handled by the command map 38 | * @param string $table The name of the table we are working on 39 | * @param int $id The id of the record that is going to be copied 40 | * @param array|string $value The value that has been sent with the copy command 41 | * @param bool $commandIsProcessed A switch to tell the parent object, if the record has been copied 42 | * @param DataHandler $parentObj The parent object that triggered this hook 43 | * @param bool|array $pasteUpdate Values to be updated after the record is pasted 44 | * @throws DBALException|DBALDriverException 45 | */ 46 | public function execute_processCmdmap( 47 | string $command, 48 | string $table, 49 | int $id, 50 | array|string $value, 51 | bool &$commandIsProcessed, 52 | DataHandler $parentObj, 53 | bool|array $pasteUpdate = false 54 | ): void { 55 | $this->init($table, $id, $parentObj); 56 | /** @var ServerRequestInterface $request */ 57 | $request = $GLOBALS['TYPO3_REQUEST'] ?? null; 58 | if (!($request instanceof ServerRequestInterface)) { 59 | return; 60 | } 61 | $queryParams = $request->getQueryParams(); 62 | $reference = isset($queryParams['reference']) ? (int)$queryParams['reference'] : null; 63 | 64 | if ( 65 | $table === 'tt_content' 66 | && $command === 'copy' 67 | && $reference === 1 68 | && !$commandIsProcessed 69 | && !$this->getTceMain()->isImporting 70 | ) { 71 | $dataArray = [ 72 | 'pid' => $value, 73 | 'CType' => 'shortcut', 74 | 'records' => $id, 75 | 'header' => 'Reference', 76 | ]; 77 | 78 | // used for overriding container and column with real target values 79 | if (is_array($pasteUpdate) && !empty($pasteUpdate)) { 80 | $dataArray = array_merge($dataArray, $pasteUpdate); 81 | } 82 | 83 | $clipBoard = $queryParams['CB'] ??= null; 84 | if (!empty($clipBoard)) { 85 | $updateArray = $clipBoard['update']; 86 | if (!empty($updateArray)) { 87 | $dataArray = array_merge($dataArray, $updateArray); 88 | } 89 | } 90 | 91 | $data = []; 92 | $data['tt_content']['NEW234134'] = $dataArray; 93 | 94 | $this->getTceMain()->start($data, []); 95 | $this->getTceMain()->process_datamap(); 96 | 97 | $commandIsProcessed = true; 98 | } 99 | 100 | if ($table === 'tt_content') { 101 | $this->cleanupWorkspacesAfterFinalizing(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Classes/EventListener/AfterTcaCompilationEventListener.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * All rights reserved 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * This script is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * This copyright notice MUST APPEAR in all copies of the script! 24 | ***************************************************************/ 25 | 26 | use EHAERER\PasteReference\PageLayoutView\ShortcutPreviewRenderer; 27 | use TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent; 28 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException; 29 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; 30 | use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; 31 | use TYPO3\CMS\Core\Utility\GeneralUtility; 32 | 33 | class AfterTcaCompilationEventListener 34 | { 35 | /** 36 | * @param AfterTcaCompilationEvent $event 37 | * @throws ExtensionConfigurationExtensionNotConfiguredException 38 | * @throws ExtensionConfigurationPathDoesNotExistException 39 | */ 40 | public function __invoke(AfterTcaCompilationEvent $event): void 41 | { 42 | $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('paste_reference'); 43 | if ( 44 | isset($extConf['enableExtendedShortcutPreviewRenderer']) 45 | && (int)$extConf['enableExtendedShortcutPreviewRenderer'] === 1 46 | ) { 47 | $tca = $event->getTca(); 48 | $tca['tt_content']['types']['shortcut']['previewRenderer'] = ShortcutPreviewRenderer::class; 49 | $event->setTca($tca); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Classes/EventListener/ModifyPageLayoutContentListener.php: -------------------------------------------------------------------------------- 1 | addHeaderContent($pageLayoutController->drawHeaderHook()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Classes/Helper/Helper.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2013 Dirk Hoffmann 9 | * All rights reserved 10 | * This script is part of the TYPO3 project. The TYPO3 project is 11 | * free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * The GNU General Public License can be found at 16 | * http://www.gnu.org/copyleft/gpl.html. 17 | * This script is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * This copyright notice MUST APPEAR in all copies of the script! 22 | ***************************************************************/ 23 | 24 | use Doctrine\DBAL\Driver\Exception as DBALDriverException; 25 | use Doctrine\DBAL\Exception as DBALException; 26 | use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; 27 | use TYPO3\CMS\Core\Database\Connection; 28 | use TYPO3\CMS\Core\Database\ConnectionPool; 29 | use TYPO3\CMS\Core\Database\Query\QueryBuilder; 30 | use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; 31 | use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; 32 | use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction; 33 | use TYPO3\CMS\Core\Localization\LanguageService; 34 | use TYPO3\CMS\Core\SingletonInterface; 35 | use TYPO3\CMS\Core\Utility\GeneralUtility; 36 | 37 | /** 38 | * Paste reference helper class 39 | * 40 | * @author Dirk Hoffmann 41 | */ 42 | class Helper implements SingletonInterface 43 | { 44 | /** 45 | * converts tt_content uid into a pid 46 | * 47 | * @param int $uid the uid value of a tt_content record 48 | * 49 | * @return int 50 | * @throws DBALException|DBALDriverException 51 | */ 52 | public function getPidFromUid(int $uid = 0): int 53 | { 54 | $queryBuilder = $this->getQueryBuilder(); 55 | /** @var array $triggerElement */ 56 | $triggerElement = $queryBuilder 57 | ->select('pid') 58 | ->from('tt_content') 59 | ->where( 60 | $queryBuilder->expr()->eq( 61 | 'uid', 62 | $queryBuilder->createNamedParameter(abs($uid), Connection::PARAM_INT) 63 | ) 64 | ) 65 | ->executeQuery() 66 | ->fetchAssociative(); 67 | $pid = (int)($triggerElement['pid'] ?? 0); 68 | return is_array($triggerElement) && $pid ? $pid : 0; 69 | } 70 | 71 | /** 72 | * @param string $table 73 | * 74 | * @return QueryBuilder 75 | */ 76 | public function getQueryBuilder(string $table = 'tt_content'): QueryBuilder 77 | { 78 | $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) 79 | ->getQueryBuilderForTable($table); 80 | $queryBuilder->getRestrictions() 81 | ->removeByType(HiddenRestriction::class) 82 | ->removeByType(StartTimeRestriction::class) 83 | ->removeByType(EndTimeRestriction::class); 84 | return $queryBuilder; 85 | } 86 | 87 | /** 88 | * @return BackendUserAuthentication|null 89 | */ 90 | public function getBackendUser(): ?BackendUserAuthentication 91 | { 92 | return $GLOBALS['BE_USER'] ?? null; 93 | } 94 | 95 | /** 96 | * @return LanguageService 97 | */ 98 | public function getLanguageService(): ?LanguageService 99 | { 100 | return $GLOBALS['LANG'] ?? null; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Classes/Hooks/DataHandler.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2013 Jo Hasenau 9 | * All rights reserved 10 | * This script is part of the TYPO3 project. The TYPO3 project is 11 | * free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * The GNU General Public License can be found at 16 | * http://www.gnu.org/copyleft/gpl.html. 17 | * This script is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * This copyright notice MUST APPEAR in all copies of the script! 22 | ***************************************************************/ 23 | 24 | use Doctrine\DBAL\Driver\Exception as DBALDriverException; 25 | use Doctrine\DBAL\Exception as DBALException; 26 | use EHAERER\PasteReference\DataHandler\ProcessCmdmap; 27 | use TYPO3\CMS\Core\DataHandling\DataHandler as CoreDataHandler; 28 | use TYPO3\CMS\Core\SingletonInterface; 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | 31 | /** 32 | * Class/Function which offers TCE main hook functions. 33 | * 34 | * @author Jo Hasenau 35 | */ 36 | class DataHandler implements SingletonInterface 37 | { 38 | /** 39 | * Function to process the drag & drop copy action 40 | * 41 | * @param string $command The command to be handled by the command map 42 | * @param string $table The name of the table we are working on 43 | * @param int $id The id of the record that is going to be copied 44 | * @param array|string $value The value that has been sent with the copy command 45 | * @param bool $commandIsProcessed A switch to tell the parent object, if the record has been copied 46 | * @param CoreDataHandler $parentObj The parent object that triggered this hook 47 | * @param bool|array $pasteUpdate Values to be updated after the record is pasted 48 | * @throws DBALException|DBALDriverException 49 | */ 50 | public function processCmdmap( 51 | string $command, 52 | string $table, 53 | int $id, 54 | array|string $value, 55 | bool &$commandIsProcessed, 56 | CoreDataHandler &$parentObj, 57 | bool|array $pasteUpdate 58 | ): void { 59 | if (!$parentObj->isImporting) { 60 | $hook = GeneralUtility::makeInstance(ProcessCmdmap::class); 61 | $hook->execute_processCmdmap($command, $table, $id, $value, $commandIsProcessed, $parentObj, $pasteUpdate); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Classes/Hooks/PageLayoutController.php: -------------------------------------------------------------------------------- 1 | , Tobias Ferger 10 | * All rights reserved 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * The GNU General Public License can be found at 17 | * http://www.gnu.org/copyleft/gpl.html. 18 | * This script is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * This copyright notice MUST APPEAR in all copies of the script! 23 | ***************************************************************/ 24 | 25 | use EHAERER\PasteReference\Helper\Helper; 26 | use TYPO3\CMS\Backend\Clipboard\Clipboard; 27 | use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException; 28 | use TYPO3\CMS\Backend\Routing\UriBuilder; 29 | use TYPO3\CMS\Backend\Utility\BackendUtility; 30 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException; 31 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; 32 | use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; 33 | use TYPO3\CMS\Core\Imaging\IconFactory; 34 | use TYPO3\CMS\Core\Imaging\IconSize; 35 | use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; 36 | use TYPO3\CMS\Core\Page\PageRenderer; 37 | use TYPO3\CMS\Core\Utility\GeneralUtility; 38 | 39 | class PageLayoutController 40 | { 41 | /** @var array */ 42 | protected array $extensionConfiguration = []; 43 | protected array $elFromTable = []; 44 | protected string $copyMode = ''; 45 | protected ?Helper $helper = null; 46 | protected ?IconFactory $iconFactory = null; 47 | protected ?PageRenderer $pageRenderer = null; 48 | 49 | /** 50 | * @param PageRenderer $pageRenderer 51 | * @param IconFactory $iconFactory 52 | * @throws ExtensionConfigurationExtensionNotConfiguredException 53 | * @throws ExtensionConfigurationPathDoesNotExistException 54 | */ 55 | public function __construct(PageRenderer $pageRenderer, IconFactory $iconFactory) 56 | { 57 | $this->extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('paste_reference') ?? []; 58 | $this->helper = GeneralUtility::makeInstance(Helper::class); 59 | $this->iconFactory = $iconFactory; 60 | $this->pageRenderer = $pageRenderer; 61 | 62 | $this->elFromTable = $this->getClipboard()->elFromTable('tt_content'); 63 | $this->copyMode = $this->getClipboard()->clipData['normal']['mode'] ?? ''; 64 | } 65 | 66 | /** 67 | * @return string 68 | */ 69 | public function drawHeaderHook(): string 70 | { 71 | $this->pageRenderer->addInlineLanguageLabelFile('EXT:paste_reference/Resources/Private/Language/locallang_db.xlf', 'tx_paste_reference_js'); 72 | 73 | $jsLines = []; 74 | 75 | $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); 76 | try { 77 | $jsLines[] = 'top.pasteReferenceAllowed = ' . (int)$this->helper->getBackendUser()->checkAuthMode('tt_content', 'CType', 'shortcut') . ';'; 78 | $jsLines[] = 'top.browserUrl = ' . json_encode((string)$uriBuilder->buildUriFromRoute('wizard_element_browser')) . ';'; 79 | } catch (RouteNotFoundException $e) { 80 | } 81 | 82 | if (!empty($this->elFromTable)) { 83 | $this->addJavaScriptModuleInstruction(); 84 | $jsLines[] = 'top.copyMode = "' . $this->copyMode . '";'; 85 | } 86 | 87 | if ( 88 | !(bool)($this->extensionConfiguration['disableCopyFromPageButton'] ?? false) 89 | && !(bool)($this->helper->getBackendUser()->uc['disableCopyFromPageButton'] ?? false) 90 | ) { 91 | $jsLines[] = 'top.copyFromAnotherPageLinkTemplate = ' . json_encode($this->getButtonTemplate()) . ';'; 92 | } 93 | 94 | $javaScript = implode("\n", $jsLines); 95 | $this->pageRenderer->addJsInlineCode('pasteReference', $javaScript, true, false, true); 96 | $this->pageRenderer->loadJavaScriptModule('@ehaerer/paste-reference/paste-reference.js'); 97 | 98 | return ''; 99 | } 100 | 101 | protected function getButtonTemplate(): string 102 | { 103 | $title = $this->helper->getLanguageService()->sL('EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:tx_paste_reference_js.copyfrompage'); 104 | $icon = $this->iconFactory->getIcon('actions-insert-reference', IconSize::SMALL)->render(); 105 | // the CSS-class "t3js-paste-new" does not exist in system extensions 106 | return ''; 107 | } 108 | 109 | protected function addJavaScriptModuleInstruction(): void 110 | { 111 | $JavaScriptModuleInstruction = JavaScriptModuleInstruction::create('@ehaerer/paste-reference/paste-reference.js'); 112 | $javaScriptRenderer = $this->pageRenderer->getJavaScriptRenderer(); 113 | $javaScriptRenderer->addJavaScriptModuleInstruction( 114 | $JavaScriptModuleInstruction->instance( 115 | $this->getJsArgumentsArray() 116 | ) 117 | ); 118 | } 119 | 120 | protected function getJsArgumentsArray(): array 121 | { 122 | $pasteItem = (int)substr((string)key($this->elFromTable), 11); 123 | $pasteRecord = BackendUtility::getRecordWSOL('tt_content', $pasteItem); 124 | $pasteTitle = BackendUtility::getRecordTitle('tt_content', $pasteRecord); 125 | return [ 126 | 'itemOnClipboardUid' => $pasteItem, 127 | 'itemOnClipboardTitle' => $pasteTitle, 128 | 'copyMode' => $this->copyMode, 129 | ]; 130 | } 131 | 132 | protected function getClipboard(): Clipboard 133 | { 134 | $clipboard = GeneralUtility::makeInstance(Clipboard::class); 135 | $clipboard->initializeClipboard(); 136 | $clipboard->lockToNormal(); 137 | $clipboard->cleanCurrent(); 138 | $clipboard->endClipboard(); 139 | return $clipboard; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /Classes/PageLayoutView/ShortcutPreviewRenderer.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * All rights reserved 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * This script is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * This copyright notice MUST APPEAR in all copies of the script! 24 | ***************************************************************/ 25 | 26 | use Doctrine\DBAL\ArrayParameterType; 27 | use Doctrine\DBAL\Exception as DBALException; 28 | use EHAERER\PasteReference\Helper\Helper; 29 | use TYPO3\CMS\Backend\Preview\PreviewRendererInterface; 30 | use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer; 31 | use TYPO3\CMS\Backend\Utility\BackendUtility; 32 | use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem; 33 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationExtensionNotConfiguredException; 34 | use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; 35 | use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; 36 | use TYPO3\CMS\Core\Database\Connection; 37 | use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction; 38 | use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction; 39 | use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction; 40 | use TYPO3\CMS\Core\Utility\GeneralUtility; 41 | 42 | class ShortcutPreviewRenderer extends StandardContentPreviewRenderer implements PreviewRendererInterface 43 | { 44 | /** 45 | * @var array 46 | */ 47 | protected array $extensionConfiguration = []; 48 | protected Helper $helper; 49 | protected bool $showHidden = true; 50 | 51 | /** 52 | * @throws ExtensionConfigurationExtensionNotConfiguredException 53 | * @throws ExtensionConfigurationPathDoesNotExistException 54 | */ 55 | public function __construct() 56 | { 57 | /** @var array $emConf */ 58 | $emConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('paste_reference') ?? []; 59 | $this->extensionConfiguration = $emConf; 60 | $this->helper = GeneralUtility::makeInstance(Helper::class); 61 | } 62 | 63 | /** 64 | * Dedicated method for rendering preview body HTML for 65 | * the page module only. Receives the GridColumnItem 66 | * that contains the record for which a preview should be 67 | * rendered and returned. 68 | * 69 | * @param $item GridColumnItem 70 | * @return string 71 | * @throws DBALException 72 | */ 73 | public function renderPageModulePreviewContent(GridColumnItem $item): string 74 | { 75 | $record = $item->getRecord(); 76 | 77 | // Check if a Fluid-based preview template was defined for this CType 78 | // and render it via Fluid. Possible option: 79 | // mod.web_layout.tt_content.preview.media = EXT:site_mysite/Resources/Private/Templates/Preview/Media.html 80 | $infoArr = []; 81 | $this->getProcessedValue($item, 'header_position,header_layout,header_link', $infoArr); 82 | $tsConfig = BackendUtility::getPagesTSconfig($record['pid'])['mod.']['web_layout.']['tt_content.']['preview.'] ?? []; 83 | 84 | if (!empty($record['records'])) { 85 | $shortCutRenderItems = $this->addShortcutRenderItems($item); 86 | $preview = ''; 87 | foreach ($shortCutRenderItems as $shortcutRecord) { 88 | $shortcutItem = GeneralUtility::makeInstance(GridColumnItem::class, $item->getContext(), $item->getColumn(), $shortcutRecord); 89 | $preview .= '

' . $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:edit') . '

'; 90 | $preview .= '
' . $shortcutItem->getPreview() . '
'; 91 | } 92 | return $preview; 93 | } 94 | 95 | return parent::renderPageModulePreviewContent($item); 96 | } 97 | 98 | /** 99 | * @param GridColumnItem $gridColumnItem 100 | * @return array> 101 | * @throws DBALException 102 | */ 103 | protected function addShortcutRenderItems(GridColumnItem $gridColumnItem): array 104 | { 105 | $renderItems = []; 106 | $record = $gridColumnItem->getRecord(); 107 | $shortcutItems = explode(',', $record['records']); 108 | $collectedItems = []; 109 | foreach ($shortcutItems as $shortcutItem) { 110 | $shortcutItem = trim($shortcutItem); 111 | if (str_contains($shortcutItem, 'pages_')) { 112 | $this->collectContentDataFromPages( 113 | $shortcutItem, 114 | $collectedItems, 115 | $record['recursive'], 116 | $record['uid'], 117 | $record['sys_language_uid'] 118 | ); 119 | } else { 120 | if (!str_contains($shortcutItem, '_') || str_contains($shortcutItem, 'tt_content_')) { 121 | $this->collectContentData( 122 | $shortcutItem, 123 | $collectedItems, 124 | $record['uid'], 125 | $record['sys_language_uid'] 126 | ); 127 | } 128 | } 129 | } 130 | if (!empty($collectedItems)) { 131 | $record['shortcutItems'] = []; 132 | foreach ($collectedItems as $item) { 133 | if ($item) { 134 | $renderItems[] = $item; 135 | } 136 | } 137 | $gridColumnItem->setRecord($record); 138 | } 139 | return $renderItems; 140 | } 141 | 142 | /** 143 | * Collects tt_content data from a single page or a page tree starting at a given page 144 | * 145 | * @todo: move this in a repository 146 | * 147 | * @param string $shortcutItem The single page to be used as the tree root 148 | * @param array> $collectedItems The collected item data rows ordered by parent position, column position and sorting 149 | * @param int $recursive The number of levels for the recursion 150 | * @param int $parentUid uid of the referencing tt_content record 151 | * @param int $language sys_language_uid of the referencing tt_content record 152 | * @throws DBALException 153 | */ 154 | protected function collectContentDataFromPages( 155 | string $shortcutItem, 156 | array &$collectedItems, 157 | int $recursive = 0, 158 | int $parentUid = 0, 159 | int $language = 0 160 | ): void { 161 | $itemList = str_replace('pages_', '', $shortcutItem); 162 | $itemList = GeneralUtility::intExplode(',', $itemList); 163 | 164 | $queryBuilder = $this->helper->getQueryBuilder(); 165 | $result = $queryBuilder 166 | ->select('*') 167 | ->addSelectLiteral($queryBuilder->expr()->inSet( 168 | 'pid', 169 | $queryBuilder->createNamedParameter($itemList, ArrayParameterType::INTEGER) 170 | ) . ' AS inSet') 171 | ->from('tt_content') 172 | ->where( 173 | $queryBuilder->expr()->neq( 174 | 'uid', 175 | $queryBuilder->createNamedParameter($parentUid, Connection::PARAM_INT) 176 | ), 177 | $queryBuilder->expr()->in( 178 | 'pid', 179 | $queryBuilder->createNamedParameter($itemList, ArrayParameterType::INTEGER) 180 | ), 181 | $queryBuilder->expr()->gte('colPos', $queryBuilder->createNamedParameter(0, Connection::PARAM_INT)), 182 | $queryBuilder->expr()->in( 183 | 'sys_language_uid', 184 | $queryBuilder->createNamedParameter([0, -1], ArrayParameterType::INTEGER) 185 | ) 186 | ) 187 | ->orderBy('inSet') 188 | ->addOrderBy('colPos') 189 | ->addOrderBy('sorting') 190 | ->executeQuery(); 191 | 192 | while ($item = $result->fetchAssociative()) { 193 | /** @var array $item */ 194 | if (!empty($this->extensionConfiguration['overlayShortcutTranslation']) && $language > 0) { 195 | $translatedItem = BackendUtility::getRecordLocalization('tt_content', (int)($item['uid'] ?? 0), $language) ?: []; 196 | if (is_array($translatedItem) && $translatedItem !== []) { 197 | $item = array_shift($translatedItem); 198 | } 199 | } 200 | if ($this->getBackendUser()->workspace > 0) { 201 | unset($item['inSet']); 202 | BackendUtility::workspaceOL('tt_content', $item, $this->getBackendUser()->workspace); 203 | } 204 | $item['tx_paste_reference_container'] = $item['pid']; 205 | $collectedItems[] = $item; 206 | } 207 | } 208 | 209 | /** 210 | * Collects tt_content data from a single tt_content element 211 | * 212 | * @todo: move this in a repository 213 | * 214 | * @param string $shortcutItem The tt_content element to fetch the data from 215 | * @param array> $collectedItems The collected item data row 216 | * @param int $parentUid uid of the referencing tt_content record 217 | * @param int $language sys_language_uid of the referencing tt_content record 218 | * @throws DBALException 219 | */ 220 | protected function collectContentData(string $shortcutItem, array &$collectedItems, int $parentUid, int $language): void 221 | { 222 | $shortcutItem = str_replace('tt_content_', '', $shortcutItem); 223 | if ((int)$shortcutItem !== $parentUid) { 224 | $queryBuilder = $this->helper->getQueryBuilder(); 225 | $queryBuilder->getRestrictions()->removeByType(StartTimeRestriction::class); 226 | $queryBuilder->getRestrictions()->removeByType(EndTimeRestriction::class); 227 | if ($this->showHidden) { 228 | $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class); 229 | } 230 | /** @var array|false $item */ 231 | $item = $queryBuilder 232 | ->select('*') 233 | ->from('tt_content') 234 | ->where( 235 | $queryBuilder->expr()->eq( 236 | 'uid', 237 | $queryBuilder->createNamedParameter((int)$shortcutItem, Connection::PARAM_INT) 238 | ) 239 | ) 240 | ->setMaxResults(1) 241 | ->executeQuery() 242 | ->fetchAssociative(); 243 | if (!empty($this->extensionConfiguration['overlayShortcutTranslation']) && $language > 0) { 244 | $translatedItem = BackendUtility::getRecordLocalization('tt_content', (int)($item['uid'] ?? 0), $language) ?: []; 245 | if (is_array($translatedItem) && $translatedItem !== []) { 246 | $item = array_shift($translatedItem); 247 | } 248 | } 249 | 250 | if ($this->getBackendUser()->workspace > 0) { 251 | BackendUtility::workspaceOL( 252 | 'tt_content', 253 | $item, 254 | $this->getBackendUser()->workspace 255 | ); 256 | } 257 | $collectedItems[] = $item; 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /Configuration/Icons.php: -------------------------------------------------------------------------------- 1 | [ 9 | 'provider' => SvgIconProvider::class, 10 | 'source' => 'EXT:paste_reference/Resources/Public/Icons/Extension.svg', 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /Configuration/JavaScriptModules.php: -------------------------------------------------------------------------------- 1 | ['core', 'backend'], 6 | 'tags' => [ 7 | 'backend.contextmenu', 8 | ], 9 | 'imports' => [ 10 | '@ehaerer/paste-reference/' => 'EXT:paste_reference/Resources/Public/JavaScript/', 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /Configuration/Services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true 4 | autoconfigure: true 5 | public: false 6 | 7 | EHAERER\PasteReference\: 8 | resource: '../Classes/*' 9 | 10 | EHAERER\PasteReference\Hooks\PageLayoutController: 11 | public: true 12 | 13 | EHAERER\PasteReference\ContextMenu\PasteReferenceItemProvider: 14 | tags: 15 | - name: backend.contextmenu.itemprovider 16 | 17 | EHAERER\PasteReference\EventListener\AfterTcaCompilationEventListener: 18 | tags: 19 | - name: event.listener 20 | identifier: 'paste-reference/after-tca-compilation-event' 21 | 22 | EHAERER\PasteReference\EventListener\ModifyPageLayoutContentListener: 23 | tags: 24 | - name: event.listener 25 | identifier: 'paste-reference/backend/modify-page-module-content' 26 | -------------------------------------------------------------------------------- /Documentation/AdministratorManual/Configuration/ExtensionConfiguration/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../../Includes.txt 7 | 8 | .. _extensionManager: 9 | 10 | Extension Manager 11 | ----------------- 12 | 13 | Some general settings can be configured in the Extension Configuration in the administration module. 14 | If you need to configure those, switch to the module "Admin tools" -> "Settings" -> "Extension Configuration", select the extension "**paste_reference**" and open it! 15 | 16 | .. image:: /Images/Administration/extension-configuration.png 17 | :border: 0 18 | :align: left 19 | :name: ExtensionConfiguration 20 | 21 | There are two settings: 22 | 23 | Properties 24 | ^^^^^^^^^^ 25 | 26 | disableCopyFromPageButton 27 | """"""""""""""""""""""""" 28 | You can disable the "copy from page button" in the page module if you don't need it. 29 | -------------------------------------------------------------------------------- /Documentation/AdministratorManual/Configuration/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | 9 | Configuration 10 | ------------- 11 | 12 | .. only:: html 13 | 14 | This chapter describes how to the extension can be configured 15 | 16 | .. toctree:: 17 | :maxdepth: 5 18 | :titlesonly: 19 | 20 | ExtensionConfiguration/Index 21 | -------------------------------------------------------------------------------- /Documentation/AdministratorManual/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _admin-manual: 10 | 11 | For administrators 12 | ================== 13 | 14 | .. only:: html 15 | 16 | This chapter describes how to manage the extension from a superuser point of view. 17 | 18 | .. toctree:: 19 | :maxdepth: 5 20 | :titlesonly: 21 | 22 | Installation/Index 23 | Update/Index 24 | Configuration/Index 25 | -------------------------------------------------------------------------------- /Documentation/AdministratorManual/Installation/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _installation: 9 | 10 | Installation 11 | ============ 12 | 13 | The extension needs to be installed as any other extension of TYPO3 CMS: 14 | 15 | #. Switch to the module “Extension Manager”. 16 | 17 | #. Get the extension 18 | #. **Use composer**: Use `composer require ehaerer/paste-reference`. 19 | 20 | #. **Get it from the Extension Manager:** Press the “Retrieve/Update” 21 | button and search for the extension key *paste_reference* and import the 22 | extension from the repository. 23 | 24 | #. **Get it from typo3.org:** You can always get current version from 25 | `https://extensions.typo3.org/extension/paste_reference/ 26 | `_ by 27 | downloading either the t3x or zip version. Upload 28 | the file afterwards in the Extension Manager. 29 | 30 | #. The Extension Manager offers some basic configuration which is 31 | explained :ref:`here `. 32 | 33 | Latest version from git 34 | ----------------------- 35 | You can get the latest version from git by using the git command: 36 | 37 | .. code-block:: bash 38 | 39 | git clone https://github.com/Kephson/paste_reference.git 40 | 41 | .. important:: 42 | 43 | The master branch supports TYPO3 CMS 9 and 10 only. 44 | 45 | -------------------------------------------------------------------------------- /Documentation/AdministratorManual/Update/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | 9 | Updating 10 | -------- 11 | If you update EXT:paste_reference to a newer version, please read this section carefully! 12 | 13 | Versioning 14 | ^^^^^^^^^^ 15 | EXT:paste_reference uses a 3-number versioning scheme: *..* 16 | 17 | - Major: Major breaking changes 18 | - Minor: Minor breaking changes 19 | - Patch: No breaking changes 20 | 21 | Before an update 22 | ^^^^^^^^^^^^^^^^ 23 | 24 | Before you start the update procedure, please read the changelog of all versions which have been 25 | released in the meantime! You can find those in the manual :ref:`here `. 26 | 27 | Furthermore it is **always** a good idea to do updates on a dedicated test installation or at least create a database backup. 28 | -------------------------------------------------------------------------------- /Documentation/DeveloperManual/Contribute/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _contribute: 9 | 10 | Contribute 11 | ---------- 12 | 13 | Contributions are essential for the success of open-source projects but certainly not limited to contribute code. A lot more can be done: 14 | 15 | - Improve documentation 16 | - Answer questions on stackoverflow.com 17 | 18 | 19 | Contribution workflow 20 | ^^^^^^^^^^^^^^^^^^^^^ 21 | 22 | Please create always an issue at https://github.com/Kephson/paste_reference/issues before starting with a change. This is essential helpful if you are unsure if your change will be accepted. 23 | 24 | Get the latest version from git 25 | """"""""""""""""""""""""""""""" 26 | 27 | Fork the repository https://github.com/Kephson/paste_reference and provide a pull request with your change 28 | -------------------------------------------------------------------------------- /Documentation/DeveloperManual/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _developer-manual: 10 | 11 | For developers 12 | ============== 13 | 14 | .. only:: html 15 | 16 | This chapter describes how you could contribute to the extension as a developer. 17 | 18 | .. toctree:: 19 | :maxdepth: 5 20 | :titlesonly: 21 | 22 | Contribute/Index 23 | -------------------------------------------------------------------------------- /Documentation/Images/Administration/extension-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kephson/paste_reference/2a46e293b0f79a0661f19cc2b09c43388fc98fed/Documentation/Images/Administration/extension-configuration.png -------------------------------------------------------------------------------- /Documentation/Images/Backend/copy-content-other-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kephson/paste_reference/2a46e293b0f79a0661f19cc2b09c43388fc98fed/Documentation/Images/Backend/copy-content-other-page.png -------------------------------------------------------------------------------- /Documentation/Images/Backend/paste-copy-or-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kephson/paste_reference/2a46e293b0f79a0661f19cc2b09c43388fc98fed/Documentation/Images/Backend/paste-copy-or-reference.png -------------------------------------------------------------------------------- /Documentation/Images/Backend/paste-into-column.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kephson/paste_reference/2a46e293b0f79a0661f19cc2b09c43388fc98fed/Documentation/Images/Backend/paste-into-column.png -------------------------------------------------------------------------------- /Documentation/Includes.txt: -------------------------------------------------------------------------------- 1 | .. This is 'Includes.txt'. It is included at the very top of each and 2 | every ReST source file in this documentation project (= manual). 3 | -------------------------------------------------------------------------------- /Documentation/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: Includes.txt 7 | 8 | 9 | .. _start: 10 | 11 | ============================================================= 12 | Paste reference 13 | ============================================================= 14 | 15 | .. only:: html 16 | 17 | :Classification: 18 | paste reference 19 | 20 | :Version: 21 | |release| 22 | 23 | :Language: 24 | en 25 | 26 | :Keywords: 27 | paste reference backend content elements 28 | 29 | :Copyright: 30 | 2024 31 | 32 | :Author: 33 | Ephraim Härer 34 | 35 | :License: 36 | This document is published under the Open Content License 37 | available from https://www.opencontent.org/opl.shtml 38 | 39 | :Rendered: 40 | |today| 41 | 42 | The content of this document is related to TYPO3, 43 | a GNU/GPL CMS/Framework available from `www.typo3.org `_. 44 | 45 | 46 | **Table of Contents** 47 | 48 | .. toctree:: 49 | :maxdepth: 3 50 | :titlesonly: 51 | :glob: 52 | 53 | Introduction/Index 54 | UsersManual/Index 55 | AdministratorManual/Index 56 | DeveloperManual/Index 57 | Misc/Index 58 | -------------------------------------------------------------------------------- /Documentation/Introduction/About/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _about: 9 | 10 | What does it do? 11 | ================ 12 | This extension brings the extracted functions from gridelements to copy and paste content elements also as reference and not only as copy. 13 | A lot of TYPO3 users love this features but don't know that this aren't core features. 14 | 15 | **Basic features (extracted from EXT:gridelements)** 16 | 17 | - Copy content elements and paste as reference (also in context menu with right click) 18 | - Copy content elements from other pages in page module 19 | -------------------------------------------------------------------------------- /Documentation/Introduction/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. _introduction: 7 | 8 | Introduction 9 | ============ 10 | 11 | .. only:: html 12 | 13 | This chapter gives you a basic introduction about the TYPO3 CMS extension "*paste_reference*". 14 | 15 | .. toctree:: 16 | :maxdepth: 5 17 | :titlesonly: 18 | 19 | About/Index 20 | Support/Index 21 | Thanks/Index 22 | -------------------------------------------------------------------------------- /Documentation/Introduction/Support/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _support: 9 | 10 | Need Support? 11 | ============= 12 | There are various ways to get support for EXT:paste_reference! 13 | 14 | Stackoverflow 15 | ------------- 16 | Please use https://stackoverflow.com to get best support. Tags you should use are `typo3` and `tx-paste-reference`, so your question will be visible at https://stackoverflow.com/questions/tagged/tx-paste-reference. 17 | 18 | Slack 19 | ----- 20 | You can use Slack to get support from other people and help about your questions! 21 | 22 | The url is: https://typo3.slack.com/ 23 | 24 | .. note:: 25 | 26 | If you are not yet registered, use https://forger.typo3.com/slack for that! 27 | 28 | Sponsoring 29 | ---------- 30 | If you need a feature which is not yet implemented, feel free to contact me anytime! 31 | 32 | You can find my contact data on https://ephra.im/kontakt. 33 | 34 | Private/Personal support 35 | ------------------------ 36 | If you need private or personal support, ask one of the developers for it. 37 | 38 | **Be aware that this support might not be free!** 39 | -------------------------------------------------------------------------------- /Documentation/Introduction/Thanks/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _thanks: 9 | 10 | Help supporting further development 11 | =================================== 12 | This extension and manual has been created with extracting a lot of code and features from the extension gridelements. 13 | 14 | If this extension helps you in anyway to achieve your requirements, please think about giving something back. 15 | Or you want to sponsor a feature to extend something that's already there? 16 | Then you can find my contact data here https://ephra.im/kontakt. 17 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/0-1-0.rst: -------------------------------------------------------------------------------- 1 | 0.1.0 - 17th February 2021 2 | ========================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - initial release for TYPO3 9.5 and 10.4 21 | - extracted most important functions from gridelements 22 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/1-0-0.rst: -------------------------------------------------------------------------------- 1 | 1.0.0 - 19th February 2021 2 | ========================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - fixed backend JavaScripts for TYPO3 10 21 | - updated database handling for TYPO3 10 22 | - added documentation and updated readme 23 | - initial release of version 1.0 also in TER 24 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-0.rst: -------------------------------------------------------------------------------- 1 | 2.0.0 - 31st August 2022 2 | ======================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - full update for TYPO3 11.5 only 21 | - thanks to @sbuerk and @nigelmann 22 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-1.rst: -------------------------------------------------------------------------------- 1 | 2.0.1 - 12 of October 2022 2 | ========================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - fixed a problem with datahandler, see issue #6, thanks to @achimfritz 21 | - fixed a problem with shortcut preview renderer, see issue #5, thanks to @akiessling 22 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-2.rst: -------------------------------------------------------------------------------- 1 | 2.0.2 - 24 of October 2022 2 | ========================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [BUG] fixed a problem with inserting elements from other pages, see issue #8, thanks to @tomashavner 21 | - [BUG] fixed problems drag and drop handling, see issue #8, thanks to @tomashavner 22 | - [FEATURE] replaced new JS window with backend Modal 23 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-3.rst: -------------------------------------------------------------------------------- 1 | 2.0.3 - 1st of February 2023 2 | ============================ 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [BUG] fixed a problem with deprecated top.rawurlencode(), see issue #11, thanks to @taieb123 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-4.rst: -------------------------------------------------------------------------------- 1 | 2.0.4 - February 8, 2023 2 | ======================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [FEATURE] added French translation files, thanks to @taieb123 21 | - [BUX] fixed problems with broken language when moving elements, thanks to @gamerxl 22 | - [BUG] fixed undefined header in modal, thanks to @taieb123 23 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/2-0-5.rst: -------------------------------------------------------------------------------- 1 | 2.0.5 - September 3, 2023 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [BUG] Inserting an Ref does not work #20, thanks to @boonkerz 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/3-0-0.rst: -------------------------------------------------------------------------------- 1 | 3.0.0 - October 16, 2023 2 | ======================== 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [CHG] updated all Classes and files to work with TYPO3 12 21 | - [CHG] moved/changed JavaScript files from RequireJS to ES6 22 | - initial release for TYPO3 >= 12.4 23 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/3-0-1.rst: -------------------------------------------------------------------------------- 1 | 3.0.1 - November 15, 2024 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [CHG] included pull request from @zoranilic and fixed problems with translations when pasting a reference 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/3-0-2.rst: -------------------------------------------------------------------------------- 1 | 3.0.2 - November 18, 2024 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [BUGFIX] fix eventHandler to show modal window for moving elements 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/4-0-0.rst: -------------------------------------------------------------------------------- 1 | 4.0.0 - November 15, 2024 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - New release of this extension for TYPO3 version 13 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/4-0-1.rst: -------------------------------------------------------------------------------- 1 | 4.0.1 - November 18, 2024 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [BUGFIX] fix eventHandler to show modal window for moving elements 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/4-0-2.rst: -------------------------------------------------------------------------------- 1 | 4.0.2 - November 28, 2024 2 | ========================= 3 | 4 | .. include:: ../../Includes.txt 5 | 6 | .. only:: html 7 | 8 | .. contents:: 9 | :local: 10 | :depth: 3 11 | 12 | 13 | Important changes 14 | ----------------- 15 | 16 | All Changes 17 | ----------- 18 | This is a list of all changes in this release: :: 19 | 20 | - [TASK] readded styling of referenced elements in backend 21 | -------------------------------------------------------------------------------- /Documentation/Misc/Changelog/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _changelog: 9 | 10 | ChangeLog 11 | --------- 12 | 13 | Please follow this link to know what has been done in which version. 14 | 15 | List of versions 16 | ================ 17 | 18 | .. toctree:: 19 | :maxdepth: 5 20 | :titlesonly: 21 | :glob: 22 | 23 | 4-0-2 24 | 4-0-1 25 | 4-0-0 26 | 3-0-2 27 | 3-0-1 28 | 3-0-0 29 | 2-0-5 30 | 2-0-4 31 | 2-0-3 32 | 2-0-2 33 | 2-0-1 34 | 2-0-0 35 | 1-0-0 36 | 0-1-0 37 | -------------------------------------------------------------------------------- /Documentation/Misc/DocumentationBestPractice/Examples/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | Examples 7 | ======== 8 | 9 | Headline 2 10 | ---------- 11 | 12 | Headline 3 13 | ^^^^^^^^^^ 14 | 15 | Headline 4 16 | """""""""" 17 | 18 | Headline 5 19 | ~~~~~~~~~~ 20 | 21 | https://wiki.typo3.org/ReST_Syntax 22 | 23 | https://docs.typo3.org/ 24 | 25 | .. note:: This is a note admonition. 26 | 27 | These notes are similar to tips, but usually contain information you should pay attention to. It might be details about a step that a whole operation hinges on or it may highlight an essential sequence of tasks. 28 | 29 | - The note contains all indented body elements following. 30 | - It includes this bullet list. 31 | 32 | .. tip:: 33 | 34 | Take a break from time to time! 35 | 36 | .. important:: 37 | 38 | Remember to always say "please" when asking your software to do something. 39 | 40 | .. attention:: 41 | 42 | Some directives of the ReST syntax have different 43 | names or work differently. 44 | 45 | .. warning:: 46 | 47 | These notes draw your attention to things that can interrupt your service or website if not done correctly. Some actions can be difficult to undo. 48 | 49 | 50 | Properties 51 | ^^^^^^^^^^ 52 | 53 | .. container:: ts-properties 54 | 55 | =========================== ===================================== ======================= ==================== 56 | Property Data type :ref:`t3tsref:stdwrap` Default 57 | =========================== ===================================== ======================= ==================== 58 | fobar_ :ref:`t3tsref:data-type-wrap` yes :code:`
|
` 59 | `subst\_elementUid`_ :ref:`t3tsref:data-type-boolean` no 0 60 | =========================== ===================================== ======================= ==================== 61 | 62 | .. _fobar: 63 | 64 | Property details 65 | ^^^^^^^^^^^^^^^^ 66 | 67 | .. only:: html 68 | 69 | .. contents:: This page 70 | :local: 71 | :depth: 1 72 | 73 | 74 | .. _subst_elementUid`: 75 | 76 | :typoscript:`plugin.tx_extensionkey.wrapItemAndSub =` :ref:`t3tsref:data-type-wrap` 77 | 78 | Wraps the whole item and any submenu concatenated to it. 79 | 80 | 81 | .. _ts-plugin-tx-extensionkey-substelementUid: 82 | 83 | subst_elementUid 84 | """""""""""""""" 85 | 86 | :typoscript:`plugin.tx_extensionkey.subst_elementUid =` :ref:`t3tsref:data-type-boolean` 87 | 88 | text text text text text text text text text text text text text text text text text text 89 | text text text text text text text text text text text text text text text text text text 90 | 91 | 92 | API 93 | --- 94 | 95 | How to use the API... 96 | 97 | .. code-block:: php 98 | 99 | $stuff = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance( 100 | '\\Foo\\Bar\\Utility\\Stuff' 101 | ); 102 | $stuff->do(); 103 | 104 | or some other language: 105 | 106 | .. code-block:: javascript 107 | :linenos: 108 | :emphasize-lines: 2-4 109 | 110 | $(document).ready( 111 | function () { 112 | doStuff(); 113 | } 114 | ); 115 | 116 | 117 | Tables 118 | ------ 119 | 120 | +------------+------------+-----------+ 121 | | Header 1 | Header 2 | Header 3 | 122 | +============+============+===========+ 123 | | body row 1 | column 2 | column 3 | 124 | +------------+------------+-----------+ 125 | | body row 3 | Cells may | - Cells | 126 | +------------+ span rows. | - contain | 127 | | body row 4 | | - blocks. | 128 | +------------+------------+-----------+ 129 | -------------------------------------------------------------------------------- /Documentation/Misc/DocumentationBestPractice/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | 7 | Documentation best practice 8 | =========================== 9 | 10 | .. only:: html 11 | 12 | Best practice for Documentation with rest 13 | 14 | .. toctree:: 15 | :maxdepth: 5 16 | :titlesonly: 17 | 18 | Examples/Index 19 | -------------------------------------------------------------------------------- /Documentation/Misc/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _misc: 10 | 11 | Miscellaneous 12 | ============= 13 | 14 | .. only:: html 15 | 16 | Misc 17 | 18 | .. toctree:: 19 | :maxdepth: 5 20 | :titlesonly: 21 | 22 | Changelog/Index 23 | MissingKnownErrors/Index 24 | DocumentationBestPractice/Index -------------------------------------------------------------------------------- /Documentation/Misc/MissingKnownErrors/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | 9 | Known problems 10 | -------------- 11 | The following issues are known problems. However those are either not fixable inside EXT:paste_reference or not too easy to solve! 12 | 13 | Nothing reported at the moment 14 | ============================== 15 | There are no known issues or problems at the moment. 16 | -------------------------------------------------------------------------------- /Documentation/UsersManual/HowToStart/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../../Includes.txt 7 | 8 | .. _howToStart: 9 | 10 | How to start 11 | ============ 12 | This walkthrough will help you to implement the extension paste_reference at your 13 | TYPO3 site. The installation is covered :ref:`here `. 14 | 15 | .. TODO: add screenshots 16 | 17 | .. only:: html 18 | 19 | .. contents:: 20 | :local: 21 | :depth: 1 22 | 23 | .. _howToStartCreateRecords: 24 | 25 | Copy content from other page 26 | ---------------------------- 27 | With the new button in page module it is possible to copy content from another page to the current page. 28 | 29 | .. image:: /Images/Backend/copy-content-other-page.png 30 | :border: 0 31 | :align: left 32 | :name: CopyContentOtherPage 33 | 34 | 35 | Paste reference into column 36 | --------------------------- 37 | Paste a copied content element like it's done normally in TYPO3 core. 38 | 39 | .. image:: /Images/Backend/paste-into-column.png 40 | :border: 0 41 | :align: left 42 | :name: PasteIntoColumn 43 | 44 | You can select if you want to copy the element or if you want to insert a reference. 45 | 46 | .. image:: /Images/Backend/paste-copy-or-reference.png 47 | :border: 0 48 | :align: left 49 | :name: PasteCopyOrReference 50 | -------------------------------------------------------------------------------- /Documentation/UsersManual/Index.rst: -------------------------------------------------------------------------------- 1 | .. ================================================== 2 | .. FOR YOUR INFORMATION 3 | .. -------------------------------------------------- 4 | .. -*- coding: utf-8 -*- with BOM. 5 | 6 | .. include:: ../Includes.txt 7 | 8 | 9 | .. _userManual: 10 | 11 | For editors 12 | =========== 13 | 14 | .. only:: html 15 | 16 | This chapter describes how to use the extension from a user point of view. 17 | 18 | .. toctree:: 19 | :maxdepth: 5 20 | :titlesonly: 21 | 22 | HowToStart/Index 23 | -------------------------------------------------------------------------------- /Documentation/guides.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 13 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ephraim Härer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TYPO3 Extension `paste_reference` 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/ehaerer/paste-reference/v)](//packagist.org/packages/ehaerer/paste-reference) 4 | [![Latest Unstable Version](https://poser.pugx.org/ehaerer/paste-reference/v/unstable)](//packagist.org/packages/ehaerer/paste-reference) 5 | [![License](https://poser.pugx.org/ehaerer/paste-reference/license)](//packagist.org/packages/ehaerer/paste-reference) 6 | [![Total Downloads](https://poser.pugx.org/ehaerer/paste-reference/downloads)](//packagist.org/packages/ehaerer/paste-reference) 7 | [![Monthly Downloads](https://poser.pugx.org/ehaerer/paste-reference/d/monthly)](//packagist.org/packages/ehaerer/paste-reference) 8 | [![CI - main](https://github.com/Kephson/paste_reference/actions/workflows/ci.yml/badge.svg)](https://github.com/Kephson/paste_reference/actions/workflows/ci.yml) 9 | 10 | > This extension brings the extracted functions from gridelements to copy and paste content elements also as reference and not only as copy. 11 | > A lot of TYPO3 users love these features but don't know that this aren't core features. 12 | 13 | ## 1 Features 14 | 15 | * Copy content elements and paste as reference (also in context menu with right click) 16 | * Copy content elements from other pages in page module 17 | * [Full documentation in TYPO3 TER][1] 18 | 19 | ## 2 Usage 20 | 21 | ### 2.1 Installation 22 | 23 | #### Installation using Composer 24 | 25 | The recommended way to install the extension is using [Composer][2]. 26 | 27 | Run the following command within your Composer based TYPO3 project: 28 | 29 | ``` 30 | composer require ehaerer/paste-reference 31 | ``` 32 | 33 | #### Installation as extension from TYPO3 Extension Repository (TER) - not recommended 34 | 35 | Download and install the [extension][3] with the extension manager module. 36 | 37 | ### 2.2 Minimal setup 38 | 39 | 1) Just install the extension and you are done 40 | 41 | ## 3 Report issues 42 | 43 | Please report issue directly in the [issue tracker in the Github repository][6]. 44 | 45 | ## 4 Administration corner 46 | 47 | ### 4.1 Settings in extension configuration 48 | 49 | * **disableCopyFromPageButton** - You can disable the "copy from page button" in the page module if you don't need it. 50 | 51 | ### 4.2 Changelog 52 | 53 | Please look into the [official extension documentation in changelog chapter][4]. 54 | 55 | ### 4.3 Release Management 56 | 57 | Paste reference uses [**semantic versioning**][5], which means, that 58 | * **bugfix updates** (e.g. 1.0.0 => 1.0.1) just includes small bugfixes or security relevant stuff without breaking changes, 59 | * **minor updates** (e.g. 1.0.0 => 1.1.0) includes new features and smaller tasks without breaking changes, 60 | * and **major updates** (e.g. 1.0.0 => 2.0.0) breaking changes wich can be refactorings, features or bugfixes. 61 | 62 | ### 4.4 Contribution 63 | 64 | **Pull Requests** are gladly welcome! Nevertheless please don't forget to add an issue and connect it to your pull requests. This 65 | is very helpful to understand what kind of issue the **PR** is going to solve. 66 | 67 | Bugfixes: Please describe what kind of bug your fix solve and give us feedback how to reproduce the issue. We're going 68 | to accept only bugfixes if we can reproduce the issue. 69 | 70 | Features: Not every feature is relevant for the bulk of `paste_reference` users. In addition: We don't want to make ``paste_reference`` 71 | even more complicated in usability for an edge case feature. It helps to have a discussion about a new feature before you open a pull request. 72 | 73 | ## 5 Local development 74 | 75 | ### 5.1 Overview 76 | 77 | This repository contains a so-called [Extension for the TYPO3 CMS](https://github.com/typo3) which cannot be used on its 78 | own but has been prepared to install required dependency to provide a TYPO3 v12 composer based installation within the 79 | untracked `.Build/` folder with `.Build/public/` being the doc-root to point a web-server on. 80 | 81 | For simpler onboarding a generic [ddev project configuration]() is included to quickstart a local TYPO3 v12 instance 82 | in a predefined environment along with data set. See [5.2](#52-use-ddev-to-setup-a-local-development-instance) for how 83 | to use ddev. 84 | 85 | ### 5.2 Use ddev to setup a local development instance 86 | 87 | > Please ensure to have the pre-requisit ddev and docker/colima/... installed and working to follow this section. 88 | 89 | #### 5.2.1 Single command start-up 90 | 91 | ```bash 92 | ddev start \ 93 | && ddev composer install \ 94 | && ddev restart \ 95 | && ddev typo3 setup \ 96 | --driver=mysqli \ 97 | --host=db \ 98 | --port=3306 \ 99 | --dbname=db \ 100 | --username=db \ 101 | --password=db \ 102 | --admin-username=john-doe \ 103 | --admin-user-password='John-Doe-1701D.' \ 104 | --admin-email="john.doe@example.com" \ 105 | --project-name='ext-paste-reference' \ 106 | --no-interaction \ 107 | --server-type=apache \ 108 | --force \ 109 | && ddev restart \ 110 | && ddev typo3 cache:warmup \ 111 | && ddev typo3 styleguide:generate --create all \ 112 | && ddev typo3 cache:warmup \ 113 | && ddev launch /typo3/ 114 | ``` 115 | 116 | which creates a instance with two different hidden page trees and a admin user without asking for it. 117 | Adjust the `--admin-*` arguments to match your needs. 118 | 119 | #### 5.2.2 Splittet startup commands 120 | 121 | **First startup and composer package installation** 122 | 123 | ```bash 124 | ddev start \ 125 | && ddev composer install \ 126 | && ddev restart 127 | ``` 128 | 129 | **Setup TYPO3 using typo3 setup command** 130 | 131 | > Note that the following command is interactive and asks for admin user credential, name and email. 132 | > Ensure to remember the values you enter here for later login into the TYPO3 backend. 133 | 134 | ```bash 135 | ddev typo3 setup \ 136 | --driver=mysqli \ 137 | --host=db \ 138 | --port=3306 \ 139 | --dbname=db \ 140 | --username=db \ 141 | --password=db \ 142 | --server-type=apache \ 143 | --force \ 144 | && ddev restart 145 | ``` 146 | 147 | **Use `EXT:styleguide` to create page trees** 148 | 149 | ```bash 150 | ddev typo3 styleguide:generate --create all \ 151 | && ddev typo3 cache:warmup 152 | ``` 153 | 154 | **Launch the backend login form** 155 | 156 | ```bash 157 | ddev launch /typo3/ 158 | ``` 159 | 160 | #### 5.2.3 Stop & destroy ddev instance 161 | 162 | **Simply stop ddev instance** 163 | 164 | ```bash 165 | ddev stop 166 | ``` 167 | 168 | **Completely remove ddev instance** 169 | 170 | ```bash 171 | ddev stop -ROU 172 | ``` 173 | 174 | ### 5.3 Render documentation 175 | 176 | To render the documentation, the TYPO3 Documentation render-guides image can be used, 177 | which is included in the `Build/Scripts/runTests.sh` dispatcher script. 178 | 179 | **Render documentation** 180 | 181 | ```bash 182 | Build/Scripts/runTests.sh -s renderDocumentation 183 | ``` 184 | 185 | **Open rendered documentation (Linux>** 186 | 187 | ```bash 188 | Build/Scripts/runTests.sh -s renderDocumentation 189 | xdg-open "Documentation-GENERATED-temp/Index.html" 190 | ``` 191 | 192 | **Open rendered documentation (MacOS)** 193 | 194 | ```bash 195 | Build/Scripts/runTests.sh -s renderDocumentation 196 | open "Documentation-GENERATED-temp/Index.html" 197 | ``` 198 | 199 | **Open rendered documentation (Windows)** 200 | 201 | ```bash 202 | Build/Scripts/runTests.sh -s renderDocumentation 203 | start "Documentation-GENERATED-temp/Index.html" 204 | ``` 205 | 206 | 207 | [1]: https://docs.typo3.org/p/ehaerer/paste-reference/master/en-us/ 208 | [2]: https://getcomposer.org/ 209 | [3]: https://extensions.typo3.org/extension/paste_reference 210 | [4]: https://docs.typo3.org/p/ehaerer/paste-reference/master/en-us/Misc/Changelog/Index.html 211 | [5]: https://semver.org/ 212 | [6]: https://github.com/Kephson/paste_reference/issues 213 | -------------------------------------------------------------------------------- /Resources/Private/Language/locallang.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 |
6 | 7 | 8 | Disable the "Copy from page" button. 9 | 10 | 11 | Use language overlay for content and page records referenced in shortcut elements. 12 | 13 | 14 | Enable extended shortcut preview renderer in TYPO3 backend. 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Resources/Private/Language/locallang_db.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 |
6 | database 7 | Language labels for database tables/fields belonging to extension 'paste_reference' 8 |
9 | 10 | 11 | Paste Reference 12 | 13 | 14 | Paste reference after 15 | 16 | 17 | Copy content from another page to this place 18 | 19 | 20 | Drop Here 21 | 22 | 23 | Missing Content Template 24 | 25 | 26 | New 27 | 28 | 29 | Paste into this column 30 | 31 | 32 | Paste after this record 33 | 34 | 35 | Paste copy into this column 36 | 37 | 38 | How do you want to paste that clipboard content here? 39 | 40 | 41 | Paste copy 42 | 43 | 44 | Paste reference into this column 45 | 46 | 47 | Paste reference 48 | 49 | 50 | No title 51 | 52 | 53 | Paste in clipboard content as reference 54 | 55 | 56 | Copy "%s" as reference after "%s"? 57 | 58 | 59 |
60 |
61 | -------------------------------------------------------------------------------- /Resources/Public/Icons/Extension.svg: -------------------------------------------------------------------------------- 1 | actions-insert-reference-v2 -------------------------------------------------------------------------------- /Resources/Public/JavaScript/context-menu-actions.js: -------------------------------------------------------------------------------- 1 | import Modal from '@typo3/backend/modal.js'; 2 | import Severity from '@typo3/backend/severity.js'; 3 | import Helper from '@ehaerer/paste-reference/helper.js'; 4 | 5 | /** 6 | * JavaScript to handle PasteReference related actions for Contextmenu 7 | * @exports @ehaerer/paste-reference/context-menu-actions 8 | */ 9 | class ContextMenuActions { 10 | /** 11 | * @returns {String} 12 | */ 13 | static getReturnUrl() { 14 | return encodeURIComponent(top.list_frame.document.location.pathname + top.list_frame.document.location.search); 15 | } 16 | 17 | /** 18 | * Paste record as a reference 19 | * 20 | * @param {String} table 21 | * @param {Number} uid of the record after which record from the clipboard will be pasted 22 | * @param dataset 23 | */ 24 | pasteReference(table, uid, dataset) { 25 | const performPaste = (dataset) => { 26 | const actionUrl = dataset.actionUrl; 27 | const url = actionUrl + '&redirect=' + ContextMenuActions.getReturnUrl(); 28 | top.TYPO3.Backend.ContentContainer.setUrl(url); 29 | } 30 | 31 | if (!dataset.title) { 32 | performPaste(dataset); 33 | return; 34 | } 35 | 36 | Modal.confirm( 37 | dataset.title, 38 | Helper.decodeHtmlspecialChars(dataset.message), 39 | Severity.warning, [ 40 | { 41 | text: dataset.buttonCloseText || TYPO3.lang['button.cancel'] || 'Cancel', 42 | active: true, 43 | btnClass: 'btn-default', 44 | name: 'cancel', 45 | trigger: function (event, modal) { 46 | modal.hideModal(); 47 | } 48 | }, 49 | { 50 | text: dataset.buttonOkText || TYPO3.lang['button.ok'] || 'OK', 51 | btnClass: 'btn-warning', 52 | name: 'ok', 53 | trigger: function (event, modal) { 54 | modal.hideModal(); 55 | performPaste(dataset); 56 | } 57 | } 58 | ]); 59 | 60 | }; 61 | } 62 | 63 | export default new ContextMenuActions(); 64 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JavaScript to add helper functions 3 | * @exports @ehaerer/paste-reference/helper.js 4 | */ 5 | class Helper { 6 | /** 7 | * @returns {String} 8 | */ 9 | decodeHtmlspecialChars(text) { 10 | const map = { 11 | '&': '&', 12 | '&': "&", 13 | '<': '<', 14 | '>': '>', 15 | '"': '"', 16 | ''': "'", 17 | '’': "’", 18 | '‘': "‘", 19 | '–': "–", 20 | '—': "—", 21 | '…': "…", 22 | '”': '”' 23 | }; 24 | 25 | return text.replace(/\&[\w\d\#]{2,5}\;/g, function (m) { 26 | return map[m]; 27 | }); 28 | } 29 | } 30 | 31 | export default new Helper(); 32 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/paste-reference-drag-drop.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TYPO3 CMS project. 3 | * 4 | * It is free software; you can redistribute it and/or modify it under 5 | * the terms of the GNU General Public License, either version 2 6 | * of the License, or any later version. 7 | * 8 | * For the full copyright and license information, please read the 9 | * LICENSE.txt file that was distributed with this source code. 10 | * 11 | * The TYPO3 project - inspiring people to share! 12 | */ 13 | /** 14 | * this JS code does the drag+drop logic for the Layout module (Web => Page) 15 | * based on jQuery UI 16 | */ 17 | 18 | import DragDrop from "@typo3/backend/layout-module/drag-drop.js"; 19 | import Paste from "@typo3/backend/layout-module/paste.js"; 20 | import AjaxDataHandler from "@typo3/backend/ajax-data-handler.js"; 21 | 22 | /** 23 | * Module: @ehaerer/paste-reference/paste-reference-drag-drop.js 24 | */ 25 | 26 | 'use strict'; 27 | 28 | /** 29 | * @exports @ehaerer/paste-reference/paste-reference-drag-drop.js 30 | */ 31 | DragDrop.default = { 32 | contentIdentifier: '.t3js-page-ce', 33 | draggableIdentifier: '.t3js-page-ce:has(.t3-page-ce-header-draggable)', 34 | newContentElementWizardIdentifier: '#new-element-drag-in-wizard', 35 | cTypeIdentifier: '.t3-ctype-identifier', 36 | contentWrapperIdentifier: '.t3-page-ce-wrapper', 37 | disabledNewContentIdentifier: '.t3-page-ce-disable-new-ce', 38 | newContentElementOnclick: '', 39 | newContentElementDefaultValues: {}, 40 | drag: {}, 41 | types: {}, 42 | ownDropZone: {}, 43 | column: {}, 44 | 45 | /** 46 | * initializes Drag+Drop for all content elements on the page 47 | */ 48 | initialize: function () { 49 | $(DragDrop.default.draggableIdentifier).draggable({ 50 | handle: this.dragHeaderIdentifier, 51 | scope: 'tt_content', 52 | cursor: 'move', 53 | distance: 20, 54 | addClasses: 'active-drag', 55 | revert: 'invalid', 56 | }); 57 | $(DragDrop.default.dropZoneIdentifier).droppable({ 58 | accept: this.contentIdentifier, 59 | scope: 'tt_content', 60 | tolerance: 'pointer', 61 | }); 62 | }, 63 | 64 | /** 65 | * this method does the whole logic when a draggable is dropped on to a dropzone 66 | * sending out the request and afterwards move the HTML element in the right place. 67 | * 68 | * @param $draggableElement 69 | * @param $droppableElement 70 | * @param {Event} evt the event 71 | * @param reference if content should be pasted as copy or reference 72 | * @private 73 | */ 74 | onDrop: function (draggableElement, droppableElement, evt, reference) { 75 | const newColumn = DragDrop.default.getColumnPositionForElement(droppableElement) ?? 0; 76 | 77 | droppableElement.classList.remove(DragDrop.default.dropPossibleHoverClass); 78 | const pasteAction = typeof draggableElement === 'number' || typeof draggableElement === 'undefined'; 79 | let pasteElement = null; 80 | if (draggableElement) { 81 | pasteElement = draggableElement; 82 | } else if (typeof top.itemOnClipboardUid === 'number') { 83 | pasteElement = top.itemOnClipboardUid; 84 | } 85 | // send an AJAX request via the AjaxDataHandler 86 | let contentElementUid = pasteAction ? pasteElement : null; 87 | if (!contentElementUid && typeof draggableElement.dataset.uid !== 'undefined') { 88 | contentElementUid = parseInt(draggableElement.dataset.uid ?? 0); 89 | } 90 | if (contentElementUid > 0 || (DragDrop.default.newContentElementDefaultValues.CType && !pasteAction)) { 91 | let parameters = {}; 92 | // add the information about a possible column position change 93 | const targetFound = droppableElement.closest(DragDrop.default.contentIdentifier)?.dataset.uid; 94 | // the item was moved to the top of the colPos, so the page ID is used here 95 | let targetPid = 0; 96 | if (typeof targetFound === 'undefined') { 97 | // the actual page is needed 98 | targetPid = document.querySelector('.t3js-page-ce[data-page]').dataset.page; 99 | } else { 100 | // the negative value of the content element after where it should be moved 101 | targetPid = 0 - parseInt(targetFound); 102 | } 103 | const closestElementWithLanguage = draggableElement || droppableElement.closest('[data-language-uid]'); 104 | let language = closestElementWithLanguage; 105 | if (language !== parseInt(closestElementWithLanguage)) { 106 | language = parseInt(closestElementWithLanguage.dataset.language-uid); 107 | } 108 | if (language !== -1) { 109 | language = parseInt(droppableElement.closest('[data-language-uid]').dataset.languageUid); 110 | } 111 | let colPos = 0; 112 | if (targetPid !== 0) { 113 | colPos = newColumn; 114 | } 115 | parameters['cmd'] = {tt_content: {}}; 116 | parameters['data'] = {tt_content: {}}; 117 | let copyAction = (evt && evt.originalEvent && evt.originalEvent.ctrlKey || droppableElement.classList.contains('t3js-paste-copy') || evt === 'copyFromAnotherPage'); 118 | if (DragDrop.default.newContentElementDefaultValues.CType) { 119 | parameters['data']['tt_content']['NEW234134'] = DragDrop.default.newContentElementDefaultValues; 120 | parameters['data']['tt_content']['NEW234134']['pid'] = targetPid; 121 | parameters['data']['tt_content']['NEW234134']['colPos'] = colPos; 122 | parameters['data']['tt_content']['NEW234134']['sys_language_uid'] = language; 123 | 124 | if (!parameters['data']['tt_content']['NEW234134']['header']) { 125 | parameters['data']['tt_content']['NEW234134']['header'] = TYPO3.l10n.localize('tx_paste_reference_js.newcontentelementheader'); 126 | } 127 | 128 | parameters['DDinsertNew'] = 1; 129 | 130 | // fire the request, and show a message if it has failed 131 | AjaxDataHandler.process(parameters).then(function (result) { 132 | if (!result.hasErrors) { 133 | // insert draggable on the new position 134 | if (!pasteAction) { 135 | if (!droppableElement.parent().hasClass(DragDrop.default.contentIdentifier.substring(1))) { 136 | draggableElement.detach().css({top: 0, left: 0}) 137 | .insertAfter(droppableElement.closest(DragDrop.default.dropZoneIdentifier)); 138 | } else { 139 | draggableElement.detach().css({top: 0, left: 0}) 140 | .insertAfter(droppableElement.closest(DragDrop.default.contentIdentifier)); 141 | } 142 | } 143 | self.location.hash = droppableElement.closest(DragDrop.default.contentIdentifier).attr('id'); 144 | self.location.reload(true); 145 | } 146 | }); 147 | } else if (copyAction) { 148 | parameters['cmd']['tt_content'][contentElementUid] = { 149 | copy: { 150 | action: 'paste', 151 | target: targetPid, 152 | update: { 153 | colPos: colPos, 154 | sys_language_uid: language 155 | } 156 | } 157 | }; 158 | if (reference === 'reference') { 159 | parameters['reference'] = 1; 160 | } 161 | if (evt === 'copyFromAnotherPage') { 162 | parameters['CB'] = {setCopyMode: 1}; 163 | } 164 | // fire the request, and show a message if it has failed 165 | // This is adding a copy from another page "to this [selected] place". 166 | AjaxDataHandler.process(parameters).then(function (result) { 167 | if (!result.hasErrors) { 168 | // insert draggable on the new position 169 | if (!pasteAction) { 170 | if (!droppableElement.parent().hasClass(DragDrop.default.contentIdentifier.substring(1))) { 171 | draggableElement.detach().css({top: 0, left: 0}) 172 | .insertAfter(droppableElement.closest(DragDrop.default.dropZoneIdentifier)); 173 | } else { 174 | draggableElement.detach().css({top: 0, left: 0}) 175 | .insertAfter(droppableElement.closest(DragDrop.default.contentIdentifier)); 176 | } 177 | } 178 | self.location.hash = droppableElement.closest(DragDrop.default.contentIdentifier).id; 179 | self.location.reload(true); 180 | } 181 | }); 182 | } else { 183 | parameters['cmd']['tt_content'][contentElementUid] = { 184 | move: { 185 | action: 'paste', 186 | target: targetPid, 187 | update: { 188 | colPos: colPos, 189 | sys_language_uid: language 190 | } 191 | } 192 | }; 193 | // fire the request, and show a message if it has failed 194 | AjaxDataHandler.process(parameters).then(function (result) { 195 | if (!result.hasErrors) { 196 | // insert draggable on the new position 197 | if (!pasteAction) { 198 | if (!droppableElement.parent().hasClass(DragDrop.default.contentIdentifier.substring(1))) { 199 | draggableElement.detach().css({top: 0, left: 0}) 200 | .insertAfter(droppableElement.closest(DragDrop.default.dropZoneIdentifier)); 201 | } else { 202 | draggableElement.detach().css({top: 0, left: 0}) 203 | .insertAfter(droppableElement.closest(DragDrop.default.contentIdentifier)); 204 | } 205 | } 206 | self.location.hash = droppableElement.closest(DragDrop.default.contentIdentifier).attr('id'); 207 | self.location.reload(); 208 | } 209 | }); 210 | } 211 | } 212 | }, 213 | 214 | /** 215 | * returns the next "upper" container colPos parameter inside the code 216 | * @param element 217 | * @return int|boolean the colPos 218 | */ 219 | getColumnPositionForElement: function (element) { 220 | const columnContainer = element && element.closest('[data-colpos]') ? element.closest('[data-colpos]') : []; 221 | if (columnContainer.length && columnContainer.dataset.colpos !== 'undefined') { 222 | return columnContainer.dataset.colpos; 223 | } else { 224 | return false; 225 | } 226 | } 227 | } 228 | 229 | export default DragDrop; 230 | -------------------------------------------------------------------------------- /Resources/Public/JavaScript/paste-reference.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the TYPO3 CMS project. 3 | * 4 | * It is free software; you can redistribute it and/or modify it under 5 | * the terms of the GNU General Public License, either version 2 6 | * of the License, or any later version. 7 | * 8 | * For the full copyright and license information, please read the 9 | * LICENSE.txt file that was distributed with this source code. 10 | * 11 | * The TYPO3 project - inspiring people to share! 12 | */ 13 | 14 | import AjaxRequest from '@typo3/core/ajax/ajax-request.js'; 15 | import DocumentService from '@typo3/core/document-service.js'; 16 | import { default as Modal } from "@typo3/backend/modal.js"; 17 | import Paste from "@typo3/backend/layout-module/paste.js"; 18 | import DragDrop from "@ehaerer/paste-reference/paste-reference-drag-drop.js"; 19 | import { MessageUtility } from "@typo3/backend/utility/message-utility.js"; 20 | import RegularEvent from '@typo3/core/event/regular-event.js'; 21 | 22 | class OnReady { 23 | openedPopupWindow = []; 24 | 25 | /** 26 | * generates the paste into / paste after modal 27 | */ 28 | copyFromAnotherPage(element) { 29 | let idString = ''; 30 | if (element.offsetParent && element.offsetParent.id) { 31 | idString = element.offsetParent.id 32 | } 33 | const url = top.browserUrl + '&mode=db&bparams=' + idString + '|||tt_content|'; 34 | const configurationIframe = { 35 | type: Modal.types.iframe, 36 | content: url, 37 | size: Modal.sizes.large 38 | }; 39 | Modal.advanced(configurationIframe); 40 | }; 41 | 42 | getClipboardData() { 43 | (new AjaxRequest(top.TYPO3.settings.Clipboard.moduleUrl)) 44 | .withQueryArguments({ action: 'getClipboardData' }) 45 | .post({ table: 'tt_content' }) 46 | .then(async (response) => { 47 | const resolvedBody = await response.resolve(); 48 | if (resolvedBody.success === true) { 49 | let data = resolvedBody.data; 50 | let record = data ? resolvedBody.data.tabs[0].items[0] : []; 51 | let identifier = record ? record.identifier : ''; 52 | let table = identifier ? identifier.split('|')[0] : ''; 53 | let uid = identifier ? identifier.split('|')[1] : 0; 54 | let title = record ? record.title.replace(/<[^>]*>?/gm, '') : ''; 55 | let clipboardData = { 56 | copyMode: resolvedBody.data.copyMode, 57 | data: record, 58 | itemOnClipboardUid: uid * 1, 59 | itemOnClipboardTitleHtml: record ? record.title : '', 60 | itemOnClipboardTitle: title, 61 | itemOnClipboardTable: table, 62 | }; 63 | top.itemOnClipboardUid = clipboardData.itemOnClipboardUid; 64 | top.itemOnClipboardTitle = clipboardData.itemOnClipboardTitle; 65 | top.itemOnClipboardTitleHtml = clipboardData.itemOnClipboardTitleHtml; 66 | top.itemOnClipboardTable = clipboardData.itemOnClipboardTable; 67 | return clipboardData; 68 | } 69 | else return { 70 | copyMode: '', 71 | data: {}, 72 | itemOnClipboardUid: 0, 73 | itemOnClipboardTitleHtml: '', 74 | itemOnClipboardTitle: '', 75 | itemOnClipboardTable: '', 76 | }; 77 | }); 78 | } 79 | 80 | waitForElm(elementAbove, selector) { 81 | return new Promise(resolve => { 82 | if (elementAbove.querySelector(selector)) { 83 | return resolve(elementAbove.querySelector(selector)); 84 | } 85 | const observer = new MutationObserver(mutations => { 86 | if (elementAbove.querySelector(selector)) { 87 | observer.disconnect(); 88 | resolve(elementAbove.querySelector(selector)); 89 | } 90 | }); 91 | // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336 92 | observer.observe(document.body, { 93 | childList: true, 94 | subtree: true 95 | }); 96 | }); 97 | } 98 | } 99 | const onReady = new OnReady; 100 | 101 | /** 102 | * generates the paste into / paste after modal 103 | */ 104 | Paste.activatePasteModal = function(element) { 105 | const url = element.dataset.url || null; 106 | const elementTitle = this.itemOnClipboardTitle != undefined ? this.itemOnClipboardTitle : "["+TYPO3.lang['tx_paste_reference_js.modal.labels.no_title']+"]"; 107 | const title = (TYPO3.lang['paste.modal.title.paste'] || 'Paste record') + ': "' + elementTitle + '"'; 108 | const severity = (typeof top.TYPO3.Severity[element.dataset.severity] !== 'undefined') ? top.TYPO3.Severity[element.dataset.severity] : top.TYPO3.Severity.info; 109 | let buttons = []; 110 | let content = ''; 111 | 112 | if (element.classList.contains('t3js-paste-copy')) { 113 | content = TYPO3.lang['tx_paste_reference_js.modal.pastecopy'] || 'How do you want to paste that clipboard content here?'; 114 | buttons = [ 115 | { 116 | text: TYPO3.lang['paste.modal.button.cancel'] || 'Cancel', 117 | active: true, 118 | btnClass: 'btn-default', 119 | trigger: (evt, modal) => modal.hideModal(), 120 | }, 121 | { 122 | text: TYPO3.lang['tx_paste_reference_js.modal.button.pastecopy'] || 'Paste as copy', 123 | btnClass: 'text-white btn-' + top.TYPO3.Severity.getCssClass(severity), 124 | trigger: function(evt, modal) { 125 | modal.hideModal(); 126 | DragDrop.default.onDrop(top.itemOnClipboardUid, element, evt); 127 | } 128 | }, 129 | { 130 | text: TYPO3.lang['tx_paste_reference_js.modal.button.pastereference'] || 'Paste as reference', 131 | btnClass: 'text-white btn-' + top.TYPO3.Severity.getCssClass(severity), 132 | trigger: function(evt, modal) { 133 | modal.hideModal(); 134 | DragDrop.default.onDrop(top.itemOnClipboardUid, element, evt, 'reference'); 135 | } 136 | } 137 | ]; 138 | if (top.pasteReferenceAllowed * 1 !== 1) { 139 | buttons.pop(); 140 | } 141 | } else { 142 | content = TYPO3.lang['paste.modal.paste'] || 'Do you want to move the record to this position?'; 143 | buttons = [ 144 | { 145 | text: TYPO3.lang['paste.modal.button.cancel'] || 'Cancel', 146 | active: true, 147 | btnClass: 'btn-default', 148 | trigger: (evt, modal) => modal.hideModal(), 149 | }, 150 | { 151 | text: TYPO3.lang['paste.modal.button.paste'] || 'Move', 152 | btnClass: 'btn-' + top.TYPO3.Severity.getCssClass(severity), 153 | trigger: function(evt, modal) { 154 | modal.hideModal(); 155 | DragDrop.default.onDrop(top.itemOnClipboardUid, element, null); 156 | } 157 | } 158 | ]; 159 | } 160 | if (url !== null) { 161 | const separator = (url.indexOf('?') > -1) ? '&' : '?'; 162 | const params = $.param({data: $element.data()}); 163 | Modal.loadUrl(title, severity, buttons, url + separator + params); 164 | } else { 165 | Modal.show(title, content, severity, buttons); 166 | } 167 | }; 168 | 169 | /** 170 | * activates the paste into / paste after and fetch copy from another page icons outside of the context menus 171 | */ 172 | Paste.activatePasteIcons = function() { 173 | if (top.copyFromAnotherPageLinkTemplate) { 174 | const allElements = document.querySelectorAll('.t3js-page-new-ce'); 175 | allElements.forEach((element, index) => { 176 | if (element.querySelector('.icon-actions-plus')) { 177 | const copyFromAnotherPageLink = document.createRange().createContextualFragment(top.copyFromAnotherPageLinkTemplate); 178 | 179 | // if any item is in the clipboard 180 | if (top.itemOnClipboardUid > 0) { 181 | 182 | // waiting till default paste-buttons are created 183 | onReady.waitForElm(element, '.t3js-paste').then((pasteButton) => { 184 | // add additional button 185 | pasteButton.after(copyFromAnotherPageLink); 186 | 187 | // 1) remove class from the default button and consequentally the click-EventHandler 188 | // 2) add class to the default button to attach an own click-EventHandler 189 | pasteButton.classList.replace('t3js-paste', 't3js-paste-default'); 190 | }); 191 | } else { 192 | // add additional button without waiting for default button 193 | // as that one won't be shown with empty clipboard 194 | element.append(copyFromAnotherPageLink); 195 | } 196 | // Assigning id to first button-bar as it's missing because 197 | // it's not connected to a distinct content-element but required for modal 198 | if (index === 0 && !element.parentElement.id) { 199 | let tmpId = allElements[index + 1].parentElement.id; 200 | let regex = /tt_content-[0-9]+/; 201 | let id = tmpId.replace(regex, 'tt_content-0'); 202 | element.parentElement.setAttribute('id', id); 203 | } 204 | }; 205 | }); 206 | }; 207 | }; 208 | 209 | Paste.initializeEvents = function() { 210 | if (top.itemOnClipboardUid > 0) { 211 | onReady.waitForElm(document, '.t3js-paste-default').then(() => { 212 | new RegularEvent('click', (evt, target) => { 213 | evt.preventDefault(); 214 | this.activatePasteModal(target); 215 | }).delegateTo(document, '.t3js-paste-default'); 216 | }); 217 | } 218 | onReady.waitForElm(document, '.t3js-paste-new').then(() => { 219 | new RegularEvent('click', (evt, target) => { 220 | evt.preventDefault(); 221 | onReady.copyFromAnotherPage(target); 222 | }).delegateTo(document, '.t3js-paste-new'); 223 | }); 224 | }; 225 | 226 | /** 227 | * gives back the data from the popup window with record-selection to the copy action 228 | * 229 | * $('.typo3-TCEforms') is not relevant here as it exists on 230 | * detail pages for single records only. 231 | */ 232 | if (!document.querySelector('.typo3-TCEforms')) { 233 | window.addEventListener('message', function(evt) { 234 | 235 | if (!MessageUtility.verifyOrigin(evt.origin)) { 236 | throw 'Denied message sent by ' + evt.origin; 237 | } 238 | 239 | if (typeof evt.data.fieldName === 'undefined') { 240 | throw 'fieldName not defined in message'; 241 | } 242 | 243 | if (typeof evt.data.value === 'undefined') { 244 | throw 'value not defined in message'; 245 | } 246 | 247 | const result = evt.data.value; 248 | const tableUid = result.replace('tt_content_', '') * 1; 249 | const elementId = evt.data.fieldName; 250 | DragDrop.default.onDrop( 251 | tableUid, 252 | document.querySelector('#' + elementId).querySelector('.t3js-paste-new'), 253 | 'copyFromAnotherPage' 254 | ); 255 | }); 256 | } 257 | 258 | DocumentService.ready().then(() => { 259 | onReady.getClipboardData(); 260 | Paste.activatePasteIcons(); 261 | Paste.initializeEvents(); 262 | }); 263 | 264 | export default OnReady; 265 | -------------------------------------------------------------------------------- /Tests/Functional/DummyTest.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * All rights reserved 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * This script is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * This copyright notice MUST APPEAR in all copies of the script! 24 | ***************************************************************/ 25 | 26 | use PHPUnit\Framework\Attributes\Test; 27 | use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; 28 | use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; 29 | 30 | final class DummyTest extends FunctionalTestCase 31 | { 32 | public function setUp(): void 33 | { 34 | $this->testExtensionsToLoad[] = 'ehaerer/paste-reference'; 35 | parent::setUp(); 36 | } 37 | 38 | #[Test] 39 | public function extensionLoaded(): void 40 | { 41 | self::assertTrue(ExtensionManagementUtility::isLoaded('paste_reference')); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/Unit/DummyTest.php: -------------------------------------------------------------------------------- 1 | 10 | * (c) 2013 Jo Hasenau 11 | * All rights reserved 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * This script is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * This copyright notice MUST APPEAR in all copies of the script! 24 | ***************************************************************/ 25 | 26 | use PHPUnit\Framework\Attributes\Test; 27 | use TYPO3\CMS\Core\Information\Typo3Version; 28 | use TYPO3\TestingFramework\Core\Unit\UnitTestCase; 29 | 30 | final class DummyTest extends UnitTestCase 31 | { 32 | #[Test] 33 | public function dummy(): void 34 | { 35 | self::assertTrue((new Typo3Version())->getMajorVersion() === 12); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ehaerer/paste-reference", 3 | "description": "Paste reference instead of copy for content elements in TYPO3", 4 | "license": "MIT", 5 | "type": "typo3-cms-extension", 6 | "keywords": [ 7 | "TYPO3 CMS", 8 | "content element", 9 | "copy paste", 10 | "references" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Ephraim Härer", 15 | "email": "mail@ephra.im", 16 | "homepage": "https://ephra.im", 17 | "role": "Developer" 18 | } 19 | ], 20 | "homepage": "https://github.com/kephson/paste-reference", 21 | "support": { 22 | "issues": "https://github.com/kephson/paste-reference/issues" 23 | }, 24 | "require": { 25 | "php": "^8.2", 26 | "typo3/cms-backend": "^13.3.1 || ^13.4 || dev-main", 27 | "typo3/cms-core": "^13.3.1 || ^13.4 || dev-main" 28 | }, 29 | "require-dev": { 30 | "ergebnis/composer-normalize": "^2.31", 31 | "friendsofphp/php-cs-fixer": "^3.64.0", 32 | "phpstan/phpstan": "^1.12.5", 33 | "phpstan/phpstan-phpunit": "^1.4.0", 34 | "phpunit/phpunit": "^10.5.35", 35 | "typo3/cms-adminpanel": "^13.3.1 || ^13.4 || dev-main", 36 | "typo3/cms-belog": "^13.3.1 || ^13.4 || dev-main", 37 | "typo3/cms-beuser": "^13.3.1 || ^13.4 || dev-main", 38 | "typo3/cms-dashboard": "^13.3.1 || ^13.4 || dev-main", 39 | "typo3/cms-extbase": "^13.3.1 || ^13.4 || dev-main", 40 | "typo3/cms-extensionmanager": "^13.3.1 || ^13.4 || dev-main", 41 | "typo3/cms-filelist": "^13.3.1 || ^13.4 || dev-main", 42 | "typo3/cms-fluid": "^13.3.1 || ^13.4 || dev-main", 43 | "typo3/cms-fluid-styled-content": "^13.3.1 || ^13.4 || dev-main", 44 | "typo3/cms-form": "^13.3.1 || ^13.4 || dev-main", 45 | "typo3/cms-frontend": "^13.3.1 || ^13.4 || dev-main", 46 | "typo3/cms-info": "^13.3.1 || ^13.4 || dev-main", 47 | "typo3/cms-install": "^13.3.1 || ^13.4 || dev-main", 48 | "typo3/cms-lowlevel": "^13.3.1 || ^13.4 || dev-main", 49 | "typo3/cms-opendocs": "^13.3.1 || ^13.4 || dev-main", 50 | "typo3/cms-recycler": "^13.3.1 || ^13.4 || dev-main", 51 | "typo3/cms-redirects": "^13.3.1 || ^13.4 || dev-main", 52 | "typo3/cms-reports": "^13.3.1 || ^13.4 || dev-main", 53 | "typo3/cms-rte-ckeditor": "^13.3.1 || ^13.4 || dev-main", 54 | "typo3/cms-scheduler": "^13.3.1 || ^13.4 || dev-main", 55 | "typo3/cms-seo": "^13.3.1 || ^13.4 || dev-main", 56 | "typo3/cms-setup": "^13.3.1 || ^13.4 || dev-main", 57 | "typo3/cms-styleguide": "^13.3", 58 | "typo3/cms-tstemplate": "^13.3.1 || ^13.4 || dev-main", 59 | "typo3/cms-viewpage": "^13.3.1 || ^13.4 || dev-main", 60 | "typo3/cms-workspaces": "^13.3.1 || ^13.4 || dev-main", 61 | "typo3/testing-framework": "^8.2.2" 62 | }, 63 | "conflict": { 64 | "gridelementsteam/gridelements": "*" 65 | }, 66 | "minimum-stability": "dev", 67 | "prefer-stable": true, 68 | "autoload": { 69 | "psr-4": { 70 | "EHAERER\\PasteReference\\": "Classes" 71 | } 72 | }, 73 | "config": { 74 | "allow-plugins": { 75 | "ergebnis/composer-normalize": true, 76 | "typo3/class-alias-loader": true, 77 | "typo3/cms-composer-installers": true 78 | }, 79 | "bin-dir": ".Build/bin", 80 | "sort-packages": true, 81 | "vendor-dir": ".Build/vendor" 82 | }, 83 | "extra": { 84 | "typo3/cms": { 85 | "extension-key": "paste_reference", 86 | "web-dir": ".Build/public" 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ext_conf_template.txt: -------------------------------------------------------------------------------- 1 | # cat=basic; type=boolean; label=LLL:EXT:paste_reference/Resources/Private/Language/locallang.xlf:disableCopyFromPageButton 2 | disableCopyFromPageButton = 0 3 | # cat=basic; type=boolean; label=LLL:EXT:paste_reference/Resources/Private/Language/locallang.xlf:overlayShortcutTranslation 4 | overlayShortcutTranslation = 0 5 | # cat=basic; type=boolean; label=LLL:EXT:paste_reference/Resources/Private/Language/locallang.xlf:enableExtendedShortcutPreviewRenderer 6 | enableExtendedShortcutPreviewRenderer = 1 -------------------------------------------------------------------------------- /ext_emconf.php: -------------------------------------------------------------------------------- 1 | 'Paste reference for content elements', 12 | 'description' => 'Paste reference instead of copy for content elements', 13 | 'category' => 'plugin', 14 | 'version' => '4.0.2', 15 | 'constraints' => [ 16 | 'depends' => [ 17 | 'typo3' => '13.3.1-13.4.99', 18 | ], 19 | 'conflicts' => [ 20 | 'gridelements' => '*', 21 | ], 22 | ], 23 | 'autoload' => [ 24 | 'psr-4' => [ 25 | 'EHAERER\\PasteReference\\' => 'Classes' 26 | ], 27 | ], 28 | 'state' => 'stable', 29 | 'clearCacheOnLoad' => true, 30 | 'author' => 'Ephraim Härer', 31 | 'author_email' => 'mail@ephra.im', 32 | 'author_company' => 'private', 33 | ]; 34 | -------------------------------------------------------------------------------- /ext_tables.php: -------------------------------------------------------------------------------- 1 | 'check', 15 | 'label' => 'LLL:EXT:paste_reference/Resources/Private/Language/locallang.xlf:disableCopyFromPageButton', 16 | ]; 17 | 18 | ExtensionManagementUtility::addFieldsToUserSettings( 19 | '--div--;LLL:EXT:paste_reference/Resources/Private/Language/locallang_db.xlf:pasteReference,disableCopyFromPageButton', 20 | ); 21 | })(); 22 | --------------------------------------------------------------------------------