├── .babelrc ├── .github └── workflows │ ├── build-2x.yml │ └── build-3x.yml ├── .gitignore ├── .nvmrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── _build ├── assets │ ├── images │ │ ├── edit.svg │ │ ├── eye-slash.svg │ │ ├── eye.svg │ │ ├── trash-restore.svg │ │ └── trash.svg │ ├── index.js │ ├── js │ │ ├── Sidebar │ │ │ ├── Actions.ts │ │ │ ├── Utils.ts │ │ │ └── index.ts │ │ └── index.ts │ └── sass │ │ └── collections.scss ├── gpm.json ├── gpm_resolvers │ ├── gpm.resolve.bootstrap.php │ └── gpm.resolve.element_property_set.php ├── gpm_scripts │ ├── gpm.script.customevents.php │ ├── gpm.script.defaulttemplate.php │ ├── gpm.script.fixoldcollections.php │ ├── gpm.script.fixselections.php │ ├── gpm.script.fixsystemsettings.php │ ├── gpm.script.fred_get_service.php │ ├── gpm.script.fred_link_element_option_set.php │ ├── gpm.script.migrator.php │ ├── gpm.script.reload_system_settings.php │ ├── gpm.script.switchback.php │ ├── gpm.script.sync_tables.php │ └── gpm.script.tables.php ├── migrations │ ├── 200pl.migration.php │ ├── 340pl.migration.php │ └── 370pl.migration.php └── scripts │ ├── customevents.gpm.php │ ├── defaulttemplate.gpm.php │ ├── fixoldcollections.gpm.php │ ├── fixselections.gpm.php │ ├── fixsystemsettings.gpm.php │ └── switchback.gpm.php ├── assets └── components │ └── collections │ ├── css │ ├── i │ │ └── collection.png │ ├── index.html │ └── mgr.css │ ├── index.html │ ├── js │ ├── index.html │ └── mgr │ │ ├── collections.js │ │ ├── extra │ │ ├── collections.combo.js │ │ ├── collections.renderers.js │ │ ├── griddraganddrop.js │ │ └── hijackclose.js │ │ ├── sections │ │ ├── category │ │ │ └── update.js │ │ └── template │ │ │ ├── home.js │ │ │ └── template.js │ │ └── widgets │ │ ├── category │ │ ├── collections.grid.resources.js │ │ ├── collections.grid.selection.js │ │ ├── collections.panel.category.js │ │ ├── collections.panel.selection.js │ │ └── collections.window.js │ │ └── template │ │ ├── column.grid.js │ │ ├── column.window.js │ │ ├── home.panel.js │ │ ├── template.grid.js │ │ ├── template.panel.js │ │ └── template.window.js │ └── web │ ├── ajax.php │ ├── bars-solid.svg │ ├── fred_integration.css │ ├── fred_integration.js │ └── index.html ├── core └── components │ └── collections │ ├── bootstrap.php │ ├── controllers │ ├── create.class.php │ ├── data.class.php │ ├── home.class.php │ ├── selection │ │ ├── create.class.php │ │ ├── data.class.php │ │ └── update.class.php │ ├── template │ │ ├── create.class.php │ │ ├── export.class.php │ │ └── update.class.php │ └── update.class.php │ ├── elements │ ├── plugins │ │ └── Collections.php │ ├── snippets │ │ └── getSelections.php │ └── widgets │ │ └── test.php │ ├── index.class.php │ ├── lexicon │ ├── cs │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── da │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── de │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── en │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── fr │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── it │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── nl │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── pt │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── ru │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ └── sv │ │ ├── custom.inc.php │ │ ├── default.inc.php │ │ ├── fred.inc.php │ │ ├── selections.inc.php │ │ └── templates.inc.php │ ├── schema │ └── collections.mysql.schema.xml │ ├── src │ ├── Collections.php │ ├── Endpoint │ │ ├── Ajax.php │ │ └── Ajax │ │ │ ├── Endpoint.php │ │ │ ├── GetAuthors.php │ │ │ ├── GetCollection.php │ │ │ ├── GetCollectionView.php │ │ │ └── GetCollections.php │ ├── Events │ │ ├── Event.php │ │ ├── FredBeforeRender.php │ │ ├── FredOnBeforeGetResourceTree.php │ │ ├── OnBeforeDocFormSave.php │ │ ├── OnBeforeEmptyTrash.php │ │ ├── OnDocFormPrerender.php │ │ ├── OnDocFormRender.php │ │ ├── OnManagerPageBeforeRender.php │ │ ├── OnManagerPageInit.php │ │ ├── OnResourceBeforeSort.php │ │ └── OnResourceDuplicate.php │ ├── Model │ │ ├── CollectionContainer.php │ │ ├── CollectionContainerCreateProcessor.php │ │ ├── CollectionContainerUpdateProcessor.php │ │ ├── CollectionResourceTemplate.php │ │ ├── CollectionSelection.php │ │ ├── CollectionSetting.php │ │ ├── CollectionTemplate.php │ │ ├── CollectionTemplateColumn.php │ │ ├── SelectionContainer.php │ │ ├── SelectionContainerCreateProcessor.php │ │ ├── SelectionContainerUpdateProcessor.php │ │ ├── metadata.mysql.php │ │ └── mysql │ │ │ ├── CollectionContainer.php │ │ │ ├── CollectionResourceTemplate.php │ │ │ ├── CollectionSelection.php │ │ │ ├── CollectionSetting.php │ │ │ ├── CollectionTemplate.php │ │ │ ├── CollectionTemplateColumn.php │ │ │ └── SelectionContainer.php │ ├── Processors │ │ ├── Extra │ │ │ ├── Breadcrumbs.php │ │ │ ├── FredGetBlueprints.php │ │ │ ├── GetContentTypes.php │ │ │ ├── GetDerivates.php │ │ │ ├── GetResources.php │ │ │ └── GetTemplates.php │ │ ├── Resource │ │ │ ├── ChangeChildParent.php │ │ │ ├── ChangeParent.php │ │ │ ├── DDReorder.php │ │ │ ├── Delete.php │ │ │ ├── DeleteMultiple.php │ │ │ ├── GetList.php │ │ │ ├── Publish.php │ │ │ ├── PublishMultiple.php │ │ │ ├── Remove.php │ │ │ ├── UnDelete.php │ │ │ ├── UnDeleteMultiple.php │ │ │ ├── UnPublish.php │ │ │ ├── UnPublishMultiple.php │ │ │ └── UpdateFromGrid.php │ │ ├── Selection │ │ │ ├── Create.php │ │ │ ├── DDReorder.php │ │ │ ├── GetList.php │ │ │ ├── Remove.php │ │ │ ├── RemoveMultiple.php │ │ │ └── UpdateFromGrid.php │ │ └── Template │ │ │ ├── Column │ │ │ ├── Create.php │ │ │ ├── DDReorder.php │ │ │ ├── GetList.php │ │ │ ├── Remove.php │ │ │ ├── Update.php │ │ │ └── UpdateFromGrid.php │ │ │ ├── Create.php │ │ │ ├── Duplicate.php │ │ │ ├── Get.php │ │ │ ├── GetList.php │ │ │ ├── Import.php │ │ │ ├── Remove.php │ │ │ ├── Update.php │ │ │ └── UpdateFromGrid.php │ └── Utils.php │ └── templates │ └── template │ ├── home.tpl │ └── template.tpl ├── crowdin.yml ├── package-lock.json ├── package.json ├── postcss.config.js ├── tsconfig.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["babel-plugin-add-module-exports"], 3 | "presets": [ 4 | ["@babel/preset-env", { "targets": "> 0.25%, not dead", "corejs": "3.37", "useBuiltIns": "usage"}] 5 | ] 6 | } -------------------------------------------------------------------------------- /.github/workflows/build-2x.yml: -------------------------------------------------------------------------------- 1 | name: Build Package 2.x 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | 8 | deploy: 9 | # The type of runner that the job will run on 10 | runs-on: ubuntu-latest 11 | environment: 2.x 12 | 13 | # Steps represent a sequence of tasks that will be executed as part of the job 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Get Branch Name 19 | uses: nelonoel/branch-name@v1.0.1 20 | 21 | - run: echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV 22 | 23 | - name: Build & Deploy GPM Package 24 | uses: appleboy/ssh-action@v1.0.3 25 | env: 26 | BRANCH: ${{ env.BRANCH_NAME }} 27 | with: 28 | command_timeout: 20m 29 | host: ${{ secrets.DEPLOY_HOST }} 30 | key: ${{ secrets.DEPLOY_KEY }} 31 | username: ${{ secrets.DEPLOY_USER }} 32 | envs: BRANCH 33 | script_stop: true 34 | script: | 35 | cd /www/pkgs/collections/ 36 | git add . 37 | git stash 38 | git fetch origin 39 | git checkout -f $BRANCH 40 | git pull origin $BRANCH 41 | npm install 42 | npm run build 43 | rm -rf _packages 44 | /www/pkgs/gpm/cli/bin/gpm package:update --pkg collections --updateDB=alter 45 | /www/pkgs/gpm/cli/bin/gpm package:build --pkg collections -------------------------------------------------------------------------------- /.github/workflows/build-3x.yml: -------------------------------------------------------------------------------- 1 | name: Build Package 3.x 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | 8 | deploy: 9 | # The type of runner that the job will run on 10 | runs-on: ubuntu-latest 11 | environment: 3.x 12 | 13 | # Steps represent a sequence of tasks that will be executed as part of the job 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Get Branch Name 19 | uses: nelonoel/branch-name@v1.0.1 20 | 21 | - run: echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV 22 | 23 | - name: Build & Deploy GPM Package 24 | uses: appleboy/ssh-action@v1.0.3 25 | env: 26 | BRANCH: ${{ env.BRANCH_NAME }} 27 | with: 28 | command_timeout: 20m 29 | host: ${{ secrets.DEPLOY_HOST }} 30 | key: ${{ secrets.DEPLOY_KEY }} 31 | username: ${{ secrets.DEPLOY_USER }} 32 | envs: BRANCH 33 | script_stop: true 34 | script: | 35 | cd /www/pkgs/collections/ 36 | git add . 37 | git stash 38 | git fetch origin 39 | git checkout -f $BRANCH 40 | git pull origin $BRANCH 41 | npm install 42 | npm run build 43 | /www/pkgs/gpm/bin/gpm collections:update --alterDB 44 | /www/pkgs/gpm/bin/gpm collections:build 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS-generated and dev tool files # 2 | ################################### 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | ehthumbs.db 9 | Thumbs.db 10 | .idea 11 | .settings 12 | nbproject 13 | .project 14 | 15 | # MODX-related files # 16 | ###################### 17 | config.core.php 18 | 19 | # Build-related files for distribution # 20 | ######################################## 21 | _build/build.config.php 22 | _packages 23 | _build/build.config.php 24 | node_modules 25 | npm-debug.log 26 | .sass-cache 27 | assets/components/collections/web/fred_integration.* 28 | package-lock.json 29 | core/components/collections/model/vendor 30 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Collections [ ![Crowdin](https://d322cqt584bo4o.cloudfront.net/collections/localized.png) ](https://crowdin.com/project/collections) 2 | =========== 3 | 4 | Collections is a MODX Revolution Extra that adds a custom `CollectionContainer` resource class with the following behaviour: 5 | 6 | 1. Any direct child resource will be hidden from the Resource Tree in the Manager, and listed in a grid view (similar to Articles) under a dedicated "Children" tab, for which the label can be customized. 7 | 2. Any children that themselves have children will be shown in the Tree, to be managed normally. 8 | 9 | ![Collections Children Grid](http://modx.com/assets/i/blogs/yj/Collections-Grid-View.png) 10 | 11 | #### Sub Collections 12 | Just like the MODX Resource Tree itself, Collections supports nesting. You can create a Collection within another Collection. Sub Collection Containers will be displayed in the resource tree and their children will be displayed in the grid view. 13 | 14 | #### Drag n Drop 15 | You can drag n drop Resources into a Collections container and if they don't have children of their own they will be listed in the grid. If they do have children, they'll just remain in the Tree as usual. 16 | 17 | ### Custom Views 18 | As of version 2.x, Collections supports customizable views. Views are configured in Collections Custom Manager Page (CMP): 19 | 20 | ![Collections CMP](http://modx.com/assets/i/blogs/yj/Collections-CMP.png) 21 | ![Collections New View](http://modx.com/assets/i/blogs/yj/Collections-New-View3.png) 22 | 23 | There are specific settings for Collections Resources vs Selections. 24 | 25 | ### Resources 26 | The official documentation for Collections can be found here: [https://docs.modx.com/extras/revo/collections](https://docs.modx.com/extras/revo/collections) 27 | -------------------------------------------------------------------------------- /_build/assets/images/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/assets/images/eye-slash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/assets/images/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/assets/images/trash-restore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/assets/images/trash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_build/assets/index.js: -------------------------------------------------------------------------------- 1 | var pkg = require('../../package.json'); 2 | var libFile = pkg.library['bundle-node'] ? pkg.library['dist-node'] : pkg.library['entry']; 3 | module.exports = require('./lib/' + libFile); -------------------------------------------------------------------------------- /_build/assets/js/Sidebar/Actions.ts: -------------------------------------------------------------------------------- 1 | export default class Actions { 2 | private config: any; 3 | private fetch: any; 4 | private emitter: any; 5 | constructor(config, fetch, emitter) { 6 | this.config = config; 7 | this.fetch = fetch; 8 | this.emitter = emitter; 9 | } 10 | 11 | getCollections() { 12 | return this.fetch(`${this.config.endpoint}?action=get-collections`).then(response => { 13 | return response.json(); 14 | }).then(data => { 15 | return data.data.collections; 16 | }); 17 | } 18 | 19 | getAuthors(collection) { 20 | return this.fetch(`${this.config.endpoint}?action=get-authors&collection=${collection}`).then(response => { 21 | return response.json(); 22 | }).then(data => { 23 | return data.data; 24 | }); 25 | } 26 | 27 | getCollectionView(collection) { 28 | return this.fetch(`${this.config.endpoint}?action=get-collection-view&collection=${collection}`).then(response => { 29 | return response.json(); 30 | }).then(data => { 31 | return data.data; 32 | }); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /_build/assets/js/Sidebar/Utils.ts: -------------------------------------------------------------------------------- 1 | export const debounce = (delay, fn) => { 2 | let timerId; 3 | 4 | return function (...args) { 5 | if (timerId) { 6 | clearTimeout(timerId); 7 | } 8 | timerId = setTimeout(() => { 9 | fn(...args); 10 | timerId = null; 11 | }, delay); 12 | }; 13 | }; -------------------------------------------------------------------------------- /_build/assets/js/index.ts: -------------------------------------------------------------------------------- 1 | import SidebarPlugin from './Sidebar'; 2 | 3 | export default { 4 | SidebarPlugin 5 | }; -------------------------------------------------------------------------------- /_build/assets/sass/collections.scss: -------------------------------------------------------------------------------- 1 | @import "tabulator-tables/dist/css/tabulator_bulma.min.css"; 2 | 3 | $white: #FEFEFE; 4 | .fred--modal-body{ 5 | .tabulator { 6 | min-height: 300px; 7 | .tabulator-header{ 8 | background-color: #f1f1f1; 9 | } 10 | } 11 | fieldset, .fred--collections-filters { 12 | vertical-align: top !important; 13 | margin-bottom: 16px !important; 14 | } 15 | } 16 | .fred--collections-sidebar:before { 17 | mask: url('./bars-solid.svg') center center no-repeat; 18 | } 19 | .fred--btn-wrapper { 20 | position: absolute; 21 | top: 12px; 22 | right: 60px; 23 | } 24 | .fred--btn{ 25 | &-collections{ 26 | &-newpage{ 27 | margin-left: 8px !important; 28 | } 29 | &-icon{ 30 | padding: 2px !important; 31 | margin: 0 !important; 32 | text-indent: -9999px !important; 33 | position: relative !important; 34 | background-color: transparent !important; 35 | height: 24px !important; 36 | width: 24px !important; 37 | &:before{ 38 | content: ""; 39 | position: absolute; 40 | display:inline-block; 41 | top: 2px; 42 | left: 2px; 43 | //padding: 6px 6px; 44 | height: 100%; 45 | width: 100%; 46 | color: #FEFEFE; 47 | background-color: #505050; 48 | background-repeat: no-repeat; 49 | background-position: center; 50 | background-size: 14px; 51 | border-radius: 4px; 52 | } 53 | } 54 | &-edit:before{ 55 | //background-image: inline-svg('edit', $white); 56 | background-image: svg-load('edit.svg'); 57 | background-position: 6px; 58 | } 59 | &-publish:before{ 60 | //background-image: inline-svg('eye', $white); 61 | background-image: svg-load('eye.svg'); 62 | } 63 | &-unpublish:before{ 64 | //background-image: inline-svg('eye-slash', $white); 65 | background-image: svg-load('eye-slash.svg'); 66 | } 67 | &-delete:before{ 68 | background-color: #dc3838; 69 | //background-image: inline-svg('trash', $white); 70 | background-image: svg-load('trash.svg'); 71 | } 72 | &-undelete:before{ 73 | //background-image: inline-svg('trash-restore', $white); 74 | background-image: svg-load('trash-restore.svg'); 75 | } 76 | } 77 | } 78 | @media screen and (min-width: 768px){ 79 | .fred .fred--modal .fred--collections-filters{ 80 | label{ 81 | display: inline-block; 82 | width: calc(100% / 3); 83 | padding: 0 8px; 84 | } 85 | } 86 | .fred--collections-createpage { 87 | fieldset{ 88 | & > label, & > div:not(.fred--btn-wrapper) { 89 | display: inline-block !important; 90 | width: calc(100% / 3) !important; 91 | padding: 0 8px !important; 92 | } 93 | //& > div{ 94 | // position: relative !important; 95 | // top: -5px !important; 96 | //} 97 | & > button{ 98 | display: inline-block !important; 99 | width: calc(24% / 2) !important; 100 | } 101 | } 102 | } 103 | } 104 | .fred--collections-createpage, 105 | .fred--collections-filters, 106 | .fred--modal-body .tabulator{ 107 | animation: fred--collections-fade .5s; 108 | } 109 | 110 | @keyframes fred--collections-fade { 111 | 0%{opacity:0} 112 | 100%{opacity:1} 113 | } 114 | -------------------------------------------------------------------------------- /_build/gpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Collections", 3 | "lowCaseName": "collections", 4 | "namespace": "Collections", 5 | "description": "Hides resources under this container in tree. Adding grid int o the update resource panel.", 6 | "author": "John Peca", 7 | "version": "4.1.1-pl", 8 | "menus": [ 9 | { 10 | "text": "collections.menu.collection_templates", 11 | "description": "collections.menu.collection_templates_desc", 12 | "action": "home" 13 | } 14 | ], 15 | "plugins": [ 16 | { 17 | "name": "Collections", 18 | "events": [ 19 | "OnManagerPageInit", 20 | "OnBeforeDocFormSave", 21 | "OnResourceBeforeSort", 22 | "OnDocFormPrerender", 23 | "OnBeforeEmptyTrash", 24 | "OnDocFormRender", 25 | "OnManagerPageBeforeRender", 26 | "FredOnBeforeGetResourceTree", 27 | "FredBeforeRender", 28 | "OnResourceDuplicate" 29 | ] 30 | } 31 | ], 32 | "snippets": [ 33 | { 34 | "name": "getSelections" 35 | } 36 | ], 37 | "systemSettings": [ 38 | { 39 | "key": "mgr_date_format", 40 | "area": "manager", 41 | "value": "M d" 42 | }, 43 | { 44 | "key": "mgr_time_format", 45 | "area": "manager", 46 | "value": "g:i a" 47 | }, 48 | { 49 | "key": "mgr_datetime_format", 50 | "area": "manager", 51 | "value": "M d, g:i a" 52 | }, 53 | { 54 | "key": "user_js", 55 | "area": "manager", 56 | "value": "" 57 | }, 58 | { 59 | "key": "user_css", 60 | "area": "manager", 61 | "value": "" 62 | }, 63 | { 64 | "key": "mgr_tree_icon_collectioncontainer", 65 | "area": "manager", 66 | "value": "collectioncontainer", 67 | "namespace": "core" 68 | }, 69 | { 70 | "key": "mgr_tree_icon_selectioncontainer", 71 | "area": "manager", 72 | "value": "selectioncontainer", 73 | "namespace": "core" 74 | }, 75 | { 76 | "key": "renderer_image_path", 77 | "area": "manager", 78 | "value": "" 79 | }, 80 | { 81 | "key": "tree_tbar_collection", 82 | "area": "manager", 83 | "type": "combo-boolean", 84 | "value": 0 85 | }, 86 | { 87 | "key": "tree_tbar_selection", 88 | "area": "manager", 89 | "type": "combo-boolean", 90 | "value": 0 91 | } 92 | ], 93 | "database": { 94 | "tables": [ 95 | "\\Collections\\Model\\CollectionSetting", 96 | "\\Collections\\Model\\CollectionTemplate", 97 | "\\Collections\\Model\\CollectionTemplateColumn", 98 | "\\Collections\\Model\\CollectionResourceTemplate", 99 | "\\Collections\\Model\\CollectionSelection" 100 | ] 101 | }, 102 | "build": { 103 | "scriptsBefore": [ 104 | "fixoldcollections.gpm.php", 105 | "customevents.gpm.php", 106 | "switchback.gpm.php" 107 | ], 108 | "scriptsAfter": [ 109 | "defaulttemplate.gpm.php", 110 | "fixselections.gpm.php", 111 | "fixsystemsettings.gpm.php" 112 | ], 113 | "requires": { 114 | "gpm": ">=3.0.0", 115 | "modx": ">=3.0.0-alpha" 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /_build/gpm_resolvers/gpm.resolve.bootstrap.php: -------------------------------------------------------------------------------- 1 | xpdo; 18 | if ($options[xPDOTransport::PACKAGE_ACTION] !== xPDOTransport::ACTION_INSTALL) return true; 19 | 20 | $bootstrap = $object->getCorePath() . 'bootstrap.php'; 21 | if (file_exists($bootstrap)) { 22 | $namespace = $object->toArray(); 23 | $namespace['path'] = $object->getCorePath(); 24 | $namespace['assets_path'] = $object->getAssetsPath(); 25 | 26 | require $object->getCorePath() . 'bootstrap.php'; 27 | } 28 | 29 | return true; 30 | -------------------------------------------------------------------------------- /_build/gpm_resolvers/gpm.resolve.element_property_set.php: -------------------------------------------------------------------------------- 1 | xpdo; 19 | if ($options[xPDOTransport::PACKAGE_ACTION] === xPDOTransport::ACTION_UNINSTALL) return true; 20 | 21 | $propertySetsCache = []; 22 | 23 | $elementClasses = [ 24 | 'snippets' => 'MODX\\Revolution\\modSnippet', 25 | 'chunks' => 'MODX\\Revolution\\modChunk', 26 | 'templates' => 'MODX\\Revolution\\modTemplate', 27 | 'plugins' => 'MODX\\Revolution\\modPlugin', 28 | ]; 29 | 30 | foreach ($elementClasses as $type => $elementClass) { 31 | if (isset($fileMeta[$type]) && is_array($fileMeta[$type])) { 32 | foreach ($fileMeta[$type] as $elementName => $propertySets) { 33 | $nameColumn = $type === 'templates' ? 'templatename' : 'name'; 34 | /** @var \MODX\Revolution\modElement $element */ 35 | $element = $modx->getObject($elementClass, [$nameColumn => $elementName]); 36 | if (!$element) continue; 37 | 38 | if (empty($propertySets)) { 39 | $modx->removeCollection(\MODX\Revolution\modElementPropertySet::class, ['element' => $element->id, 'element_class' => $elementClass]); 40 | continue; 41 | } 42 | 43 | if (!is_array($propertySets)) continue; 44 | 45 | foreach ($propertySets as $propertySetName) { 46 | if (!isset($propertySetsCache[$propertySetName])) { 47 | /** @var \MODX\Revolution\modPropertySet $propertySet */ 48 | $propertySet = $modx->getObject(\MODX\Revolution\modPropertySet::class, ['name' => $propertySetName]); 49 | if (!$propertySet) continue; 50 | 51 | $propertySetsCache[$propertySetName] = $propertySet->id; 52 | } 53 | 54 | $elementPropertySet = $modx->getObject(\MODX\Revolution\modElementPropertySet::class, ['element' => $element->id, 'element_class' => $elementClass, 'property_set' => $propertySetsCache[$propertySetName]]); 55 | if ($elementPropertySet) continue; 56 | 57 | $elementPropertySet = $modx->newObject(\MODX\Revolution\modElementPropertySet::class); 58 | $elementPropertySet->set('element', $element->id); 59 | $elementPropertySet->set('element_class', $elementClass); 60 | $elementPropertySet->set('property_set', $propertySetsCache[$propertySetName]); 61 | $elementPropertySet->save(); 62 | } 63 | } 64 | } 65 | } 66 | 67 | return true; 68 | -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.customevents.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 35 | $this->action = $action; 36 | 37 | $events = [ 38 | 'CollectionsOnResourceSort', 39 | ]; 40 | 41 | switch ($this->action) { 42 | case \xPDO\Transport\xPDOTransport::ACTION_INSTALL: 43 | case \xPDO\Transport\xPDOTransport::ACTION_UPGRADE: 44 | foreach ($events as $eventName) { 45 | $event = $this->modx->getObject(\MODX\Revolution\modEvent::class, ['name' => $eventName]); 46 | if (!$event) { 47 | $event = $this->modx->newObject(\MODX\Revolution\modEvent::class); 48 | $event->set('name', $eventName); 49 | $event->set('service', 6); 50 | $event->set('groupname', 'Collections'); 51 | $event->save(); 52 | } 53 | } 54 | 55 | break; 56 | case \xPDO\Transport\xPDOTransport::ACTION_UNINSTALL: 57 | foreach ($events as $eventName) { 58 | $event = $this->modx->getObject(\MODX\Revolution\modEvent::class, ['name' => $eventName]); 59 | if ($event) { 60 | $event->remove(); 61 | } 62 | } 63 | 64 | break; 65 | } 66 | 67 | return true; 68 | } 69 | }; 70 | })()($transport->xpdo, $options[xPDOTransport::PACKAGE_ACTION]); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.fixoldcollections.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 39 | $this->action = $action; 40 | 41 | if ($this->action === xPDOTransport::ACTION_UNINSTALL) { 42 | return true; 43 | } 44 | 45 | if (isset($this->modx->packages['collections'])) { 46 | unset($this->modx->packages['collections']); 47 | } 48 | 49 | $this->modx->removeExtensionPackage('collections'); 50 | 51 | $this->modx->updateCollection(modResource::class, ['class_key' => 'Collections\\Model\\CollectionContainer'], ['class_key' => 'CollectionContainer']); 52 | $this->modx->updateCollection(modResource::class, ['class_key' => 'Collections\\Model\\SelectionContainer'], ['class_key' => 'SelectionContainer']); 53 | 54 | return true; 55 | } 56 | 57 | }; 58 | })()($transport->xpdo, $options[xPDOTransport::PACKAGE_ACTION]); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.fixselections.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 40 | $this->action = $action; 41 | 42 | if ($this->action !== xPDOTransport::ACTION_UPGRADE) { 43 | return true; 44 | } 45 | 46 | $this->modx->updateCollection(modResource::class, ['hide_children_in_tree' => 1], ['class_key' => SelectionContainer::class]); 47 | 48 | return true; 49 | } 50 | 51 | }; 52 | })()($transport->xpdo, $options[xPDOTransport::PACKAGE_ACTION]); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.fixsystemsettings.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 39 | $this->action = $action; 40 | 41 | if ($this->action !== xPDOTransport::ACTION_UPGRADE) { 42 | return true; 43 | } 44 | 45 | /** @var modSystemSetting $ss */ 46 | $ss = $this->modx->getObject(modSystemSetting::class, ['key' => 'renderer_image_path']); 47 | if ($ss) { 48 | $ss->remove(); 49 | } 50 | 51 | return true; 52 | } 53 | 54 | }; 55 | })()($transport->xpdo, $options[xPDOTransport::PACKAGE_ACTION]); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.fred_get_service.php: -------------------------------------------------------------------------------- 1 | xpdo; 21 | $fred = $modx->services->get('fred'); 22 | } 23 | -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.fred_link_element_option_set.php: -------------------------------------------------------------------------------- 1 | xpdo; 23 | 24 | $map = $object['map']; 25 | if (empty($map)) { 26 | return true; 27 | } 28 | 29 | foreach ($map as $uuid => $optionSetName) { 30 | /** @var FredElement $element */ 31 | $element = $modx->getObject(FredElement::class, ['uuid' => $uuid]); 32 | if (!$element) continue; 33 | 34 | $category = $element->getOne('Category'); 35 | if (!$category) continue; 36 | 37 | $optionSet = $modx->getObject(FredElementOptionSet::class, ['name' => $optionSetName, 'theme' => $category->get('theme')]); 38 | if (!$optionSet) continue; 39 | 40 | $element->set('option_set', $optionSet->id); 41 | $element->save(); 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.reload_system_settings.php: -------------------------------------------------------------------------------- 1 | xpdo; 24 | 25 | $modx->reloadConfig(); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.switchback.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 42 | $this->action = $action; 43 | 44 | if ($this->action !== xPDOTransport::ACTION_UNINSTALL) { 45 | return true; 46 | } 47 | 48 | $c = $this->modx->newQuery(modResource::class); 49 | $c->where(['class_key' => CollectionContainer::class]); 50 | 51 | /** @var modResource $collections[] */ 52 | $collections = $this->modx->getCollection(modResource::class, $c); 53 | foreach ($collections as $collection) { 54 | $children = $collection->Children; 55 | foreach ($children as $child) { 56 | $child->set('show_in_tree', 1); 57 | $child->save(); 58 | } 59 | 60 | $collection->set('class_key', modDocument::class); 61 | $collection->save(); 62 | } 63 | 64 | $c = $this->modx->newQuery(modResource::class); 65 | $c->where(['class_key' => SelectionContainer::class]); 66 | 67 | /** @var modResource[] $selections */ 68 | $selections = $this->modx->getCollection(modResource::class, $c); 69 | foreach ($selections as $selection) { 70 | $selection->set('hide_children_in_tree', 0); 71 | $selection->set('class_key', modDocument::class); 72 | $selection->save(); 73 | } 74 | 75 | return true; 76 | } 77 | 78 | }; 79 | })()($transport->xpdo, $options[xPDOTransport::PACKAGE_ACTION]); -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.sync_tables.php: -------------------------------------------------------------------------------- 1 | getTableName($table); 21 | $tableName = str_replace('`', '', $tableName); 22 | $dbname = $modx->getOption('dbname'); 23 | 24 | $c = $modx->prepare("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = :dbName AND table_name = :tableName"); 25 | $c->bindParam(':dbName', $dbname); 26 | $c->bindParam(':tableName', $tableName); 27 | $c->execute(); 28 | 29 | $unusedColumns = $c->fetchAll(PDO::FETCH_COLUMN, 0); 30 | $unusedColumns = array_flip($unusedColumns); 31 | 32 | $meta = $modx->getFieldMeta($table); 33 | $columns = array_keys($meta); 34 | 35 | $m = $modx->getManager(); 36 | 37 | foreach ($columns as $column) { 38 | if (isset($unusedColumns[$column])) { 39 | $m->alterField($table, $column); 40 | $modx->log(modX::LOG_LEVEL_INFO, ' -- altered column: ' . $column); 41 | unset($unusedColumns[$column]); 42 | } else { 43 | $m->addField($table, $column); 44 | $modx->log(modX::LOG_LEVEL_INFO, ' -- added column: ' . $column); 45 | } 46 | } 47 | 48 | foreach ($unusedColumns as $column => $v) { 49 | $m->removeField($table, $column); 50 | $modx->log(modX::LOG_LEVEL_INFO, ' -- removed column: ' . $column); 51 | } 52 | } 53 | } 54 | 55 | if (!function_exists('updateTableIndexes')) { 56 | /** 57 | * @param $modx 58 | * @param string $table 59 | */ 60 | function updateTableIndexes($modx, $table) 61 | { 62 | $tableName = $modx->getTableName($table); 63 | $tableName = str_replace('`', '', $tableName); 64 | $dbname = $modx->getOption('dbname'); 65 | 66 | $c = $modx->prepare("SELECT DISTINCT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = :dbName AND table_name = :tableName AND INDEX_NAME != 'PRIMARY'"); 67 | $c->bindParam(':dbName', $dbname); 68 | $c->bindParam(':tableName', $tableName); 69 | $c->execute(); 70 | 71 | $oldIndexes = $c->fetchAll(PDO::FETCH_COLUMN, 0); 72 | 73 | $m = $modx->getManager(); 74 | 75 | foreach ($oldIndexes as $oldIndex) { 76 | $m->removeIndex($table, $oldIndex); 77 | $modx->log(modX::LOG_LEVEL_INFO, ' -- removed index: ' . $oldIndex); 78 | } 79 | 80 | $meta = $modx->getIndexMeta($table); 81 | $indexes = array_keys($meta); 82 | 83 | foreach ($indexes as $index) { 84 | if ($index == 'PRIMARY') continue; 85 | $m->addIndex($table, $index); 86 | $modx->log(modX::LOG_LEVEL_INFO, ' -- added index: ' . $index); 87 | } 88 | } 89 | } 90 | 91 | if (!function_exists('alterTable')) { 92 | /** 93 | * @param $modx 94 | * @param string $table 95 | */ 96 | function alterTable($modx, $table) 97 | { 98 | $modx->log(modX::LOG_LEVEL_INFO, ' - Updating columns'); 99 | updateTableColumns($modx, $table); 100 | 101 | $modx->log(modX::LOG_LEVEL_INFO, ' - Updating indexes'); 102 | updateTableIndexes($modx, $table); 103 | } 104 | } 105 | 106 | if ($transport->xpdo) { 107 | switch ($options[xPDOTransport::PACKAGE_ACTION]) { 108 | case xPDOTransport::ACTION_UPGRADE: 109 | $modx =& $transport->xpdo; 110 | 111 | $tables = [ 112 | \Collections\Model\CollectionSetting::class, 113 | \Collections\Model\CollectionTemplate::class, 114 | \Collections\Model\CollectionTemplateColumn::class, 115 | \Collections\Model\CollectionResourceTemplate::class, 116 | \Collections\Model\CollectionSelection::class, 117 | ]; 118 | 119 | foreach ($tables as $table) { 120 | $modx->log(modX::LOG_LEVEL_INFO, 'Altering table: ' . $table); 121 | alterTable($modx, $table); 122 | } 123 | 124 | break; 125 | } 126 | } 127 | return true; 128 | -------------------------------------------------------------------------------- /_build/gpm_scripts/gpm.script.tables.php: -------------------------------------------------------------------------------- 1 | xpdo; 18 | 19 | if ($options[xPDOTransport::PACKAGE_ACTION] === xPDOTransport::ACTION_UNINSTALL) return true; 20 | 21 | $manager = $modx->getManager(); 22 | 23 | $manager->createObjectContainer(\Collections\Model\CollectionSetting::class); 24 | $manager->createObjectContainer(\Collections\Model\CollectionTemplate::class); 25 | $manager->createObjectContainer(\Collections\Model\CollectionTemplateColumn::class); 26 | $manager->createObjectContainer(\Collections\Model\CollectionResourceTemplate::class); 27 | $manager->createObjectContainer(\Collections\Model\CollectionSelection::class); 28 | 29 | return true; 30 | -------------------------------------------------------------------------------- /_build/migrations/200pl.migration.php: -------------------------------------------------------------------------------- 1 | currently installed version 7 | const VERSION = '2.0.0-pl'; 8 | 9 | /** 10 | * @var \MODX\Revolution\modX 11 | */ 12 | private $modx; 13 | 14 | /** 15 | * @param \MODX\Revolution\modX $modx 16 | * @return void 17 | */ 18 | public function __invoke(&$modx) 19 | { 20 | $this->modx =& $modx; 21 | 22 | $date = $this->modx->getObject(modSystemSetting::class, ['key' => 'collections.mgr_date_format']); 23 | if (!$date) { 24 | $date = $this->modx->newObject(modSystemSetting::class); 25 | $date->set('key', 'collections.mgr_date_format'); 26 | $date->set('namespace', 'collections'); 27 | $date->set('xtype', 'textfield'); 28 | } 29 | 30 | $date->set('value', 'M d'); 31 | $date->save(); 32 | 33 | $time = $this->modx->getObject(modSystemSetting::class, ['key' => 'collections.mgr_time_format']); 34 | if (!$time) { 35 | $time = $this->modx->newObject(modSystemSetting::class); 36 | $time->set('key', 'collections.mgr_time_format'); 37 | $time->set('namespace', 'collections'); 38 | $time->set('xtype', 'textfield'); 39 | } 40 | 41 | $time->set('value', 'g:i a'); 42 | $time->save(); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /_build/migrations/340pl.migration.php: -------------------------------------------------------------------------------- 1 | currently installed version 9 | const VERSION = '3.4.0-pl'; 10 | 11 | /** 12 | * @var \MODX\Revolution\modX 13 | */ 14 | private $modx; 15 | 16 | /** 17 | * @param \MODX\Revolution\modX $modx 18 | * @return void 19 | */ 20 | public function __invoke(&$modx) 21 | { 22 | $this->modx =& $modx; 23 | 24 | /** @var modResource[] $collections */ 25 | $collections = $this->modx->getIterator(modResource::class, ['class_key' => CollectionContainer::class]); 26 | foreach ($collections as $collection) { 27 | $this->modx->updateCollection(modResource::class, ['show_in_tree' => 0], ['parent' => $collection->id, 'class_key:!=' => CollectionContainer::class]); 28 | } 29 | 30 | /** @var CollectionTemplate[] $views */ 31 | $views = $this->modx->getIterator(CollectionTemplate::class); 32 | foreach ($views as $view) { 33 | $buttons = $view->get('buttons'); 34 | if (strpos($buttons, 'open') === false) { 35 | $buttons = 'open,' . $buttons; 36 | $view->set('buttons', $buttons); 37 | $view->save(); 38 | } 39 | } 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /_build/migrations/370pl.migration.php: -------------------------------------------------------------------------------- 1 | currently installed version 7 | const VERSION = '3.7.0-pl'; 8 | 9 | /** 10 | * @var \MODX\Revolution\modX 11 | */ 12 | private $modx; 13 | 14 | /** 15 | * @param \MODX\Revolution\modX $modx 16 | * @return void 17 | */ 18 | public function __invoke(&$modx) 19 | { 20 | $this->modx =& $modx; 21 | 22 | /** @var CollectionTemplate[] $views */ 23 | $views = $this->modx->getIterator(CollectionTemplate::class); 24 | foreach ($views as $view) { 25 | $buttons = $view->get('buttons'); 26 | if (strpos($buttons, 'changeparent') === false) { 27 | $buttons = $buttons . ',changeparent'; 28 | $view->set('buttons', $buttons); 29 | $view->save(); 30 | } 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /_build/scripts/customevents.gpm.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 22 | $this->action = $action; 23 | 24 | $events = [ 25 | 'CollectionsOnResourceSort', 26 | ]; 27 | 28 | switch ($this->action) { 29 | case \xPDO\Transport\xPDOTransport::ACTION_INSTALL: 30 | case \xPDO\Transport\xPDOTransport::ACTION_UPGRADE: 31 | foreach ($events as $eventName) { 32 | $event = $this->modx->getObject(\MODX\Revolution\modEvent::class, ['name' => $eventName]); 33 | if (!$event) { 34 | $event = $this->modx->newObject(\MODX\Revolution\modEvent::class); 35 | $event->set('name', $eventName); 36 | $event->set('service', 6); 37 | $event->set('groupname', 'Collections'); 38 | $event->save(); 39 | } 40 | } 41 | 42 | break; 43 | case \xPDO\Transport\xPDOTransport::ACTION_UNINSTALL: 44 | foreach ($events as $eventName) { 45 | $event = $this->modx->getObject(\MODX\Revolution\modEvent::class, ['name' => $eventName]); 46 | if ($event) { 47 | $event->remove(); 48 | } 49 | } 50 | 51 | break; 52 | } 53 | 54 | return true; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /_build/scripts/fixoldcollections.gpm.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 25 | $this->action = $action; 26 | 27 | if ($this->action === xPDOTransport::ACTION_UNINSTALL) { 28 | return true; 29 | } 30 | 31 | if (isset($this->modx->packages['collections'])) { 32 | unset($this->modx->packages['collections']); 33 | } 34 | 35 | $this->modx->removeExtensionPackage('collections'); 36 | 37 | $this->modx->updateCollection(modResource::class, ['class_key' => 'Collections\\Model\\CollectionContainer'], ['class_key' => 'CollectionContainer']); 38 | $this->modx->updateCollection(modResource::class, ['class_key' => 'Collections\\Model\\SelectionContainer'], ['class_key' => 'SelectionContainer']); 39 | 40 | return true; 41 | } 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /_build/scripts/fixselections.gpm.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 26 | $this->action = $action; 27 | 28 | if ($this->action !== xPDOTransport::ACTION_UPGRADE) { 29 | return true; 30 | } 31 | 32 | $this->modx->updateCollection(modResource::class, ['hide_children_in_tree' => 1], ['class_key' => SelectionContainer::class]); 33 | 34 | return true; 35 | } 36 | 37 | }; 38 | -------------------------------------------------------------------------------- /_build/scripts/fixsystemsettings.gpm.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 26 | $this->action = $action; 27 | 28 | if ($this->action !== xPDOTransport::ACTION_UPGRADE) { 29 | return true; 30 | } 31 | 32 | /** @var modSystemSetting $ss */ 33 | $ss = $this->modx->getObject(modSystemSetting::class, ['key' => 'renderer_image_path']); 34 | if ($ss) { 35 | $ss->remove(); 36 | } 37 | 38 | return true; 39 | } 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /_build/scripts/switchback.gpm.php: -------------------------------------------------------------------------------- 1 | modx =& $modx; 28 | $this->action = $action; 29 | 30 | if ($this->action !== xPDOTransport::ACTION_UNINSTALL) { 31 | return true; 32 | } 33 | 34 | $c = $this->modx->newQuery(modResource::class); 35 | $c->where(['class_key' => CollectionContainer::class]); 36 | 37 | /** @var modResource $collections[] */ 38 | $collections = $this->modx->getCollection(modResource::class, $c); 39 | foreach ($collections as $collection) { 40 | $children = $collection->Children; 41 | foreach ($children as $child) { 42 | $child->set('show_in_tree', 1); 43 | $child->save(); 44 | } 45 | 46 | $collection->set('class_key', modDocument::class); 47 | $collection->save(); 48 | } 49 | 50 | $c = $this->modx->newQuery(modResource::class); 51 | $c->where(['class_key' => SelectionContainer::class]); 52 | 53 | /** @var modResource[] $selections */ 54 | $selections = $this->modx->getCollection(modResource::class, $c); 55 | foreach ($selections as $selection) { 56 | $selection->set('hide_children_in_tree', 0); 57 | $selection->set('class_key', modDocument::class); 58 | $selection->save(); 59 | } 60 | 61 | return true; 62 | } 63 | 64 | }; 65 | -------------------------------------------------------------------------------- /assets/components/collections/css/i/collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Collections/97de34662aee2488ac38b4f1fc7bef01a2967137/assets/components/collections/css/i/collection.png -------------------------------------------------------------------------------- /assets/components/collections/css/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Collections/97de34662aee2488ac38b4f1fc7bef01a2967137/assets/components/collections/css/index.html -------------------------------------------------------------------------------- /assets/components/collections/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Collections/97de34662aee2488ac38b4f1fc7bef01a2967137/assets/components/collections/index.html -------------------------------------------------------------------------------- /assets/components/collections/js/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Collections/97de34662aee2488ac38b4f1fc7bef01a2967137/assets/components/collections/js/index.html -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/collections.js: -------------------------------------------------------------------------------- 1 | var Collections = function(config) { 2 | config = config || {}; 3 | Collections.superclass.constructor.call(this,config); 4 | }; 5 | Ext.extend(Collections,Ext.Component,{ 6 | page:{},window:{},grid:{},tree:{},panel:{},combo:{},config: {},renderer: {}, 7 | getPageUrl: function(action, parameters) { 8 | // Handles url, passed as first argument 9 | var parts = []; 10 | if (action) { 11 | if (isNaN(parseInt(action)) && (action.substr(0,1) == '?' || (action.substr(0, "index.php?".length) == 'index.php?'))) { 12 | parts.push(action); 13 | } else { 14 | parts.push('?a=' + action); 15 | } 16 | } 17 | 18 | if (parameters) { 19 | parts.push(parameters); 20 | } 21 | 22 | return parts.join('&'); 23 | } 24 | }); 25 | Ext.reg('collections',Collections); 26 | collections = new Collections(); 27 | -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/extra/hijackclose.js: -------------------------------------------------------------------------------- 1 | Ext.override(MODx.page[Collections_mode + Collections_type], { 2 | collectionsOriginals: { 3 | getButtons: MODx.page[Collections_mode + Collections_type].prototype.getButtons 4 | } 5 | ,getButtons: function(config) { 6 | var buttons = this.collectionsOriginals.getButtons.call(this, config); 7 | 8 | if (MODx.request.selection) { 9 | buttons.unshift({ 10 | text: _(Collections_labels.back_to_selection) ? _(Collections_labels.back_to_selection) : Collections_labels.back_to_selection 11 | ,handler: function() { 12 | MODx.loadPage('resource/update', 'id=' + MODx.request.selection); 13 | } 14 | }); 15 | } else { 16 | buttons.unshift({ 17 | text: _(Collections_labels.back_to_collection) ? _(Collections_labels.back_to_collection) : Collections_labels.back_to_collection 18 | ,handler: function() { 19 | var targetCollection = (Collections_collection_get || MODx.request.collection || config.record.parent); 20 | 21 | var folder = parseInt(MODx.request.folder); 22 | if (folder > 0) { 23 | folder = '&folder=' + folder; 24 | } else { 25 | var parent = parseInt(MODx.request.parent); 26 | 27 | if ((parent > 0) && (targetCollection != parent)) { 28 | folder = '&folder=' + parent; 29 | } else { 30 | folder = ''; 31 | } 32 | } 33 | 34 | MODx.loadPage('resource/update', 'id=' + targetCollection + folder); 35 | } 36 | }); 37 | } 38 | 39 | return buttons; 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/sections/category/update.js: -------------------------------------------------------------------------------- 1 | collections.page.UpdateCategory = function(config) { 2 | config = config || {record:{}}; 3 | config.record = config.record || {}; 4 | Ext.applyIf(config,{ 5 | panelXType: 'collections-panel-category' 6 | }); 7 | config.canDelete = true; 8 | collections.page.UpdateCategory.superclass.constructor.call(this,config); 9 | }; 10 | Ext.extend(collections.page.UpdateCategory,MODx.page.UpdateResource,{ 11 | 12 | }); 13 | Ext.reg('collections-page-category-update',collections.page.UpdateCategory); 14 | 15 | collections.page.UpdateSelection = function(config) { 16 | config = config || {record:{}}; 17 | config.record = config.record || {}; 18 | Ext.applyIf(config,{ 19 | panelXType: 'collections-panel-selection' 20 | }); 21 | config.canDelete = true; 22 | collections.page.UpdateSelection.superclass.constructor.call(this,config); 23 | }; 24 | Ext.extend(collections.page.UpdateSelection,MODx.page.UpdateResource,{ 25 | 26 | }); 27 | Ext.reg('collections-page-selection-update',collections.page.UpdateSelection); 28 | -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/sections/template/home.js: -------------------------------------------------------------------------------- 1 | collections.page.TemplateHome = function(config) { 2 | config = config || {}; 3 | Ext.applyIf(config,{ 4 | components: [{ 5 | xtype: 'collections-panel-template-home' 6 | ,renderTo: 'collections-panel-template-home-div' 7 | }] 8 | }); 9 | collections.page.TemplateHome.superclass.constructor.call(this,config); 10 | }; 11 | Ext.extend(collections.page.TemplateHome,MODx.Component); 12 | Ext.reg('collections-page-template-home',collections.page.TemplateHome); -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/sections/template/template.js: -------------------------------------------------------------------------------- 1 | collections.page.Template = function(config) { 2 | config = config || {}; 3 | 4 | config.isUpdate = (MODx.request.id) ? true : false; 5 | 6 | Ext.applyIf(config,{ 7 | formpanel: 'collections-panel-template' 8 | ,buttons: [{ 9 | text: _('save') 10 | ,method: 'remote' 11 | ,process: config.isUpdate ? 'Collections\\Processors\\Template\\Update' : 'Collections\\Processors\\Template\\Create' 12 | ,keys: [{ 13 | key: MODx.config.keymap_save || 's' 14 | ,ctrl: true 15 | }] 16 | },{ 17 | text: _('cancel') 18 | ,params: {a:'home', namespace:'collections'} 19 | }] 20 | ,components: [{ 21 | xtype: 'collections-panel-template' 22 | ,renderTo: 'collections-panel-template-div' 23 | ,isUpdate: config.isUpdate 24 | ,fredInstalled: !!config.fredInstalled 25 | }] 26 | }); 27 | collections.page.Template.superclass.constructor.call(this,config); 28 | }; 29 | Ext.extend(collections.page.Template,MODx.Component); 30 | Ext.reg('collections-page-template',collections.page.Template); 31 | -------------------------------------------------------------------------------- /assets/components/collections/js/mgr/widgets/template/home.panel.js: -------------------------------------------------------------------------------- 1 | collections.panel.TemplateHome = function(config) { 2 | config = config || {}; 3 | Ext.apply(config,{ 4 | border: false 5 | ,baseCls: 'modx-formpanel' 6 | ,cls: 'container' 7 | ,items: [{ 8 | html: '

