diff --git a/README.md b/README.md index f3204a7..f6b72f5 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,12 @@ Nato poženemo izgrajevalnik / strežnik: Dodatne testne vsebine lahko definiramo v modulu `yufu_data`, v poddirektoriju `content`. Pazit moramo na ime - vsak fajl se mora končati s `.content.yml`. Navodila za konstruiranje fajlov pa so tukaj: https://www.drupal.org/docs/contributed-modules/yaml-content/examples + + +# Dokumentacija + +## Backend + +### Endpoint za dodajanje pojmov + +V modulu [yufu_concept](web/modules/custom/yufu_concept/yufu_concept.info.yml "yufu_concept"). diff --git a/composer.json b/composer.json index 3d20ec8..f2c9cf1 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ ], "require": { "composer/installers": "^2.0", + "cweagans/composer-patches": "^1.7", "drupal/cer": "^5.0@beta", "drupal/content_as_config": "^1.0", "drupal/core-composer-scaffold": "^10.0", @@ -24,7 +25,9 @@ "drupal/gin": "^3.0@RC", "drupal/gin_toolbar": "^1.0@RC", "drupal/jsonapi_menu_items": "^1.2", + "drupal/restui": "^1.21", "drupal/single_content_sync": "^1.3", + "drupal/token": "^1.12", "drupal/yaml_content": "^1.0@dev", "drush/drush": "^11.4" }, @@ -39,11 +42,17 @@ "drupal/core-composer-scaffold": true, "drupal/core-project-message": true, "phpstan/extension-installer": true, - "dealerdirect/phpcodesniffer-composer-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true, + "cweagans/composer-patches": true }, "sort-packages": true }, "extra": { + "patches": { + "drupal/restui": { + "#3337894: Get class methods wrong argument.": "https://www.drupal.org/files/issues/2023-06-23/type-error-class-3337894-10.patch" + } + }, "drupal-scaffold": { "locations": { "web-root": "web/" diff --git a/composer.lock b/composer.lock index 2623222..92946b4 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": "f739ff77797412cd5de7292408a1d098", + "content-hash": "d5d4122da2b7a3502081e8d79d06641a", "packages": [ { "name": "asm89/stack-cors", @@ -870,6 +870,54 @@ }, "time": "2022-12-06T17:57:16+00:00" }, + { + "name": "cweagans/composer-patches", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/cweagans/composer-patches.git", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3.0" + }, + "require-dev": { + "composer/composer": "~1.0 || ~2.0", + "phpunit/phpunit": "~4.6" + }, + "type": "composer-plugin", + "extra": { + "class": "cweagans\\Composer\\Patches" + }, + "autoload": { + "psr-4": { + "cweagans\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Cameron Eagans", + "email": "me@cweagans.net" + } + ], + "description": "Provides a way to patch Composer packages.", + "support": { + "issues": "https://github.com/cweagans/composer-patches/issues", + "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" + }, + "time": "2022-12-20T22:53:13+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", @@ -1856,6 +1904,62 @@ "source": "https://git.drupalcode.org/project/jsonapi_resources" } }, + { + "name": "drupal/restui", + "version": "1.21.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/restui.git", + "reference": "8.x-1.21" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/restui-8.x-1.21.zip", + "reference": "8.x-1.21", + "shasum": "2a67dc2c1953dced0bddaff25e5c60784ee0178c" + }, + "require": { + "drupal/core": "^8.7.7 || ^9 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.21", + "datestamp": "1659086914", + "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": "-enzo-", + "homepage": "https://www.drupal.org/user/294937" + }, + { + "name": "clemens.tolboom", + "homepage": "https://www.drupal.org/user/125814" + }, + { + "name": "juampynr", + "homepage": "https://www.drupal.org/user/682736" + }, + { + "name": "klausi", + "homepage": "https://www.drupal.org/user/262198" + } + ], + "description": "Provides a user interface to manage REST resources.", + "homepage": "https://www.drupal.org/project/restui", + "support": { + "source": "https://git.drupalcode.org/project/restui" + } + }, { "name": "drupal/single_content_sync", "version": "1.3.13", @@ -1897,6 +2001,10 @@ "homepage": "https://www.drupal.org/u/nginex", "role": "Maintainer" }, + { + "name": "artycal", + "homepage": "https://www.drupal.org/user/3708798" + }, { "name": "nginex", "homepage": "https://www.drupal.org/user/2822325" @@ -1909,6 +2017,75 @@ "issues": "https://drupal.org/project/issues/single_content_sync" } }, + { + "name": "drupal/token", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/token.git", + "reference": "8.x-1.12" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/token-8.x-1.12.zip", + "reference": "8.x-1.12", + "shasum": "cefe1b203b793682f74ea43e18d0a814cf768763" + }, + "require": { + "drupal/core": "^9.2 || ^10" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.12", + "datestamp": "1688015262", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + }, + "drush": { + "services": { + "drush.services.yml": "^9 || ^10" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Berdir", + "homepage": "https://www.drupal.org/user/214652" + }, + { + "name": "Dave Reid", + "homepage": "https://www.drupal.org/user/53892" + }, + { + "name": "eaton", + "homepage": "https://www.drupal.org/user/16496" + }, + { + "name": "fago", + "homepage": "https://www.drupal.org/user/16747" + }, + { + "name": "greggles", + "homepage": "https://www.drupal.org/user/36762" + }, + { + "name": "mikeryan", + "homepage": "https://www.drupal.org/user/4420" + } + ], + "description": "Provides a user interface for the Token API, some missing core tokens.", + "homepage": "https://www.drupal.org/project/token", + "support": { + "source": "https://git.drupalcode.org/project/token" + } + }, { "name": "drupal/yaml_content", "version": "dev-1.x", @@ -1929,8 +2106,8 @@ "dev-1.x": "1.x-dev" }, "drupal": { - "version": "8.x-1.0-alpha8+1-dev", - "datestamp": "1666891400", + "version": "8.x-1.0-alpha8+4-dev", + "datestamp": "1690395239", "security-coverage": { "status": "not-covered", "message": "Project has not opted into security advisory coverage!" @@ -6528,5 +6705,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_form_display.node.concept.default.yml b/config/sync/core.entity_form_display.node.concept.default.yml index ddc33ca..72a0ef6 100644 --- a/config/sync/core.entity_form_display.node.concept.default.yml +++ b/config/sync/core.entity_form_display.node.concept.default.yml @@ -8,7 +8,9 @@ dependencies: - field.field.node.concept.field_related_concept - field.field.node.concept.field_tags - node.type.concept + - workflows.workflow.concept_workflow module: + - content_moderation - media_library - path - text @@ -19,7 +21,7 @@ mode: default content: body: type: text_textarea_with_summary - weight: 121 + weight: 1 region: content settings: rows: 9 @@ -29,20 +31,20 @@ content: third_party_settings: { } created: type: datetime_timestamp - weight: 10 + weight: 9 region: content settings: { } third_party_settings: { } field_media: type: media_library_widget - weight: 122 + weight: 5 region: content settings: media_types: { } third_party_settings: { } field_related_concept: type: entity_reference_autocomplete - weight: 123 + weight: 2 region: content settings: match_operator: CONTAINS @@ -52,7 +54,7 @@ content: third_party_settings: { } field_tags: type: entity_reference_autocomplete - weight: 124 + weight: 4 region: content settings: match_operator: CONTAINS @@ -62,41 +64,47 @@ content: third_party_settings: { } langcode: type: language_select - weight: 2 + weight: 3 region: content settings: include_locked: true third_party_settings: { } + moderation_state: + type: moderation_state_default + weight: 6 + region: content + settings: { } + third_party_settings: { } path: type: path - weight: 30 + weight: 13 region: content settings: { } third_party_settings: { } promote: type: boolean_checkbox - weight: 15 + weight: 11 region: content settings: display_label: true third_party_settings: { } status: type: boolean_checkbox - weight: 120 + weight: 7 region: content settings: display_label: true third_party_settings: { } sticky: type: boolean_checkbox - weight: 16 + weight: 12 region: content settings: display_label: true third_party_settings: { } title: type: string_textfield - weight: -5 + weight: 0 region: content settings: size: 60 @@ -109,7 +117,7 @@ content: third_party_settings: { } uid: type: entity_reference_autocomplete - weight: 5 + weight: 8 region: content settings: match_operator: CONTAINS diff --git a/config/sync/core.entity_view_mode.block.token.yml b/config/sync/core.entity_view_mode.block.token.yml new file mode 100644 index 0000000..e99e4d9 --- /dev/null +++ b/config/sync/core.entity_view_mode.block.token.yml @@ -0,0 +1,10 @@ +uuid: be4c3be6-4cc3-4c4a-9c5e-d58e55903e85 +langcode: en +status: true +dependencies: + module: + - block +id: block.token +label: Token +targetEntityType: block +cache: true diff --git a/config/sync/core.entity_view_mode.block_content.token.yml b/config/sync/core.entity_view_mode.block_content.token.yml new file mode 100644 index 0000000..c3ce211 --- /dev/null +++ b/config/sync/core.entity_view_mode.block_content.token.yml @@ -0,0 +1,10 @@ +uuid: b57556dc-3181-40a4-83b6-ee55e3c2f942 +langcode: en +status: true +dependencies: + module: + - block_content +id: block_content.token +label: Token +targetEntityType: block_content +cache: true diff --git a/config/sync/core.entity_view_mode.comment.token.yml b/config/sync/core.entity_view_mode.comment.token.yml new file mode 100644 index 0000000..9f1d65f --- /dev/null +++ b/config/sync/core.entity_view_mode.comment.token.yml @@ -0,0 +1,10 @@ +uuid: 45f19d69-ef2b-4758-a9f8-e31279d31317 +langcode: en +status: true +dependencies: + module: + - comment +id: comment.token +label: Token +targetEntityType: comment +cache: true diff --git a/config/sync/core.entity_view_mode.content_moderation_state.token.yml b/config/sync/core.entity_view_mode.content_moderation_state.token.yml new file mode 100644 index 0000000..36902ae --- /dev/null +++ b/config/sync/core.entity_view_mode.content_moderation_state.token.yml @@ -0,0 +1,10 @@ +uuid: 655d18e2-71f8-49bf-ac6c-e0526bdd049e +langcode: en +status: true +dependencies: + module: + - content_moderation +id: content_moderation_state.token +label: Token +targetEntityType: content_moderation_state +cache: true diff --git a/config/sync/core.entity_view_mode.file.token.yml b/config/sync/core.entity_view_mode.file.token.yml new file mode 100644 index 0000000..5b0d61b --- /dev/null +++ b/config/sync/core.entity_view_mode.file.token.yml @@ -0,0 +1,10 @@ +uuid: 6f5ca935-a97f-41ba-ad0e-3f284cf5e536 +langcode: en +status: true +dependencies: + module: + - file +id: file.token +label: Token +targetEntityType: file +cache: true diff --git a/config/sync/core.entity_view_mode.media.token.yml b/config/sync/core.entity_view_mode.media.token.yml new file mode 100644 index 0000000..8f3c5ec --- /dev/null +++ b/config/sync/core.entity_view_mode.media.token.yml @@ -0,0 +1,10 @@ +uuid: 194fc976-a674-4d3f-b70f-087e97ab7e25 +langcode: en +status: true +dependencies: + module: + - media +id: media.token +label: Token +targetEntityType: media +cache: true diff --git a/config/sync/core.entity_view_mode.menu_link_content.token.yml b/config/sync/core.entity_view_mode.menu_link_content.token.yml new file mode 100644 index 0000000..9f138bd --- /dev/null +++ b/config/sync/core.entity_view_mode.menu_link_content.token.yml @@ -0,0 +1,10 @@ +uuid: 984fe3ef-e532-4bd9-8937-af7001e7a3bd +langcode: en +status: true +dependencies: + module: + - menu_link_content +id: menu_link_content.token +label: Token +targetEntityType: menu_link_content +cache: true diff --git a/config/sync/core.entity_view_mode.node.token.yml b/config/sync/core.entity_view_mode.node.token.yml new file mode 100644 index 0000000..e4d5b00 --- /dev/null +++ b/config/sync/core.entity_view_mode.node.token.yml @@ -0,0 +1,10 @@ +uuid: f9b3366d-d376-41b1-8c3f-06edb76a96fb +langcode: en +status: true +dependencies: + module: + - node +id: node.token +label: Token +targetEntityType: node +cache: true diff --git a/config/sync/core.entity_view_mode.path_alias.token.yml b/config/sync/core.entity_view_mode.path_alias.token.yml new file mode 100644 index 0000000..244e9e9 --- /dev/null +++ b/config/sync/core.entity_view_mode.path_alias.token.yml @@ -0,0 +1,10 @@ +uuid: 600d4fed-e248-4be2-818a-4442c5f9e07a +langcode: en +status: true +dependencies: + module: + - path_alias +id: path_alias.token +label: Token +targetEntityType: path_alias +cache: true diff --git a/config/sync/core.entity_view_mode.taxonomy_term.token.yml b/config/sync/core.entity_view_mode.taxonomy_term.token.yml new file mode 100644 index 0000000..7219212 --- /dev/null +++ b/config/sync/core.entity_view_mode.taxonomy_term.token.yml @@ -0,0 +1,10 @@ +uuid: 5e3cc117-bf76-406a-ba35-cf9b5cfbe4b9 +langcode: en +status: true +dependencies: + module: + - taxonomy +id: taxonomy_term.token +label: Token +targetEntityType: taxonomy_term +cache: true diff --git a/config/sync/core.entity_view_mode.user.token.yml b/config/sync/core.entity_view_mode.user.token.yml new file mode 100644 index 0000000..128fc71 --- /dev/null +++ b/config/sync/core.entity_view_mode.user.token.yml @@ -0,0 +1,10 @@ +uuid: d8e3dde9-87a9-4209-ac1d-8961ff0a3ceb +langcode: en +status: true +dependencies: + module: + - user +id: user.token +label: Token +targetEntityType: user +cache: true diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index 747e02b..a42ca64 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -42,12 +42,15 @@ module: page_cache: 0 path: 0 path_alias: 0 + rest: 0 + restui: 0 search: 0 serialization: 0 single_content_sync: 0 system: 0 taxonomy: 0 text: 0 + token: 0 toolbar: 0 update: 0 user: 0 @@ -55,6 +58,7 @@ module: workflows: 0 yaml_content: 0 yufu_admin: 0 + yufu_concept: 0 yufu_data: 0 content_translation: 10 views: 10 diff --git a/config/sync/dblog.settings.yml b/config/sync/dblog.settings.yml index fbd17ea..ed40eb0 100644 --- a/config/sync/dblog.settings.yml +++ b/config/sync/dblog.settings.yml @@ -1,3 +1,3 @@ _core: default_config_hash: e883aGsrt1wFrsydlYU584PZONCSfRy0DtkZ9KzHb58 -row_limit: 1000 +row_limit: 100000 diff --git a/config/sync/language/sl/views.view.moderated_content.yml b/config/sync/language/sl/views.view.moderated_content.yml index d7d4a40..bc51f86 100644 --- a/config/sync/language/sl/views.view.moderated_content.yml +++ b/config/sync/language/sl/views.view.moderated_content.yml @@ -1,42 +1,42 @@ -label: 'Moderirani sadržaj' +label: 'Moderirane vsebine' display: default: display_title: Zadano display_options: - title: 'Moderirani sadržaj' + title: 'Moderirane vsebine' fields: title: - label: Naziv + label: Naslov separator: ', ' type: - label: 'Vrsta sadržaja' + label: 'Tip vsebin' separator: ', ' name: - label: Autor + label: Avtor separator: ', ' moderation_state: separator: ', ' changed: - label: Osvježeno + label: Posodobljeno separator: ', ' operations: label: Operacije exposed_form: options: - submit_button: Pročisti - reset_button_label: Poništi + submit_button: Počisti + reset_button_label: Ponastavi exposed_sorts_label: 'Rasporedi po' - sort_asc_label: Uzlazno - sort_desc_label: Silazno + sort_asc_label: Naraščajoče + sort_desc_label: Padajoče filters: title: expose: - label: Naziv + label: Naslov type: expose: - label: 'Vrsta sadržaja' + label: '' langcode: expose: label: Jezik moderated_content: - display_title: 'Moderirani sadržaj' + display_title: 'Moderirane vsebine' diff --git a/config/sync/rest.resource.add_concept_rest_resource.yml b/config/sync/rest.resource.add_concept_rest_resource.yml new file mode 100644 index 0000000..888e462 --- /dev/null +++ b/config/sync/rest.resource.add_concept_rest_resource.yml @@ -0,0 +1,18 @@ +uuid: 589ee957-7efd-45d3-979b-93b5b1018761 +langcode: en +status: true +dependencies: + module: + - serialization + - user + - yufu_concept +id: add_concept_rest_resource +plugin_id: add_concept_rest_resource +granularity: resource +configuration: + methods: + - POST + formats: + - json + authentication: + - cookie diff --git a/config/sync/rest.resource.concept_autocomplete_rest_resource.yml b/config/sync/rest.resource.concept_autocomplete_rest_resource.yml new file mode 100644 index 0000000..b10bc21 --- /dev/null +++ b/config/sync/rest.resource.concept_autocomplete_rest_resource.yml @@ -0,0 +1,18 @@ +uuid: 96dd83b8-5a29-43d5-a87e-a3be17c8b96c +langcode: en +status: true +dependencies: + module: + - serialization + - user + - yufu_concept +id: concept_autocomplete_rest_resource +plugin_id: concept_autocomplete_rest_resource +granularity: resource +configuration: + methods: + - GET + formats: + - json + authentication: + - cookie diff --git a/config/sync/system.logging.yml b/config/sync/system.logging.yml index d6164de..135cce7 100644 --- a/config/sync/system.logging.yml +++ b/config/sync/system.logging.yml @@ -1,3 +1,3 @@ _core: default_config_hash: u3-njszl92FaxjrCMiq0yDcjAfcdx72w1zT1O9dx6aA -error_level: hide +error_level: some diff --git a/config/sync/user.role.anonymous.yml b/config/sync/user.role.anonymous.yml index 7c52d43..6ba9c02 100644 --- a/config/sync/user.role.anonymous.yml +++ b/config/sync/user.role.anonymous.yml @@ -4,11 +4,14 @@ status: true dependencies: config: - node.type.concept + - rest.resource.add_concept_rest_resource + - rest.resource.concept_autocomplete_rest_resource - workflows.workflow.concept_workflow module: - content_moderation - media - node + - rest - system _core: default_config_hash: j5zLMOdJBqC0bMvSdth5UebkprJB8g_2FXHqhfpJzow @@ -19,5 +22,8 @@ is_admin: false permissions: - 'access content' - 'create concept content' + - 'edit any concept content' + - 'restful get concept_autocomplete_rest_resource' + - 'restful post add_concept_rest_resource' - 'use concept_workflow transition create_new_draft' - 'view media' diff --git a/config/sync/user.role.authenticated.yml b/config/sync/user.role.authenticated.yml index d5f6112..5adfc21 100644 --- a/config/sync/user.role.authenticated.yml +++ b/config/sync/user.role.authenticated.yml @@ -5,12 +5,16 @@ dependencies: config: - node.type.article - node.type.concept + - rest.resource.add_concept_rest_resource + - rest.resource.concept_autocomplete_rest_resource - taxonomy.vocabulary.tags - workflows.workflow.concept_workflow module: - content_moderation + - file - media - node + - rest - system - taxonomy - yufu_admin @@ -26,6 +30,10 @@ permissions: - 'create concept content' - 'create media' - 'create terms in tags' + - 'delete own files' + - 'edit any concept content' + - 'restful get concept_autocomplete_rest_resource' + - 'restful post add_concept_rest_resource' - 'use concept_workflow transition create_new_draft' - 'use jsonapi operation link' - 'view any unpublished content' diff --git a/config/sync/user.role.editor.yml b/config/sync/user.role.editor.yml index c014288..faa7b66 100644 --- a/config/sync/user.role.editor.yml +++ b/config/sync/user.role.editor.yml @@ -11,6 +11,7 @@ dependencies: - node.type.article - node.type.concept - node.type.page + - rest.resource.add_concept_rest_resource - taxonomy.vocabulary.tags - workflows.workflow.concept_workflow module: @@ -23,6 +24,7 @@ dependencies: - media - node - path + - rest - taxonomy - toolbar id: editor @@ -85,6 +87,7 @@ permissions: - 'edit terms in tags' - 'export configuration' - 'import configuration' + - 'restful post add_concept_rest_resource' - 'revert article revisions' - 'revert concept revisions' - 'revert page revisions' diff --git a/config/sync/views.view.moderated_content.yml b/config/sync/views.view.moderated_content.yml index 7a4010e..228b536 100644 --- a/config/sync/views.view.moderated_content.yml +++ b/config/sync/views.view.moderated_content.yml @@ -2,7 +2,10 @@ uuid: 8347c43e-4379-4de8-9577-a481a608a372 langcode: en status: true dependencies: + config: + - workflows.workflow.concept_workflow module: + - content_moderation - node - user enforced: @@ -677,18 +680,18 @@ display: plugin_id: moderation_state_filter operator: 'not in' value: - editorial-published: editorial-published + concept_workflow-published: concept_workflow-published group: 1 exposed: false expose: - operator_id: '' - label: '' - description: '' + operator_id: moderation_state_1_op + label: 'Moderation state' + description: null use_operator: false - operator: '' + operator: moderation_state_1_op operator_limit_selection: false operator_list: { } - identifier: '' + identifier: moderation_state_1 required: false remember: false multiple: false @@ -811,7 +814,8 @@ display: - url.query_args - 'user.node_grants:view' - user.permissions - tags: { } + tags: + - 'config:workflow_list' moderated_content: id: moderated_content display_title: 'Moderated content' @@ -830,4 +834,5 @@ display: - url.query_args - 'user.node_grants:view' - user.permissions - tags: { } + tags: + - 'config:workflow_list' diff --git a/web/modules/custom/yufu_concept/README.md b/web/modules/custom/yufu_concept/README.md new file mode 100644 index 0000000..cd86f25 --- /dev/null +++ b/web/modules/custom/yufu_concept/README.md @@ -0,0 +1,15 @@ +# Yufu Concept + +Vsebuje funkcionalnosti povezane s pojmi - osnovnimi gradniki manifesta. + +## Endpoint /api/pojem/dodaj + +Post request endpoint za dodajanje pojmov. + +## Autocomplete za (povezane) pojme + +@TODO: To be implemented. + +## Posiljanje emailov + +https://drupal.stackexchange.com/questions/235157/message-not-being-sent-in-the-body-of-the-mail diff --git a/web/modules/custom/yufu_concept/config/install/rest.resource.add_concept_rest_resource.yml b/web/modules/custom/yufu_concept/config/install/rest.resource.add_concept_rest_resource.yml new file mode 100644 index 0000000..282f31e --- /dev/null +++ b/web/modules/custom/yufu_concept/config/install/rest.resource.add_concept_rest_resource.yml @@ -0,0 +1,17 @@ +langcode: en +status: true +dependencies: + module: + - serialization + - user + - yufu_concept +id: add_concept_rest_resource +plugin_id: add_concept_rest_resource +granularity: resource +configuration: + methods: + - POST + formats: + - json + authentication: + - cookie diff --git a/web/modules/custom/yufu_concept/config/install/rest.resource.concept_autocomplete_rest_resource.yml b/web/modules/custom/yufu_concept/config/install/rest.resource.concept_autocomplete_rest_resource.yml new file mode 100644 index 0000000..5495447 --- /dev/null +++ b/web/modules/custom/yufu_concept/config/install/rest.resource.concept_autocomplete_rest_resource.yml @@ -0,0 +1,17 @@ +langcode: en +status: true +dependencies: + module: + - serialization + - user + - yufu_concept +id: concept_autocomplete_rest_resource +plugin_id: concept_autocomplete_rest_resource +granularity: resource +configuration: + methods: + - GET + formats: + - json + authentication: + - cookie 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 new file mode 100644 index 0000000..258d09d --- /dev/null +++ b/web/modules/custom/yufu_concept/src/Plugin/rest/resource/AddConcept.php @@ -0,0 +1,221 @@ +currentUser = $current_user; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->getParameter('serializer.formats'), + $container->get('logger.channel.yufu_concept'), + $container->get('current_user'), + $container->get('entity_type.manager') + ); + } + + /** + * Ustvari nov koncept. + * + * Logika: + * 1. Zloadamo uporabnika preko emaila, ce ne obstaja, ga ustvarimo. + * 2. Zloadamo koncept prek uuid, ce ne obstaja, da ustvarimo. + * - avtor je zloadan prek maila + * + * @param \Symfony\Component\HttpFoundation\Request; + * Post request. + * + * @return \Drupal\rest\ResourceResponse + * Returns rest resource. + */ + public function post(Request $request) { + $response_status['status'] = FALSE; + $data = json_decode($request->getContent(), TRUE); + // You must to implement the logic of your REST Resource here. + // Use current user after pass authentication to validate access. + if (!$this->currentUser->hasPermission('access content')) { + throw new AccessDeniedHttpException(); + } + // Fields: naslov, text, povezani pojmi, email + // Optional fields: language, related concept. + if (empty($data['title']) || empty($data['text']) || empty($data['uuid'])) { + throw new MissingDataException('Title, uuid or text missing.'); + } + $uid = $this->getUserIdByEmail($data['email'] ?? null); + if ($concept = $this->getConceptFromUuid($data['uuid'])) { + // Concept exists - create a new revision. + $concept->setNewRevision(TRUE); + $concept->setRevisionUserId($uid); + $concept->set('title', $data['title']); + $concept->set('body', $data['text'] ?? $concept->body->value); + $concept->isDefaultRevision(FALSE); + $concept->setRevisionLogMessage('New revision by concept endpoint.'); + $concept->moderation_state->target_id = 'draft'; + $concept->save(); + $this->logger->notice('New concept @title revision @revid created by uid @uid.', [ + '@title' => $concept->getTitle(), + '@revid' => $concept->getRevisionId(), + '@uid' => $uid, + ]); + } + else { + // Concept does not exist - create a new node. + $concept = [ + 'type' => 'concept', + 'title' => $data['title'], + 'body' => $data['text'], + 'uuid' => $data['uuid'], + 'uid' => $uid, + 'moderation_state' => 'draft', + ]; + // @TODO Check if related concepts are set and add them to the concept. + // @TODO Check language and add set it on concept if exists. + /** @var \Drupal\node\Entity\NodeInterface $concept */ + $concept = $this->entityTypeManager->getStorage('node')->create($concept); + $concept->save(); + $this->logger->log(LogLevel::NOTICE, $this->t('Creating concept: @title', [ + '@title' => $concept->getTitle(), + ])); + } + $response_status['concept'] = [ + 'title' => $concept->label(), + 'id' => $concept->id(), + 'uuid' => $concept->uuid(), + 'revision_id' => $concept->getRevisionId(), + 'revision_create_time' => $concept->getRevisionCreationTime(), + 'revision_uid' => $concept->getRevisionUserId(), + 'uid' => $concept->uid->target_id, + ]; + $response = new ResourceResponse($response_status); + return $response; + } + + /** + * Get concept from uuid. + * + * If concept is new, return null. + * + * @param string|null $uuid + * Uuid of the node. + * + * @return \Drupal\node\NodeInterface|null + */ + protected function getConceptFromUuid(?string $uuid): ?NodeInterface { + if (!$uuid) { + return NULL; + } + $node_storage = $this->entityTypeManager->getStorage('node'); + if ($concept = end($node_storage->loadByProperties(['uuid' => $uuid]))) { + return $concept; + } + return NULL; + } + + /** + * Loads user from email or creates a new blocked user. + * + * Side effect is to create a new user. + * + * @param string|null $email + * Email provided by the fe call to endpoint. + * + * @return int + * Id of the user. + * + */ + protected function getUserIdByEmail(?string $email): int { + if (!$email) { + return 0; + } + // Load user by email. If it doesn't exist, create one. + $user_storage = $this->entityTypeManager->getStorage('user'); + if ($user = end($user_storage->loadByProperties(['mail' => $email]))) { + return $user->id(); + } + $user = [ + 'mail' => $email, + 'name' => $email, + 'status' => 0, + ]; + $new_user = $user_storage->create($user); + $new_user->save(); + return $new_user->id(); + } + +} diff --git a/web/modules/custom/yufu_concept/src/Plugin/rest/resource/ConceptAutocomplete.php b/web/modules/custom/yufu_concept/src/Plugin/rest/resource/ConceptAutocomplete.php new file mode 100644 index 0000000..ca8086a --- /dev/null +++ b/web/modules/custom/yufu_concept/src/Plugin/rest/resource/ConceptAutocomplete.php @@ -0,0 +1,116 @@ +entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->getParameter('serializer.formats'), + $container->get('logger.channel.yufu_concept'), + $container->get('entity_type.manager') + ); + } + + public function get(Request $request) { + $results = []; + $input = $request->query->get('q');// Get the typed string from the URL, if + // it exists. + $num = $request->query->get('num') ?? 10; + $start = $request->query->get('start') ?? 0; + if (!$input) { + return new JsonResponse($results); + } + $input = Xss::filter($input); + $node_storage = $this->entityTypeManager->getStorage('node'); + $query = $node_storage->getQuery() + ->condition('type', 'concept') + ->condition('title', $input, 'CONTAINS') + ->groupBy('nid') + ->sort('created', 'DESC') + ->accessCheck(TRUE) + ->range($start, $num); + $ids = $query->execute(); + $nodes = $ids ? $node_storage->loadMultiple($ids) : []; + foreach ($nodes as $id => $node) { + $results[$id] = [ + 'title' => $node->getTitle(), + 'id' => $node->id(), + 'revision_id' => $node->getRevisionId(), + 'revision_uid' => $node->getRevisionUserId(), + 'uuid' => $node->uuid(), + 'uid' => $node->uid->target_id, + ]; + } + return new JsonResponse($results); + } + +} diff --git a/web/modules/custom/yufu_concept/tests/local-emacs-test.rest b/web/modules/custom/yufu_concept/tests/local-emacs-test.rest new file mode 100644 index 0000000..d83459e --- /dev/null +++ b/web/modules/custom/yufu_concept/tests/local-emacs-test.rest @@ -0,0 +1,23 @@ +# _*_ restclient _*_ + +# Makes post http request to a resource from yufu_concept module. +# Make sure uuid is uniq or it will create new revision. + +POST https://yufu-manifest.ddev.site/api/pojem/dodaj +User-Agent: Emacs Restclient +Content-Type: application/json +Cookies: "SSESSb6f5da6ef565138b872e3e58509d8150=0z6Zk4tRwTF4WAR%2CSO6rH0K5F7hqKpbe28XRRHwvTxo%2CUnpC" + +{ +"title": "888 Emacs restclient test 888", +"text": "O wau. Emacs restclient. How nice. Dodam text. Spet.", +"email": "lio.novelli@radiostudent.si", +"uuid": "70c1ace2-aaaa-4dc5-84ed-3f21b83cb23a" +} + +# Testiramo autocomplete + +GET https://yufu-manifest.ddev.site/api/pojem/autocomplete?q=jadr +User-Agent: Emacs Restclient +Content-Type: application/json +Cookies: "SSESSb6f5da6ef565138b872e3e58509d8150=0z6Zk4tRwTF4WAR%2CSO6rH0K5F7hqKpbe28XRRHwvTxo%2CUnpC" diff --git a/web/modules/custom/yufu_concept/yufu_concept.info.yml b/web/modules/custom/yufu_concept/yufu_concept.info.yml new file mode 100644 index 0000000..cfc664a --- /dev/null +++ b/web/modules/custom/yufu_concept/yufu_concept.info.yml @@ -0,0 +1,7 @@ +name: Yufu Concept +type: module +description: Provides concept content type related functionality. +package: Yufu +core_version_requirement: ^9 || ^10 +dependencies: + - drupal:rest diff --git a/web/modules/custom/yufu_concept/yufu_concept.module b/web/modules/custom/yufu_concept/yufu_concept.module new file mode 100644 index 0000000..0a8a750 --- /dev/null +++ b/web/modules/custom/yufu_concept/yufu_concept.module @@ -0,0 +1,6 @@ +