├── logs └── .gitkeep ├── tests ├── Fixture │ └── empty ├── files │ ├── empty.csv │ ├── input.txt │ ├── projects │ │ ├── undefined.php │ │ └── test.php │ ├── test.png │ ├── test2.png │ ├── import.csv │ └── manifest.json ├── Utils │ ├── MyDummyImportFilter.php │ ├── ImportFilterSampleError.php │ ├── ImportFilterSample.php │ ├── DummyImportFilter.php │ └── ModulesControllerSample.php ├── TestCase │ ├── Controller │ │ ├── CustomBulkAction.php │ │ ├── CourtesyPageControllerTest.php │ │ ├── Admin │ │ │ └── CacheControllerTest.php │ │ ├── ErrorControllerTest.php │ │ └── MultiuploadControllerTest.php │ ├── Utility │ │ ├── ApiClientTraitTest.php │ │ └── RelationsToolsTest.php │ ├── PluginTest.php │ ├── View │ │ └── Helper │ │ │ └── DatesHelperTest.php │ └── Core │ │ └── I18n │ │ └── DummyTranslator.php ├── .env.example └── bootstrap.php ├── config ├── projects │ └── .gitkeep ├── version.ini ├── bedita-api-version.ini ├── relations.php ├── schema │ ├── i18n.sql │ └── sessions.sql ├── requirements.php └── oembed.php ├── webroot ├── bundle-report │ └── .gitkeep ├── robots.txt ├── js │ └── libs │ │ ├── README │ │ └── timezone.js ├── favicon.ico ├── favicon.png ├── fonts │ ├── be-icons.eot │ ├── be-icons.ttf │ ├── be-icons.woff │ └── be-icons.woff2 ├── img │ └── iconLocked.png ├── .htaccess ├── svg │ ├── iconLocked.svg │ ├── iconDraft.svg │ ├── iconFuture.svg │ ├── iconExpired.svg │ ├── concurrent-editors.svg │ └── iconFloppy.svg └── index.php ├── templates ├── Layout │ ├── ajax.twig │ ├── rss │ │ └── default.ctp │ └── Email │ │ ├── text │ │ └── default.ctp │ │ └── html │ │ └── default.ctp ├── CourtesyPage │ └── index.twig ├── Element │ ├── Dashboard │ │ └── messages.twig │ ├── flash │ │ ├── info.twig │ │ ├── error.twig │ │ ├── success.twig │ │ ├── warning.twig │ │ └── flash.twig │ ├── Form │ │ ├── empty.twig │ │ ├── locations.twig │ │ ├── dropupload.twig │ │ ├── publish_properties.scss │ │ ├── categories.scss │ │ ├── custom_left.twig │ │ ├── custom_right.twig │ │ ├── title.twig │ │ ├── calendar.scss │ │ ├── core_properties.twig │ │ ├── publish_properties.twig │ │ ├── advanced_properties.twig │ │ ├── fast_create.twig │ │ ├── bulk_trash.twig │ │ ├── bulk_category.twig │ │ ├── permissions.twig │ │ ├── multiupload.twig │ │ ├── history.twig │ │ ├── tags.twig │ │ ├── group_properties.twig │ │ ├── meta.twig │ │ ├── other_properties.twig │ │ ├── resource_relations.twig │ │ ├── categories.twig │ │ ├── calendar.twig │ │ ├── form_file_upload.twig │ │ ├── history.scss │ │ ├── bulk_custom.twig │ │ ├── captions.twig │ │ ├── bulk_position.twig │ │ └── map.twig │ ├── Modules │ │ ├── index_properties_date_ranges.twig │ │ ├── tree.twig │ │ ├── list.twig │ │ ├── index_properties.scss │ │ ├── index_bulk.twig │ │ └── index_header.twig │ ├── json_meta_config.twig │ ├── Menu │ │ ├── colophon.scss │ │ └── colophon.twig │ ├── FilterBox │ │ └── filter_box.twig │ ├── Panel │ │ ├── panel.twig │ │ └── panel.scss │ ├── Model │ │ ├── relation_types.twig │ │ └── sidebar_links.twig │ ├── Admin │ │ └── sidebar.twig │ └── custom_colors.twig ├── Pages │ ├── Translations │ │ ├── add.twig │ │ ├── edit.twig │ │ └── view.scss │ ├── Multiupload │ │ └── index.twig │ ├── Admin │ │ ├── AsyncJobs │ │ │ └── index.twig │ │ ├── Config │ │ │ └── index.twig │ │ ├── Endpoints │ │ │ └── index.twig │ │ ├── Roles │ │ │ └── index.twig │ │ ├── Applications │ │ │ └── index.twig │ │ ├── AuthProviders │ │ │ └── index.twig │ │ ├── ExternalAuth │ │ │ └── index.twig │ │ ├── EndpointPermissions │ │ │ └── index.twig │ │ ├── UserAccesses │ │ │ └── index.twig │ │ ├── ObjectsHistory │ │ │ └── index.twig │ │ ├── Statistics │ │ │ └── index.twig │ │ ├── Appearance │ │ │ └── index.twig │ │ ├── SystemInfo │ │ │ └── index.twig │ │ └── _admin.scss │ ├── Model │ │ ├── ObjectTypes │ │ │ └── index.twig │ │ ├── Categories │ │ │ └── index.twig │ │ ├── Tags │ │ │ └── index.twig │ │ └── _model-view.scss │ ├── Password │ │ ├── request_sent.twig │ │ └── change.twig │ ├── Modules │ │ ├── setup.twig │ │ └── index.twig │ ├── Categories │ │ └── index.twig │ └── Import │ │ ├── index.twig │ │ └── import.scss ├── Email │ ├── text │ │ └── default.ctp │ └── html │ │ └── default.ctp └── Error │ ├── error400.twig │ └── error500.twig ├── phpstan.neon.dist ├── .htaccess ├── .dockerignore ├── COPYING ├── resources ├── styles │ ├── _mixins.scss │ ├── _non-production.scss │ └── _base.scss └── js │ ├── config │ └── locales.js │ ├── app │ ├── pages │ │ ├── trash │ │ │ ├── view.js │ │ │ └── index.js │ │ ├── admin │ │ │ └── index.js │ │ └── dashboard │ │ │ └── index.js │ ├── components │ │ ├── tag-form │ │ │ └── tag-form.scss │ │ ├── event-bus.vue │ │ ├── charts │ │ │ └── bar-chart.vue │ │ ├── form │ │ │ ├── field-title.vue │ │ │ ├── field-string.vue │ │ │ ├── field-plaintext.vue │ │ │ ├── field-integer.vue │ │ │ ├── field-number.vue │ │ │ ├── field-checkbox.vue │ │ │ ├── field-date.vue │ │ │ ├── field-json.vue │ │ │ ├── field-select.vue │ │ │ ├── field-radio.vue │ │ │ └── field-textarea.vue │ │ ├── horizontal-tab-view.js │ │ ├── email-input.js │ │ ├── thumbnail │ │ │ └── thumbnail.vue │ │ ├── autosize-textarea.js │ │ ├── clipboard-item │ │ │ └── clipboard-item.vue │ │ ├── object-types-list │ │ │ └── object-types-list.js │ │ ├── permission │ │ │ └── permission.vue │ │ ├── form-file-upload.js │ │ ├── secret │ │ │ └── secret.js │ │ ├── json-editor │ │ │ └── json-editor.vue │ │ ├── menu.js │ │ ├── staggered-list.js │ │ ├── object-nav │ │ │ └── object-nav.vue │ │ └── show-hide │ │ │ └── show-hide.vue │ ├── locales.js │ ├── directives │ │ ├── uri.js │ │ ├── email.js │ │ └── jsoneditor.js │ ├── helpers │ │ └── text-helper.js │ └── mixins │ │ └── fetch.js │ └── libs │ ├── filters.js │ ├── bedita.js │ └── urlUtils.js ├── webpack-gettext-loader.js ├── .scrutinizer.yml ├── bin ├── cake.php ├── cake.bat ├── perms.sh └── bash_completion.sh ├── .editorconfig ├── phpcs.xml.dist ├── src ├── View │ ├── Helper │ │ ├── DatesHelper.php │ │ └── EditorsHelper.php │ └── AjaxView.php ├── Controller │ ├── Admin │ │ ├── UserAccessesController.php │ │ ├── ObjectsHistoryController.php │ │ ├── EndpointsController.php │ │ ├── CacheController.php │ │ ├── ApplicationsController.php │ │ ├── AsyncJobsController.php │ │ └── AuthProvidersController.php │ ├── CourtesyPageController.php │ ├── Component │ │ └── ParentsComponent.php │ ├── Model │ │ └── ExportController.php │ └── MultiuploadController.php ├── Form │ ├── CustomHandlerInterface.php │ └── Form.php ├── Core │ ├── Bulk │ │ └── CustomBulkActionInterface.php │ └── Result │ │ └── ImportResult.php ├── Utility │ ├── System.php │ ├── Schema.php │ ├── ApiClientTrait.php │ ├── Translate.php │ └── RelationsTools.php ├── Plugin.php └── Middleware │ └── ConfigurationMiddleware.php ├── .github ├── dependabot.yml └── workflows │ ├── javascript.yml │ └── php.yml ├── index.php ├── psalm.xml ├── phpunit.xml.dist ├── .gitignore └── Dockerfile /logs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/Fixture/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/files/empty.csv: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/projects/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webroot/bundle-report/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/files/input.txt: -------------------------------------------------------------------------------- 1 | Some text in here 2 | -------------------------------------------------------------------------------- /webroot/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /config/version.ini: -------------------------------------------------------------------------------- 1 | [Manager] 2 | version=5.17.2 3 | -------------------------------------------------------------------------------- /templates/Layout/ajax.twig: -------------------------------------------------------------------------------- 1 | {{ fetch('content') }} 2 | -------------------------------------------------------------------------------- /templates/CourtesyPage/index.twig: -------------------------------------------------------------------------------- 1 |

