diff --git a/install.php b/install.php index 443fcb7..74112a4 100644 --- a/install.php +++ b/install.php @@ -2,9 +2,9 @@ namespace FriendsOfRedaxo\Neues; +use FriendsOfRedaxo\Neues\Cronjob\Publish; use rex; use rex_addon; -use rex_article; use rex_config; use rex_file; use rex_media; @@ -12,77 +12,102 @@ use rex_path; use rex_sql; use rex_yform_manager_table_api; +use Url\Cache; +use Url\Profile; -use function count; +/** @var rex_addon $this */ $sql = rex_sql::factory(); -/* Tablesets aktualisieren */ -if (rex_plugin::get('yform', 'manager')->isAvailable()) { - rex_yform_manager_table_api::importTablesets(rex_file::get(__DIR__ . '/install/tableset.json')); +/** + * Tablesets aktualisieren + * - Datenbanktabellen anlegen bzw. das Schema aktualisieren + * - YForm-Tablesets eintragen bzw. aktualisieren (soweit das möglich ist) + * - bei vorhandenen Datensätzen ggf. leere UUIDs füllen. + */ +$this->includeFile(__DIR__ . '/install/update_scheme.php'); - // Vorhandene leere UUID-Felder aktualisieren - $sql->setQuery('UPDATE ' . rex::getTable('neues_author') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); - $sql->setQuery('UPDATE ' . rex::getTable('neues_category') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); - $sql->setQuery('UPDATE ' . rex::getTable('neues_entry') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); +rex_yform_manager_table_api::importTablesets(rex_file::get(__DIR__ . '/install/tableset.json', '[]')); - require_once __DIR__ . '/install/update_scheme.php'; -} +$sql->setQuery('UPDATE ' . rex::getTable('neues_author') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); +$sql->setQuery('UPDATE ' . rex::getTable('neues_category') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); +$sql->setQuery('UPDATE ' . rex::getTable('neues_entry') . ' SET uuid = uuid() WHERE uuid IS NULL OR uuid = ""'); -if (null === rex_media::get('neues_entry_fallback_image.png')) { - rex_file::copy(__DIR__ . '/install/neues_entry_fallback_image.png', rex_path::media('neues_entry_fallback_image.png')); +/** + * Fallback-Image bereitstellen falls noch nicht in der Mediathek. + */ +$fallbackImage = 'neues_entry_fallback_image.png'; +if (null === rex_media::get($fallbackImage)) { + rex_file::copy(__DIR__ . '/install/' . $fallbackImage, rex_path::media($fallbackImage)); $data = []; $data['title'] = 'Aktuelles - Fallback-Image'; $data['category_id'] = 0; $data['file'] = [ - 'name' => 'neues_entry_fallback_image.png', - 'path' => rex_path::media('neues_entry_fallback_image.png'), + 'name' => $fallbackImage, + 'path' => rex_path::media($fallbackImage), ]; rex_media_service::addMedia($data, false); } -/* Cronjob installieren */ -if (rex_addon::get('cronjob')->isAvailable()) { - $cronjobPublish = 'FriendsOfRedaxo\\Neues\\Cronjob\\Publish'; +/** + * für nachfolgende $this->includeFile. + */ +$subScriptParams = ['sql' => $sql, 'installUser' => 'neues']; +/** + * Optional: Cronjob installieren falls das Cronjob-Addon aktiviert ist. + */ +if (rex_addon::get('cronjob')->isAvailable()) { /** * ggf Update von früheren Versionen mit dem alten Klassennamen * -> rex_cronjob_neues_publish ändern in FriendsOfRedaxo\Neues\Cronjob\Publish. */ $sql->setTable(rex::getTable('cronjob')); - $sql->setValue('type', $cronjobPublish); + $sql->setValue('type', Publish::class); $sql->setWhere('`type` = :class', [':class' => 'rex_cronjob_neues_publish']); $sql->update(); /** * Fehlenden CronJob eintragen. */ - $sql = rex_sql::factory(); $sql->setTable(rex::getTable('cronjob')); - $sql->setWhere('`type` = :class', [':class' => $cronjobPublish]); + $sql->setWhere('`type` = :class', [':class' => Publish::class]); $sql->select(); if (0 === $sql->getRows()) { - $query = rex_file::get(__DIR__ . '/install/rex_cronjob_neues_publish.sql'); - rex_sql::factory()->setQuery($query); + $this->includeFile(__DIR__ . '/install/cronjob_publish.php', $subScriptParams); } } -/* URL-Profile installieren */ +/** + * Optional: URL-Profile installieren falls das Url-Addon aktiviert ist + * Nach einer erfolgreichen Installation wird in der Config ein Flag gesetzt + * um eine erneute Installation zu vermeiden + * (rex_config::set('neues', 'url_profile', true)). + */ if (rex_addon::get('url')->isAvailable()) { if (false === rex_config::get('neues', 'url_profile', false)) { - $rex_neues_category = array_filter(rex_sql::factory()->getArray("SELECT * FROM rex_url_generator_profile WHERE `table_name` = '1_xxx_rex_neues_category'")); - if (0 < count($rex_neues_category)) { - $query = str_replace('999999', (string) rex_article::getSiteStartArticleId(), rex_file::get(__DIR__ . '/install/rex_url_profile_neues_category.sql')); - rex_sql::factory()->setQuery($query); + $urlProfileTable = rex::getTable(Profile::TABLE_NAME); + // Category + $sql->setTable($urlProfileTable); + $sql->setWhere('table_name = :tn', [':tn' => '1_xxx_rex_neues_category']); + $sql->select(); + if (0 === $sql->getRows()) { + $this->includeFile(__DIR__ . '/install/url_profile_category.php', $subScriptParams); } - $rex_neues_entry = array_filter(rex_sql::factory()->getArray("SELECT * FROM rex_url_generator_profile WHERE `table_name` = '1_xxx_rex_neues_entry'")); - if (0 < count($rex_neues_entry)) { - $query = str_replace('999999', (string) rex_article::getSiteStartArticleId(), rex_file::get(__DIR__ . '/install/rex_url_profile_neues_entry.sql')); - rex_sql::factory()->setQuery($query); + + // Entry + $sql->setTable($urlProfileTable); + $sql->setWhere('table_name = :tn', [':tn' => '1_xxx_rex_neues_entry']); + $sql->select(); + if (0 === $sql->getRows()) { + $this->includeFile(__DIR__ . '/install/url_profile_entry.php', $subScriptParams); } - /* URL-Profile wurden bereits einmal installiert, daher nicht nochmals installieren und Entwickler-Einstellungen respektieren */ + + Cache::deleteProfiles(); + + // URL-Profile als installiert markieren rex_config::set('neues', 'url_profile', true); } } diff --git a/install/cronjob_publish.php b/install/cronjob_publish.php new file mode 100644 index 0000000..c83045d --- /dev/null +++ b/install/cronjob_publish.php @@ -0,0 +1,35 @@ + 'all', + 'hours' => 'all', + 'days' => 'all', + 'weekdays' => 'all', + 'month' => 'all', +]; + +$timestamp = rex_cronjob_manager_sql::calculateNextTime($job_intervall); + +$sql->setTable(rex::getTable('cronjob')); +$sql->setValue('name', '[neues] Geplante Beiträge veröffentlichen'); +$sql->setValue('description', 'Veröffentlicht alle Beiträge (status = 1), deren Status geplant (status = 0) ist und deren Veröffentlichungszeitpunkt erreicht wurde (publishdate < now()).'); +$sql->setValue('type', Publish::class); +$sql->setValue('parameters', '[]'); +$sql->setValue('interval', json_encode($job_intervall)); +$sql->setValue('nexttime', rex_sql::datetime($timestamp)); +$sql->setValue('environment', '|frontend|backend|script|'); +$sql->setValue('execution_moment', 0); +$sql->setValue('execution_start', '0000-00-00 00:00:00'); +$sql->setValue('status', 1); +$sql->addGlobalUpdateFields($installUser); +$sql->addGlobalCreateFields($installUser); +$sql->insert(); diff --git a/install/rex_cronjob_neues_publish.sql b/install/rex_cronjob_neues_publish.sql deleted file mode 100644 index 9e2f691..0000000 --- a/install/rex_cronjob_neues_publish.sql +++ /dev/null @@ -1,4 +0,0 @@ -SET NAMES utf8mb4; - -INSERT INTO `rex_cronjob` (`name`, `description`, `type`, `parameters`, `interval`, `nexttime`, `environment`, `execution_moment`, `execution_start`, `status`, `createdate`, `createuser`, `updatedate`, `updateuser`) VALUES -('[neues] Geplante Beiträge veröffentlichen', 'Veröffentlicht alle Beiträge (status = 1), deren Status geplant (status = 0) ist und deren Veröffentlichungszeitpunkt erreicht wurde (publishdate < now()).', 'FriendsOfRedaxo\\Neues\\Cronjob\\Publish', '[]', '{\"minutes\":\"all\",\"hours\":\"all\",\"days\":\"all\",\"weekdays\":\"all\",\"months\":\"all\"}', NOW(), '|frontend|backend|script|', 0, NOW(), 1, NOW(), 'neues', NOW(), 'neues'); diff --git a/install/rex_url_profile_neues_category.sql b/install/rex_url_profile_neues_category.sql deleted file mode 100644 index a9d335e..0000000 --- a/install/rex_url_profile_neues_category.sql +++ /dev/null @@ -1,4 +0,0 @@ -SET NAMES utf8mb4; - -INSERT INTO `rex_url_generator_profile` (`namespace`, `article_id`, `clang_id`, `ep_pre_save_called`, `table_name`, `table_parameters`, `relation_1_table_name`, `relation_1_table_parameters`, `relation_2_table_name`, `relation_2_table_parameters`, `relation_3_table_name`, `relation_3_table_parameters`, `createdate`, `createuser`, `updatedate`, `updateuser`) VALUES -('neues-category-id', 999999, 1, 0, '1_xxx_rex_neues_category', '{\"column_id\":\"id\",\"column_clang_id\":\"\",\"restriction_1_column\":\"status\",\"restriction_1_comparison_operator\":\">\",\"restriction_1_value\":\"0\",\"restriction_2_logical_operator\":\"\",\"restriction_2_column\":\"\",\"restriction_2_comparison_operator\":\"=\",\"restriction_2_value\":\"\",\"restriction_3_logical_operator\":\"\",\"restriction_3_column\":\"\",\"restriction_3_comparison_operator\":\"=\",\"restriction_3_value\":\"\",\"column_segment_part_1\":\"name\",\"column_segment_part_2_separator\":\"\\/\",\"column_segment_part_2\":\"\",\"column_segment_part_3_separator\":\"\\/\",\"column_segment_part_3\":\"\",\"relation_1_column\":\"\",\"relation_1_position\":\"BEFORE\",\"relation_2_column\":\"\",\"relation_2_position\":\"BEFORE\",\"relation_3_column\":\"\",\"relation_3_position\":\"BEFORE\",\"append_user_paths\":\"\",\"append_structure_categories\":\"0\",\"column_seo_title\":\"name\",\"column_seo_description\":\"\",\"column_seo_image\":\"\",\"sitemap_add\":\"1\",\"sitemap_frequency\":\"weekly\",\"sitemap_priority\":\"0.5\",\"column_sitemap_lastmod\":\"\"}', '', '[]', '', '[]', '', '[]', NOW(), 'neues', NOW(), 'neues'); diff --git a/install/rex_url_profile_neues_entry.sql b/install/rex_url_profile_neues_entry.sql deleted file mode 100644 index 1d6458f..0000000 --- a/install/rex_url_profile_neues_entry.sql +++ /dev/null @@ -1,4 +0,0 @@ -SET NAMES utf8mb4; - -INSERT INTO `rex_url_generator_profile` (`namespace`, `article_id`, `clang_id`, `ep_pre_save_called`, `table_name`, `table_parameters`, `relation_1_table_name`, `relation_1_table_parameters`, `relation_2_table_name`, `relation_2_table_parameters`, `relation_3_table_name`, `relation_3_table_parameters`, `createdate`, `createuser`, `updatedate`, `updateuser`) VALUES -('neues-entry-id', 999999, 1, 0, '1_xxx_rex_neues_entry', '{\"column_id\":\"id\",\"column_clang_id\":\"\",\"restriction_1_column\":\"status\",\"restriction_1_comparison_operator\":\">\",\"restriction_1_value\":\"0\",\"restriction_2_logical_operator\":\"\",\"restriction_2_column\":\"\",\"restriction_2_comparison_operator\":\"=\",\"restriction_2_value\":\"\",\"restriction_3_logical_operator\":\"\",\"restriction_3_column\":\"\",\"restriction_3_comparison_operator\":\"=\",\"restriction_3_value\":\"\",\"column_segment_part_1\":\"name\",\"column_segment_part_2_separator\":\"\\/\",\"column_segment_part_2\":\"\",\"column_segment_part_3_separator\":\"\\/\",\"column_segment_part_3\":\"\",\"relation_1_column\":\"\",\"relation_1_position\":\"BEFORE\",\"relation_2_column\":\"\",\"relation_2_position\":\"BEFORE\",\"relation_3_column\":\"\",\"relation_3_position\":\"BEFORE\",\"append_user_paths\":\"\",\"append_structure_categories\":\"0\",\"column_seo_title\":\"name\",\"column_seo_description\":\"teaser\",\"column_seo_image\":\"image\",\"sitemap_add\":\"1\",\"sitemap_frequency\":\"daily\",\"sitemap_priority\":\"0.7\",\"column_sitemap_lastmod\":\"updatedate\"}', '', '[]', '', '[]', '', '[]', NOW(), 'neues', NOW(), 'neues'), diff --git a/install/url_profile_category.php b/install/url_profile_category.php new file mode 100644 index 0000000..f1b6ff9 --- /dev/null +++ b/install/url_profile_category.php @@ -0,0 +1,59 @@ +setTable(rex::getTable('url_generator_profile')); +$sql->setValue('namespace', 'neues-category-id'); +$sql->setValue('article_id', rex_article::getSiteStartArticleId()); +$sql->setValue('clang_id', 1); +$sql->setValue('ep_pre_save_called', 0); +$sql->setValue('table_name', '1_xxx_rex_neues_category'); +$sql->setValue('table_parameters', json_encode([ + 'column_id' => 'id', + 'column_clang_id' => '', + 'restriction_1_column' => 'status', + 'restriction_1_comparison_operator' => '>', + 'restriction_1_value' => '0', + 'restriction_2_logical_operator' => '', + 'restriction_2_column' => '', + 'restriction_2_comparison_operator' => '=', + 'restriction_2_value' => '', + 'restriction_3_logical_operator' => '', + 'restriction_3_column' => '', + 'restriction_3_comparison_operator' => '=', + 'restriction_3_value' => '', + 'column_segment_part_1' => 'name', + 'column_segment_part_2_separator' => '/', + 'column_segment_part_2' => '', + 'column_segment_part_3_separator' => '/', + 'column_segment_part_3' => '', + 'relation_1_column' => '', + 'relation_1_position' => 'BEFORE', + 'relation_2_column' => '', + 'relation_2_position' => 'BEFORE', + 'relation_3_column' => '', + 'relation_3_position' => 'BEFORE', + 'append_user_paths' => '', + 'append_structure_categories' => '0', + 'column_seo_title' => 'name', + 'column_seo_description' => '', + 'column_seo_image' => '', + 'sitemap_add' => '1', + 'sitemap_frequency' => 'weekly', + 'sitemap_priority' => '0.5', + 'column_sitemap_lastmod' => '', +])); +$sql->setValue('relation_1_table_name', ''); +$sql->setValue('relation_1_table_parameters', '[]'); +$sql->setValue('relation_2_table_name', ''); +$sql->setValue('relation_2_table_parameters', '[]'); +$sql->setValue('relation_3_table_name', ''); +$sql->setValue('relation_3_table_parameters', '[]'); +$sql->addGlobalCreateFields($installUser); +$sql->addGlobalUpdateFields($installUser); +$sql->insert(); diff --git a/install/url_profile_entry.php b/install/url_profile_entry.php new file mode 100644 index 0000000..2a0c033 --- /dev/null +++ b/install/url_profile_entry.php @@ -0,0 +1,58 @@ +setTable(rex::getTable('url_generator_profile')); +$sql->setValue('namespace', 'neues-entry-id'); +$sql->setValue('article_id', rex_article::getSiteStartArticleId()); +$sql->setValue('clang_id', 1); +$sql->setValue('ep_pre_save_called', 0); +$sql->setValue('table_name', '1_xxx_rex_neues_entry'); +$sql->setValue('table_parameters', json_encode([ + 'column_id' => 'id', + 'column_clang_id' => '', + 'restriction_1_column' => 'status', + 'restriction_1_comparison_operator' => '>', + 'restriction_1_value' => '0', + 'restriction_2_logical_operator' => '', + 'restriction_2_column' => '', + 'restriction_2_comparison_operator' => '=', + 'restriction_2_value' => '', + 'restriction_3_logical_operator' => '', + 'restriction_3_column' => '', + 'restriction_3_comparison_operator' => '=', + 'restriction_3_value' => '', + 'column_segment_part_1' => 'name', + 'column_segment_part_2_separator' => '/', + 'column_segment_part_2' => '', + 'column_segment_part_3_separator' => '/', + 'column_segment_part_3' => '', + 'relation_1_column' => '', + 'relation_1_position' => 'BEFORE', + 'relation_2_column' => '', + 'relation_2_position' => 'BEFORE', + 'relation_3_column' => '', + 'relation_3_position' => 'BEFORE', + 'append_user_paths' => '', + 'append_structure_categories' => '0', + 'column_seo_title' => 'name', + 'column_seo_description' => 'teaser', + 'column_seo_image' => 'image', + 'sitemap_add' => '1', + 'sitemap_frequency' => 'daily', + 'sitemap_priority' => '0.7', + 'column_sitemap_lastmod' => 'updatedate', +])); +$sql->setValue('relation_1_table_name', ''); +$sql->setValue('relation_1_table_parameters', '[]'); +$sql->setValue('relation_2_table_name', ''); +$sql->setValue('relation_2_table_parameters', '[]'); +$sql->setValue('relation_3_table_name', ''); +$sql->setValue('relation_3_table_parameters', '[]'); +$sql->addGlobalCreateFields($installUser); +$sql->addGlobalUpdateFields($installUser); +$sql->insert(); diff --git a/uninstall.php b/uninstall.php index 8d372bd..5a16990 100644 --- a/uninstall.php +++ b/uninstall.php @@ -1,10 +1,104 @@ isAvailable() && !rex::isSafeMode()) { - rex_yform_manager_table_api::removeTable(rex::getTable('neues_category')); - rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry')); - rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry_category_rel')); - rex_yform_manager_table_api::removeTable(rex::getTable('neues_author')); - rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry_lang')); +/** + * De-Installieren des Addons. + * + * Der Umfang kann interaktiv festgelegt werden: + * - Minimal: nur die YForm-Tablesets; ansonsten bleibt alles erhalten + * - Vollständig: entfernt auch Tabellen, Fallback-Image, Cronjobs usw. + * + * Steuerung mit einem Trick: Ohne den Url-Parameter nscope wird die Deinstallation + * mit einem Fake-Fehler abgebrochen. Die "Fehlermeldung" ist eine Abfrage des + * Scopes. Je nach Auswahl wird ein Link abgesetzt, der den Zusatzparameter nscope + * mit der jeweiligen ID (1 oder 2) mitführt. Darüber steuert sich dann der + * Deinstallations-Umfang. + */ + +use FriendsOfRedaxo\Neues\Cronjob\Publish; +use Url\Cache; +use Url\Profile; + +/** + * Url-Parameter nscope auswerten: unbekannt oder ungültig lösen die + * "Fehlermeldung" mit der Abfrage aus + * (Text als Markdown schreiben; dann muss man hier nicht soviel HTML basteln). + */ +$scope = rex_request::get('nscope', 'int', 0); +if (!in_array($scope, [1, 2], true)) { + $context = rex_context::fromGet(); + $msg = '### Bitte den De-Installations-Umfang auswählen' . PHP_EOL; + $msg .= '- **Minimal** (YForm-Tablesets entfernen) ⇒ **[Start](' . $context->getUrl(['nscope' => 1], false) . ')**' . PHP_EOL; + $msg .= '- **Vollständig** (Tabellen, Tablesets, Cronjobs etc. entfernen) ⇒ **[Start](' . $context->getUrl(['nscope' => 2], false) . ')**' . PHP_EOL; + $msg = rex_markdown::factory()->parse($msg); + throw new rex_functional_exception($msg); +} + +/** + * Minimale Lösch-Aktivitäten: scope in [1,2] + * - YForm-Tablesets entfernen. + */ +rex_yform_manager_table_api::removeTable(rex::getTable('neues_category')); +rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry')); +rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry_category_rel')); +rex_yform_manager_table_api::removeTable(rex::getTable('neues_author')); +rex_yform_manager_table_api::removeTable(rex::getTable('neues_entry_lang')); + +/** + * Vollständig löschen: scope in [2] + * - Cronjobs des eigenen Typs + * - UrlAddon-Profile löschen + * - Fallback-Bild entfernen + * - Tabellen. + */ +if (2 !== $scope) { + return; +} + +$sql = rex_sql::factory(); + +try { + $sql->setTable(rex::getTable('cronjob')); + $sql->setWhere('`type` = :class', [':class' => Publish::class]); + $sql->delete(); +} catch (Throwable $th) { + // void; falls rex_cronjob nicht existiert / cronjob-Addon fehlt } + +try { + /** + * In enger Anlehnung an den Originalcode (Profil-Löschen) in der Datei + * «pages/generator.profiles.php» des Url-Addons. + * Siehe: if ($func == 'delete' && $id > 0) usw. + * + * Funktioniert nur bei aktiviertem Url-Addon. Bei deaktiviertem Url-Addon + * bleiben die Einträge erhalten. + */ + $urlProfileTable = rex::getTable(Profile::TABLE_NAME); + + $profiles = $sql->setTable($urlProfileTable) + ->setWhere('table_name LIKE :tn', [':tn' => '1_xxx_rex_neues_%']) + ->select('id') + ->getArray(fetchType: PDO::FETCH_COLUMN); + + foreach ($profiles as $profileId) { + $profile = Profile::get($profileId); + if (null !== $profile) { + $profile->deleteUrls(); + } + $sql->setTable($urlProfileTable) + ->setWhere('id = :id', ['id' => $profileId]) + ->delete(); + } + + Cache::deleteProfiles(); +} catch (Throwable $th) { + // void; falls rex_url_generator_profile nicht existiert / Url-Addon fehlt +} + +rex_media_service::deleteMedia('neues_entry_fallback_image.png'); + +rex_sql_table::get(rex::getTable('neues_category'))->drop(); +rex_sql_table::get(rex::getTable('neues_entry'))->drop(); +rex_sql_table::get(rex::getTable('neues_entry_category_rel'))->drop(); +rex_sql_table::get(rex::getTable('neues_author'))->drop(); +rex_sql_table::get(rex::getTable('neues_entry_lang'))->drop();