Compare commits

...

27 Commits

Author SHA1 Message Date
Lio Novelli 13ac1bd83d Small changes but not fixing workflow. 2024-03-17 20:32:11 +01:00
Lio Novelli 2f0aff3f95 Improve email messages. 2024-03-17 20:32:11 +01:00
Lio Novelli 3e527de407 Olepsava kode in prevodi. 2024-03-17 20:32:11 +01:00
Lio Novelli 296398da36 Posiljanje mailov na prave naslove ob pravih spremembah. 2024-03-17 20:32:11 +01:00
Lio Novelli 2908033b20 WIP: Dodaj confige. 2024-03-17 20:32:11 +01:00
Lio Novelli 5ec525f3c1 WIP: Ogrodje za posiljanje mailov. Tranzicije konceptov. 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 1afd5ff56d Odstranjen ostanek 2024-03-17 20:32:11 +01:00
Jurij Podgoršek d07644853d Popravkci 2024-03-17 20:32:11 +01:00
Jurij Podgoršek a117e67d2f Popravki stilov in obnasanja 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 7f57bce85f Vizualno pedenanje, preimenovanje route manifesta, pogled posameznih
pojmov
2024-03-17 20:32:11 +01:00
Jurij Podgoršek ddcd40b56b Popravki, dokumentiranje 2024-03-17 20:32:11 +01:00
Jurij Podgoršek df89a76305 Config ne rabi bit property classa 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 33bdbba430 Popravljeno ime routa 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 5215860b6d Popravljena config lapsusa 2024-03-17 20:32:11 +01:00
janko 926d62a540 noga 2024-03-17 20:32:11 +01:00
janko 180baa39db css popravki. dodana provizorična 'navodila' za manifest. 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 9ced8ec152 Sortiranje, bugfix 2024-03-17 20:32:11 +01:00
janko 8e8c19ff9a dodaj pojem center, pojem css na pojem.vue (se mi zdi da je to to) 2024-03-17 20:32:11 +01:00
janko 4140184e60 glava img sizing 2024-03-17 20:32:11 +01:00
janko 021cbaffb7 ozadje naj bo fixed 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 0bcf439741 Min visina forma 2024-03-17 20:32:11 +01:00
Jurij Podgoršek 4b47a99b78 Popravki za dodajanje pojma 2024-03-17 20:32:11 +01:00
janko ab7c083530 oblikovanje pojma in mobilni stili. 2024-03-17 20:32:11 +01:00
Jurij Podgoršek b1a2f63e0d Envrc za composer izvajanje (drush), urejen services 2024-03-17 20:32:11 +01:00
Jurij Podgoršek fb75e98054 Zapri pojem form po predlogu 2024-03-17 20:32:11 +01:00
Jurij Podgoršek afe0878f6d Nekaj popravkov, pedenanje dodajanja novega pojma 2024-03-17 20:32:11 +01:00
Jurij Podgoršek c79472379d Oblikovanje glave, poskrolaj na pojem ce gres na link zanj 2024-03-17 20:32:11 +01:00
41 changed files with 1502 additions and 167 deletions

2
.envrc 100644
View File

@ -0,0 +1,2 @@
PATH_add vendor/bin

View File

@ -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.

View File

@ -23,6 +23,7 @@
"drupal/core-composer-scaffold": "^10.0",
"drupal/core-project-message": "^10.0",
"drupal/core-recommended": "^10.0",
"drupal/diff": "^1.1",
"drupal/gin": "^3.0@RC",
"drupal/gin_toolbar": "^1.0@RC",
"drupal/jsonapi_menu_items": "^1.2",

256
composer.lock generated
View File

@ -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": "185dc0d2be2be9514fc2dad73c4205d1",
"content-hash": "2ca7e5f483ed127efa757db4cb8d35ca",
"packages": [
{
"name": "asm89/stack-cors",
@ -62,6 +62,67 @@
},
"time": "2022-01-18T09:12:03+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": "2.6.2",
@ -1718,6 +1779,97 @@
},
"time": "2023-12-06T09:22: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",
@ -2476,6 +2628,67 @@
},
"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",
@ -3051,6 +3264,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": "v4.17.1",

View File

@ -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

View File

@ -25,9 +25,9 @@ content:
weight: 101
region: content
content_moderation_control:
weight: -20
settings: { }
third_party_settings: { }
weight: -20
region: content
links:
settings: { }

View File

@ -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

View File

