From 7410e107a214dd25957c5315f96a5d72028fa397 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:04:28 +0200 Subject: [PATCH 01/12] HyphaDatatypePage: Add getLanguages method This returns a list of all the languages for which this page is defined (i.e. has an entry in hypha.xml). --- system/core/pages.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/system/core/pages.php b/system/core/pages.php index 5da80d22..183d735c 100644 --- a/system/core/pages.php +++ b/system/core/pages.php @@ -103,6 +103,18 @@ public function renderExcerpt(HyphaDomElement $container) { $this->renderSingleLine($container); } + /** + * Returns a list of language codes this page is + * translated in, based on the languages listed in + * hypha.xml. + */ + public function getLanguages() { + $result = []; + foreach($this->pageListNode->getElementsByTagName('language') as $lang) + $result[] = $lang->getAttribute('id'); + return $result; + } + /* * Returns a date typically used for sorting these pages * (i.e. the publish date or last update date). From 504ce0a9fadaeace194678602049f05cdb3da183 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:08:34 +0200 Subject: [PATCH 02/12] hypha_indexLanguages: Allow linking to subpages --- system/core/base.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/system/core/base.php b/system/core/base.php index e69314aa..fb6addf3 100644 --- a/system/core/base.php +++ b/system/core/base.php @@ -783,8 +783,9 @@ function hypha_incrementStats($timestamp) { Parameters: $page - DOMElement containing page settings $language - page language + $subpage - added to the page url, a / is added automatically */ - function hypha_indexLanguages($page, $language) { + function hypha_indexLanguages($page, $language, $subpage='') { $langList = hypha_getUsedContentLanguages(); if (count($langList)) asort($langList); @@ -797,8 +798,12 @@ function hypha_indexLanguages($page, $language) { foreach($langList as $lang) { if ($lang == $language) $index.= ''.$lang.''; elseif (!$page || array_key_exists($lang, $pageLangList)) { - if ($page) $index.= ''.$lang.''; - else $index.= ''.$lang.''; + if ($page) + $url = $lang.'/'.$pageLangList[$lang]; + else + $url = 'index/' . $lang; + if ($subpage) $url .= '/'.$subpage; + $index.= ''.$lang.''; } else $index.= ''.$lang.''; } From cb053f5d42d7513423b8848832cd5f7ef38b305f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 11:07:58 +0200 Subject: [PATCH 03/12] WIP: festival: Add about view This just adds code to render the page, editing must be done manually. --- system/datatypes/festival.php | 10 ++++++++++ system/languages/en.php | 1 + system/languages/nl.php | 1 + 3 files changed, 12 insertions(+) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index d41cc7a8..78913ff0 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -24,6 +24,7 @@ class festivalpage extends HyphaDatatypePage { const FIELD_NAME_TITLE = 'title'; const FIELD_NAME_WEBSITE = 'website'; + const PATH_ABOUT = 'about'; const PATH_CONFIRMATION_NEEDED = 'confirmation-needed'; const PATH_CONFIRM = 'confirm'; const PATH_CONTRIBUTE = 'contribute'; @@ -45,6 +46,7 @@ class festivalpage extends HyphaDatatypePage { const CONFIG_TAG_FORM = 'form'; const CONFIG_TAG_DAYS = 'config'; const CONFIG_TAG_LOCATIONS = 'config'; + const CONFIG_ID_ABOUT = 'about'; const CONFIG_ID_TITLE = 'festival-title'; const CONFIG_ID_SIGNUP_FORM = 'signup-form'; const CONFIG_ID_CONTRIBUTION_FORM = 'contribution-form'; @@ -107,6 +109,7 @@ public function process(HyphaRequest $request) { if (isUser() && !in_array($request->getView(), [self::PATH_SETTINGS])) { $commands = $this->html->find('#pageCommands'); + $commands->append($this->makeActionButton(__('about'), self::PATH_ABOUT)); $commands->append($this->makeActionButton(__('settings'), self::PATH_SETTINGS)); $commands->append($this->makeActionButton(__('festival-signup'), self::PATH_SIGNUP)); $commands->append($this->makeActionButton(__('festival-contribute'), self::PATH_CONTRIBUTE)); @@ -124,6 +127,7 @@ public function process(HyphaRequest $request) { switch ([$request->getView(), $request->getCommand()]) { case [null, null]: return $this->lineupView($request); case [null, self::CMD_DELETE]: return $this->deleteAction($request); + case [self::PATH_ABOUT, null]: return $this->aboutView($request); case [self::PATH_CONFIRMATION_NEEDED, null]: return $this->confirmationNeededView($request); case [self::PATH_CONFIRM, null]: return $this->confirmView($request); case [self::PATH_CONTRIBUTE, null]: return $this->contributeView($request); @@ -852,6 +856,12 @@ protected function contributionSaveAction(HyphaRequest $request) { return ['redirect', $lineup_url]; } + protected function aboutView() { + $this->html->find('#pagename')->text($this->getConfig('festival-title')); + $content = $this->getConfigElement(self::CONFIG_ID_ABOUT); + if ($content) + $this->html->find('#main')->append($content->children()); + } protected function lineupView(HyphaRequest $request) { $html = ''; diff --git a/system/languages/en.php b/system/languages/en.php index bfc256f2..7a166e56 100644 --- a/system/languages/en.php +++ b/system/languages/en.php @@ -288,6 +288,7 @@ "festival-contribution-added-body" => '

Your contribution "[[title]]" has been added and is now shown on the lineup.

To edit your contribution, use this link.

', // festival - buttons & links + "festival-about" => "about", "festival-modify" => "modify", "festival-contribute" => "contribute", "festival-edit-contribution" => "edit contribution", diff --git a/system/languages/nl.php b/system/languages/nl.php index 4c3ba3e4..4ac1e161 100644 --- a/system/languages/nl.php +++ b/system/languages/nl.php @@ -283,6 +283,7 @@ "festival-contribution-added-body" => '

Je bijdrage "[[title]]" is toegevoegd en wordt nu in de lineup weergegeven.

Om je bijdrage te wijzigen, gebruik deze link.

', // festival - buttons & links + "festival-about" => "info", "festival-modify" => "wijzigen", "festival-contribute" => "bijdragen", "festival-edit-contribution" => "wijzig bijdrage", From dab685341a3ec918e21c94e9fcf460aaf003e3c9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 11:09:09 +0200 Subject: [PATCH 04/12] WIP: festival: Show configurable headers on timetable and lineup This just adds code to render the headers, editing must be done manually. --- system/datatypes/festival.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 78913ff0..52b7d36d 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -52,6 +52,8 @@ class festivalpage extends HyphaDatatypePage { const CONFIG_ID_CONTRIBUTION_FORM = 'contribution-form'; const CONFIG_ID_DAYS = 'days'; const CONFIG_ID_LOCATIONS = 'locations'; + const CONFIG_ID_LINEUP_HEADER = 'lineup-header'; + const CONFIG_ID_TIMETABLE_HEADER = 'timetable-header'; const CONFIG_ATTR_VALUE = 'value'; const TAG_PARTICIPANTS_CONTAINER = 'participants'; @@ -865,6 +867,9 @@ protected function aboutView() { protected function lineupView(HyphaRequest $request) { $html = ''; + $header = $this->getConfigElement(self::CONFIG_ID_LINEUP_HEADER); + if ($header) + $html .= '
' . $header->html() . '
'; $contributions = $this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->children(); foreach($contributions as $contribution) { $html.= $this->buildContribution($contribution); @@ -943,8 +948,11 @@ protected function timetableView(HyphaRequest $request) { $days = $this->getConfigElement(self::CONFIG_ID_DAYS, self::CONFIG_TAG_DAYS)->children(); $locations = $this->getConfigElement(self::CONFIG_ID_LOCATIONS, self::CONFIG_TAG_LOCATIONS)->children(); - // iterate over all dates $html = ''; + $header = $this->getConfigElement(self::CONFIG_ID_TIMETABLE_HEADER); + if ($header) + $html .= '
' . $header->html() . '
'; + // iterate over all dates $d = 0; foreach($days as $day) { $daybegin = $day->getAttribute(self::ATTR_DAY_BEGIN); From 0c2ce8aaaf8517657892e113f1fa76543eca4472 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:10:16 +0200 Subject: [PATCH 05/12] WIP: festival: Allow translating config elements This allows passing a language parameter to the getConfigElement helper, to retrieve a translated value. --- system/datatypes/festival.php | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 52b7d36d..b4a3704e 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -45,6 +45,7 @@ class festivalpage extends HyphaDatatypePage { const CONFIG_TAG = 'config'; const CONFIG_TAG_FORM = 'form'; const CONFIG_TAG_DAYS = 'config'; + const CONFIG_TAG_LANGUAGE = 'language'; const CONFIG_TAG_LOCATIONS = 'config'; const CONFIG_ID_ABOUT = 'about'; const CONFIG_ID_TITLE = 'festival-title'; @@ -96,6 +97,8 @@ class festivalpage extends HyphaDatatypePage { const ATTR_DAY_END = 'end'; const ATTR_LOCATION_DISPLAY = 'display'; + const ATTR_LANGUAGE_ID = 'id'; + public function __construct($pageListNode, RequestContext $O_O) { parent::__construct($pageListNode, $O_O); $this->xml = new Xml('festival', Xml::multiLingualOn, Xml::versionsOff); @@ -172,18 +175,39 @@ protected function getConfig($id, $attribute = self::CONFIG_ATTR_VALUE) { * Retrieve the XML element with the given id and return * it. * + * If a language is given, return a language subtag with + * the given id, or if it does not exist, any other + * language subtag. + * * If the element does not exist and a tagname is given, * it is created using the given tagname. Otherwise, * null is returned. */ - protected function getConfigElement($id, $tagname = null) { + protected function getConfigElement($id, $tagname = null, $language = null) { $elem = $this->xml->getElementById($id); if (!$elem && $tagname) { $elem = $this->xml->createElement($tagname); $elem->setId($id); $this->xml->documentElement->appendChild($elem); } - return $elem; + + if ($language) { + $firstlang = null; + foreach ($elem->find(self::CONFIG_TAG_LANGUAGE) as $lang) { + if ($firstlang === null) + $firstlang = $lang; + if ($lang->getAttribute(self::ATTR_LANGUAGE_ID) == $language) + return $lang; + } + if ($firstlang === null && $tagname) { + $firstlang = $this->xml->createElement(self::CONFIG_TAG_LANGUAGE); + $firstlang->setAttribute(self::ATTR_LANGUAGE_ID, $language); + $elem->appendChild($firstlang); + } + return $firstlang; + } else { + return $elem; + } } /** From 2d2fbd5cb91737b3033d3fe2cc38b1cf435fbade Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:15:55 +0200 Subject: [PATCH 06/12] WIP: festival: Allow translating signup and contribution form This no longer reads the forms from the config elements directly, but looks for a language subtag with the right id. Note that this does not allow the contributions themselves to be translated yet, just the displayed form. Also note that this breaks compatibility, without some form of migration, any custom forms will become unused. --- system/datatypes/festival.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index b4a3704e..810e052c 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -395,7 +395,7 @@ protected function deleteAction(HyphaRequest $request) { * Create a signup form, based on the one configured. */ private function createSignupForm(array $values = []) { - $html = $this->getConfigElement(self::CONFIG_ID_SIGNUP_FORM, self::CONFIG_TAG_FORM)->children(); + $html = $this->getConfigElement(self::CONFIG_ID_SIGNUP_FORM, self::CONFIG_TAG_FORM, $this->language)->children(); if ($html->count() == 0) { $html = <<html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, join('/', $request->getArgs()))); $form->updateDom(); $this->html->find('#main')->append($form); @@ -657,7 +658,7 @@ protected function paymenthookView(HyphaRequest $request) { } private function createContributionForm(array $values = []) { - $html = $this->getConfigElement(self::CONFIG_ID_CONTRIBUTION_FORM, self::CONFIG_TAG_FORM)->children(); + $html = $this->getConfigElement(self::CONFIG_ID_CONTRIBUTION_FORM, self::CONFIG_TAG_FORM, $this->language)->children(); if ($html->count() == 0) { $html = <<html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, join('/', $request->getArgs()))); + // Update the form to include any data $form->updateDom(); From e9a09e31e22ae979482e0d20b71662c17ec76071 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:28:16 +0200 Subject: [PATCH 07/12] WIP: festival: Allow translation contributions This moves most attributes on the contribution tag to new language subtags, allowing to specify most things for multiple languages. The contribution form can now be displayed in different languages, which then also edits the selected language. The timetable and lineup views now also have a language selector, causing all contributions to be displayed in the selected language (if available, falling back to another language otherwise). This introduces a new FestivalContribution class that simply wraps the contribution XML node, adding some methods to access translated attributes and subtags. --- system/datatypes/festival.php | 181 +++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 46 deletions(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 810e052c..6007153d 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -75,6 +75,7 @@ class festivalpage extends HyphaDatatypePage { const TAG_CONTRIBUTION_DESCRIPTION = 'description'; const TAG_CONTRIBUTION_NOTES = 'notes'; const TAG_CONTRIBUTION_EVENT = 'event'; + const TAG_CONTRIBUTION_LANGUAGE = 'language'; const ATTR_PARTICIPANT_EMAIL_CONFIRMED = 'email-confirmed'; const ATTR_PARTICIPANT_EMAIL = 'email'; @@ -222,6 +223,14 @@ protected function setConfig($id, $value, $tagname = self::CONFIG_TAG, $attribut return $config->setAttribute($attribute, $value); } + protected function getContributions() { + $result = []; + foreach ($this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->children() as $contribution) { + $result[] = new FestivalContribution($contribution, $this->language); + } + return $result; + } + /** * Show the admin display with registrations. */ @@ -283,18 +292,18 @@ protected function contributionsView(HyphaRequest $request) { $this->html->find('#main')->appendChild($table); $table->addClass('contributions'); $table->addHeaderRow()->addCells([__('name'), __('title'), __('category'), __('website')]); - foreach ($this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->children() as $contribution) { + foreach ($this->getContributions() as $contribution) { $row = $table->addRow(); - $row->addCell($contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME)); - $row->addCell($contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE)); - $row->addCell($contribution->getAttribute(self::ATTR_CONTRIBUTION_CATEGORY)); - $row->addCell($contribution->getAttribute(self::ATTR_CONTRIBUTION_WEBSITE)); + $row->addCell($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME)); + $row->addCell($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE)); + $row->addCell($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_CATEGORY)); + $row->addCell($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_WEBSITE)); $button = $this->makeActionButton(__('edit'), self::PATH_CONTRIBUTE . '/'.$contribution->getId()); $row->addCell()->append($button); - $description = $contribution->getOrCreate(self::TAG_CONTRIBUTION_DESCRIPTION)->text(); - $imgfilename = $contribution->getAttribute(self::ATTR_CONTRIBUTION_IMAGE); + $description = $contribution->getDescription(); + $imgfilename = $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_IMAGE); if ($description || $imgfilename) { $cell = $table->addRow()->addCell(__('description') . ': ' . $description); $cell->setAttribute('colspan', 5); @@ -302,7 +311,7 @@ protected function contributionsView(HyphaRequest $request) { if ($imgfilename) { $imgtag = $this->html->createElement('img'); $cell->insertBefore($imgtag, $cell->firstChild); - $image = new HyphaImage($contribution->getAttribute(self::ATTR_CONTRIBUTION_IMAGE)); + $image = new HyphaImage($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_IMAGE)); $imgtag->setAttribute('src', $image->getUrl(50, 50)); } } @@ -750,16 +759,16 @@ protected function contributeView(HyphaRequest $request) { $form = $this->createContributionForm(); if ($editing) { + $contribution = new FestivalContribution($obj, $this->language); // create form - $description = $obj->get(self::TAG_CONTRIBUTION_DESCRIPTION); $notes = $obj->get(self::TAG_CONTRIBUTION_NOTES); $formData = [ - self::FIELD_NAME_NAME => $obj->getAttr(self::ATTR_CONTRIBUTION_NAME), - self::FIELD_NAME_TITLE => $obj->getAttr(self::ATTR_CONTRIBUTION_TITLE), - self::FIELD_NAME_CATEGORY => $obj->getAttr(self::ATTR_CONTRIBUTION_CATEGORY), - self::FIELD_NAME_IMAGE => $obj->getAttr(self::ATTR_CONTRIBUTION_IMAGE), - self::FIELD_NAME_WEBSITE => $obj->getAttr(self::ATTR_CONTRIBUTION_WEBSITE), - self::FIELD_NAME_DESCRIPTION => $description ? $description->text() : null, + self::FIELD_NAME_NAME => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME), + self::FIELD_NAME_TITLE => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE), + self::FIELD_NAME_CATEGORY => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_CATEGORY), + self::FIELD_NAME_IMAGE => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_IMAGE), + self::FIELD_NAME_WEBSITE => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_WEBSITE), + self::FIELD_NAME_DESCRIPTION => $contribution->getDescription(), self::FIELD_NAME_NOTES => $notes ? $notes->text() : null, ]; @@ -813,26 +822,26 @@ protected function contributionSaveAction(HyphaRequest $request) { // get contribution element or create new contribution element if ($obj->tagName == self::TAG_CONTRIBUTION) { - $contribution = $obj; + $contribution = new FestivalContribution($obj, $this->language); } else { - $contribution = $this->xml->createElement(self::TAG_CONTRIBUTION); - $contribution->generateId(); - $contribution->setAttribute(self::ATTR_CONTRIBUTION_KEY, bin2hex(openssl_random_pseudo_bytes(8))); + $node = $this->xml->createElement(self::TAG_CONTRIBUTION); + $node->generateId(); + $node->setAttribute(self::ATTR_CONTRIBUTION_KEY, bin2hex(openssl_random_pseudo_bytes(8))); if ($obj ->tagName == self::TAG_PARTICIPANT) - $contribution->setAttribute(self::ATTR_CONTRIBUTION_PARTICIPANT, $obj->getId()); + $node->setAttribute(self::ATTR_CONTRIBUTION_PARTICIPANT, $obj->getId()); - $this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->appendChild($contribution); + $this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->appendChild($node); + $contribution = new FestivalContribution($node, $this->language); } // set attributes - $contribution->setAttribute(self::ATTR_CONTRIBUTION_NAME, $form->dataFor(self::FIELD_NAME_NAME)); - $contribution->setAttribute(self::ATTR_CONTRIBUTION_TITLE, $form->dataFor(self::FIELD_NAME_TITLE)); - $contribution->setAttribute(self::ATTR_CONTRIBUTION_CATEGORY, $form->dataFor(self::FIELD_NAME_CATEGORY)); - $contribution->setAttribute(self::ATTR_CONTRIBUTION_IMAGE, $form->dataFor(self::FIELD_NAME_IMAGE)); - $contribution->setAttribute(self::ATTR_CONTRIBUTION_WEBSITE, $form->dataFor(self::FIELD_NAME_WEBSITE)); + $contribution->setTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME, $form->dataFor(self::FIELD_NAME_NAME)); + $contribution->setTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE, $form->dataFor(self::FIELD_NAME_TITLE)); + $contribution->setTranslatedAttribute(self::ATTR_CONTRIBUTION_CATEGORY, $form->dataFor(self::FIELD_NAME_CATEGORY)); + $contribution->setTranslatedAttribute(self::ATTR_CONTRIBUTION_IMAGE, $form->dataFor(self::FIELD_NAME_IMAGE)); + $contribution->setTranslatedAttribute(self::ATTR_CONTRIBUTION_WEBSITE, $form->dataFor(self::FIELD_NAME_WEBSITE)); - $description = $contribution->getOrCreate(self::TAG_CONTRIBUTION_DESCRIPTION); - $description->setText($form->dataFor(self::FIELD_NAME_DESCRIPTION, '')); + $contribution->setDescription($form->dataFor(self::FIELD_NAME_DESCRIPTION, '')); $notes = $contribution->getOrCreate(self::TAG_CONTRIBUTION_NOTES); $notes->setText($form->dataFor(self::FIELD_NAME_NOTES, '')); @@ -854,7 +863,7 @@ protected function contributionSaveAction(HyphaRequest $request) { $vars = [ 'name' => htmlspecialchars($name), - 'contribution'=> htmlspecialchars($contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE) . ' - ' . $contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME)), + 'contribution'=> htmlspecialchars($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE) . ' - ' . $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME)), ]; if ($editing) $digest = __('festival-edited-contribution', $vars); @@ -870,7 +879,7 @@ protected function contributionSaveAction(HyphaRequest $request) { // Send email $vars = [ 'festival-title' => $this->getConfig(self::CONFIG_ID_TITLE), - 'title' => $contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE), + 'title' => $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE), 'editlink' => $edit_url, ]; $this->sendMail($email, 'festival-contribution-added', $vars); @@ -893,12 +902,13 @@ protected function aboutView() { } protected function lineupView(HyphaRequest $request) { + $this->html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, self::PATH_LINEUP)); + $html = ''; $header = $this->getConfigElement(self::CONFIG_ID_LINEUP_HEADER); if ($header) $html .= '
' . $header->html() . '
'; - $contributions = $this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->children(); - foreach($contributions as $contribution) { + foreach($this->getContributions() as $contribution) { $html.= $this->buildContribution($contribution); $html.= '
'; } @@ -918,14 +928,14 @@ protected function buildContribution($contribution) { $editurl = $this->constructFullPath($this->pagename.'/' . self::PATH_CONTRIBUTE . '/'.$contribution->getId()); $title = ''; - if ($contribution->getAttribute(self::ATTR_CONTRIBUTION_CATEGORY)) - $title .= $contribution->getAttribute(self::ATTR_CONTRIBUTION_CATEGORY) . ': '; - if ($contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME)) - $title .= $contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME); - if ($contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME) && $contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE)) + if ($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_CATEGORY)) + $title .= $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_CATEGORY) . ': '; + if ($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME)) + $title .= $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME); + if ($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME) && $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE)) $title .= ' - '; - if ($contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE)) - $title .= $contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE); + if ($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE)) + $title .= $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE); $html.= '

'.htmlspecialchars($title).''; if (isUser()) @@ -933,15 +943,15 @@ protected function buildContribution($contribution) { $html.= '

'; // image and description - $image_filename = $contribution->getAttribute(self::ATTR_CONTRIBUTION_IMAGE); + $image_filename = $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_IMAGE); if ($image_filename) { $img_width = 150; $img_height = 150; $image = new HyphaImage($image_filename); $html.= ''; } - $description = $contribution->getElementsByTagName(self::TAG_CONTRIBUTION_DESCRIPTION)->Item(0); - if ($description) $html.= nl2br(htmlspecialchars($description->text())); + $description = $contribution->getDescription(); + if ($description) $html.= nl2br(htmlspecialchars($description)); $days = $this->getConfigElement(self::CONFIG_ID_DAYS, self::CONFIG_TAG_DAYS)->children(); $locations = $this->getConfigElement(self::CONFIG_ID_LOCATIONS, self::CONFIG_TAG_LOCATIONS)->children(); @@ -960,7 +970,7 @@ protected function buildContribution($contribution) { } if ($timesHtml) $html.= '
'.htmlspecialchars($day->getAttribute(self::ATTR_DAY_DISPLAY)).'
'.$timesHtml.'
'; } - $website = htmlspecialchars($contribution->getAttribute(self::ATTR_CONTRIBUTION_WEBSITE)); + $website = htmlspecialchars($contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_WEBSITE)); if ($website) $html.= ""; $html.= ''; @@ -969,9 +979,11 @@ protected function buildContribution($contribution) { } protected function timetableView(HyphaRequest $request) { + $this->html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, self::PATH_TIMETABLE)); + // Make a list of all days, and per day all // locations and the begin and end time. - $contributions = $this->xml->documentElement->getOrCreate(self::TAG_CONTRIBUTION_CONTAINER)->children(); + $contributions = $this->getContributions(); $days = $this->getConfigElement(self::CONFIG_ID_DAYS, self::CONFIG_TAG_DAYS)->children(); $locations = $this->getConfigElement(self::CONFIG_ID_LOCATIONS, self::CONFIG_TAG_LOCATIONS)->children(); @@ -1025,9 +1037,9 @@ protected function timetableView(HyphaRequest $request) { $locevents[] = [ $this->timetocols($daybegin, $event->getAttribute(self::ATTR_EVENT_BEGIN)), $this->timetocols($daybegin, $event->getAttribute(self::ATTR_EVENT_END)), - $contribution->getAttribute(self::ATTR_CONTRIBUTION_NAME), + $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_NAME), $contribution->getId(), - $contribution->getAttribute(self::ATTR_CONTRIBUTION_TITLE), + $contribution->getTranslatedAttribute(self::ATTR_CONTRIBUTION_TITLE), ]; } } @@ -1309,3 +1321,80 @@ protected function constructFullPath($path, $language = null) { return $rootUrl . $language . $path; } } + + + /** + * Helper class to get retrieve (translated) info for + * contributions. + */ + class FestivalContribution { + private $node; + private $language_nodes; + private $language; + + public function __construct($node, $language) { + $this->node = $node; + $this->language = $language; + $this->language_nodes = []; + foreach ($node->find(festivalpage::TAG_CONTRIBUTION_LANGUAGE) as $node) { + if ($node->getAttribute(festivalpage::ATTR_LANGUAGE_ID) == $language) + array_unshift($this->language_nodes, $node); + else + $this->language_nodes[] = $node; + } + } + + public function getOrCreateLanguage() { + if (empty($this->language_nodes) || $this->language_nodes[0]->getAttribute(festivalpage::ATTR_LANGUAGE_ID) != $this->language) { + $lang = $this->node->document()->createElement(festivalpage::TAG_CONTRIBUTION_LANGUAGE); + $lang->setAttribute(festivalpage::ATTR_LANGUAGE_ID, $this->language); + $this->node->appendChild($lang); + array_unshift($this->language_nodes, $lang); + } + return $this->language_nodes[0]; + } + + public function getTranslatedAttribute($attr) { + foreach ($this->language_nodes as $node) { + if ($node->hasAttribute($attr)) + return $node->getAttribute($attr); + } + return null; + } + + public function setTranslatedAttribute($attr, $val) { + $lang = $this->getOrCreateLanguage(); + $lang->setAttribute($attr, $val); + } + + + public function getDescription() { + $tag = $this->getTranslated(festivalpage::TAG_CONTRIBUTION_DESCRIPTION); + if ($tag === null) + return ''; + return $tag->text(); + } + + public function setDescription($val) { + $lang = $this->getOrCreateLanguage(); + $description = $lang->getOrCreate(festivalpage::TAG_CONTRIBUTION_DESCRIPTION); + $description->setText($val); + } + + public function getTranslated($tag) { + foreach ($this->language_nodes as $node) { + $element = $node->get($tag); + if ($element !== null) + return $element; + } + return null; + } + + + /** + * Forward any other calls to the underlying node. + */ + public function __call($name, $arguments) { + return call_user_func_array([$this->node, $name], $arguments); + } + } From eeb2690fbe72bdbc8d1eb60e1dc419cdeddccff4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:48:48 +0200 Subject: [PATCH 08/12] WIP: festival: Make about page translateable --- system/datatypes/festival.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 6007153d..0e3905b0 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -895,8 +895,10 @@ protected function contributionSaveAction(HyphaRequest $request) { } protected function aboutView() { + $this->html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, self::PATH_ABOUT)); + $this->html->find('#pagename')->text($this->getConfig('festival-title')); - $content = $this->getConfigElement(self::CONFIG_ID_ABOUT); + $content = $this->getConfigElement(self::CONFIG_ID_ABOUT, null, $this->language); if ($content) $this->html->find('#main')->append($content->children()); } From a53beed0382b28dc8d6d5969ec249131a7afa582 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 17:49:13 +0200 Subject: [PATCH 09/12] WIP: festival: Add clunky but working migration --- system/datatypes/festival.php | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 0e3905b0..6402d570 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -104,8 +104,59 @@ public function __construct($pageListNode, RequestContext $O_O) { parent::__construct($pageListNode, $O_O); $this->xml = new Xml('festival', Xml::multiLingualOn, Xml::versionsOff); $this->xml->loadFromFile('data/pages/'.$pageListNode->getAttribute('id')); + + $this->migrate(); + } + + public function migrate() { + if ($this->xml->documentElement->getAttribute('migrated')) + return; + $this->xml->lockAndReload(); + + // Make sure day ids are valid ids (starting + // with a letter). + foreach ($this->getConfigElement(self::CONFIG_ID_DAYS, self::CONFIG_TAG_DAYS)->children() as $day) + $day->setId('day' . $day->getId()); + + foreach ($this->xml->find(self::TAG_CONTRIBUTION_EVENT) as $event) + $event->setAttribute(self::ATTR_EVENT_DAY, 'day' . $event->getAttribute(self::ATTR_EVENT_DAY)); + + // Duplicate the header + $header = $this->getConfigElement('header'); + if ($header) { + $this->getConfigElement(self::CONFIG_ID_LINEUP_HEADER, self::CONFIG_TAG)->append($header->html()); + $this->getConfigElement(self::CONFIG_ID_TIMETABLE_HEADER, self::CONFIG_TAG)->append($header->html()); + $header->remove(); + } + + // Make stuff translated + foreach ($this->xml->find(self::TAG_CONTRIBUTION) as $c) { + $lang = $this->xml->createElement(self::TAG_CONTRIBUTION_LANGUAGE); + $lang->setAttribute(self::ATTR_LANGUAGE_ID, 'nl'); + $c->append($lang); + foreach ([self::ATTR_CONTRIBUTION_IMAGE, self::ATTR_CONTRIBUTION_NAME, self::ATTR_CONTRIBUTION_TITLE, self::ATTR_CONTRIBUTION_WEBSITE, self::ATTR_CONTRIBUTION_CATEGORY] as $attr) { + $lang->setAttribute($attr, $c->getAttribute($attr)); + $c->removeAttribute($attr); + } + $lang->append($c->get(self::TAG_CONTRIBUTION_DESCRIPTION)); + } + + foreach ([self::CONFIG_ID_CONTRIBUTION_FORM, self::CONFIG_ID_SIGNUP_FORM, self::CONFIG_ID_LINEUP_HEADER, self::CONFIG_ID_TIMETABLE_HEADER, self::CONFIG_ID_ABOUT] as $id) { + $c = $this->getConfigElement($id); + if ($c) { + $lang = $this->xml->createElement(self::CONFIG_TAG_LANGUAGE); + $lang->setAttribute(self::ATTR_LANGUAGE_ID, 'nl'); + $lang->append($c->children()); + $c->append($lang); + } + } + + + $this->xml->documentElement->setAttribute('migrated', 1); + $this->xml->saveAndUnlock(); } + public static function getDatatypeName() { return __('datatype.name.festivalpage'); } From 80823c817738d7d49a2825941c5df35493c95801 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 1 Sep 2020 11:09:55 +0200 Subject: [PATCH 10/12] festival: Show some commands to non-logged-in users too --- system/datatypes/festival.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index 6402d570..f1095fc6 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -164,16 +164,17 @@ public static function getDatatypeName() { public function process(HyphaRequest $request) { $this->html->writeToElement('pagename', showPagename($this->pagename) . ' ' . asterisk($this->privateFlag)); + $commands = $this->html->find('#pageCommands'); + $commands->append($this->makeActionButton(__('about'), self::PATH_ABOUT)); + $commands->append($this->makeActionButton(__('festival-signup'), self::PATH_SIGNUP)); + $commands->append($this->makeActionButton(__('festival-lineup'), self::PATH_LINEUP)); + $commands->append($this->makeActionButton(__('festival-timetable'), self::PATH_TIMETABLE)); + if (isUser() && !in_array($request->getView(), [self::PATH_SETTINGS])) { - $commands = $this->html->find('#pageCommands'); - $commands->append($this->makeActionButton(__('about'), self::PATH_ABOUT)); $commands->append($this->makeActionButton(__('settings'), self::PATH_SETTINGS)); - $commands->append($this->makeActionButton(__('festival-signup'), self::PATH_SIGNUP)); $commands->append($this->makeActionButton(__('festival-contribute'), self::PATH_CONTRIBUTE)); $commands->append($this->makeActionButton(__('festival-participants'), self::PATH_PARTICIPANTS)); $commands->append($this->makeActionButton(__('festival-contributions'), self::PATH_CONTRIBUTIONS)); - $commands->append($this->makeActionButton(__('festival-lineup'), self::PATH_LINEUP)); - $commands->append($this->makeActionButton(__('festival-timetable'), self::PATH_TIMETABLE)); if (isAdmin()) { $action = 'if(confirm(\'' . __('sure-to-delete') . '\'))' . makeAction($this->language . '/' . $this->pagename, 'delete', ''); From d693a4022c4da504f0fd5c8fcb68d91468ee7baa Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 2 Sep 2020 11:28:47 +0200 Subject: [PATCH 11/12] WIP: festival: Allow translating lineup and timetable headers --- system/datatypes/festival.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/datatypes/festival.php b/system/datatypes/festival.php index f1095fc6..377b3c42 100644 --- a/system/datatypes/festival.php +++ b/system/datatypes/festival.php @@ -959,7 +959,7 @@ protected function lineupView(HyphaRequest $request) { $this->html->find('#langList')->append(hypha_indexLanguages($this->pageListNode, $this->language, self::PATH_LINEUP)); $html = ''; - $header = $this->getConfigElement(self::CONFIG_ID_LINEUP_HEADER); + $header = $this->getConfigElement(self::CONFIG_ID_LINEUP_HEADER, null, $this->language); if ($header) $html .= '
' . $header->html() . '
'; foreach($this->getContributions() as $contribution) { @@ -1042,7 +1042,7 @@ protected function timetableView(HyphaRequest $request) { $locations = $this->getConfigElement(self::CONFIG_ID_LOCATIONS, self::CONFIG_TAG_LOCATIONS)->children(); $html = ''; - $header = $this->getConfigElement(self::CONFIG_ID_TIMETABLE_HEADER); + $header = $this->getConfigElement(self::CONFIG_ID_TIMETABLE_HEADER, null, $this->language); if ($header) $html .= '
' . $header->html() . '
'; // iterate over all dates From cd1ee22d6de200ca051e44bf13972bb1c44e8679 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 11 Sep 2020 11:38:58 +0200 Subject: [PATCH 12/12] festival: Add mising translation --- system/languages/nl.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/languages/nl.php b/system/languages/nl.php index 4ac1e161..850f4478 100644 --- a/system/languages/nl.php +++ b/system/languages/nl.php @@ -255,6 +255,7 @@ "festival-signed-up-for" => " heeft zich ingeschreven voor ", "festival-failed-to-pay-for" => " kon niet betalen voor ", "festival-payed-for" => " heeft betaald voor ", + "festival-confirmed-for" => " heeft email bevestigd voor ", // festival - notify & messages "festival-successful-signup-for" => "Je bent ingeschreven voor ",