{{ message }}

2 | -------------------------------------------------------------------------------- /config/bedita-api-version.ini: -------------------------------------------------------------------------------- 1 | [BEditaAPI] 2 | versions[]=5.36.0 3 | -------------------------------------------------------------------------------- /templates/Element/Dashboard/messages.twig: -------------------------------------------------------------------------------- 1 | {{ Flash.render()|raw }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Translations/add.twig: -------------------------------------------------------------------------------- 1 | {{ element('translation') }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Translations/edit.twig: -------------------------------------------------------------------------------- 1 | {{ element('translation') }} 2 | -------------------------------------------------------------------------------- /webroot/js/libs/README: -------------------------------------------------------------------------------- 1 | This folder is not deleted by webpack build 2 | -------------------------------------------------------------------------------- /templates/Pages/Multiupload/index.twig: -------------------------------------------------------------------------------- 1 | {{ element(Element.multiupload()) }} 2 | -------------------------------------------------------------------------------- /tests/files/projects/undefined.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'name' => 'Test', 5 | ], 6 | ]; 7 | -------------------------------------------------------------------------------- /templates/Element/flash/error.twig: -------------------------------------------------------------------------------- 1 | {{ element('flash/flash', {'level': 'error', 'message': message, 'params': params}) }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Admin/Applications/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/index_content', { 'title': __(resourceType|humanize) }) }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Admin/AuthProviders/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/index_content', { 'title': __(resourceType|humanize) }) }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Admin/ExternalAuth/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/index_content', { 'title': __(resourceType|humanize) }) }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Model/ObjectTypes/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Model/index_content', { 'title': __(resourceType|humanize) }) }} 2 | -------------------------------------------------------------------------------- /templates/Element/Form/empty.twig: -------------------------------------------------------------------------------- 1 | {# Empty element used to hide property groups or relations via `Properties` configuraion #} 2 | -------------------------------------------------------------------------------- /templates/Element/flash/success.twig: -------------------------------------------------------------------------------- 1 | {{ element('flash/flash', {'level': 'success', 'message': message, 'params': params}) }} 2 | -------------------------------------------------------------------------------- /templates/Element/flash/warning.twig: -------------------------------------------------------------------------------- 1 | {{ element('flash/flash', {'level': 'warning', 'message': message, 'params': params}) }} 2 | -------------------------------------------------------------------------------- /templates/Pages/Admin/EndpointPermissions/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/index_content', { 'title': __(resourceType|humanize) }) }} 2 | -------------------------------------------------------------------------------- /tests/files/import.csv: -------------------------------------------------------------------------------- 1 | Column 1,Column 2, Column 3 2 | Name,Surname, "123,23" 3 | Another,Value, "35,99" 4 | Bad Line Ignored 5 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | bootstrapFiles: 3 | - tests/bootstrap.php 4 | paths: 5 | - src 6 | - tests 7 | level: 5 8 | -------------------------------------------------------------------------------- /templates/Element/Modules/index_properties_date_ranges.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /webroot/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine On 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteRule ^ index.php [L] 5 | 6 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | RewriteEngine on 3 | RewriteRule ^$ webroot/ [L] 4 | RewriteRule (.*) webroot/$1 [L] 5 | 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .github 3 | .phpunit.result.cache 4 | .vscode 5 | coverage 6 | node_modules 7 | plugins 8 | config/app_local.php 9 | config/projects 10 | config/.env 11 | -------------------------------------------------------------------------------- /templates/Pages/Password/request_sent.twig: -------------------------------------------------------------------------------- 1 |
2 |

{{ __('Password reset request sent') }}

3 |

{{ __("Please check your mailbox and follow the instructions") }}

4 |
5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The license text can be found in LICENSE.LGPL 2 | 3 | This license applies only to BEdita, third party libraries/components 4 | have their own licenses: they can be found inside cake/ and vendors/ -------------------------------------------------------------------------------- /templates/Pages/Translations/view.scss: -------------------------------------------------------------------------------- 1 | .lang-header { 2 | grid-column: span 3; 3 | gap: 12px; 4 | align-items: flex-end; 5 | } 6 | 7 | .module-form { 8 | margin-bottom: $gutter; 9 | } 10 | -------------------------------------------------------------------------------- /resources/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin hide-text { 2 | border: 0; 3 | // overflow: hidden; 4 | font: 0/0 a; 5 | white-space: nowrap; 6 | text-indent: 100%; 7 | color: transparent; 8 | text-shadow: none; 9 | } 10 | -------------------------------------------------------------------------------- /resources/js/config/locales.js: -------------------------------------------------------------------------------- 1 | 2 | // define locales available to webpack check config/app.php 3 | // TO-DO dynamic 4 | const locales = [ 5 | 'en', 6 | 'it', 7 | ]; 8 | 9 | module.exports = { 10 | locales: locales, 11 | } 12 | -------------------------------------------------------------------------------- /templates/Element/json_meta_config.twig: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /templates/Pages/Admin/UserAccesses/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/sidebar') }} 2 | 3 | {% do _view.assign('title', __('Administration') ~ ' ' ~ __('User Accesses')) %} 4 | {% do _view.assign('bodyViewClass', 'view-module view-admin') %} 5 | 6 | 7 | -------------------------------------------------------------------------------- /templates/Pages/Admin/ObjectsHistory/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/sidebar') }} 2 | 3 | {% do _view.assign('title', __('Administration') ~ ' ' ~ __('Objects History')) %} 4 | {% do _view.assign('bodyViewClass', 'view-module view-admin') %} 5 | 6 | 7 | -------------------------------------------------------------------------------- /webpack-gettext-loader.js: -------------------------------------------------------------------------------- 1 | const po = require('gettext-parser').po; 2 | 3 | /** 4 | * @see https://github.com/cah4a/po-gettext-loader 5 | * Like the project above, but with updated dependencies. 6 | */ 7 | module.exports = function(source) { 8 | this.cacheable(); 9 | return JSON.stringify(po.parse(source, 'utf-8')); 10 | }; 11 | -------------------------------------------------------------------------------- /templates/Element/Modules/tree.twig: -------------------------------------------------------------------------------- 1 | {% set hasPermissions = 'Permissions' in schema.associations %} 2 | 3 | 4 | -------------------------------------------------------------------------------- /tests/Utils/MyDummyImportFilter.php: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /templates/Layout/rss/default.ctp: -------------------------------------------------------------------------------- 1 | fetch('title'); 10 | endif; 11 | 12 | echo $this->Rss->document( 13 | $this->Rss->channel([], $channel, $this->fetch('content')) 14 | ); 15 | -------------------------------------------------------------------------------- /resources/js/app/pages/trash/view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Templates that uses this component (directly or indirectly) 3 | * Template/Trash/view.twig 4 | * 5 | * component used for TrashPage -> View 6 | * 7 | * @extends ModulesView 8 | */ 9 | 10 | import ModulesView from 'app/pages/modules/view'; 11 | 12 | export default { 13 | extends: ModulesView, 14 | } 15 | -------------------------------------------------------------------------------- /resources/js/app/components/tag-form/tag-form.scss: -------------------------------------------------------------------------------- 1 | .tags-container { 2 | margin-bottom: 2rem; 3 | .tags { 4 | .list-objects { 5 | .disabled { 6 | color: gray; 7 | } 8 | 9 | input[type=checkbox]:disabled { 10 | filter: invert(1) brightness(3); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: true 3 | filter: 4 | paths: 5 | - 'src/*' 6 | dependency_paths: 7 | - 'vendor/*' 8 | 9 | build: 10 | image: default-jammy 11 | environment: 12 | node: v22 13 | nodes: 14 | analysis: 15 | environment: 16 | php: 17 | version: 8.3.3 18 | tests: 19 | override: 20 | - php-scrutinizer-run 21 | -------------------------------------------------------------------------------- /tests/TestCase/Controller/CustomBulkAction.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /templates/Element/Form/locations.twig: -------------------------------------------------------------------------------- 1 | {# Locations: show add relation form if the relation is set #} 2 | 8 | {% do Form.unlockField('relations.' ~ relationName ~ '.replaceRelated') %} 9 | -------------------------------------------------------------------------------- /templates/Element/Form/dropupload.twig: -------------------------------------------------------------------------------- 1 | {# :accepted-drop="[`.from-relation-${relationName}`,isRelationWithMedia && 'from-files']"> #} 2 | {% set ot = objectTypes|length == 1 ? objectTypes[0] : 'media' %} 3 | 9 | 10 | -------------------------------------------------------------------------------- /templates/Element/Menu/colophon.scss: -------------------------------------------------------------------------------- 1 | .menu-colophon { 2 | padding: $gutter * 1.5 0 0; 3 | border-top: 1px solid #495057; 4 | color: $gray-500; 5 | font-size: 12px; 6 | font-weight: 100; 7 | 8 | @media (min-width: 568px) { 9 | padding: 0; 10 | border: none; 11 | } 12 | 13 | span { 14 | display: block; 15 | } 16 | span.error { 17 | color: red; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bin/cake.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | run($argv)); 13 | -------------------------------------------------------------------------------- /resources/js/app/locales.js: -------------------------------------------------------------------------------- 1 | import { addLocale, useLocale } from 'ttag'; 2 | 3 | /** 4 | * Setup locale using locale parameter 5 | */ 6 | export default function setupLocale(locale) { 7 | if (locale) { 8 | // Locale is the webpack alias that points to locales path 9 | const translationObj = require(`Locale/${locale}/default.po`); 10 | addLocale(locale, translationObj); 11 | useLocale(locale); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /templates/Element/Form/publish_properties.scss: -------------------------------------------------------------------------------- 1 | // TODO: refactor 2 | .fieldset#publish-properties { 3 | .tab-container { 4 | display: flex; 5 | flex-wrap: wrap; 6 | 7 | > div { 8 | width: calc(50% - 1em); 9 | 10 | &:first-child { width: 100%; } 11 | 12 | &.input.status { 13 | width: 100%; 14 | margin-bottom: 0; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/files/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "app.css": "/css/app.css", 3 | "app.js": "/js/app.bundle.a34d91.js", 4 | "manifest.js": "/js/manifest.bundle.61b75d.js", 5 | "menu.js": "/js/menu.bundle.8efe63.js", 6 | "category.js": "/js/category.bundle.fb6024.js", 7 | "index-cell.js": "/js/index-cell.bundle.62f0cc.js", 8 | "vendors.css": "/css/vendors.css", 9 | "vendors.js": "/js/vendors.bundle.a0cb2d.js", 10 | "css/richeditor.lazy.scss": "/css/richeditor.lazy.css" 11 | } 12 | -------------------------------------------------------------------------------- /resources/js/app/components/event-bus.vue: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /webroot/svg/iconLocked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; This file is for unifying the coding style for different editors and IDEs. 2 | ; More information at http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.bat] 14 | end_of_line = crlf 15 | 16 | [*.yml] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [Vagrantfile] 21 | indent_style = space 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /templates/Element/FilterBox/filter_box.twig: -------------------------------------------------------------------------------- 1 |
2 | {% if not hideFilter %} 3 |
4 | {{ element('FilterBox/filter_box_common', { showFilterSearchByType, hideFilterRelations }) }} 5 |
6 | {% endif %} 7 | 8 | 15 |
16 | -------------------------------------------------------------------------------- /tests/.env.example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Used as a default to seed `tests/.env` which 3 | # enables you to use environment variables to configure 4 | # the test environment. 5 | # 6 | # To use this file, first copy it into `tests/.env`. 7 | 8 | # Uncomment these to define BEDITA API base URL and API KEY 9 | export BEDITA_API="https://bedita-api-url" 10 | export BEDITA_API_KEY="bedita-api-key" 11 | 12 | # Set admin credentials 13 | export BEDITA_ADMIN_USR="admin" 14 | export BEDITA_ADMIN_PWD="admin" 15 | -------------------------------------------------------------------------------- /templates/Element/Panel/panel.twig: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | {{ element('Panel/relations_add') }} 7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./config 4 | ./src 5 | ./tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /webroot/svg/iconDraft.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /templates/Pages/Model/Categories/index.twig: -------------------------------------------------------------------------------- 1 | {% do _view.assign('title', 'Model ' ~ resourceType|humanize) %} 2 | {% do _view.assign('bodyViewClass', 'view-module view-model') %} 3 | 4 |
5 |
6 |

{{ title }}

7 |
8 |
9 | 10 | {{ element('Modules/index_header') }} 11 | 12 |
13 | 14 | {{ element('Modules/index_categories', { showType: 1 }) }} 15 | {{ element('Model/sidebar_links') }} 16 | 17 |
{# end module-content #} 18 | -------------------------------------------------------------------------------- /templates/Element/Form/categories.scss: -------------------------------------------------------------------------------- 1 | .categories-container { 2 | .categories { 3 | margin: $gutter 0; 4 | 5 | .select { 6 | columns: 2 auto; 7 | } 8 | 9 | h3 { 10 | font-weight: bold; 11 | text-transform: uppercase; 12 | margin: 0; 13 | } 14 | 15 | + .categories { 16 | margin-top: $gutter * 2; 17 | 18 | &:last-child { 19 | margin-bottom: $gutter * 4; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /templates/Element/Form/custom_left.twig: -------------------------------------------------------------------------------- 1 | {# Intentionally left blank: you may override this element in a plugin to create a custom view block 2 | 3 | To do so use a configuration like this in config/app.php: 4 | 5 | 'Elements' => [ 6 | 'my_models' => [ 7 | // element path => plugin to use 8 | 'Form/custom_left' => 'MyPlugin.Form/my_custom_template', 9 | ], 10 | ], 11 | 12 | This way for `my_models` object type a template file `MyPlugin.Form/my_custom_template` is loaded instead of this blank file #} 13 | -------------------------------------------------------------------------------- /templates/Element/Form/custom_right.twig: -------------------------------------------------------------------------------- 1 | {# Intentionally left blank: you may override this element in a plugin to create a custom view block 2 | 3 | To do so use a configuration like this in config/app.php: 4 | 5 | 'Elements' => [ 6 | 'my_models' => [ 7 | // element path => plugin to use 8 | 'Form/custom_right' => 'MyPlugin.Form/my_custom_template', 9 | ], 10 | ], 11 | 12 | This way for `my_models` object type a template file `MyPlugin.Form/my_custom_template` is loaded instead of this blank file #} 13 | -------------------------------------------------------------------------------- /templates/Pages/Admin/Appearance/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/sidebar') }} 2 | 3 | {% do _view.assign('title', __('Administration') ~ ' ' ~ __('Appearance')) %} 4 | {% do _view.assign('bodyViewClass', 'view-module view-admin') %} 5 | 6 | {# 7 | // i18n 8 | __('Alert Message') 9 | __('Alert Message By Area') 10 | __('Export') 11 | __('Modules') 12 | __('Pagination') 13 | __('Project') 14 | __('Properties') 15 | #} 16 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /tests/Utils/ImportFilterSampleError.php: -------------------------------------------------------------------------------- 1 | 4 |
5 |

{{ __('Setup module') }}

6 |
7 | 8 | 9 | 13 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /templates/Element/Form/title.twig: -------------------------------------------------------------------------------- 1 | {% if object.id %} 2 |

3 | 4 | {% for obj in included %} 5 | {% if obj.type == 'streams' %} 6 | 7 | 8 | {{ __('Open File') }} 9 | 10 | {% endif %} 11 | {% endfor %} 12 |

13 | {% else %} 14 |

{{ __('New object in') }} 15 | {{ Layout.tr(object.type) }}

16 | {% endif %} 17 | -------------------------------------------------------------------------------- /templates/Element/Form/calendar.scss: -------------------------------------------------------------------------------- 1 | .date-ranges-list { 2 | display: flex; 3 | flex-direction: column; 4 | margin-bottom: $gutter; 5 | 6 | .date-ranges-item { 7 | display: grid; 8 | grid-template-columns: 250px 250px 1fr 1fr 1fr; 9 | gap: $gutter; 10 | margin-bottom: $gutter * .5; 11 | align-items: flex-end; 12 | 13 | .weekdays { 14 | grid-column-start: 1; 15 | grid-column-gap: $gutter * .5; 16 | grid-column-end: 5; 17 | 18 | label { 19 | display: inline !important; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /templates/Pages/Modules/index.twig: -------------------------------------------------------------------------------- 1 | {% do _view.assign('title', __(currentModule.name|humanize)) %} 2 | {% set indexViewType = Layout.moduleIndexViewType() %} 3 | 4 | {{ element('Modules/index_header', { 'meta': meta, 'filter': filter, 'Schema': Schema, 'hidePagination': indexViewType == 'tree'}) }} 5 | 6 | {% set ids = Array.extract(objects, '{*}.id') %} 7 | 8 |
9 | {{ element('Modules/' ~ indexViewType) }} 10 | {{ element(Element.sidebar()) }} 11 |
12 |
13 | -------------------------------------------------------------------------------- /templates/Pages/Admin/SystemInfo/index.twig: -------------------------------------------------------------------------------- 1 | {{ element('Admin/sidebar') }} 2 | 3 | {% do _view.assign('title', __('Administration') ~ ' ' ~ __('System information')) %} 4 | {% do _view.assign('bodyViewClass', 'view-module view-admin') %} 5 | 6 |

BEdita Manager

7 | 8 | 9 | 10 | 11 |

API

12 | 13 |

14 | 15 | {{ __('API could be distributed in multiple nodes. The following data refers to a single node') }} 16 |

17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /templates/Element/Form/core_properties.twig: -------------------------------------------------------------------------------- 1 | 2 |
3 |
6 |

{{ __('General') }}

7 |
8 |
9 | {{ element('Form/group_properties', {'properties' : properties.core, 'group': 'core'}) }} 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /templates/Element/Form/publish_properties.twig: -------------------------------------------------------------------------------- 1 | 2 |
3 |
6 |

{{ __('Publishing properties') }}

7 |
8 |
9 | {{ element('Form/group_properties', {'properties' : properties.publish, 'group': 'publish'}) }} 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /src/View/Helper/DatesHelper.php: -------------------------------------------------------------------------------- 1 | diff(new DateTime($then)); 24 | 25 | return $diff->days; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /config/relations.php: -------------------------------------------------------------------------------- 1 | [ 9 | 'children' => [ 10 | 'type' => 'relations', 11 | 'attributes' => [ 12 | 'name' => 'children', 13 | 'inverse_name' => 'children', 14 | 'label' => 'Folder children', 15 | 'inverse_label' => 'Folder children', 16 | 'params' => null, 17 | ], 18 | 'left' => ['folders'], 19 | 'right' => ['objects'], 20 | ], 21 | ], 22 | ]; 23 | -------------------------------------------------------------------------------- /templates/Pages/Categories/index.twig: -------------------------------------------------------------------------------- 1 | {% do _view.assign('bodyViewClass', 'view-module view-model') %} 2 | 3 |
4 |
5 |

{{ __('Categories') }}

6 |
7 |
8 | 9 | {{ element('Modules/index_header', {'resourceType': 'categories'}) }} 10 | 11 |
12 | {{ element('Modules/index_categories') }} 13 | 14 | {% do _view.append('app-module-buttons', 15 | Html.link(__('List'), 16 | { 17 | '_name': 'modules:list', 18 | 'object_type': objectType 19 | }, 20 | {'class': 'button button-outlined'} 21 | ) 22 | ) %} 23 |
24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | groups: 14 | gh-actions: 15 | patterns: ['actions/*'] 16 | bedita: 17 | patterns: ['bedita/*'] 18 | docker: 19 | patterns: ['docker/*'] 20 | -------------------------------------------------------------------------------- /templates/Element/Form/advanced_properties.twig: -------------------------------------------------------------------------------- 1 | {% if properties.advanced %} 2 | 3 |
4 | 5 |
8 |

{{ __('Advanced') }}

9 |
10 | 11 |
12 | 13 | {{ element('Form/group_properties', {'properties' : properties.advanced, 'group': 'advanced'}) }} 14 | 15 |
16 | 17 |
18 |
19 | {% endif %} 20 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | Snake Case 7 | * 8 | * @param {String} str the string to convert 9 | * @return {String} 10 | */ 11 | Vue.filter('humanize', function(str) { 12 | return humanizeString(str); 13 | }); 14 | 15 | /** 16 | * Capitalize a string. 17 | * 18 | * @param {String} str The string to capitalize 19 | */ 20 | Vue.filter('capitalize', function(str) { 21 | if (!str) { 22 | return ''; 23 | } 24 | 25 | str = str.toString(); 26 | return str.charAt(0).toUpperCase() + str.slice(1); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/Utils/ImportFilterSample.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | {% endif %} 16 | -------------------------------------------------------------------------------- /webroot/js/libs/timezone.js: -------------------------------------------------------------------------------- 1 | // timezone offset used at login 2 | Date.prototype.stdTimezoneOffset = function() { 3 | var jan = new Date(this.getFullYear(), 0, 1); 4 | var jul = new Date(this.getFullYear(), 6, 1); 5 | return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset()); 6 | } 7 | 8 | Date.prototype.dst = function() { 9 | return this.getTimezoneOffset() < this.stdTimezoneOffset(); 10 | } 11 | 12 | document.addEventListener("DOMContentLoaded", function() { 13 | var inputTZElement = document.getElementById('timezone-offset'); 14 | if (inputTZElement) { 15 | var tz = (-60) * (new Date().getTimezoneOffset()) + ' ' + (new Date().dst() ? '1' : '0'); 16 | inputTZElement.value = tz; 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /config/schema/i18n.sql: -------------------------------------------------------------------------------- 1 | # Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 2 | # 3 | # Licensed under The MIT License 4 | # For full copyright and license information, please see the LICENSE.txt 5 | # Redistributions of files must retain the above copyright notice. 6 | # MIT License (https://opensource.org/licenses/mit-license.php) 7 | 8 | CREATE TABLE i18n ( 9 | id int NOT NULL auto_increment, 10 | locale varchar(6) NOT NULL, 11 | model varchar(255) NOT NULL, 12 | foreign_key int(10) NOT NULL, 13 | field varchar(255) NOT NULL, 14 | content text, 15 | PRIMARY KEY (id), 16 | UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), 17 | INDEX I18N_FIELD(model, foreign_key, field) 18 | ); 19 | -------------------------------------------------------------------------------- /templates/Layout/Email/text/default.ctp: -------------------------------------------------------------------------------- 1 | fetch('content'); 18 | -------------------------------------------------------------------------------- /templates/Element/Form/bulk_trash.twig: -------------------------------------------------------------------------------- 1 | {% if (objects) and Perms.canDelete({type: objectType}) %} 2 | {{ Form.create(null, { 3 | 'id': 'form-delete', 4 | 'url': {'_name': 'modules:delete', 'object_type': objectType} 5 | })|raw}} 6 | 7 | {% do Form.unlockField('ids') %} 8 | 9 |
10 | 18 |
19 | {{ Form.end()|raw }} 20 | {% endif %} 21 | -------------------------------------------------------------------------------- /templates/Email/text/default.ctp: -------------------------------------------------------------------------------- 1 | module.default) 10 | .then((component) => { 11 | const Constructor = Vue.extend(component); 12 | const vm = new Constructor({ 13 | propsData: { 14 | el, 15 | } 16 | }); 17 | vm.$mount(); 18 | }); 19 | }, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/js/app/directives/email.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Uri vue directive 3 | */ 4 | export default { 5 | install(Vue) { 6 | Vue.directive('email', { 7 | inserted (el) { 8 | import(/* webpackChunkName: "email-input" */'app/components/email-input') 9 | .then(module => module.default) 10 | .then((component) => { 11 | const Constructor = Vue.extend(component); 12 | const vm = new Constructor({ 13 | propsData: { 14 | el, 15 | } 16 | }); 17 | vm.$mount(); 18 | }); 19 | }, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /webroot/svg/iconFuture.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /resources/js/app/components/charts/bar-chart.vue: -------------------------------------------------------------------------------- 1 | 8 | 32 | -------------------------------------------------------------------------------- /templates/Pages/Model/Tags/index.twig: -------------------------------------------------------------------------------- 1 | {% do _view.assign('title', 'Model ' ~ resourceType|humanize) %} 2 | {% do _view.assign('bodyViewClass', 'view-module view-model') %} 3 | 4 | {% set ids = Array.extract(resources, '{*}.id') %} 5 | {% set _csrfToken = _view.request.params['_csrfToken']|default('')|json_encode %} 6 | 7 | {{ element('Modules/index_header', { 'hideFilter': 1 }) }} 8 | 9 |
10 |
11 |

{{ title }}

12 |
13 |
14 | 15 | 16 |
17 | {{ element('Modules/index_tags') }} 18 | {% if not hideSidebar %} 19 | {{ element('Model/sidebar_links') }} 20 | {% endif %} 21 |
22 |
23 | -------------------------------------------------------------------------------- /config/schema/sessions.sql: -------------------------------------------------------------------------------- 1 | # Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 2 | # 3 | # Licensed under The MIT License 4 | # For full copyright and license information, please see the LICENSE.txt 5 | # Redistributions of files must retain the above copyright notice. 6 | # MIT License (https://opensource.org/licenses/mit-license.php) 7 | 8 | CREATE TABLE `sessions` ( 9 | `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, 10 | `created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+ 11 | `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+ 12 | `data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob 13 | `expires` int(10) unsigned DEFAULT NULL, 14 | PRIMARY KEY (`id`) 15 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 16 | -------------------------------------------------------------------------------- /templates/Email/html/default.ctp: -------------------------------------------------------------------------------- 1 | ' . $line . "

\n"; 21 | endforeach; 22 | -------------------------------------------------------------------------------- /templates/Element/Panel/panel.scss: -------------------------------------------------------------------------------- 1 | .main-panel-container { 2 | position: fixed; 3 | top: 0; right: 0; 4 | min-width: 20 * $gutter; 5 | width: 100%; 6 | @media screen and (min-width: 768px) { 7 | width: 60%; 8 | } 9 | height: 100vh; 10 | transform: translateX(101%); 11 | transition: .3s transform; 12 | will-change: transform; 13 | z-index: 1001; 14 | 15 | .main-panel { 16 | overflow-x: hidden; 17 | overflow-y: auto; 18 | height: 100%; 19 | background-color: $gray-800; 20 | } 21 | 22 | .panel-slot { 23 | height: 100%; 24 | } 25 | 26 | &.on { 27 | transform: translateX(0); 28 | box-shadow: 0 0 32px $black; 29 | } 30 | } 31 | 32 | .main-panel { 33 | .fieldset:first-child { 34 | padding-top: $gutter; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /templates/Pages/Import/index.twig: -------------------------------------------------------------------------------- 1 | {% do _view.assign('title', __('Data Import')) %} 2 | 3 | {% if 'POST' in jobsAllow %} 4 | {% if filters %} 5 | {{ Form.create(null, { 'id': 'form-import', 'type': 'file', 'url': {'_name': 'import:file'} })|raw }} 6 | 7 | 8 | {{ Form.hidden('MAX_FILE_SIZE', { 'value': System.getMaxFileSize() })|raw }} 9 | {{ Form.end()|raw }} 10 | {% else %} 11 |
12 | {{ __('No import filters set') }} 13 |
14 | {% endif %} 15 | 16 | 17 | 18 | {% endif %} 19 | 20 | {% if 'GET' in jobsAllow %} 21 | 22 | 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /src/Controller/Admin/UserAccessesController.php: -------------------------------------------------------------------------------- 1 | for more details. 12 | */ 13 | namespace App\Controller\Admin; 14 | 15 | use Cake\Http\Response; 16 | 17 | /** 18 | * User Accesses Controller 19 | */ 20 | class UserAccessesController extends AdministrationBaseController 21 | { 22 | /** 23 | * @inheritDoc 24 | */ 25 | public function index(): ?Response 26 | { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/js/app/components/form/field-title.vue: -------------------------------------------------------------------------------- 1 | 10 | 34 | 39 | -------------------------------------------------------------------------------- /templates/Element/Model/relation_types.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | {% do Form.unlockField('current_left') %} 6 | {% do Form.unlockField('change_left') %} 7 |
8 |
9 |
10 | 11 | {% do Form.unlockField('current_right') %} 12 | {% do Form.unlockField('change_right') %} 13 |
14 |
15 | -------------------------------------------------------------------------------- /webroot/svg/iconExpired.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/js/app/components/form/field-string.vue: -------------------------------------------------------------------------------- 1 | 12 | 40 | -------------------------------------------------------------------------------- /templates/Layout/Email/html/default.ctp: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | <?= $this->fetch('title') ?> 21 | 22 | 23 | fetch('content') ?> 24 | 25 | 26 | -------------------------------------------------------------------------------- /resources/js/app/components/horizontal-tab-view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Templates that uses this component (directly or indirectly): 3 | * ... 4 | * 5 | * component used for ModulesPage -> View 6 | * 7 | * Handle horizontal tabs 8 | * 9 | * @prop {Array} labels 10 | * @prop {int} defaultActive index of the default active label in labels 11 | * 12 | */ 13 | export default { 14 | components: { 15 | FormFileUpload: () => import(/* webpackChunkName: "form-file-upload" */'app/components/form-file-upload'), 16 | }, 17 | 18 | props: { 19 | labels: { 20 | type: Array, 21 | default: [], 22 | }, 23 | defaultActive: { 24 | type: Number, 25 | default: 0, 26 | }, 27 | }, 28 | 29 | data() { 30 | return { 31 | activeIndex: this.defaultActive, 32 | } 33 | }, 34 | 35 | methods: { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/js/app/components/form/field-plaintext.vue: -------------------------------------------------------------------------------- 1 |