@ -17,6 +17,7 @@ module:
contextual: 0
datetime: 0
dblog: 0
diff: 0
dynamic_page_cache: 0
editor: 0
etherpad_api: 0

View File

@ -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: { }

View File

@ -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

View File

@ -1,3 +1,3 @@
_core:
default_config_hash: 2OMXCScXUOLSYID9-phjO4q36nnnaMWNUlDxEqZzG1U
use_admin_theme: false
use_admin_theme: true

View File

@ -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'

View File

@ -8,7 +8,7 @@ dependencies:
_core:
default_config_hash: 12Bd0mJQFIaXAkRfMVCAAcZ0oaxm94PoK8oHR9hkLmY
id: content
label: Sadržaj
label: Vsebine
module: node
description: 'Find and manage content.'
tag: default

View File

@ -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:

View File

@ -4,6 +4,9 @@
<div class="vsebina">
<NuxtPage />
</div>
<footer>
<Noga />
</footer>
</section>
</template>
@ -12,6 +15,7 @@
:root {
--siva: #D9D9D9;
--bela: #fff;
--crna: #000;
--rdeca: #f00;
--roza: #ED008C;
}
@ -22,18 +26,36 @@
background: var(--siva);
}
a {
color: var(--crna);
}
h1 {
text-align: center;
font-size: 5rem;
margin: .2rem 0 1rem 0;
}
h2 {
text-transform: uppercase;
margin-top: 0;
}
.vsebina {
position: relative;
z-index: 50;
padding: 0 32px;
}
a.gumb {
text-decoration: underline;
font-size: 3rem;
position: relative;
display: flex;
width: 100%;
justify-content: center;
}
.stran {
position: relative;
background: var(--bela);
@ -63,4 +85,33 @@
width: fit-content;
}
footer {
position: relative;
z-index: 100;
}
@media screen and (max-width: 420px) {
.vsebina {
padding-right: 0;
padding-left: 0;
}
.stran {
margin: 0;
}
}
.okvir {
position: relative;
background: var(--bela);
margin: 2rem;
border-radius: 24px;
padding: 2rem;
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
max-width: 1216px;
margin-left: auto;
margin-right: auto;
}
</style>

View File

