diff --git a/README.md b/README.md index f6b72f5..db9d13c 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,14 @@ Navodila za konstruiranje fajlov pa so tukaj: https://www.drupal.org/docs/contri ### Endpoint za dodajanje pojmov V modulu [yufu_concept](web/modules/custom/yufu_concept/yufu_concept.info.yml "yufu_concept"). + + +### Obveščanje prek mailov + +1. Ko je dodan nov pojem ali posodobljen star pojem, obvesti o tem vse urednike. +- ob novi draft revizija se poslje mail urednikom + +2. Ko je sprejet nov pojem ali revizija, obvesti uporabnika in ga odblokiraj. +- ob novi publishani revizijami se poslje mail uporabniku, ki jo je naredil. + +Funkcionalnost se nahaja v yufu_admin modulu. diff --git a/composer.json b/composer.json index 05171c7..48d691d 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,8 @@ "drupal/content_as_config": "^1.0", "drupal/core-composer-scaffold": "^10.0", "drupal/core-project-message": "^10.0", + "drupal/core-recommended": "^10.0", + "drupal/diff": "^1.1", "drupal/core-recommended": "^10.2.4", "drupal/gin": "^3.0@RC", "drupal/gin_toolbar": "^1.0@RC", diff --git a/composer.lock b/composer.lock index 8390142..0b0b543 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "361a800ffbcff2a4014b57ce994816cf", + "content-hash": "1e4c156c402a50481a77ff4b24a87020", "packages": [ { "name": "asm89/stack-cors", @@ -62,6 +62,67 @@ }, "time": "2023-11-14T13:51:46+00:00" }, + { + "name": "caxy/php-htmldiff", + "version": "v0.1.15", + "source": { + "type": "git", + "url": "https://github.com/caxy/php-htmldiff.git", + "reference": "6342b02ddb86fd36093ad7e2db2efc21f01ab7cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/caxy/php-htmldiff/zipball/6342b02ddb86fd36093ad7e2db2efc21f01ab7cd", + "reference": "6342b02ddb86fd36093ad7e2db2efc21f01ab7cd", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "ezyang/htmlpurifier": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "doctrine/cache": "~1.0", + "phpunit/phpunit": "~9.0" + }, + "suggest": { + "doctrine/cache": "Used for caching the calculated diffs using a Doctrine Cache Provider" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Caxy\\HtmlDiff": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0" + ], + "authors": [ + { + "name": "Josh Schroeder", + "email": "jschroeder@caxy.com", + "homepage": "http://www.caxy.com" + } + ], + "description": "A library for comparing two HTML files/snippets and highlighting the differences using simple HTML.", + "homepage": "https://github.com/caxy/php-htmldiff", + "keywords": [ + "diff", + "html" + ], + "support": { + "issues": "https://github.com/caxy/php-htmldiff/issues", + "source": "https://github.com/caxy/php-htmldiff/tree/v0.1.15" + }, + "time": "2023-11-05T23:49:04+00:00" + }, { "name": "chi-teck/drupal-code-generator", "version": "3.4.0", @@ -1724,6 +1785,97 @@ }, "time": "2024-03-06T08:23:56+00:00" }, + { + "name": "drupal/diff", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/diff.git", + "reference": "8.x-1.1" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/diff-8.x-1.1.zip", + "reference": "8.x-1.1", + "shasum": "b7558b0f431d5945289829946e0beba61bf7ae18" + }, + "require": { + "drupal/core": "^9.3 || ^10", + "mkalkbrenner/php-htmldiff-advanced": "~0.0.8" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.1", + "datestamp": "1665437355", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Miro Dietiker (miro_dietiker)", + "homepage": "https://www.drupal.org/u/miro_dietiker", + "role": "Maintainer" + }, + { + "name": "Juampy NR (juampynr)", + "homepage": "https://www.drupal.org/u/juampynr", + "role": "Maintainer" + }, + { + "name": "Lucian Hangea (lhangea)", + "homepage": "https://www.drupal.org/u/lhangea", + "role": "Maintainer" + }, + { + "name": "Alan D.", + "homepage": "https://www.drupal.org/u/alan-d.", + "role": "Maintainer" + }, + { + "name": "Brian Gilbert (realityloop).", + "homepage": "https://www.drupal.org/u/realityloop", + "role": "Maintainer" + }, + { + "name": "lhangea", + "homepage": "https://www.drupal.org/user/2743803" + }, + { + "name": "miro_dietiker", + "homepage": "https://www.drupal.org/user/227761" + }, + { + "name": "phenaproxima", + "homepage": "https://www.drupal.org/user/205645" + }, + { + "name": "realityloop", + "homepage": "https://www.drupal.org/user/139189" + }, + { + "name": "rötzi", + "homepage": "https://www.drupal.org/user/73064" + }, + { + "name": "yhahn", + "homepage": "https://www.drupal.org/user/264833" + } + ], + "description": "Compares two entity revisions", + "homepage": "https://www.drupal.org/project/diff", + "support": { + "source": "http://cgit.drupalcode.org/diff", + "issues": "https://www.drupal.org/project/issues/diff" + } + }, { "name": "drupal/gin", "version": "3.0.0-rc7", @@ -2415,6 +2567,133 @@ ], "time": "2023-10-06T06:47:41+00:00" }, + { + "name": "enlightn/security-checker", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/enlightn/security-checker.git", + "reference": "68df5c7256c84b428bf8fcff0d249de06ce362d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/enlightn/security-checker/zipball/68df5c7256c84b428bf8fcff0d249de06ce362d2", + "reference": "68df5c7256c84b428bf8fcff0d249de06ce362d2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.3|^7.0", + "php": ">=5.6", + "symfony/console": "^3.4|^4|^5|^6|^7", + "symfony/finder": "^3|^4|^5|^6|^7", + "symfony/process": "^3.4|^4|^5|^6|^7", + "symfony/yaml": "^3.4|^4|^5|^6|^7" + }, + "require-dev": { + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^2.18|^3.0", + "phpunit/phpunit": "^5.5|^6|^7|^8|^9" + }, + "bin": [ + "security-checker" + ], + "type": "library", + "autoload": { + "psr-4": { + "Enlightn\\SecurityChecker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paras Malhotra", + "email": "paras@laravel-enlightn.com" + }, + { + "name": "Miguel Piedrafita", + "email": "soy@miguelpiedrafita.com" + } + ], + "description": "A PHP dependency vulnerabilities scanner based on the Security Advisories Database.", + "keywords": [ + "package", + "php", + "scanner", + "security", + "security advisories", + "vulnerability scanner" + ], + "support": { + "issues": "https://github.com/enlightn/security-checker/issues", + "source": "https://github.com/enlightn/security-checker/tree/v1.11.0" + }, + "time": "2023-11-17T07:53:29+00:00" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.17.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "shasum": "" + }, + "require": { + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0" + }, + "time": "2023-11-17T15:01:25+00:00" + }, { "name": "grasmash/expander", "version": "3.0.0", @@ -3046,6 +3325,47 @@ }, "time": "2023-08-12T08:29:29+00:00" }, + { + "name": "mkalkbrenner/php-htmldiff-advanced", + "version": "0.0.8", + "source": { + "type": "git", + "url": "https://github.com/mkalkbrenner/php-htmldiff.git", + "reference": "3a714b48c9c3d3730baaf6d3949691e654cd37c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mkalkbrenner/php-htmldiff/zipball/3a714b48c9c3d3730baaf6d3949691e654cd37c9", + "reference": "3a714b48c9c3d3730baaf6d3949691e654cd37c9", + "shasum": "" + }, + "require": { + "caxy/php-htmldiff": ">=0.0.6", + "php": ">=5.5.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/HtmlDiffAdvancedInterface.php", + "src/HtmlDiffAdvanced.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GNU General Public License V2" + ], + "description": "An add-on for the php-htmldiff library for comparing two HTML files/snippets and highlighting the differences using simple HTML.", + "homepage": "https://github.com/mkalkbrenner/php-htmldiff", + "keywords": [ + "diff", + "html" + ], + "support": { + "issues": "https://github.com/mkalkbrenner/php-htmldiff/issues", + "source": "https://github.com/mkalkbrenner/php-htmldiff/tree/master" + }, + "time": "2016-07-25T17:07:32+00:00" + }, { "name": "nikic/php-parser", "version": "v5.0.2", @@ -3259,9 +3579,9 @@ }, "type": "library", "autoload": { - "psr-0": { - "": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "include-path": [ @@ -6842,5 +7162,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/config/sync/core.entity_view_display.node.concept.default.yml b/config/sync/core.entity_view_display.node.concept.default.yml index 545104e..f0faf1c 100644 --- a/config/sync/core.entity_view_display.node.concept.default.yml +++ b/config/sync/core.entity_view_display.node.concept.default.yml @@ -23,9 +23,9 @@ content: weight: 101 region: content content_moderation_control: + weight: -20 settings: { } third_party_settings: { } - weight: -20 region: content field_media: type: entity_reference_entity_view diff --git a/config/sync/core.entity_view_display.node.concept.teaser.yml b/config/sync/core.entity_view_display.node.concept.teaser.yml index 66c79a4..58e7bc4 100644 --- a/config/sync/core.entity_view_display.node.concept.teaser.yml +++ b/config/sync/core.entity_view_display.node.concept.teaser.yml @@ -25,9 +25,9 @@ content: weight: 101 region: content content_moderation_control: + weight: -20 settings: { } third_party_settings: { } - weight: -20 region: content links: settings: { } diff --git a/config/sync/core.entity_view_mode.node.diff.yml b/config/sync/core.entity_view_mode.node.diff.yml new file mode 100644 index 0000000..df4242b --- /dev/null +++ b/config/sync/core.entity_view_mode.node.diff.yml @@ -0,0 +1,16 @@ +uuid: 2051b25b-0ca8-4406-a590-921e0d66c18d +langcode: en +status: false +dependencies: + module: + - node + enforced: + module: + - node + - diff +_core: + default_config_hash: pqZNtad5J9THcdbYjwPD4qINqvrTxnOd8KCWn6tUBRs +id: node.diff +label: 'Revision comparison' +targetEntityType: node +cache: true diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index 083257a..ac517ee 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -17,6 +17,7 @@ module: contextual: 0 datetime: 0 dblog: 0 + diff: 0 dynamic_page_cache: 0 editor: 0 etherpad_api: 0 diff --git a/config/sync/diff.plugins.yml b/config/sync/diff.plugins.yml new file mode 100644 index 0000000..282eafe --- /dev/null +++ b/config/sync/diff.plugins.yml @@ -0,0 +1,345 @@ +fields: + block_content: + langcode: + type: hidden + settings: { } + revision_created: + type: hidden + settings: { } + revision_user: + type: hidden + settings: { } + status: + type: hidden + settings: { } + info: + type: hidden + settings: { } + changed: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } + body: + type: text_summary_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_format: 0 + compare_summary: 0 + content_moderation_state: + langcode: + type: hidden + settings: { } + uid: + type: hidden + settings: { } + workflow: + type: hidden + settings: { } + moderation_state: + type: hidden + settings: { } + content_entity_type_id: + type: hidden + settings: { } + content_entity_id: + type: hidden + settings: { } + content_entity_revision_id: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } + media: + langcode: + type: hidden + settings: { } + revision_created: + type: hidden + settings: { } + revision_user: + type: hidden + settings: { } + revision_log_message: + type: hidden + settings: { } + status: + type: hidden + settings: { } + uid: + type: hidden + settings: { } + name: + type: hidden + settings: { } + thumbnail: + type: image_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 0 + compare_alt_field: 1 + compare_title_field: 1 + property_separator: nl + show_thumbnail: 1 + created: + type: hidden + settings: { } + changed: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } + field_media_audio_file: + type: file_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 1 + compare_description_field: 0 + compare_display_field: 0 + property_separator: nl + field_media_document: + type: file_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 1 + compare_description_field: 0 + compare_display_field: 0 + property_separator: nl + field_media_image: + type: image_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 0 + compare_alt_field: 1 + compare_title_field: 1 + property_separator: nl + show_thumbnail: 1 + field_media_oembed_video: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + field_media_video_file: + type: file_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 1 + compare_description_field: 0 + compare_display_field: 0 + property_separator: nl + menu_link_content: + langcode: + type: hidden + settings: { } + revision_created: + type: hidden + settings: { } + revision_user: + type: hidden + settings: { } + revision_log_message: + type: hidden + settings: { } + enabled: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + title: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + description: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + link: + type: hidden + settings: { } + external: + type: hidden + settings: { } + changed: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } + node: + langcode: + type: hidden + settings: { } + revision_timestamp: + type: hidden + settings: { } + status: + type: hidden + settings: { } + uid: + type: entity_reference_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_entity_reference: 1 + title: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + created: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + changed: + type: hidden + settings: { } + promote: + type: hidden + settings: { } + sticky: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } + content_translation_source: + type: hidden + settings: { } + content_translation_outdated: + type: hidden + settings: { } + body: + type: text_summary_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_format: 0 + compare_summary: 0 + comment: + type: hidden + settings: { } + field_image: + type: image_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + show_id: 0 + compare_alt_field: 1 + compare_title_field: 1 + property_separator: nl + show_thumbnail: 1 + field_media: + type: entity_reference_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_entity_reference: 1 + field_related_concept: + type: entity_reference_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_entity_reference: 1 + field_tags: + type: entity_reference_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + compare_entity_reference: 1 + path_alias: + langcode: + type: hidden + settings: { } + path: + type: hidden + settings: { } + alias: + type: hidden + settings: { } + status: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + taxonomy_term: + langcode: + type: hidden + settings: { } + revision_created: + type: hidden + settings: { } + revision_user: + type: hidden + settings: { } + revision_log_message: + type: hidden + settings: { } + status: + type: hidden + settings: { } + name: + type: core_field_diff_builder + settings: + show_header: 1 + markdown: drupal_html_to_text + description: + type: hidden + settings: { } + parent: + type: hidden + settings: { } + changed: + type: hidden + settings: { } + default_langcode: + type: hidden + settings: { } + revision_default: + type: hidden + settings: { } + revision_translation_affected: + type: hidden + settings: { } diff --git a/config/sync/diff.settings.yml b/config/sync/diff.settings.yml new file mode 100644 index 0000000..699bbbb --- /dev/null +++ b/config/sync/diff.settings.yml @@ -0,0 +1,18 @@ +_core: + default_config_hash: oXwX3NzLv9QK_LbNEvpQ9OPwH9tqtMSJzq5y8t63Q8w +general_settings: + radio_behavior: simple + context_lines_leading: 1 + context_lines_trailing: 1 + revision_pager_limit: 50 + layout_plugins: + visual_inline: + enabled: true + weight: 0 + split_fields: + enabled: true + weight: 1 + unified_fields: + enabled: true + weight: 2 + visual_inline_theme: default diff --git a/config/sync/user.role.editor.yml b/config/sync/user.role.editor.yml index faa7b66..8e6110a 100644 --- a/config/sync/user.role.editor.yml +++ b/config/sync/user.role.editor.yml @@ -19,6 +19,7 @@ dependencies: - config_translation - content_moderation - content_translation + - file - language - locale - media @@ -65,6 +66,7 @@ permissions: - 'delete own audio media' - 'delete own concept content' - 'delete own document media' + - 'delete own files' - 'delete own page content' - 'delete page revisions' - 'delete terms in tags' diff --git a/config/sync/workflows.workflow.concept_workflow.yml b/config/sync/workflows.workflow.concept_workflow.yml index 6a1a08f..2a9f526 100644 --- a/config/sync/workflows.workflow.concept_workflow.yml +++ b/config/sync/workflows.workflow.concept_workflow.yml @@ -11,6 +11,11 @@ label: 'Concept workflow' type: content_moderation type_settings: states: + archived: + label: Archived + weight: 2 + published: false + default_revision: false draft: label: Draft weight: 0 @@ -22,6 +27,13 @@ type_settings: published: true default_revision: true transitions: + archive: + label: Archive + from: + - draft + - published + to: archived + weight: 2 create_new_draft: label: 'Create New Draft' from: diff --git a/web/modules/custom/yufu_admin/README.md b/web/modules/custom/yufu_admin/README.md new file mode 100644 index 0000000..acd912b --- /dev/null +++ b/web/modules/custom/yufu_admin/README.md @@ -0,0 +1,12 @@ +# Yufu Admin + +Administrativna in uredniška funkcionalnost. + +Obveščanje preko mailov, ko se spremeni revizija koncepta. + +## Testiranje + +1. Kot anonimni uporabnik predlagamo spremembe koncepta preko frontend apija. +2. Kot urednik objavimo predlagane spremembe. + - Fino bi bilo imet pregled (view) predlaganih in še ne objavljenih sprememb. +3. Pogledamo v mailhog, če so se maili poslali. (ddev status) diff --git a/web/modules/custom/yufu_admin/translations/yufu_admin.sl.po b/web/modules/custom/yufu_admin/translations/yufu_admin.sl.po new file mode 100644 index 0000000..c027755 --- /dev/null +++ b/web/modules/custom/yufu_admin/translations/yufu_admin.sl.po @@ -0,0 +1,41 @@ +# Slovenian translations for yufu_admin module +# + +msgid "jsonapi Output" +msgstr "Izpljunek jsonapija" + +msgid "[YUFU] New concept draft: @title" +msgstr "[YUFU] Nov osnutek pojma: @title" + +msgid "[YUFU] Your concept (changes) were approved: @title" +msgstr "[YUFU] Tvoj predlog sprememb pojma @title je bil potrjen." + +msgid "[YUFU] Your concept (changes) were rejected: @title" +msgstr "[YUFU] Tvoj predlog sprememb pojma @title je bil zavržen" + +msgid "New concept created: @title - @url" +msgstr "Ustvrjen je bil nov pojem @title - @url" + +msgid "For transition @transition of concept @title (@nid) to recipients @recipients." +msgstr "Za tranzicijo @transition koncepta @title (@nid) prejemnikom @recipients." + +msgid "Unknown" +msgstr "Neznan" + +msgid "There was a problem sending your message and it was not sent. " +msgstr "Prišlo je do problema pri pošiljanju e-poštnega sporočila in le no ni bilo poslano. " + +msgid "Your message has been sent. " +msgstr "E-poštno sporočilo je bilo poslano. " + +msgid "@editor (@uid) is missing email." +msgstr "Urednik @editor (@uid) nima nastavljenega emaila." + +msgid "New concept created: @title - @url" +msgstr "Ustvarjen je bil nov koncept: @title - @url" + +msgid "Published changes not attributed to any of knownn users: @title - @url" +msgstr "Potrjene spremembe ne pripadajo nobenemu znanemu uporabniku: @title - @url" + +msgid "There was a problem sending your message and it was not sent. For transition @transition." +msgstr "Prišlo je do problema pri pošiljanju e-poštnega sporočila in le to ni bilo poslano. Za tranzicijo @transition" diff --git a/web/modules/custom/yufu_admin/yufu_admin.module b/web/modules/custom/yufu_admin/yufu_admin.module index 7dd23f6..69ed080 100644 --- a/web/modules/custom/yufu_admin/yufu_admin.module +++ b/web/modules/custom/yufu_admin/yufu_admin.module @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Url; +use Drupal\node\NodeInterface; /** * Implements hook_entity_operation(). @@ -14,7 +15,7 @@ use Drupal\Core\Url; function yufu_admin_entity_operation(EntityInterface $entity) { $operations = []; $entityType = $entity->getEntityType(); - // @todo Only for entity node - should we add other entities as well? + // @TODO Only for entity node - should we add other entities as well? if ($entityType->id() === 'node' && \Drupal::currentUser()->hasPermission('use jsonapi operation link')) { // Build the url. $url = Url::fromRoute(sprintf('jsonapi.%s--%s.individual', $entityType->id(), $entity->bundle()), @@ -28,3 +29,249 @@ function yufu_admin_entity_operation(EntityInterface $entity) { } return $operations; } + +/** + * Implements hook_mail(). + */ +function yufu_admin_mail($key, &$message, $params) { + $options = ['langcode' => $message['langcode']]; + + switch ($key) { + case 'concept_drafted': + $message['from'] = \Drupal::config('system.site')->get('mail'); + $message['subject'] = t('[YUFU] New concept draft: @title', ['@title' => $params['node_title']], $options); + $message['body'][] = $params['message']; + break; + case 'concept_approved': + $message['from'] = \Drupal::config('system.site')->get('mail'); + $message['subject'] = t('[YUFU] Concept changes approved: @title', ['@title' => $params['node_title']], $options); + $message['body'][] = $params['message']; + break; + case 'concept_rejected': + $message['from'] = \Drupal::config('system.site')->get('mail'); + $message['subject'] = t('[YUFU] Concept changes rejected: @title', ['@title' => $params['node_title']], $options); + $message['body'][] = $params['message']; + } +} + +/** + * Implements hook_ENTITY_TYPE_presave(). + */ +function yufu_admin_node_presave(EntityInterface $entity) { + if ($entity instanceOf NodeInterface && $entity->bundle() == 'concept') { + // Get transition: concept_drafted, concept_approved, concept_rejected. + if ($entity->isNew()) { + $transition = 'concept_drafted'; + } + else { + $transition = _yufu_admin_get_node_transition($entity); + } + switch ($transition) { + case 'stay_draft': + case 'concept_drafted': + // Send email to editors. + $params = _yufu_admin_concept_drafted_mail($entity); + $result = _yufu_admin_send_email($transition, $params['to'], $params); + _yufu_admin_status_message($result['result'], $transition, $entity, $params['to']); + break; + case 'concept_approved': + // Send email to revision creator (user). + // @TODO Maybe notify other editors. + $params = _yufu_admin_concept_approved_get_message($entity); + $result = _yufu_admin_send_email($transition, $params['to'], $params); + _yufu_admin_status_message($result['result'], $transition, $entity, $params['to']); + break; + case 'concept_rejectd': + // Send email to revision creator (user). + // This transition happens on revision delete. + // Curently it is dead. + // @TODO https://git.kompot.si/yufu/manifest/issues/45 + break; + } + } +} + +/** + * Compare original node moderation state with current state. + * + * @param \Drupal\node\NodeInterface $node + * Node to check transition on. + * + * @return string|null + * Name of the transition. + */ +function _yufu_admin_get_node_transition(NodeInterface $node) { + $original = $node->original ?? NULL; + $moderation_state = $node->moderation_state->value; + $previous_state = $original?->moderation_state->value ?? NULL; + if ($moderation_state == 'draft' && in_array($previous_state, ['published', NULL])) { + return 'concept_drafted'; + } + else if ($moderation_state == 'published' && $previous_state == 'draft') { + return 'concept_approved'; + } + else if ($moderation_state == 'published' && $previous_state == 'published') { + return 'stay_published'; + } + else if ($moderation_state == 'draft' && $previous_state == 'draft') { + return 'stay_draft'; + } + return NULL; +} + +/** + * Notify user that their concept (revision) was published. + * + * @param \Drupal\node\NodeInterface $node + * Concept node that was transitioned into published state. + */ +function _yufu_admin_concept_drafted_mail(NodeInterface $node) { + $to = _yufu_admin_get_all_editors_emails(); + if ($node->isNew()) { + $params['message'] = t('New concept created: @title - @url', [ + '@title' => $node->getTitle(), + '@url' => !$node->isNew() ? $node?->toUrl()?->toString() : '', + ]); + } + else { + $params['message'] = t('New changes to concept proposed: @title - @url', [ + '@title' => $node->getTitle(), + '@url' => !$node->isNew() ? $node?->toUrl()?->toString() : '', + ]); + } + $params['node_title'] = $node->getTitle(); + $params['to'] = implode(',', $to); + return $params; +} + +/** + * Function for sending emails. + * + * @param string $key + * Transition of a concept. + * @param string $to + * List of recipients. + * @param array $params + * Message parameters. + * + * @return array + * Mail manager response. + */ +function _yufu_admin_send_email($key = '', $to = '', $params = []) { + $mailManager = \Drupal::service('plugin.manager.mail'); + $module = 'yufu_admin'; + $langcode = \Drupal::currentUser()->getPreferredLangcode(); + $send = TRUE; + $result = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send); +} + +/** + * Add notifications on sending of emails. + */ +function _yufu_admin_status_message($result, $transition = '', $node = NULL, $recipients = '') { + $log = t('For transition @transition of concept @title (@nid) to recipients @recipients.', [ + '@transition' => $transition, + '@title' => $node?->getTitle() ?? t('Unknown'), + '@nid' => $node?->id() ?? '', + '@recipients' => $recipients, + ]); + if ($result !== TRUE) { + $message = t('There was a problem sending your message and it was not sent. '); + Drupal::logger('yufu_admin')->error($message . $log); + Drupal::messenger()->addError($message . $log); + } + else { + $message = t('Your message has been sent. '); + Drupal::logger('yufu_admin')->notice($message . $log); + Drupal::messenger()->addStatus($message . $log); + } +} + +/** + * Get a list of emails of all editors. + * + * @return array + * List of strings. + */ +function _yufu_admin_get_all_editors_emails() { + $user_storage = \Drupal::entityTypeManager()->getStorage('user'); + $ids = $user_storage->getQuery() + ->condition('status', 1) + ->condition('roles', 'administrator') + ->accessCheck(FALSE) + ->execute(); + $editors = $user_storage->loadMultiple($ids); + $emails = []; + foreach ($editors as $editor) { + /* @var \Drupal\user\UserInterface $user */ + if ($email = $editor?->getEmail()) { + $emails[] = $email; + } + else { + \Drupal::logger('yufu_admin')->warning('@editor (@uid) is missing email.',[ + '@editor' => $editor?->getDisplayName() ?? '', + '@uid' => $editor->id(), + ]); + } + } + return $emails; +} + +/** + * Notify user that their concept (revision) was published. + * + * @param \Drupal\node\NodeInterface $node + * Concept node that was transitioned into published state. + */ +function _yufu_admin_concept_approved_get_message(NodeInterface $node) { + // To the revision's authors. + // @TODO - to make this work properly, we should get a list of all authors of + // unpublished revisions so far since the last published revision. + if ($to = _yufu_admin_get_pioneer_email($node)) { + $params['message'] = t('Your proposal of changes to concept were approved: @title - @url', [ + '@title' => $node->getTitle(), + '@url' => $node?->toUrl()?->toString() ?? '', + ]); + } + else { + $to = implode(',', _yufu_admin_get_all_editors_emails()); + $params['message'] = t('Published changes not attributed to any of knownn users: @title - @url', [ + '@title' => $node->getTitle(), + '@url' => !$node->isNew() ? $node?->toUrl()?->toString() : '', + ]); + } + $params['node_title'] = $node->getTitle(); + $params['to'] = $to; + return $params; +} + +/** + * Get the email of person (pioneer) that worked on proposed changes. + * + * @return string|NULL + * List of strings. + */ +function _yufu_admin_get_pioneer_email(NodeInterface $node) { + // $email = $node->uid->entity; + if ($original = $node->original) { + $user = $original->getRevisionUser(); + $email = $user->getEmail(); + return $email ?? NULL; + } + // @TODO: Make this code nicer. + $user = $node->getRevisionUser(); + $email = $user->getEmail(); + return $email ?? NULL; +} + + +/** + * Get a list of emails of people that worked on proposed changes. + * + * @return array + * List of strings. + */ +function _yufu_admin_get_all_pioneer_emails() { + // @TODO: Think it through if it is really needed. + return []; +} diff --git a/web/modules/custom/yufu_concept/src/Plugin/rest/resource/AddConcept.php b/web/modules/custom/yufu_concept/src/Plugin/rest/resource/AddConcept.php index 258d09d..318568c 100644 --- a/web/modules/custom/yufu_concept/src/Plugin/rest/resource/AddConcept.php +++ b/web/modules/custom/yufu_concept/src/Plugin/rest/resource/AddConcept.php @@ -2,7 +2,6 @@ namespace Drupal\yufu_concept\Plugin\rest\resource; -use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Session\AccountProxyInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -127,6 +126,8 @@ class AddConcept extends ResourceBase { $concept->isDefaultRevision(FALSE); $concept->setRevisionLogMessage('New revision by concept endpoint.'); $concept->moderation_state->target_id = 'draft'; + $concept->set('status', 0); + $concept->setUnpublished(); $concept->save(); $this->logger->notice('New concept @title revision @revid created by uid @uid.', [ '@title' => $concept->getTitle(),