'+_('collections.template.page_title')+'

' 9 | ,border: false 10 | ,cls: 'modx-page-header' 11 | },{ 12 | xtype: 'modx-tabs' 13 | ,defaults: { border: false ,autoHeight: true } 14 | ,border: true 15 | ,activeItem: 0 16 | ,hideMode: 'offsets' 17 | ,items: [{ 18 | title: _('collections.template.templates') 19 | ,layout: 'form' 20 | ,items: [{ 21 | html: '

'+_('collections.template.templates_desc')+'

' 22 | ,border: false 23 | ,bodyCssClass: 'panel-desc' 24 | },{ 25 | xtype: 'collections-grid-template' 26 | ,preventRender: true 27 | ,cls: 'main-wrapper' 28 | }] 29 | }] 30 | }] 31 | }); 32 | collections.panel.TemplateHome.superclass.constructor.call(this,config); 33 | }; 34 | Ext.extend(collections.panel.TemplateHome,MODx.Panel); 35 | Ext.reg('collections-panel-template-home',collections.panel.TemplateHome); -------------------------------------------------------------------------------- /assets/components/collections/web/ajax.php: -------------------------------------------------------------------------------- 1 | startTime= $tStart; 26 | 27 | $modx->initialize('web'); 28 | 29 | /** @var Collections\Collections $collections */ 30 | $collections = $modx->services->get('collections'); 31 | 32 | $ajax = new \Collections\Endpoint\Ajax($collections); 33 | $ajax->run(); 34 | -------------------------------------------------------------------------------- /assets/components/collections/web/bars-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/components/collections/web/index.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modxcms/Collections/97de34662aee2488ac38b4f1fc7bef01a2967137/assets/components/collections/web/index.html -------------------------------------------------------------------------------- /core/components/collections/bootstrap.php: -------------------------------------------------------------------------------- 1 | addPackage('Collections\Model', $namespace['path'] . 'src/', null, 'Collections\\'); 7 | 8 | $modx->services->add('collections', function($c) use ($modx) { 9 | return new Collections\Collections($modx); 10 | }); 11 | -------------------------------------------------------------------------------- /core/components/collections/controllers/create.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('collections.system.new_container'); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/components/collections/controllers/data.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('collections.template.page_title'); 22 | } 23 | 24 | public function process(array $scriptProperties = []) {} 25 | 26 | public function loadCustomCssJs() { 27 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/template.window.js'); 28 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/template.grid.js'); 29 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/home.panel.js'); 30 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/sections/template/home.js'); 31 | 32 | $this->addHtml(' 33 | 38 | '); 39 | } 40 | 41 | public function getTemplateFile() { return $this->collections->getOption('templatesPath').'template/home.tpl'; } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/components/collections/controllers/selection/create.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('selections.system.new_container'); 20 | } 21 | } -------------------------------------------------------------------------------- /core/components/collections/controllers/selection/data.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('collections.template.page_title'); 22 | } 23 | 24 | public function process(array $scriptProperties = []) {} 25 | 26 | public function loadCustomCssJs() { 27 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/extra/collections.combo.js'); 28 | 29 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/template.panel.js'); 30 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/sections/template/template.js'); 31 | 32 | $this->addHtml(' 33 | 41 | '); 42 | } 43 | 44 | public function getTemplateFile() { return $this->collections->getOption('templatesPath').'template/template.tpl'; } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/components/collections/controllers/template/export.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('collections.err.template_ns')); 28 | } 29 | 30 | /** @var CollectionTemplate[] $templates */ 31 | $templates = $this->modx->getIterator(CollectionTemplate::class, ['id:IN' => $templateIDs]); 32 | 33 | $fileContent = []; 34 | $fileName = ''; 35 | 36 | foreach ($templates as $template) { 37 | $export = $template->toArray(); 38 | unset($export['id'], $export['global_template']); 39 | $export['columns'] = []; 40 | 41 | foreach ($template->Columns as $column) { 42 | $exportColumn = $column->toArray(); 43 | unset($exportColumn['id'], $exportColumn['template']); 44 | $export['columns'][] = $exportColumn; 45 | } 46 | 47 | $fileContent[] = $export; 48 | 49 | if (empty($fileName)) { 50 | $fileName = 'collection_view_' . strtolower($template->name); 51 | } else { 52 | $fileName = 'collection_views'; 53 | } 54 | } 55 | 56 | if (empty($fileContent)) { 57 | die($this->modx->lexicon('collections.err.template_ns')); 58 | } 59 | 60 | session_write_close(); 61 | ob_clean(); 62 | 63 | header('Pragma: public'); // required 64 | header('Expires: 0'); // no cache 65 | header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 66 | header('Cache-Control: private', false); 67 | header('Content-Description: File Transfer'); 68 | header('Content-Type:'); //added to fix ZIP file corruption 69 | 70 | header('Content-Type: "application/force-download"'); 71 | header('Content-Disposition: attachment; filename="' . $fileName . '.json"'); 72 | header('Content-Transfer-Encoding: binary'); 73 | header('Connection: close'); 74 | 75 | echo json_encode($fileContent, JSON_PRETTY_PRINT); 76 | 77 | die(); 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /core/components/collections/controllers/template/update.class.php: -------------------------------------------------------------------------------- 1 | modx->lexicon('collections.template.page_title'); 22 | } 23 | 24 | public function process(array $scriptProperties = []) {} 25 | 26 | public function loadCustomCssJs() { 27 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/extra/griddraganddrop.js'); 28 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/extra/collections.renderers.js'); 29 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/extra/collections.combo.js'); 30 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/column.window.js'); 31 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/column.grid.js'); 32 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/widgets/template/template.panel.js'); 33 | $this->addLastJavascript($this->collections->getOption('jsUrl').'mgr/sections/template/template.js'); 34 | 35 | $this->addHtml(' 36 | 44 | '); 45 | } 46 | 47 | public function getTemplateFile() { return $this->collections->getOption('templatesPath').'template/template.tpl'; } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/elements/plugins/Collections.php: -------------------------------------------------------------------------------- 1 | services->has('collections')) { 15 | return; 16 | } 17 | 18 | /** @var Collections\Collections $collections */ 19 | $collections = $modx->services->get('collections'); 20 | if (!($collections instanceof Collections\Collections)) return ''; 21 | 22 | $className = "\\Collections\\Events\\{$modx->event->name}"; 23 | if (class_exists($className)) { 24 | /** @var \Collections\Events\Event $handler */ 25 | $handler = new $className($modx, $scriptProperties); 26 | $handler->run(); 27 | } 28 | 29 | return; -------------------------------------------------------------------------------- /core/components/collections/elements/snippets/getSelections.php: -------------------------------------------------------------------------------- 1 | services->get('collections'); 35 | if (!($collections instanceof Collections\Collections)) return ''; 36 | 37 | $getResourcesSnippet = $modx->getOption('getResourcesSnippet', $scriptProperties, 'getResources'); 38 | 39 | $getResourcesExists = $modx->getCount(modSnippet::class, ['name' => $getResourcesSnippet]); 40 | if ($getResourcesExists == 0) return 'getResources not found'; 41 | 42 | $sortDir = strtolower($modx->getOption('sortdir', $scriptProperties, 'asc')); 43 | $selections = $modx->getOption('selections', $scriptProperties, ''); 44 | $sortBy = $modx->getOption('sortby', $scriptProperties, ''); 45 | $excludeToPlaceholder = $modx->getOption('excludeToPlaceholder', $scriptProperties, ''); 46 | 47 | $selections = Utils::explodeAndClean($selections); 48 | 49 | if ($sortDir != 'asc') { 50 | $sortDir = 'desc'; 51 | } 52 | 53 | $linkedResourcesQuery = $modx->newQuery(CollectionSelection::class); 54 | 55 | if (!empty($selections)) { 56 | $linkedResourcesQuery->where([ 57 | 'collection:IN' => $selections 58 | ]); 59 | } 60 | 61 | if ($sortBy == '') { 62 | $linkedResourcesQuery->sortby('menuindex', $sortDir); 63 | } 64 | 65 | $linkedResourcesQuery->select([ 66 | 'resource' => 'DISTINCT(resource)', 67 | 'menuindex' => 'menuindex' 68 | ]); 69 | 70 | $linkedResourcesQuery->prepare(); 71 | 72 | $linkedResourcesQuery->stmt->execute(); 73 | 74 | $linkedResources = $linkedResourcesQuery->stmt->fetchAll(PDO::FETCH_COLUMN, 0); 75 | 76 | if (!empty($excludeToPlaceholder)) { 77 | $excludeResources = []; 78 | foreach($linkedResources as $res) { 79 | $excludeResources[] = '-' . $res; 80 | } 81 | $excludeResources = implode(',', $excludeResources); 82 | $modx->setPlaceholder($excludeToPlaceholder, $excludeResources); 83 | } 84 | 85 | $linkedResources = implode(',', $linkedResources); 86 | 87 | $properties = $scriptProperties; 88 | unset($properties['selections']); 89 | 90 | $properties['parents'] = ($properties['getResourcesSnippet'] == 'pdoResources') ? 0 : -1; 91 | 92 | if (empty($linkedResources)) { 93 | $properties['where'] = '2=1'; 94 | } else { 95 | $properties['resources'] = $linkedResources; 96 | 97 | if ($sortBy == '') { 98 | $properties['sortby'] = 'FIELD(modResource.id, ' . $linkedResources . ' )'; 99 | $properties['sortdir'] = 'asc'; 100 | } 101 | }; 102 | 103 | return $modx->runSnippet($getResourcesSnippet, $properties); 104 | -------------------------------------------------------------------------------- /core/components/collections/elements/widgets/test.php: -------------------------------------------------------------------------------- 1 | Test"; 18 | } 19 | 20 | 21 | /** 22 | * @return string 23 | * @throws Exception 24 | */ 25 | public function process() 26 | { 27 | return "
Test2
"; 28 | } 29 | } 30 | 31 | return 'TestWidget'; 32 | -------------------------------------------------------------------------------- /core/components/collections/index.class.php: -------------------------------------------------------------------------------- 1 | collections = $this->modx->services->get('collections'); 13 | 14 | $this->addCss($this->collections->config['cssUrl'] . 'mgr.css'); 15 | $this->addJavascript($this->collections->config['jsUrl'] . 'mgr/collections.js'); 16 | 17 | $this->addHtml(''); 22 | 23 | parent::initialize(); 24 | } 25 | 26 | public function getLanguageTopics() 27 | { 28 | return ['collections:default']; 29 | } 30 | 31 | public function checkPermissions() 32 | { 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/components/collections/lexicon/cs/custom.inc.php: -------------------------------------------------------------------------------- 1 | Tato změna je nevratná.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Odpojit označené'; 35 | $_lang['selections.back_to_selection_label'] = 'Zpět na výběr'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Dokument nebo výběr nebyl specifikován.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Tento výběr není možné změnit na dokument, jelikož obsahuje připojené dokumenty.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/cs/templates.inc.php: -------------------------------------------------------------------------------- 1 | Denne handling er permanent og kan ikke omgøres.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Fjern sammenkædningen af flere'; 35 | $_lang['selections.back_to_selection_label'] = 'Tilbage til Selection'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Ressource eller Collection blev ikke angivet.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Du kan ikke ændre denne ressource fra Selection fordi den har tilknyttede ressourcer.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/da/templates.inc.php: -------------------------------------------------------------------------------- 1 | Dies kann nicht rückgängig gemacht werden.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Auswahl Verlinkung aufheben'; 35 | $_lang['selections.back_to_selection_label'] = 'Zurück zur Selektion'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Ressource oder Kollektion wurde nicht angegeben.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Sie können diese Ressource nicht von einer Selektion umwandeln, da sie verlinkte Ressourcen enthält.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/de/templates.inc.php: -------------------------------------------------------------------------------- 1 | This operation is permanent and can\'t be reverted.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Unlink multiple'; 35 | $_lang['selections.back_to_selection_label'] = 'Back to Selection'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Resource or Collection was not specified.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'You can\'t switch this Resource from Selection, because it has linked Resources.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/en/templates.inc.php: -------------------------------------------------------------------------------- 1 | Cette opération est irrémédiable et irréversible.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Délier multiple'; 35 | $_lang['selections.back_to_selection_label'] = 'Retour à la sélection'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Ressource ou Collection non spécifiée.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Vous ne pouvez pas basculer cette ressource depuis la Sélection, elle a Ressources liées.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/fr/templates.inc.php: -------------------------------------------------------------------------------- 1 | Questa operazione è permanente e irreversibile.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Scollegamento multiplo'; 35 | $_lang['selections.back_to_selection_label'] = 'Torna alla Selezione'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'La risorsa o la Collezione non è stata specificata.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Non puoi spostare questa Risorsa dalla Selezione, perchè contiene Risorse collegate.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/it/templates.inc.php: -------------------------------------------------------------------------------- 1 | Deze handeling is definitief en kan niet worden teruggedraaid.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Ontkoppel meerdere'; 35 | $_lang['selections.back_to_selection_label'] = 'Terug naar Selectie'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Document of Collection is niet gespecificeerd.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Je kunt dit document niet omzetten van de selectie, want het heeft gekoppelde documenten.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/nl/templates.inc.php: -------------------------------------------------------------------------------- 1 | Esta operação é permanente e não pode ser revertida.'; 33 | $_lang['selections.resource'] = 'Recurso'; 34 | $_lang['selections.unlink_multiple'] = 'Desassociar múltiplos'; 35 | $_lang['selections.back_to_selection_label'] = 'Voltar à Selecção'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Recurso ou Coleção não especificado.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Não pode modificar este Recurso de Seleção para outro tipo, porque tem Recursos associados.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/pt/templates.inc.php: -------------------------------------------------------------------------------- 1 | Это действие нельзя отменить.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Отвязать несколько'; 35 | $_lang['selections.back_to_selection_label'] = 'Вернуться в Подборку'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Ресурс или коллекция не указаны.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Вы не можете переключить этот Ресурс на Подборку, т.к. он имеет связанные Ресурсы.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/ru/templates.inc.php: -------------------------------------------------------------------------------- 1 | Denna åtgärd är permanent och kan inte återställas.'; 33 | $_lang['selections.resource'] = 'Resource'; 34 | $_lang['selections.unlink_multiple'] = 'Avlänka flera'; 35 | $_lang['selections.back_to_selection_label'] = 'Tillbaka till urval'; 36 | 37 | // Errors 38 | $_lang['collections.err.selection_res_col_ns'] = 'Resurs eller samling angavs inte.'; 39 | $_lang['collections.err.cant_switch_from_selection_linked'] = 'Du kan inte byta denna resurs från urval, eftersom den har länkade resurser.'; -------------------------------------------------------------------------------- /core/components/collections/lexicon/sv/templates.inc.php: -------------------------------------------------------------------------------- 1 | collections =& $collections; 31 | $this->modx =& $collections->modx; 32 | 33 | if ($this->modx->services->has('fred')) { 34 | $this->fred = $this->modx->services->get('fred'); 35 | } 36 | } 37 | 38 | public function run() 39 | { 40 | if ($this->fred === null) { 41 | http_response_code(500); 42 | return; 43 | } 44 | 45 | if (!$this->modx->user) { 46 | http_response_code(401); 47 | return; 48 | } 49 | 50 | try { 51 | $payload = $this->fred->getJWTPayload(); 52 | 53 | $this->modx->switchContext($payload['context']); 54 | 55 | if (!$this->modx->hasPermission('fred')) { 56 | http_response_code(403); 57 | return; 58 | } 59 | 60 | if ($payload['iss'] !== $this->modx->user->id) { 61 | http_response_code(403); 62 | return; 63 | } 64 | } catch (\Exception $e) { 65 | http_response_code(403); 66 | return; 67 | } 68 | 69 | $action = $this->modx->getOption('action', $_REQUEST, ''); 70 | if (empty($action)) return; 71 | 72 | $action = str_replace('/', '', $action); 73 | $action = str_replace('\\', '', $action); 74 | $action = explode('-', $action); 75 | $action = array_map('ucfirst', $action); 76 | $action = implode('', $action); 77 | 78 | $className = "\\Collections\\Endpoint\\Ajax\\{$action}"; 79 | if (class_exists($className) !== true) { 80 | http_response_code(404); 81 | return; 82 | } 83 | 84 | /** @var Ajax\Endpoint $ajaxEndpoint */ 85 | $ajaxEndpoint = new $className($this->collections, $this->fred, $payload); 86 | 87 | header('Content-Type: application/json; charset=UTF-8'); 88 | 89 | echo $ajaxEndpoint->run(); 90 | 91 | return; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /core/components/collections/src/Endpoint/Ajax/Endpoint.php: -------------------------------------------------------------------------------- 1 | collections =& $collections; 36 | $this->fred =& $fred; 37 | $this->modx =& $collections->modx; 38 | $this->jwtPayload = $payload; 39 | } 40 | 41 | public function run() 42 | { 43 | if (!$this->modx->user) { 44 | http_response_code(401); 45 | return ''; 46 | } 47 | 48 | if (!$this->modx->hasPermission('fred')) { 49 | http_response_code(403); 50 | return ''; 51 | } 52 | 53 | /** @var bool|string $checked */ 54 | $checked = $this->checkMethod(); 55 | if ($checked !== true) { 56 | return $checked; 57 | } 58 | 59 | if ($this->method === 'POST') { 60 | $this->body = json_decode(file_get_contents('php://input'), true); 61 | } 62 | 63 | 64 | return $this->process(); 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | abstract function process(); 71 | 72 | /** 73 | * @param string|array $message 74 | * @param array $fields 75 | * @return string 76 | */ 77 | protected function failure($message, $fields = []) 78 | { 79 | http_response_code(400); 80 | 81 | if (!is_array($message)) { 82 | $message = ['message' => $message]; 83 | } 84 | 85 | $message['_fields'] = $fields; 86 | 87 | return json_encode($message); 88 | } 89 | 90 | /** 91 | * @param string|array $message 92 | * @return string 93 | */ 94 | protected function success($message = '') 95 | { 96 | http_response_code(200); 97 | 98 | if (!is_array($message)) { 99 | $message = ['message' => $message]; 100 | } 101 | 102 | return json_encode($message); 103 | } 104 | 105 | /** 106 | * @param array $data 107 | * @param array $meta 108 | * @return string 109 | */ 110 | protected function data($data, $meta = []) 111 | { 112 | http_response_code(200); 113 | 114 | $meta['data'] = $data; 115 | 116 | return json_encode($meta); 117 | } 118 | 119 | protected function checkMethod() 120 | { 121 | $this->method = strtoupper($_SERVER['REQUEST_METHOD']); 122 | 123 | if (in_array($this->method, $this->allowedMethod)) return true; 124 | 125 | http_response_code(405); 126 | return '{}'; 127 | } 128 | 129 | protected function verifyClaim($name, $value) 130 | { 131 | if (!isset($this->jwtPayload[$name])) return false; 132 | 133 | return $this->jwtPayload[$name] === $value; 134 | } 135 | 136 | protected function getClaim($name) 137 | { 138 | if (!isset($this->jwtPayload[$name])) return false; 139 | 140 | return $this->jwtPayload[$name]; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /core/components/collections/src/Endpoint/Ajax/GetAuthors.php: -------------------------------------------------------------------------------- 1 | failure('No collection was provided'); 19 | } 20 | 21 | $templates = $this->fred->getFredTemplates(); 22 | if (empty($templates)) { 23 | return $this->failure('No Fred templates'); 24 | } 25 | 26 | 27 | $c = $this->modx->newQuery(modResource::class); 28 | $c->where([ 29 | 'parent' => $collection, 30 | 'template:IN' => $templates 31 | ]); 32 | 33 | $c->select([ 34 | 'createdby' => 'distinct(createdby)' 35 | ]); 36 | 37 | $c->prepare(); 38 | $c->stmt->execute(); 39 | 40 | $authorIDs = $c->stmt->fetchAll(\PDO::FETCH_COLUMN, 0); 41 | if (empty($authorIDs)) { 42 | return $this->data([]); 43 | } 44 | 45 | $cProfile = $this->modx->newQuery(modUserProfile::class); 46 | $cProfile->where([ 47 | 'internalKey:IN' => $authorIDs 48 | ]); 49 | $cProfile->select($this->modx->getSelectColumns(modUserProfile::class, 'modUserProfile', '', ['internalKey', 'fullname'])); 50 | $cProfile->sortby('fullname', 'asc'); 51 | $cProfile->prepare(); 52 | $cProfile->stmt->execute(); 53 | 54 | $authors = ['-1' => 'Any']; 55 | while($row = $cProfile->stmt->fetch(\PDO::FETCH_ASSOC)) { 56 | $authors[(string)$row['internalKey']] = $row['fullname']; 57 | } 58 | 59 | return $this->data($authors); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /core/components/collections/src/Endpoint/Ajax/GetCollectionView.php: -------------------------------------------------------------------------------- 1 | failure('No Collection'); 15 | 16 | /** @var \modResource $collection */ 17 | $collection = $this->modx->getObject(modResource::class, ['id' => $collection]); 18 | if (!$collection) return $this->failure('No Collection'); 19 | 20 | $view = $this->collections->getCollectionsView($collection); 21 | $templates = $this->fred->getFredTemplates(); 22 | $templates = array_flip($templates); 23 | 24 | $childTemplate = isset($templates[$view->child_template]) ? $view->child_template : 0; 25 | $defaultBlueprint = 0; 26 | 27 | if (($childTemplate > 0) && ($view->fred_default_blueprint > 0)) { 28 | /** @var \FredBlueprint $blueprint */ 29 | $blueprint = $this->modx->getObject('Fred\\Model\\FredBlueprint', ['uuid' => $view->fred_default_blueprint]); 30 | 31 | if ($blueprint) { 32 | $category = $blueprint->Category; 33 | if ($category) { 34 | $themedTemplate = $this->modx->getCount('Fred\\Model\\FredThemedTemplate', [ 35 | 'theme' => $category->get('theme'), 36 | 'template' => $childTemplate 37 | ]); 38 | 39 | if ($themedTemplate === 1) { 40 | $defaultBlueprint = $blueprint->id; 41 | } 42 | } 43 | } 44 | } 45 | 46 | return $this->data([ 47 | 'template' => $childTemplate, 48 | 'blueprint' => $defaultBlueprint, 49 | 'sort' => $view->sort_field, 50 | 'sortDir' => $view->sort_dir, 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/components/collections/src/Endpoint/Ajax/GetCollections.php: -------------------------------------------------------------------------------- 1 | getClaim('context'); 15 | 16 | $c = $this->modx->newQuery(modResource::class); 17 | $c->where([ 18 | 'class_key' => CollectionContainer::class, 19 | 'context_key' => $context 20 | ]); 21 | $c->sortby('pagetitle'); 22 | 23 | /** @var \modResource[] $resources */ 24 | $resources = $this->modx->getIterator(modResource::class, $c); 25 | $data = []; 26 | 27 | foreach ($resources as $resource) { 28 | $data[] = [ 29 | 'id' => $resource->get('id'), 30 | 'pagetitle' => $resource->get('pagetitle'), 31 | 'published' => $resource->get('published'), 32 | 'deleted' => $resource->get('deleted'), 33 | ]; 34 | } 35 | 36 | return $this->data(['collections' => $data]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/Event.php: -------------------------------------------------------------------------------- 1 | scriptProperties =& $scriptProperties; 20 | $this->modx = $modx; 21 | $this->collections = $this->modx->services->get('collections'); 22 | } 23 | 24 | abstract public function run(); 25 | } 26 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/FredBeforeRender.php: -------------------------------------------------------------------------------- 1 | modx->services->has('fred')) { 18 | $this->fred = $this->modx->services->get('fred'); 19 | } else { 20 | $this->preventRun = true; 21 | } 22 | } 23 | 24 | public function run() 25 | { 26 | if ($this->preventRun) return true; 27 | 28 | $assetsPath = $this->collections->getOption('assets_url', [], $this->modx->getOption('assets_url', null, MODX_ASSETS_URL) . 'components/collections/'); 29 | 30 | $assetsPath = rtrim($assetsPath, '/') . '/web/'; 31 | 32 | $includes = ' 33 | 34 | 35 | '; 36 | 37 | $endpoint = $assetsPath . 'ajax.php'; 38 | 39 | $beforeRender = ' 40 | this.registerSidebarPlugin("CollectionsSidebar", FredCollections.SidebarPlugin({endpoint:"' . $endpoint . '"})); 41 | '; 42 | 43 | $this->modx->event->_output = [ 44 | 'includes' => $includes, 45 | 'beforeRender' => $beforeRender, 46 | 'lexicons' => ['collections:fred'], 47 | ]; 48 | 49 | return true; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/FredOnBeforeGetResourceTree.php: -------------------------------------------------------------------------------- 1 | modx->services->has('fred')) { 20 | $this->fred = $this->modx->services->get('fred'); 21 | } else { 22 | $this->preventRun = true; 23 | } 24 | } 25 | 26 | public function run() 27 | { 28 | if ($this->preventRun) return true; 29 | 30 | $params = $this->modx->getOption('params', $this->scriptProperties); 31 | if (empty($params)) return false; 32 | 33 | $query = $this->modx->newQuery(modResource::class, [ 34 | 'class_key' => CollectionContainer::class 35 | ]); 36 | 37 | $query->select($this->modx->getSelectColumns(modResource::class, 'modResource', '', ['id'])); 38 | 39 | $query->prepare(); 40 | $query->stmt->execute(); 41 | 42 | while ($row = $query->stmt->fetch(\PDO::FETCH_ASSOC)) { 43 | $params->hideChildren[] = $row['id']; 44 | } 45 | 46 | return true; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnBeforeEmptyTrash.php: -------------------------------------------------------------------------------- 1 | scriptProperties['ids']; 14 | 15 | foreach ($ids as $id) { 16 | /** @var \modResource $resource */ 17 | $resource = $this->modx->getObject(modResource::class, $id); 18 | if (!$resource) return; 19 | 20 | $this->modx->removeCollection(CollectionSelection::class, ['resource' => $resource->id]); 21 | 22 | /** @var \modResource $parent */ 23 | $parent = $resource->Parent; 24 | if (!$parent) return; 25 | 26 | /** @var \modResource $grandParent */ 27 | $grandParent = $parent->Parent; 28 | if (!$grandParent) return; 29 | 30 | if (($grandParent->class_key == CollectionContainer::class) && ($parent->class_key != CollectionContainer::class)) { 31 | $parent->set('show_in_tree', 0); 32 | $parent->save(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnDocFormRender.php: -------------------------------------------------------------------------------- 1 | scriptProperties['mode']) || $this->scriptProperties['mode'] !== 'new') { 13 | return; 14 | } 15 | 16 | /** @var modResource $parent */ 17 | $parent = $this->scriptProperties['resource']->Parent; 18 | 19 | if (empty($parent) || $parent->class_key != CollectionContainer::class) { 20 | return; 21 | } 22 | 23 | /** @var CollectionTemplate $template */ 24 | $template = $this->collections->getCollectionsView($parent); 25 | 26 | $this->setConfig('hidemenu_default', $template->child_hide_from_menu); 27 | $this->setConfig('publish_default', $template->child_published); 28 | $this->setConfig('cache_default', $template->child_cacheable); 29 | $this->setConfig('richtext_default', $template->child_richtext); 30 | $this->setConfig('search_default', $template->child_searchable); 31 | $this->setConfig('default_content_type', $template->child_content_type, 0); 32 | 33 | $this->modx->regClientStartupHTMLBlock(' 34 | '); 39 | } 40 | 41 | private function setConfig($name, $value, $default = null) 42 | { 43 | if ($value !== $default) { 44 | $this->modx->_userConfig[$name] = intval($value); 45 | } 46 | } 47 | 48 | private function setJSConfig($name, $value, $default = null) 49 | { 50 | if ($value !== $default) { 51 | return 'if (Ext.getCmp("' . $name . '")) {Ext.getCmp("' . $name . '").setValue(' . intval($value) . ');}'; 52 | } 53 | 54 | return ''; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnManagerPageBeforeRender.php: -------------------------------------------------------------------------------- 1 | scriptProperties['controller']->addLexiconTopic('collections:default'); 10 | 11 | $this->scriptProperties['controller']->addHtml(" 12 | 49 | "); 50 | 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnManagerPageInit.php: -------------------------------------------------------------------------------- 1 | collections->getOption('assetsUrl') . 'css/mgr.css'; 10 | $this->modx->regClientCSS($cssFile); 11 | } 12 | } -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnResourceBeforeSort.php: -------------------------------------------------------------------------------- 1 | scriptProperties['nodes']; 15 | 16 | foreach ($nodes as $id => $node) { 17 | /** @var modResource $resource */ 18 | $resource = $this->modx->getObject(modResource::class, $node['id']); 19 | if (!$resource) continue; 20 | 21 | /** @var modResource $originalParent */ 22 | $originalParent = $resource->Parent; 23 | 24 | /** @var modResource $parent */ 25 | $parent = $this->modx->getObject(modResource::class, $node['parent']); 26 | 27 | if (($parent && $originalParent && $parent->id == $originalParent->id) || (!$parent && !$originalParent)) { 28 | continue; 29 | } 30 | 31 | 32 | $this->handleParent($parent, $resource, $originalParent); 33 | 34 | // if ($originalParent) { 35 | // $this->handleOriginalParent($originalParent); 36 | // } 37 | 38 | if ($resource->class_key == CollectionContainer::class) { 39 | $resource->set('show_in_tree', 1); 40 | } 41 | 42 | $resource->save(); 43 | 44 | } 45 | } 46 | 47 | /** 48 | * @param modResource $parent 49 | * @param modResource $resource 50 | * @param modResource $originalParent 51 | */ 52 | protected function handleParent($parent, $resource, $originalParent) 53 | { 54 | if ($parent) { 55 | if ($parent->class_key == CollectionContainer::class) { 56 | $resource->set('show_in_tree', 0); 57 | } else { 58 | if (($originalParent->class_key == CollectionContainer::class) && ($parent->class_key != CollectionContainer::class)) { 59 | $resource->set('show_in_tree', 1); 60 | } 61 | } 62 | } else { 63 | if ($originalParent && ($originalParent->class_key == CollectionContainer::class)) { 64 | $resource->set('show_in_tree', 1); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * @param modResource $originalParent 71 | */ 72 | protected function handleOriginalParent($originalParent) 73 | { 74 | $originalGreatParent = $originalParent->Parent; 75 | 76 | if ($originalGreatParent && ($originalGreatParent->class_key == CollectionContainer::class) && ($originalParent->hasChildren() == 0)) { 77 | $originalParent->set('show_in_tree', 0); 78 | $originalParent->save(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/components/collections/src/Events/OnResourceDuplicate.php: -------------------------------------------------------------------------------- 1 | scriptProperties['newResource']; 14 | 15 | /** @var modResource $oldResource */ 16 | $oldResource = $this->scriptProperties['oldResource']; 17 | 18 | if ($oldResource->class_key !== CollectionContainer::class) return; 19 | 20 | /** @var CollectionSetting $oldSettings */ 21 | $oldSettings = $this->modx->getObject(CollectionSetting::class, ['collection' => $oldResource->id]); 22 | if ($oldSettings) { 23 | /** @var CollectionSetting $newSettings */ 24 | $newSettings = $this->modx->newObject(CollectionSetting::class); 25 | $newSettings->set('collection', $newResource->id); 26 | $newSettings->set('template', $oldSettings->template); 27 | $newSettings->save(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/CollectionContainer.php: -------------------------------------------------------------------------------- 1 | set('class_key', CollectionContainer::class); 24 | } 25 | 26 | public static function getControllerPath(xPDO &$modx) 27 | { 28 | return $modx->getOption('collections.core_path', null, $modx->getOption('core_path') . 'components/collections/') . 'controllers/'; 29 | } 30 | 31 | public function getContextMenuText() 32 | { 33 | $this->xpdo->lexicon->load('collections:default'); 34 | return [ 35 | 'text_create' => $this->xpdo->lexicon('collections.system.text_create'), 36 | 'text_create_here' => $this->xpdo->lexicon('collections.system.text_create_here'), 37 | ]; 38 | } 39 | 40 | public function getResourceTypeName() 41 | { 42 | $this->xpdo->lexicon->load('collections:default'); 43 | return $this->xpdo->lexicon('collections.system.type_name'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/CollectionContainerCreateProcessor.php: -------------------------------------------------------------------------------- 1 | getProperty('collections_template'); 9 | if ($collectionsTemplate === null) { 10 | return parent::afterSave(); 11 | } 12 | 13 | $collectionsTemplate = (int)$collectionsTemplate; 14 | 15 | $config = $this->modx->getObject(CollectionSetting::class, ['collection' => $this->object->id]); 16 | 17 | if (!$config) { 18 | $config = $this->modx->newObject(CollectionSetting::class); 19 | $config->set('collection', $this->object->id); 20 | } 21 | 22 | $config->set('template', $collectionsTemplate); 23 | 24 | $config->save(); 25 | 26 | return parent::afterSave(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/CollectionContainerUpdateProcessor.php: -------------------------------------------------------------------------------- 1 | getProperty('collections_template'); 19 | if ($collectionsTemplate === null) { 20 | return parent::afterSave(); 21 | } 22 | 23 | $collectionsTemplate = (int)$collectionsTemplate; 24 | 25 | $config = $this->modx->getObject(CollectionSetting::class, ['collection' => $this->object->id]); 26 | 27 | if (!$config) { 28 | $config = $this->modx->newObject(CollectionSetting::class); 29 | $config->set('collection', $this->object->id); 30 | } 31 | 32 | $config->set('template', $collectionsTemplate); 33 | 34 | $config->save(); 35 | 36 | return parent::afterSave(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/CollectionResourceTemplate.php: -------------------------------------------------------------------------------- 1 | xpdo->removeCollection(CollectionResourceTemplate::class, ['collection_template' => $this->id]); 60 | 61 | if (!empty($templates)) { 62 | foreach ($templates as $idTemplate) { 63 | $newTemplate = $this->xpdo->newObject(CollectionResourceTemplate::class); 64 | $newTemplate->set('collection_template', $this->id); 65 | $newTemplate->set('resource_template', $idTemplate); 66 | $newTemplate->save(); 67 | } 68 | } 69 | 70 | return true; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/CollectionTemplateColumn.php: -------------------------------------------------------------------------------- 1 | set('class_key', SelectionContainer::class); 24 | } 25 | 26 | public static function getControllerPath(xPDO &$modx) 27 | { 28 | return $modx->getOption('collections.core_path', null, $modx->getOption('core_path') . 'components/collections/') . 'controllers/selection/'; 29 | } 30 | 31 | public function getContextMenuText() 32 | { 33 | $this->xpdo->lexicon->load('collections:default', 'collections:selections'); 34 | 35 | return [ 36 | 'text_create' => $this->xpdo->lexicon('selections.system.text_create'), 37 | 'text_create_here' => $this->xpdo->lexicon('selections.system.text_create_here'), 38 | ]; 39 | } 40 | 41 | public function getResourceTypeName() 42 | { 43 | $this->xpdo->lexicon->load('collections:default', 'collections:selections'); 44 | return $this->xpdo->lexicon('selections.system.type_name'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/SelectionContainerCreateProcessor.php: -------------------------------------------------------------------------------- 1 | '3.0', 4 | 'namespace' => 'Collections\\Model', 5 | 'namespacePrefix' => 'Collections', 6 | 'class_map' => 7 | array ( 8 | 'MODX\\Revolution\\modResource' => 9 | array ( 10 | 0 => 'Collections\\Model\\CollectionContainer', 11 | ), 12 | 'Collections\\Model\\CollectionContainer' => 13 | array ( 14 | 0 => 'Collections\\Model\\SelectionContainer', 15 | ), 16 | 'xPDO\\Om\\xPDOSimpleObject' => 17 | array ( 18 | 0 => 'Collections\\Model\\CollectionSetting', 19 | 1 => 'Collections\\Model\\CollectionTemplate', 20 | 2 => 'Collections\\Model\\CollectionTemplateColumn', 21 | ), 22 | 'xPDO\\Om\\xPDOObject' => 23 | array ( 24 | 0 => 'Collections\\Model\\CollectionResourceTemplate', 25 | 1 => 'Collections\\Model\\CollectionSelection', 26 | ), 27 | ), 28 | ); -------------------------------------------------------------------------------- /core/components/collections/src/Model/mysql/CollectionContainer.php: -------------------------------------------------------------------------------- 1 | 'Collections\\Model\\', 11 | 'version' => '3.0', 12 | 'extends' => 'MODX\\Revolution\\modResource', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'MyISAM', 16 | ), 17 | 'fields' => 18 | array ( 19 | ), 20 | 'fieldMeta' => 21 | array ( 22 | ), 23 | 'composites' => 24 | array ( 25 | 'Setting' => 26 | array ( 27 | 'class' => 'Collections\\Model\\CollectionSetting', 28 | 'local' => 'id', 29 | 'foreign' => 'collection', 30 | 'cardinality' => 'one', 31 | 'owner' => 'local', 32 | ), 33 | ), 34 | ); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/mysql/CollectionResourceTemplate.php: -------------------------------------------------------------------------------- 1 | 'Collections\\Model\\', 11 | 'version' => '3.0', 12 | 'table' => 'collection_resource_template', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'MyISAM', 16 | ), 17 | 'fields' => 18 | array ( 19 | 'collection_template' => NULL, 20 | 'resource_template' => NULL, 21 | ), 22 | 'fieldMeta' => 23 | array ( 24 | 'collection_template' => 25 | array ( 26 | 'dbtype' => 'int', 27 | 'attributes' => 'unsigned', 28 | 'precision' => '10', 29 | 'phptype' => 'integer', 30 | 'null' => false, 31 | 'index' => 'pk', 32 | ), 33 | 'resource_template' => 34 | array ( 35 | 'dbtype' => 'int', 36 | 'attributes' => 'unsigned', 37 | 'precision' => '10', 38 | 'phptype' => 'integer', 39 | 'null' => false, 40 | 'index' => 'pk', 41 | ), 42 | ), 43 | 'indexes' => 44 | array ( 45 | 'PRIMARY' => 46 | array ( 47 | 'alias' => 'PRIMARY', 48 | 'primary' => true, 49 | 'unique' => true, 50 | 'type' => 'BTREE', 51 | 'columns' => 52 | array ( 53 | 'collection_template' => 54 | array ( 55 | 'length' => '', 56 | 'collation' => 'A', 57 | 'null' => false, 58 | ), 59 | 'resource_template' => 60 | array ( 61 | 'length' => '', 62 | 'collation' => 'A', 63 | 'null' => false, 64 | ), 65 | ), 66 | ), 67 | ), 68 | 'aggregates' => 69 | array ( 70 | 'CollectionTemplate' => 71 | array ( 72 | 'class' => 'Collections\\Model\\CollectionTemplate', 73 | 'local' => 'collection_template', 74 | 'foreign' => 'id', 75 | 'cardinality' => 'one', 76 | 'owner' => 'foreign', 77 | ), 78 | 'ResourceTemplate' => 79 | array ( 80 | 'class' => 'MODX\\Revolution\\modTemplate', 81 | 'local' => 'resource_template', 82 | 'foreign' => 'id', 83 | 'cardinality' => 'one', 84 | 'owner' => 'foreign', 85 | ), 86 | ), 87 | ); 88 | 89 | } 90 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/mysql/CollectionSelection.php: -------------------------------------------------------------------------------- 1 | 'Collections\\Model\\', 11 | 'version' => '3.0', 12 | 'table' => 'collection_selections', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'MyISAM', 16 | ), 17 | 'fields' => 18 | array ( 19 | 'collection' => NULL, 20 | 'resource' => NULL, 21 | 'menuindex' => 0, 22 | ), 23 | 'fieldMeta' => 24 | array ( 25 | 'collection' => 26 | array ( 27 | 'dbtype' => 'int', 28 | 'attributes' => 'unsigned', 29 | 'precision' => '10', 30 | 'phptype' => 'integer', 31 | 'null' => false, 32 | 'index' => 'pk', 33 | ), 34 | 'resource' => 35 | array ( 36 | 'dbtype' => 'int', 37 | 'attributes' => 'unsigned', 38 | 'precision' => '10', 39 | 'phptype' => 'integer', 40 | 'null' => false, 41 | 'index' => 'pk', 42 | ), 43 | 'menuindex' => 44 | array ( 45 | 'dbtype' => 'int', 46 | 'attributes' => 'unsigned', 47 | 'precision' => '10', 48 | 'phptype' => 'integer', 49 | 'null' => false, 50 | 'default' => 0, 51 | ), 52 | ), 53 | 'indexes' => 54 | array ( 55 | 'PRIMARY' => 56 | array ( 57 | 'alias' => 'PRIMARY', 58 | 'primary' => true, 59 | 'unique' => true, 60 | 'type' => 'BTREE', 61 | 'columns' => 62 | array ( 63 | 'collection' => 64 | array ( 65 | 'length' => '', 66 | 'collation' => 'A', 67 | 'null' => false, 68 | ), 69 | 'resource' => 70 | array ( 71 | 'length' => '', 72 | 'collation' => 'A', 73 | 'null' => false, 74 | ), 75 | ), 76 | ), 77 | ), 78 | 'aggregates' => 79 | array ( 80 | 'Collection' => 81 | array ( 82 | 'class' => 'Collections\\Model\\CollectionContainer', 83 | 'local' => 'collection', 84 | 'foreign' => 'id', 85 | 'cardinality' => 'one', 86 | 'owner' => 'foreign', 87 | ), 88 | 'CollectionSetting' => 89 | array ( 90 | 'class' => 'Collections\\Model\\CollectionSetting', 91 | 'local' => 'collection', 92 | 'foreign' => 'id', 93 | 'cardinality' => 'one', 94 | 'owner' => 'foreign', 95 | ), 96 | 'Resource' => 97 | array ( 98 | 'class' => 'MODX\\Revolution\\modResource', 99 | 'local' => 'resource', 100 | 'foreign' => 'id', 101 | 'cardinality' => 'one', 102 | 'owner' => 'foreign', 103 | ), 104 | ), 105 | ); 106 | 107 | } 108 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/mysql/CollectionSetting.php: -------------------------------------------------------------------------------- 1 | 'Collections\\Model\\', 11 | 'version' => '3.0', 12 | 'table' => 'collection_settings', 13 | 'extends' => 'xPDO\\Om\\xPDOSimpleObject', 14 | 'tableMeta' => 15 | array ( 16 | 'engine' => 'MyISAM', 17 | ), 18 | 'fields' => 19 | array ( 20 | 'collection' => NULL, 21 | 'template' => 0, 22 | ), 23 | 'fieldMeta' => 24 | array ( 25 | 'collection' => 26 | array ( 27 | 'dbtype' => 'int', 28 | 'attributes' => 'unsigned', 29 | 'precision' => '10', 30 | 'phptype' => 'integer', 31 | 'null' => false, 32 | 'index' => 'unique', 33 | ), 34 | 'template' => 35 | array ( 36 | 'dbtype' => 'int', 37 | 'attributes' => 'unsigned', 38 | 'precision' => '10', 39 | 'phptype' => 'integer', 40 | 'null' => false, 41 | 'default' => 0, 42 | ), 43 | ), 44 | 'aggregates' => 45 | array ( 46 | 'Collection' => 47 | array ( 48 | 'class' => 'Collections\\Model\\CollectionContainer', 49 | 'local' => 'collection', 50 | 'foreign' => 'id', 51 | 'cardinality' => 'one', 52 | 'owner' => 'foreign', 53 | ), 54 | 'Template' => 55 | array ( 56 | 'class' => 'Collections\\Model\\CollectionTemplate', 57 | 'local' => 'template', 58 | 'foreign' => 'id', 59 | 'cardinality' => 'one', 60 | 'owner' => 'local', 61 | ), 62 | ), 63 | ); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /core/components/collections/src/Model/mysql/SelectionContainer.php: -------------------------------------------------------------------------------- 1 | 'Collections\\Model\\', 11 | 'version' => '3.0', 12 | 'extends' => 'Collections\\Model\\CollectionContainer', 13 | 'tableMeta' => 14 | array ( 15 | 'engine' => 'MyISAM', 16 | ), 17 | 'fields' => 18 | array ( 19 | ), 20 | 'fieldMeta' => 21 | array ( 22 | ), 23 | 'composites' => 24 | array ( 25 | 'Selection' => 26 | array ( 27 | 'class' => 'Collections\\Model\\CollectionSelection', 28 | 'local' => 'id', 29 | 'foreign' => 'collection', 30 | 'cardinality' => 'many', 31 | 'owner' => 'local', 32 | ), 33 | ), 34 | ); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Extra/Breadcrumbs.php: -------------------------------------------------------------------------------- 1 | getProperty('folder', 0); 12 | $collection = (int)$this->getProperty('collection', 0); 13 | 14 | if (($folder <= 0) || ($collection <= 0)) { 15 | return $this->failure(); 16 | } 17 | 18 | /** @var modResource $resource */ 19 | $resource = $this->modx->getObject(modResource::class, $folder); 20 | if (!$resource) { 21 | return $this->failure(); 22 | } 23 | 24 | $haveParent = false; 25 | $parents = [ 26 | [ 27 | 'id' => $resource->id, 28 | 'text' => $resource->pagetitle 29 | ] 30 | ]; 31 | 32 | while ($haveParent === false) { 33 | $parent = $resource->Parent; 34 | 35 | if ($parent->id == $collection) { 36 | $haveParent = true; 37 | } else { 38 | $parents[] = [ 39 | 'id' => $parent->id, 40 | 'text' => $parent->pagetitle 41 | ]; 42 | $resource = $parent; 43 | } 44 | 45 | } 46 | 47 | return $this->outputArray(array_reverse($parents)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Extra/FredGetBlueprints.php: -------------------------------------------------------------------------------- 1 | modx->services->has('fred')) { 19 | return false; 20 | } 21 | 22 | $this->setDefaultProperties([ 23 | 'start' => 0, 24 | 'limit' => 20, 25 | 'query' => '', 26 | ]); 27 | 28 | return parent::initialize(); 29 | } 30 | 31 | public function beforeIteration(array $list) 32 | { 33 | $addNone = (int)$this->getProperty('addNone', 0); 34 | 35 | if ($addNone === 1) { 36 | $list[] = [ 37 | 'id' => '', 38 | 'name' => $this->modx->lexicon('fred.global.none') 39 | ]; 40 | } 41 | 42 | return parent::beforeIteration($list); 43 | } 44 | 45 | public function prepareQueryBeforeCount(xPDOQuery $c) 46 | { 47 | $uuid = $this->getProperty('uuid', ''); 48 | if (!empty($uuid)) { 49 | $c->where(['uuid' => $uuid]); 50 | } 51 | 52 | $query = $this->getProperty('query', ''); 53 | if (!empty($query)) { 54 | $c->where(['name:LIKE' => "%{$query}%"]); 55 | } 56 | 57 | $c->where([ 58 | 'public' => true, 59 | 'Category.public' => true 60 | ]); 61 | 62 | $template = (int)$this->getProperty('template', 0); 63 | if (!empty($template)) { 64 | $c->where([ 65 | 'Templates.template' => $template 66 | ]); 67 | } 68 | 69 | return parent::prepareQueryBeforeCount($c); 70 | } 71 | 72 | public function prepareQueryAfterCount(xPDOQuery $c) 73 | { 74 | $c->leftJoin('Fred\\Model\\FredBlueprintCategory', 'Category'); 75 | $c->leftJoin('Fred\\Model\\FredTheme', 'Theme', 'Category.theme = Theme.id'); 76 | $c->leftJoin('Fred\\Model\\FredThemedTemplate', 'Templates', 'Templates.theme = Theme.id'); 77 | 78 | $c->select($this->modx->getSelectColumns('Fred\\Model\\FredBlueprint', 'FredBlueprint', '', ['data'], true)); 79 | $c->select($this->modx->getSelectColumns('Fred\\Model\\FredBlueprintCategory', 'Category', 'category_', ['name'])); 80 | $c->select($this->modx->getSelectColumns('Fred\\Model\\FredTheme', 'Theme', 'theme_', ['name'])); 81 | 82 | return parent::prepareQueryAfterCount($c); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Extra/GetContentTypes.php: -------------------------------------------------------------------------------- 1 | 0, 'name' => $this->modx->lexicon('collections.global.use_default')]); 15 | 16 | return $list; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Extra/GetDerivates.php: -------------------------------------------------------------------------------- 1 | modx->hasPermission('class_map'); 12 | } 13 | 14 | public function initialize() 15 | { 16 | $this->setDefaultProperties([ 17 | 'class' => '', 18 | 'skip' => 'modXMLRPCResource', 19 | ]); 20 | return true; 21 | } 22 | 23 | public function process() 24 | { 25 | $class = $this->getProperty('class'); 26 | if (empty($class)) $this->failure($this->modx->lexicon('class_err_ns')); 27 | 28 | $skip = explode(',', $this->getProperty('skip')); 29 | $descendants = $this->modx->getDescendants($class); 30 | 31 | $list = []; 32 | foreach ($descendants as $descendant) { 33 | if (in_array($descendant, $skip)) continue; 34 | 35 | /** @var modResource $obj */ 36 | $obj = $this->modx->newObject($descendant); 37 | if (!$obj) continue; 38 | 39 | if ($class == 'MODX\\Revolution\\modResource' && !$obj->allowListingInClassKeyDropdown) continue; 40 | if ($class == 'MODX\\Revolution\\modResource') { 41 | $name = $obj->getResourceTypeName(); 42 | } else { 43 | $name = $descendant; 44 | } 45 | 46 | $list[$descendant] = [ 47 | 'id' => $descendant, 48 | 'name' => $name, 49 | ]; 50 | } 51 | 52 | return $this->outputArray($list); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Extra/GetTemplates.php: -------------------------------------------------------------------------------- 1 | getProperty('addEmpty', false); 19 | 20 | if ($addEmpty) { 21 | $list[] = [ 22 | 'id' => 0, 23 | 'templatename' => $this->modx->lexicon('template_empty'), 24 | ]; 25 | } 26 | 27 | return $list; 28 | } 29 | 30 | public function prepareQueryBeforeCount(xPDOQuery $c) 31 | { 32 | $query = $this->getProperty('query'); 33 | $template = (int)$this->getProperty('template'); 34 | 35 | $where = []; 36 | 37 | if ($template != 0) { 38 | $where[] = "NOT EXISTS (SELECT 1 FROM {$this->modx->getTableName(CollectionResourceTemplate::class)} r WHERE r.resource_template = modTemplate.id AND r.collection_template != " . $template . ")"; 39 | } 40 | 41 | if (!empty($query)) { 42 | $valuesqry = $this->getProperty('valuesqry'); 43 | 44 | if ($valuesqry == true) { 45 | $where['id:IN'] = Utils::explodeAndClean($query, '|'); 46 | } else { 47 | $where['templatename:LIKE'] = '%' . $query . '%'; 48 | 49 | } 50 | } 51 | 52 | $c->where($where); 53 | 54 | return $c; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/ChangeChildParent.php: -------------------------------------------------------------------------------- 1 | getProperty('parent', ''); 17 | 18 | if ($parent == '') { 19 | return $this->modx->lexicon('collections.err.parent_ns'); 20 | } 21 | 22 | return parent::beforeSet(); 23 | } 24 | 25 | public function beforeSave() 26 | { 27 | /** @var modResource $parent */ 28 | $parent = $this->modx->getObject(modResource::class, $this->object->parent); 29 | if ($parent && ($parent->class_key == CollectionContainer::class)) { 30 | $this->object->set('show_in_tree', 0); 31 | } else { 32 | $this->object->set('show_in_tree', 1); 33 | } 34 | 35 | return parent::beforeSave(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/ChangeParent.php: -------------------------------------------------------------------------------- 1 | getProperty('parent', ''); 18 | 19 | if ($parent == '') { 20 | return $this->modx->lexicon('collections.err.parent_ns'); 21 | } else { 22 | $parent = explode('_', $parent); 23 | 24 | /** @var modContext $ctx */ 25 | $ctx = $this->modx->getObject(modContext::class, $parent[0]); 26 | if ($ctx) { 27 | $this->setProperty('context_key', $ctx->key); 28 | $this->setProperty('parent', intval($parent[1])); 29 | } else { 30 | return $this->modx->lexicon('collections.err.common'); 31 | } 32 | } 33 | 34 | return parent::beforeSet(); 35 | } 36 | 37 | public function beforeSave() 38 | { 39 | /** @var modResource $parent */ 40 | $parent = $this->modx->getObject(modResource::class, $this->object->parent); 41 | if ($parent && ($parent->class_key == CollectionContainer::class)) { 42 | $this->object->set('show_in_tree', 0); 43 | } else { 44 | $this->object->set('show_in_tree', 1); 45 | } 46 | 47 | return parent::beforeSave(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/DDReorder.php: -------------------------------------------------------------------------------- 1 | getProperty('idItem'); 15 | $oldIndex = $this->getProperty('oldIndex'); 16 | $newIndex = $this->getProperty('newIndex'); 17 | $parent = $this->getProperty('parent'); 18 | 19 | if (intval($parent) == 0) { 20 | return $this->failure($this->modx->lexicon('collections.err.parent_ns')); 21 | } 22 | 23 | $items = $this->modx->newQuery($this->classKey); 24 | $items->where([ 25 | 'id:!=' => $idItem, 26 | 'parent' => $parent, 27 | 'menuindex:>=' => min($oldIndex, $newIndex), 28 | 'menuindex:<=' => max($oldIndex, $newIndex), 29 | ]); 30 | 31 | $items->sortby('menuindex', 'ASC'); 32 | 33 | $itemsCollection = $this->modx->getCollection($this->classKey, $items); 34 | $affectedResources = []; 35 | 36 | if (min($oldIndex, $newIndex) == $newIndex) { 37 | foreach ($itemsCollection as $item) { 38 | $itemObject = $this->modx->getObject($this->classKey, $item->get('id')); 39 | $itemObject->set('menuindex', $itemObject->get('menuindex') + 1); 40 | $itemObject->save(); 41 | 42 | $affectedResources[] = $itemObject; 43 | } 44 | } else { 45 | foreach ($itemsCollection as $item) { 46 | $itemObject = $this->modx->getObject($this->classKey, $item->get('id')); 47 | $itemObject->set('menuindex', $itemObject->get('menuindex') - 1); 48 | $itemObject->save(); 49 | 50 | $affectedResources[] = $itemObject; 51 | } 52 | } 53 | 54 | $itemObject = $this->modx->getObject($this->classKey, $idItem); 55 | $itemObject->set('menuindex', $newIndex); 56 | $itemObject->save(); 57 | 58 | $this->modx->invokeEvent('CollectionsOnResourceSort', [ 59 | 'resourceId' => $idItem, 60 | 'parentId' => $parent, 61 | 'oldIndex' => $oldIndex, 62 | 'newIndex' => $newIndex, 63 | 'affectedResources' => &$affectedResources, 64 | ]); 65 | 66 | return $this->success('', $itemObject); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/DeleteMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids', null); 14 | if (empty($ids)) { 15 | return $this->failure($this->modx->lexicon('collections.children.err_ns_multiple')); 16 | } 17 | $ids = is_array($ids) ? $ids : explode(',', $ids); 18 | 19 | $lastId = 0; 20 | 21 | foreach ($ids as $id) { 22 | if (empty($id)) continue; 23 | $lastId = $id; 24 | $this->modx->runProcessor('Collections\\Processors\\Resource\\Delete', [ 25 | 'id' => $id, 26 | 'skipClearCache' => true, 27 | ]); 28 | } 29 | 30 | /** @var modResource $res */ 31 | $res = $this->modx->getObject(modResource::class, $lastId); 32 | 33 | if ($res) { 34 | $this->clearCache($res->context_key); 35 | } 36 | 37 | return $this->success(); 38 | } 39 | 40 | public function clearCache($context) 41 | { 42 | $this->modx->cacheManager->refresh([ 43 | 'db' => [], 44 | 'auto_publish' => ['contexts' => [$context]], 45 | 'context_settings' => ['contexts' => [$context]], 46 | 'resource' => ['contexts' => [$context]], 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/PublishMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids', null); 14 | if (empty($ids)) { 15 | return $this->failure($this->modx->lexicon('collections.children.err_ns_multiple')); 16 | } 17 | $ids = is_array($ids) ? $ids : explode(',', $ids); 18 | 19 | $lastId = 0; 20 | 21 | foreach ($ids as $id) { 22 | if (empty($id)) continue; 23 | $lastId = $id; 24 | $this->modx->runProcessor('Collections\\Processors\\Resource\\Publish', [ 25 | 'id' => $id, 26 | 'skipClearCache' => true, 27 | ]); 28 | } 29 | 30 | /** @var modResource $res */ 31 | $res = $this->modx->getObject(modResource::class, $lastId); 32 | 33 | if ($res) { 34 | $this->clearCache($res->context_key); 35 | } 36 | 37 | return $this->success(); 38 | } 39 | 40 | public function clearCache($context) 41 | { 42 | $this->modx->cacheManager->refresh([ 43 | 'db' => [], 44 | 'auto_publish' => ['contexts' => [$context]], 45 | 'context_settings' => ['contexts' => [$context]], 46 | 'resource' => ['contexts' => [$context]], 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/UnDeleteMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids', null); 14 | if (empty($ids)) { 15 | return $this->failure($this->modx->lexicon('collections.children.err_ns_multiple')); 16 | } 17 | $ids = is_array($ids) ? $ids : explode(',', $ids); 18 | 19 | $lastId = 0; 20 | 21 | foreach ($ids as $id) { 22 | if (empty($id)) continue; 23 | $lastId = $id; 24 | $this->modx->runProcessor('Collections\\Processors\\Resource\\UndDelete', [ 25 | 'id' => $id, 26 | 'skipClearCache' => true, 27 | ]); 28 | } 29 | 30 | /** @var modResource $res */ 31 | $res = $this->modx->getObject(modResource::class, $lastId); 32 | 33 | if ($res) { 34 | $this->clearCache($res->context_key); 35 | } 36 | 37 | return $this->success(); 38 | } 39 | 40 | public function clearCache($context) 41 | { 42 | $this->modx->cacheManager->refresh([ 43 | 'db' => [], 44 | 'auto_publish' => ['contexts' => [$context]], 45 | 'context_settings' => ['contexts' => [$context]], 46 | 'resource' => ['contexts' => [$context]], 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Resource/UnPublishMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('ids', null); 14 | if (empty($ids)) { 15 | return $this->failure($this->modx->lexicon('collections.children.err_ns_multiple')); 16 | } 17 | $ids = is_array($ids) ? $ids : explode(',', $ids); 18 | 19 | $lastId = 0; 20 | 21 | foreach ($ids as $id) { 22 | if (empty($id)) continue; 23 | $lastId = $id; 24 | $this->modx->runProcessor('Collections\\Processors\\Resource\\UnPublish', [ 25 | 'id' => $id, 26 | 'skipClearCache' => true, 27 | ]); 28 | } 29 | 30 | /** @var modResource $res */ 31 | $res = $this->modx->getObject(modResource::class, $lastId); 32 | 33 | if ($res) { 34 | $this->clearCache($res->context_key); 35 | } 36 | 37 | return $this->success(); 38 | } 39 | 40 | public function clearCache($context) 41 | { 42 | $this->modx->cacheManager->refresh([ 43 | 'db' => [], 44 | 'auto_publish' => ['contexts' => [$context]], 45 | 'context_settings' => ['contexts' => [$context]], 46 | 'resource' => ['contexts' => [$context]], 47 | ]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Selection/Create.php: -------------------------------------------------------------------------------- 1 | getProperty('collection'); 23 | $resource = $this->getProperty('resource'); 24 | 25 | if (empty($collection) || empty($resource)) { 26 | if (empty($collection)) { 27 | $this->addFieldError('collection', $this->modx->lexicon('collections.err.selection_ns_collection')); 28 | } 29 | 30 | if (empty($resource)) { 31 | $this->addFieldError('resource', $this->modx->lexicon('collections.err.selection_ns_resource')); 32 | } 33 | } else { 34 | if ($this->doesAlreadyExist(['collections' => $collection, 'resource' => $resource])) { 35 | $this->addFieldError('resource', $this->modx->lexicon('collections.err.selection_ae_resource')); 36 | } 37 | } 38 | 39 | return parent::beforeSet(); 40 | } 41 | 42 | public function beforeSave() 43 | { 44 | $collection = $this->getProperty('collection'); 45 | $resource = $this->getProperty('resource'); 46 | 47 | $menuindex = $this->modx->newQuery(CollectionSelection::class); 48 | $menuindex->where(['collection' => $collection]); 49 | $menuindex->sortby('menuindex', 'DESC'); 50 | $menuindex->limit(1); 51 | $menuindex->select($this->modx->getSelectColumns(CollectionSelection::class, 'CollectionSelection', '', ['menuindex'])); 52 | 53 | $menuindex->prepare(); 54 | $menuindex->stmt->execute(); 55 | 56 | $lastMenuindex = (int)$menuindex->stmt->fetchColumn(0); 57 | 58 | $this->object->set('collection', $collection); 59 | $this->object->set('resource', $resource); 60 | $this->object->set('menuindex', $lastMenuindex + 1); 61 | 62 | return parent::beforeSave(); 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Selection/DDReorder.php: -------------------------------------------------------------------------------- 1 | getProperty('idItem'); 21 | $oldIndex = $this->getProperty('oldIndex'); 22 | $newIndex = $this->getProperty('newIndex'); 23 | $parent = $this->getProperty('parent'); 24 | 25 | if (intval($parent) == 0) { 26 | return $this->failure($this->modx->lexicon('collections.err.parent_ns')); 27 | } 28 | 29 | $items = $this->modx->newQuery($this->classKey); 30 | $items->where([ 31 | 'resource:!=' => $idItem, 32 | 'collection' => $parent, 33 | 'menuindex:>=' => min($oldIndex, $newIndex), 34 | 'menuindex:<=' => max($oldIndex, $newIndex), 35 | ]); 36 | 37 | $items->sortby('menuindex', 'ASC'); 38 | 39 | $itemsCollection = $this->modx->getCollection($this->classKey, $items); 40 | 41 | if (min($oldIndex, $newIndex) == $newIndex) { 42 | foreach ($itemsCollection as $item) { 43 | $itemObject = $this->modx->getObject($this->classKey, ['resource' => $item->get('resource'), 'collection' => $parent]); 44 | $itemObject->set('menuindex', $itemObject->get('menuindex') + 1); 45 | $itemObject->save(); 46 | } 47 | } else { 48 | foreach ($itemsCollection as $item) { 49 | $itemObject = $this->modx->getObject($this->classKey, ['resource' => $item->get('resource'), 'collection' => $parent]); 50 | $itemObject->set('menuindex', $itemObject->get('menuindex') - 1); 51 | $itemObject->save(); 52 | } 53 | } 54 | 55 | $itemObject = $this->modx->getObject($this->classKey, ['resource' => $idItem, 'collection' => $parent]); 56 | $itemObject->set('menuindex', $newIndex); 57 | $itemObject->save(); 58 | 59 | 60 | return $this->success('', $itemObject); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Selection/Remove.php: -------------------------------------------------------------------------------- 1 | getProperty('collection'); 22 | $resource = $this->getProperty('resource'); 23 | 24 | if (empty($collection) || empty($resource)) { 25 | return $this->modx->lexicon('collections.err.selection_res_col_ns'); 26 | } 27 | 28 | $this->object = $this->modx->getObject($this->classKey, ['collection' => $collection, 'resource' => $resource]); 29 | if (empty($this->object)) return $this->modx->lexicon($this->objectType . '_err_nfs', [$this->primaryKeyField => $collection . ',' . $resource]); 30 | 31 | if ($this->checkRemovePermission && $this->object instanceof modAccessibleObject && !$this->object->checkPolicy('remove')) { 32 | return $this->modx->lexicon('access_denied'); 33 | } 34 | 35 | return true; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Selection/RemoveMultiple.php: -------------------------------------------------------------------------------- 1 | getProperty('collection'); 21 | $resources = $this->getProperty('resources', ''); 22 | 23 | $resources = Utils::explodeAndClean($resources); 24 | 25 | if (empty($collection) || empty($resources)) { 26 | return $this->modx->lexicon('collections.err.selection_res_col_ns'); 27 | } 28 | 29 | $this->modx->removeCollection($this->classKey, ['resource:IN' => $resources, 'collection' => $collection]); 30 | 31 | return $this->success(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Column/DDReorder.php: -------------------------------------------------------------------------------- 1 | getProperty('idItem'); 16 | $oldIndex = $this->getProperty('oldIndex'); 17 | $newIndex = $this->getProperty('newIndex'); 18 | $template = $this->getProperty('template'); 19 | 20 | if (intval($template) == 0) { 21 | return $this->failure($this->modx->lexicon('collections.err.template_ns')); 22 | } 23 | 24 | $items = $this->modx->newQuery($this->classKey); 25 | $items->where([ 26 | 'id:!=' => $idItem, 27 | 'template' => $template, 28 | 'position:>=' => min($oldIndex, $newIndex), 29 | 'position:<=' => max($oldIndex, $newIndex), 30 | ]); 31 | 32 | $items->sortby('position', 'ASC'); 33 | 34 | $itemsColumn = $this->modx->getCollection($this->classKey, $items); 35 | 36 | if (min($oldIndex, $newIndex) == $newIndex) { 37 | foreach ($itemsColumn as $item) { 38 | $itemObject = $this->modx->getObject($this->classKey, $item->get('id')); 39 | $itemObject->set('position', $itemObject->get('position') + 1); 40 | $itemObject->save(); 41 | } 42 | } else { 43 | foreach ($itemsColumn as $item) { 44 | $itemObject = $this->modx->getObject($this->classKey, $item->get('id')); 45 | $itemObject->set('position', $itemObject->get('position') - 1); 46 | $itemObject->save(); 47 | } 48 | } 49 | 50 | $itemObject = $this->modx->getObject($this->classKey, $idItem); 51 | $itemObject->set('position', $newIndex); 52 | $itemObject->save(); 53 | 54 | 55 | return $this->success('', $itemObject); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Column/GetList.php: -------------------------------------------------------------------------------- 1 | getProperty('template'); 19 | 20 | if (!empty($template)) { 21 | $c->where([ 22 | 'template' => $template 23 | ]); 24 | } 25 | 26 | return $c; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Column/Remove.php: -------------------------------------------------------------------------------- 1 | object->name == 'id') { 17 | return $this->modx->lexicon('collections.err.cant_remove_id_column'); 18 | } 19 | 20 | return parent::beforeRemove(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Column/UpdateFromGrid.php: -------------------------------------------------------------------------------- 1 | getProperty('data'); 9 | if (empty($data)) return $this->modx->lexicon('invalid_data'); 10 | $data = json_decode($data, true); 11 | if (empty($data)) return $this->modx->lexicon('invalid_data'); 12 | $this->setProperties($data); 13 | $this->unsetProperty('data'); 14 | 15 | return parent::initialize(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Duplicate.php: -------------------------------------------------------------------------------- 1 | getProperty('id', false); 22 | if (empty($id)) return $this->modx->lexicon('collections.err.template_ns'); 23 | 24 | $this->oldTemplate = $this->modx->getObject($this->classKey, $id); 25 | if (empty($this->oldTemplate)) return $this->modx->lexicon('collections.err.template_ns'); 26 | 27 | return true; 28 | } 29 | 30 | public function process() 31 | { 32 | $canDuplicate = $this->beforeDuplicate(); 33 | if ($canDuplicate !== true) { 34 | return $this->failure($canDuplicate); 35 | } 36 | 37 | $oldValues = $this->oldTemplate->toArray(); 38 | 39 | /** @var CollectionTemplateColumn[] $oldColumns */ 40 | $oldColumns = $this->oldTemplate->Columns; 41 | 42 | $oldValues['name'] = $this->getProperty('name'); 43 | $oldValues['description'] = $this->getProperty('description'); 44 | $oldValues['global_template'] = false; 45 | 46 | /** @var CollectionTemplate $newTemplate */ 47 | $newTemplate = $this->modx->newObject($this->classKey); 48 | $newTemplate->fromArray($oldValues); 49 | 50 | $newColumns = []; 51 | 52 | foreach ($oldColumns as $column) { 53 | $columnArray = $column->toArray(); 54 | unset($columnArray['template']); 55 | 56 | /** @var CollectionTemplateColumn $newColumn */ 57 | $newColumn = $this->modx->newObject(CollectionTemplateColumn::class); 58 | $newColumn->fromArray($columnArray); 59 | 60 | $newColumns[] = $newColumn; 61 | } 62 | 63 | $newTemplate->addMany($newColumns, 'Columns'); 64 | 65 | $newTemplate->save(); 66 | 67 | return $this->success('', ['id' => $newTemplate->get('id')]); 68 | } 69 | 70 | public function beforeDuplicate() 71 | { 72 | $name = $this->getProperty('name', ''); 73 | 74 | if (empty($name)) { 75 | $this->addFieldError('name', $this->modx->lexicon('collections.err.template_ns_name')); 76 | } else { 77 | if ($this->modx->getCount($this->classKey, ['name' => $name]) > 0) { 78 | $this->addFieldError('name', $this->modx->lexicon('collections.err.template_ae_name')); 79 | } 80 | } 81 | 82 | return !$this->hasErrors(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Get.php: -------------------------------------------------------------------------------- 1 | modx->newQuery(CollectionResourceTemplate::class); 17 | $c->where(array( 18 | 'collection_template' => $this->object->id 19 | )); 20 | $c->select($this->modx->getSelectColumns(CollectionResourceTemplate::class, 'CollectionResourceTemplate', '', array('resource_template'))); 21 | 22 | $c->prepare(); 23 | $c->stmt->execute(); 24 | $templates = $c->stmt->fetchAll(\PDO::FETCH_COLUMN, 0); 25 | 26 | $this->object->set('templates[]', $templates); 27 | 28 | return true; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/GetList.php: -------------------------------------------------------------------------------- 1 | getProperty('addEmpty', false); 20 | 21 | if ($addEmpty) { 22 | $list[] = [ 23 | 'id' => 0, 24 | 'name' => $this->modx->lexicon('collections.template.empty'), 25 | ]; 26 | } 27 | return $list; 28 | } 29 | 30 | public function prepareRow(xPDOObject $object) 31 | { 32 | $template = $object->toArray(); 33 | 34 | $c = $this->modx->newQuery(CollectionResourceTemplate::class); 35 | $c->leftJoin(modTemplate::class, 'ResourceTemplate'); 36 | $c->where([ 37 | 'collection_template' => $template['id'] 38 | ]); 39 | $c->select([ 40 | 'test' => 'IF(resource_template = 0, \'' . $this->modx->lexicon('template_empty') . '\', ResourceTemplate.templatename)' 41 | ]); 42 | $c->prepare(); 43 | $c->stmt->execute(); 44 | 45 | $template['default_for_templates'] = $c->stmt->fetchAll(\PDO::FETCH_COLUMN, 0); 46 | 47 | return $template; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Import.php: -------------------------------------------------------------------------------- 1 | failure(); 15 | } 16 | 17 | $templates = file_get_contents($_FILES['file']['tmp_name']); 18 | 19 | $templates = json_decode($templates, true); 20 | 21 | if (!is_array($templates)) { 22 | return $this->failure(); 23 | } 24 | 25 | $importTemplates = $this->getProperty('template', null); 26 | 27 | foreach ($templates as $template) { 28 | if (!in_array($template['name'], $importTemplates) || $importTemplates === null) continue; 29 | 30 | $exists = $this->modx->getCount(CollectionTemplate::class, ['name' => $template['name']]); 31 | if ($exists > 0) { 32 | $newName = $template['name'] . ' Imported'; 33 | $exists = $this->modx->getCount(CollectionTemplate::class, ['name' => $newName]); 34 | 35 | $i = 0; 36 | $testName = $newName; 37 | while ($exists > 0) { 38 | $testName = $newName . $i; 39 | $exists = $this->modx->getCount(CollectionTemplate::class, ['name' => $testName]); 40 | $i++; 41 | } 42 | 43 | $template['name'] = $testName; 44 | } 45 | 46 | /** @var CollectionTemplate $tplObject */ 47 | $tplObject = $this->modx->newObject(CollectionTemplate::class); 48 | $tplObject->fromArray($template); 49 | 50 | $columns = array(); 51 | foreach ($template['columns'] as $column) { 52 | /** @var CollectionTemplateColumn $colObject */ 53 | $colObject = $this->modx->newObject(CollectionTemplateColumn::class); 54 | $colObject->fromArray($column); 55 | 56 | $columns[] = $colObject; 57 | } 58 | 59 | $tplObject->addMany($columns, 'Columns'); 60 | $tplObject->save(); 61 | } 62 | 63 | return $this->success(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/Remove.php: -------------------------------------------------------------------------------- 1 | modx->getCount($this->classKey); 17 | if ($templatesCount == 1) { 18 | return $this->modx->lexicon('collections.err.template_remove_last'); 19 | } 20 | 21 | if ($this->object->global_template == 1) { 22 | return $this->modx->lexicon('collections.err.template_remove_global'); 23 | } 24 | 25 | return parent::beforeRemove(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/components/collections/src/Processors/Template/UpdateFromGrid.php: -------------------------------------------------------------------------------- 1 | getProperty('data'); 18 | if (empty($data)) return $this->modx->lexicon('invalid_data'); 19 | $data = json_decode($data, true); 20 | if (empty($data)) return $this->modx->lexicon('invalid_data'); 21 | 22 | if (isset($data['id'])) { 23 | $this->setProperty('id', $data['id']); 24 | } 25 | 26 | if (isset($data['name'])) { 27 | $this->setProperty('name', $data['name']); 28 | } 29 | 30 | if (isset($data['description'])) { 31 | $this->setProperty('description', $data['description']); 32 | } 33 | 34 | if (isset($data['global_template'])) { 35 | $this->setProperty('global_template', $data['global_template']); 36 | } 37 | 38 | $this->unsetProperty('data'); 39 | 40 | return parent::initialize(); 41 | } 42 | 43 | public function beforeSet() 44 | { 45 | $name = $this->getProperty('name'); 46 | 47 | if (empty($name)) { 48 | $this->addFieldError('name', $this->modx->lexicon('collections.err.template_ns_name')); 49 | } else { 50 | if ($this->modx->getCount($this->classKey, ['name' => $name, 'id:!=' => $this->object->id]) > 0) { 51 | $this->addFieldError('name', $this->modx->lexicon('collections.err.template_ae_name')); 52 | } 53 | } 54 | 55 | $global = $this->getProperty('global_template'); 56 | 57 | if ($global == false) { 58 | $templatesCount = $this->modx->getCount(CollectionTemplate::class, ['global_template' => 1, 'id:!=' => $this->object->id]); 59 | if ($templatesCount == 0) { 60 | $this->setProperty('global_template', true); 61 | } 62 | } 63 | 64 | return parent::beforeSet(); 65 | } 66 | 67 | public function afterSave() 68 | { 69 | $global = $this->getProperty('global_template'); 70 | 71 | if ($global == true) { 72 | $this->modx->updateCollection(CollectionTemplate::class, ['global_template' => false], ['id:!=' => $this->object->id]); 73 | } 74 | 75 | return parent::afterSave(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /core/components/collections/src/Utils.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/components/collections/templates/template/template.tpl: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /core/components/collections/lexicon/en/*.inc.php 3 | translation: >- 4 | /core/components/collections/lexicon/%two_letters_code%/%original_file_name% 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "collections", 3 | "version": "3.7.0", 4 | "description": "", 5 | "main": "./_build/assets/index.js", 6 | "scripts": { 7 | "build": "webpack --progress --mode production", 8 | "dev": "webpack --watch --progress --mode development" 9 | }, 10 | "license": "MIT", 11 | "files": [ 12 | "LICENSE", 13 | "README.md", 14 | "index.js", 15 | "lib", 16 | "dist" 17 | ], 18 | "devDependencies": { 19 | "@babel/core": "^7.24.9", 20 | "@babel/plugin-transform-class-properties": "^7.24.7", 21 | "@babel/preset-env": "^7.24.8", 22 | "autoprefixer": "^10.4.17", 23 | "babel-loader": "^9.1.3", 24 | "css-loader": "^6.9.1", 25 | "cssnano": "^6.0.3", 26 | "mini-css-extract-plugin": "^2.8.1", 27 | "path-browserify": "^1.0.1", 28 | "postcss": "^8.4.38", 29 | "postcss-inline-svg": "^6.0.0", 30 | "postcss-loader": "^7.3.4", 31 | "postcss-svgo": "^6.0.3", 32 | "sass": "^1.70.0", 33 | "sass-loader": "^13.3.3", 34 | "ts-loader": "^9.5.1", 35 | "typescript": "^5.4.4", 36 | "url": "^0.11.3", 37 | "webpack": "^5.90.0", 38 | "webpack-cli": "^5.1.4" 39 | }, 40 | "library": { 41 | "name": "FredCollections", 42 | "entry": "index.js", 43 | "dist-web": "fred_integration.js" 44 | }, 45 | "dependencies": { 46 | "@types/tabulator-tables": "^5.6.0", 47 | "babel-plugin-add-module-exports": "^1.0.4", 48 | "tabulator-tables": "^5.6.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-inline-svg')({ 4 | paths: ['./_build/assets/images'] 5 | }), 6 | require('postcss-svgo'), 7 | require('autoprefixer'), 8 | require('cssnano'), 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./_build/assets/js", 4 | "paths": { 5 | }, 6 | "outDir": "./assets/components/collections/web", 7 | "noImplicitAny": false, 8 | "moduleResolution": "node", 9 | "module": "es6", 10 | "target": "es6", 11 | "allowJs": true, 12 | "allowSyntheticDefaultImports": true 13 | }, 14 | "include": [ 15 | "_build/assets/js/**/*" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | 4 | module.exports = (env, options) => { 5 | const isProd = options.mode === 'production'; 6 | 7 | return { 8 | mode: options.mode, 9 | devtool: isProd ? false : 'source-map', 10 | 11 | entry: [ 12 | './_build/assets/sass/collections.scss', 13 | './_build/assets/js/index.ts' 14 | ], 15 | 16 | output: { 17 | path: path.resolve(__dirname, './assets/components/collections/web'), 18 | library: 'FredCollections', 19 | libraryTarget: 'umd', 20 | libraryExport: 'default', 21 | filename: 'fred_integration.js' 22 | }, 23 | 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.ts$/, 28 | use: 'ts-loader', 29 | exclude: /node_modules/, 30 | }, 31 | { 32 | test: /\.js$/, 33 | exclude: /(node_modules)/, 34 | use: { 35 | loader: 'babel-loader' 36 | } 37 | }, 38 | { 39 | test: /\.(sa|sc|c)ss$/, 40 | use: [ 41 | { 42 | loader: MiniCssExtractPlugin.loader 43 | }, 44 | { 45 | loader: "css-loader", 46 | options: { 47 | url: false, 48 | sourceMap: true 49 | } 50 | }, 51 | { 52 | loader: "postcss-loader" 53 | }, 54 | { 55 | loader: "sass-loader", 56 | options: { 57 | implementation: require("sass") 58 | } 59 | } 60 | ] 61 | } 62 | ] 63 | }, 64 | 65 | resolve: { 66 | alias: { 67 | }, 68 | extensions: [ '.ts', '.js' ], 69 | fallback: { 70 | "path": require.resolve("path-browserify"), 71 | "url": require.resolve("url/"), 72 | } 73 | }, 74 | 75 | plugins: [ 76 | new MiniCssExtractPlugin({ 77 | filename: "fred_integration.css" 78 | }) 79 | ] 80 | }; 81 | }; 82 | --------------------------------------------------------------------------------