@ -1,26 +0,0 @@
<script setup="setup">
const { etherpadApiUrl } = useEtherpadApi()
const prikazi = ref(false)
const revisionId = ref(null)
const dodajPojem = async () => {
revisionId.value = crypto.randomUUID()
// Ustvari nov, prazen pad
const padId = revisionId.value
const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${padId}`)
prikazi.value = true
}
</script>
<template>
<div>
<div class="gumb" @click="dodajPojem()">
Dodaj pojem
</div>
<PojemForm v-if="prikazi" :revisionId="revisionId" />
</div>
</template>

View File

@ -2,21 +2,34 @@
const route = useRoute()
const pot = computed(() => route.path)
const naManifestu = computed(() => route.path.indexOf('/pojmi') === 0)
const naManifestu = computed(() => route.path.indexOf('/manifest') === 0)
</script>
<template>
<section class="glava" :class="{ manifest: naManifestu }">
<ul class="meni">
<li><NuxtLink to="/">Domov</NuxtLink></li>
<li><NuxtLink to="/pojmi">Manifest</NuxtLink></li>
<li><NuxtLink to="https://yugofuturism.kompot.si/user" target="_blank">admin</NuxtLink></li>
</ul>
<img src="/images/zvezda.png">
<section class="glava" :class="{ manifest: naManifestu }">
<ul class="meni">
<li>
<NuxtLink to="/">
Domov
</NuxtLink>
</li>
<li>
<NuxtLink to="/manifest">
Manifest
</NuxtLink>
</li>
<li>
<NuxtLink to="https://yugofuturism.kompot.si/user" target="_blank">
admin
</NuxtLink>
</li>
</ul>
<h1 v-if="naManifestu">MANIFEST</h1>
<h1 v-else>JUGOFUTURIZEM</h1>
</section>
<img src="/images/zvezda.png">
<h1 v-if="naManifestu">MANIFEST</h1>
<h1 v-else>JUGOFUTURIZEM</h1>
</section>
</template>
<style scoped>
@ -34,17 +47,40 @@ const naManifestu = computed(() => route.path.indexOf('/pojmi') === 0)
h1 {
font-family: Trailers;
font-size: 5rem;
font-size: 16rem;
z-index: 50;
}
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 10;
@media screen and (max-width: 1090px) {
h1 { font-size: 10rem; }
}
@media screen and (max-width: 768px) {
h1 { font-size: 8rem; }
}
@media screen and (max-width: 560px) {
h1 { font-size: 5rem; }
}
img {
position: fixed;
top: -10vh;
left: -10vw;
width: 120vw;
height: 120vh;
z-index: 10;
object-fit: cover;
}
@media screen and (min-width: 1980px) {
img {
width: 100vw;
height: 100vh;
left: 0;
top: 0;
}
}
.meni {
position: absolute;
@ -80,4 +116,14 @@ ul {
}
}
@media screen and (min-width: 768px) {
.glava img {
width: 768px;
height: auto;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
</style>

View File

@ -0,0 +1,118 @@
<script setup="setup">
const route = useRoute()
const pot = computed(() => route.path)
const naManifestu = computed(() => route.path.indexOf('/pojmi') === 0)
</script>
<template>
<section class="noga">
<div class="maska">
<a href="https://maska.si" target="_blank">
<img src="/images/maska-logo.svg" />
</a>
</div>
<div class="kompot">
<p>
<a href="https://kompot.si" target="_blank">
<img src="/images/kompot_sm.png">
2024
Kompot
</a>
</p>
</div>
<div class="eu-projekti">
<div class="eu-projekti-logos">
<img src="/images/peripheralvisions.png" />
<img src="/images/eu-logo.svg" />
</div>
<p>Funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or European Education and Culture Executive Agency (EACEA). Neither the European Union nor the granting authority can be held responsible for them.</p>
</div>
</section>
</template>
<style scoped>
.noga {
background-color: black;
z-index: 100;
display: flex;
flex-direction: row;
justify-content: space-around;
align-content: center;
flex-wrap: wrap;
padding: 2rem;
align-content: center;
}
p, a {
color: white;
text-decoration: none;
}
a {
margin-left: auto;
margin-right: auto;
}
.noga > div {
position: relative;
display: block;
height: 70%;
max-width: 30%;
margin-top: auto;
margin-bottom: auto;
min-height: 2rem;
height: fit-content;
}
img {
width: 100%;
height: auto;
max-width: 300px;
}
.eu-projekti {
padding: 1rem;
display: flex;
flex-direction: column;
background-color: #D9D9D9;
border-radius: 16px;
}
a {
display: flex;
flex-direction: column;
align-items: center;
}
.eu-projekti-logos {
position: relative;
width: 100%;
display: flex;
justify-content: space-between;
}
.eu-projekti-logos img {
max-width: 40%;
}
.eu-projekti p {
color: black;
}
@media screen and (max-width: 764px) {
.noga {
display: block;
}
.noga div {
max-width: 100%;
}
.kompot {
text-align: center;
}
}
</style>

View File

@ -3,19 +3,23 @@
import { stripHtml } from 'string-strip-html'
const { etherFetch } = useEtherpadApi()
const store = usePojmiStore()
const route = useRoute()
const props = defineProps({
naslov: String
})
const pojem = computed(() => store.pojmi[props.naslov])
const revisionId = computed(() => pojem.value.id)
if (props.naslov && !(props.naslov in store.pojmi)) {
await store.naloziPojme()
}
await store.naloziPojme()
const pojem = computed(() => store.pojmi[props.naslov])
const revisionId = computed(() => pojem.value ? pojem.value.id : null)
const urejanje = ref(false)
const container = ref(null)
const obrazec = ref(null)
const urediPojem = async () => {
// Ustvari pad s tekstom pojma, ce se ne obstaja
@ -27,78 +31,78 @@ const urediPojem = async () => {
urejanje.value = true
}
onMounted(() => {
// Prazen pojem? Nazaj na manifest
if (!pojem.value.id) {
navigateTo('/manifest#skrol')
}
// Link na editiranje pojma? Poskrolaj nanj + odpri editiranje
if (route.params.guid === revisionId.value) {
urejanje.value = true
setTimeout(() => obrazec.value.scrollIntoView({ behavior: 'smooth' }), 50)
} else {
// Sicer samo poskrolaj na pojem
setTimeout(() => container.value.parentNode.scrollIntoView({ behavior: 'smooth' }), 50)
}
})
</script>
<template>
<section class="pojem">
<div>
<h2>{{ pojem.naslov }}</h2>
<section ref="container">
<div v-if="!urejanje" class="gumb" @click="urediPojem">Uredi</div>
<div v-if="pojem" class="pojem">
<h2>{{ naslov }}</h2>
<div class="tekst" v-html="pojem.tekst" />
</div>
<PojemForm v-if="urejanje"
<PojemForm v-if="urejanje"
ref="obrazec"
:revisionId="revisionId"
:pojem="pojem"
:urejanje="urejanje"
:onZapri="()=>urejanje=false" />
<div>
<div v-if="!urejanje" class="gumb" @click="urediPojem">Uredi</div>
</div>
:pojem="pojem"
:onZapri="() => { urejanje = false; store.naloziPojme() }" />
</section>
</template>
<style scoped>
section.pojem {
position: relative;
background: var(--bela);
margin: 2rem;
border-radius: 24px;
padding: 2rem;
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
max-width: 1216px;
margin-left: auto;
margin-right: auto;
section {
width: 100%;
}
form.pojem {
width: calc(50% - 32px);
margin-left: 32px;
gap: 1rem;
}
.pojem > div {
.pojem {
position: relative;
width: 50%;
float: left;
}
h2 {
text-transform: uppercase;
margin-top: 0;
}
form.pojem {
width: calc(50% - 32px);
margin-left: 32px;
gap: 1rem;
float: right;
}
.gumb {
position: absolute;
bottom: 0;
position: sticky;
top: 0;
right: 0;
text-decoration: underline;
font-size: 3rem;
float: right;
}
@media screen and (max-width: 768px) {
PojemForm {
width: 50%;
float: left;
}
.pojem > div {
.pojem {
min-height: 4rem;
width: 100%;
}
}
@media screen and (max-width: 325px) {
.pojem {
margin-right: 0;
form.pojem {
margin-left: 0;
min-height: 70vh;
}
}
</style>

View File

@ -7,7 +7,6 @@ const store = usePojmiStore()
const props = defineProps({
revisionId: String,
pojem: Object,
urejanje: Object,
onZapri: Function
})
@ -16,45 +15,76 @@ let email = ref('')
const oddajPredlog = async data => {
// @TODO vsebina pada v tekst, testirat
if (!naslov.value) {
alert("Manjka naslov pojma!")
return
}
const starNaslov = props.pojem.naslov
const resp = await etherFetch('/getText', { padID: props.revisionId })
store.ustvariPojem({
const stvarjenje = store.ustvariPojem({
title: naslov.value,
email: email.value,
text: resp.data.text,
uuid: props.revisionId
})
console.log(stvarjenje)
alert("sprememba predlagana!")
// @TODO vrni urejanje v navaden prikaz, ko je končano!
urejanje.set(false)
stvarjenje.then(() => {
console.log('NOV NASLOV?', starNaslov, props.pojem.naslov)
if (starNaslov !== props.pojem.naslov) {
navigateTo('/manifest/' + encodeURIComponent(props.pojem.naslov))
}
})
props.onZapri()
}
// @TODO tole raje v pojmi.vue oz nov_pojem.vue - page!
const etherNalozen = ev => {
window.location.hash = props.revisionId
if (props.pojem.nov) {
navigateTo('/manifest/dodaj/' + props.revisionId, {
replace: true
})
} else {
navigateTo('/manifest/' + encodeURIComponent(props.pojem.naslov) + '/uredi/' + props.revisionId, {
replace: true
})
}
//window.location.hash = props.revisionId
}
</script>
<template>
<form class="pojem" @submit.prevent="oddajPredlog">
<input name="naslov" type="text" v-model="naslov">
<form class="pojem" @submit.prevent>
<input name="naslov" type="text" placeholder="Naslov" v-model="naslov">
<EtherpadTextarea :onLoad="etherNalozen" :revisionId="props.revisionId" />
<EtherpadTextarea :onLoad="etherNalozen" :revisionId="props.revisionId" />
<input name="email" type="email" placeholder="E-poštni naslov" v-model="email">
<div class="gumbi">
<button class="gumb" @click="props.onZapri">Zapri</button>
<input class="gumb" type="submit" value="Predlagaj">
</div>
</form>
<input name="email" type="email" placeholder="E-poštni naslov" v-model="email">
<div class="gumbi">
<button class="gumb" @click="props.onZapri">
{{ props.zapriLabel ?? 'Zapri' }}
</button>
<button class="gumb oddaj" @click="oddajPredlog">
Predlagaj
</button>
</div>
</form>
</template>
<style>
.etherpad-textarea {
width: 100%;
flex-grow: 1;
border-radius: 16px;
border: 2px solid var(--siva);
width: 100%;
flex-grow: 1;
border-radius: 16px;
border: 2px solid var(--siva);
box-sizing: border-box;
min-height: 500px;
}
input {
@ -68,12 +98,19 @@ input {
flex-direction: row;
justify-content: space-between;
}
.gumb,
.gumbi input,
.gumbi button {
text-transform: uppercase;
padding: 0;
margin: 0;
}
.pojem.form .gumb {
width: 50%;
}
.pojem input[type=submit] {
.pojem button.oddaj {
color: var(--roza);
}

View File

@ -0,0 +1,10 @@
import * as cryptoServer from 'crypto'
export const useCrypto = () => {
return {
getRandomUUID: () => (typeof window === 'undefined')
? cryptoServer.randomBytes(16).toString('hex') // Server code
: crypto.randomUUID() // Browser code
}
}

View File

@ -28,7 +28,6 @@ export default defineNuxtConfig({
changeOrigin: true,
pathFilter:
process.env.FILE_PATH
}
},
runtimeConfig: {
@ -38,5 +37,22 @@ export default defineNuxtConfig({
etherpadUrl: process.env.ETHERPAD_URL,
etherpadPrefix: process.env.ETHERPAD_PREFIX
}
},
hooks: {
'pages:extend' (pages) {
pages.push({
name: 'poglej_pojem',
path: '/manifest/:naslov',
file: '~/pages/manifest/pojem.vue'
}, {
name: 'uredi_pojem',
path: '/manifest/:naslov/uredi/:guid',
file: '~/pages/manifest/pojem.vue'
}, {
name: 'nov_pojem_guid',
path: '/manifest/dodaj/:guid',
file: '~/pages/manifest/dodaj.vue'
})
}
}
})

View File

@ -0,0 +1,47 @@
<script setup="setup">
const { getRandomUUID } = useCrypto()
const { etherpadApiUrl } = useEtherpadApi()
const route = useRoute()
const revisionId = ref(null)
const obrazec = ref(null)
revisionId.value = route.params.guid ? route.params.guid : getRandomUUID()
// Ustvari nov, prazen pad
// @TODO parameter za seranje linka?
const padId = revisionId.value
const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${padId}`)
const onZapri = () => { navigateTo('/manifest#skrol') }
onMounted(() => {
setTimeout(() => {
console.log('element?', obrazec.value)
debugger
obrazec.value.scrollIntoView({ behavior: 'smooth' })
}, 50)
})
</script>
<template>
<section class="okvir" ref="obrazec">
<PojemForm
:pojem="{ nov: true }"
:revisionId="revisionId"
zapriLabel="Nazaj"
:onZapri="onZapri" />
</section>
</template>
<style scoped>
section.okvir {
max-width: 608px;
}
form.pojem {
width: 100%;
}
</style>

View File

@ -0,0 +1,45 @@
<script setup="setup">
const store = usePojmiStore()
await store.naloziPojme()
const navodila = ref(null)
onMounted(() => {
if (location && location.hash == '#skrol') {
setTimeout(() => {
navodila.value.scrollIntoView({ behavior: 'smooth' })
}, 50)
}
})
</script>
<template>
<h3 class="navodila" ref="navodila">Vsak lahko prispeva k vsebinam manifesta. Predlaga lahko nov pojem ali ureja, dopolni ali predela obstoječe.</h3>
<NuxtLink class="gumb" to="/manifest/dodaj">
Dodaj Nov Pojem
</NuxtLink>
<NuxtLink
v-for="pojem in Object.keys(store.pojmi)"
:naslov="pojem"
:to="`/manifest/${encodeURIComponent(pojem)}`"
class="okvir">
<h2>{{ pojem }}</h2>
</NuxtLink>
</template>
<style scoped>
a {
clear: both;
}
h2 {
margin-bottom: 0;
}
.navodila {
padding-left: 2rem;
padding-right: 2rem;
text-align: center;
}
</style>

View File

@ -0,0 +1,30 @@
<script setup="setup">
const { etherpadApiUrl } = useEtherpadApi()
const { naslov, guid } = useRoute().params
const { getRandomUUID } = useCrypto()
const revisionId = ref(null)
revisionId.value = guid ?? getRandomUUID()
// Ustvari nov, prazen pad
// @TODO parameter za seranje linka?
const padId = revisionId.value
const resp = await $fetch(`${etherpadApiUrl}/createPad?padID=${padId}`)
</script>
<template>
<div class="okvir">
<Pojem :naslov="naslov" />
</div>
<NuxtLink class="gumb" to="/manifest#skrol">
Manifest
</NuxtLink>
<br>
<br>
</template>

View File

@ -1,16 +0,0 @@
<script setup="setup">
const store = usePojmiStore()
await store.naloziPojme()
</script>
<template>
<DodajPojem />
<!--
<Pojem naslov="Yugofuturist manifesto" />
<Pojem naslov="Jadran potem" />
<Pojem naslov="JUGA 2023" />
-->
<Pojem v-for="pojem in Object.keys(store.pojmi)" :naslov="pojem" />
</template>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 498 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -6,11 +6,11 @@ export const usePojmiStore = defineStore('pojmi', {
async naloziPojme() {
const { jsonApiUrl, headers, deserialize } = useApi()
const data = await $fetch(`${jsonApiUrl}/node/concept`, { headers })
const data = await $fetch(`${jsonApiUrl}/node/concept?sort=-changed`, { headers })
this.pojmi = await deserialize(data, s => ({
id: s.id,
naslov: s.title,
tekst: s.body.processed,
tekst: s.body ? s.body.processed : '',
media: s.field_media
}), 'naslov')
},

View File

@ -1,4 +1,4 @@
etherpad_api.example:
etherpad_api.proxy:
path: '/etherpad-api/{components}'
defaults:
_title: 'Etherpad API'

View File

@ -17,13 +17,6 @@ class Client {
*/
protected $httpClient;
/**
* The config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
protected $apiKey = null;
protected $baseUrl = null;
@ -40,11 +33,11 @@ class Client {
*/
public function __construct(ClientInterface $httpClient, ImmutableConfig $config) {
$this->httpClient = $httpClient;
$this->config = $config;
$this->baseUrl = $config->get('url') ? rtrim($config->get('url'), '/') : null;
$this->apiKey = $config->get('key');
}
/* Preveri veljavnost tokena-a s klicom na etherpadov api. */
public function checkToken() {
if ($this->baseUrl) {
return $this->request('checkToken');
@ -53,7 +46,8 @@ class Client {
}
/**
* Method description.
* Poizvedi na etherpadov API. Doda baseURL in verzijo pred zeljen url, doda
* API ključ in uredi parametre. Vendo je POST!
*/
public function request($url, $opts = []) {
$uri = "{$this->baseUrl}/" . self::API_VERSION . '/' . explode('?', $url)[0];

View File

@ -36,6 +36,8 @@ class EtherpadApiController extends ControllerBase {
$data = array_merge($request->query->all(), $request->request->all());
if ($data['padID']) {
// @TODO prefix v config!
// Zahtevnejše ampak lepše bi bilo pa uporabit group API:
// https://etherpad.org/doc/v1.8.4/#index_creategroup
$data['padID'] = 'yufu-' . $data['padID'];
}
$opts = ['form_params' => $data];

View File

@ -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)

View File

@ -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"

View File

@ -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 [];
}

View File

@ -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(),

View File

@ -208,19 +208,18 @@ parameters:
cors.config:
enabled: true
# Specify allowed headers, like 'x-allowed-header'.
allowedHeaders: ['x-csrf-token','authorization','content-type','accept','origin','x-requested-with','access-control-allow-origin','x-allowed-header','*']
allowedHeaders: []
# Specify allowed request methods, specify ['*'] to allow all possible ones.
allowedMethods: ['*']
allowedMethods: []
# Configure requests allowed from specific origins. Do not include trailing
# slashes with URLs.
allowedOrigins: ['*']
allowedOrigins: ['yufu.kompot.si', 'localhost']
# Sets the Access-Control-Expose-Headers header.
exposedHeaders: false
# Sets the Access-Control-Max-Age header.
maxAge: false
# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: true
allowCredentials: true
supportsCredentials: false
queue.config:
# The maximum number of seconds to wait if a queue is temporarily suspended.

View File

@ -4,21 +4,6 @@
# 'example.settings.local.php' file, which sits next to this file.
parameters:
http.response.debug_cacheability_headers: true
cors.config:
enabled: true
# Specify allowed headers, like 'x-allowed-header'.
allowedHeaders: ['x-csrf-token','authorization','content-type','accept','origin','x-requested-with']
# Specify allowed request methods, specify ['*'] to allow all possible ones.
allowedMethods: ['*']
# Configure requests allowed from specific origins. Do not include trailing
# slashes with URLs.
allowedOrigins: ['*']
# Sets the Access-Control-Expose-Headers header.
exposedHeaders: false
# Sets the Access-Control-Max-Age header.
maxAge: false
# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: false
services:
cache.backend.null:
class: Drupal\Core\Cache\NullBackendFactory