From 2df806ab549ac7b7522fb3443e654d83b7095843 Mon Sep 17 00:00:00 2001 From: Jan-Hendrik Willms Date: Wed, 3 May 2023 10:00:38 +0000 Subject: complete separate regular and responsive view settings for my courses, fixes #2071 Closes #2071 Merge request studip/studip!1557 --- app/controllers/my_courses.php | 4 +- .../5.4.3_combine_my_courses_view_settings.php | 167 +++++++++++++++++++++ .../Routes/ConfigValues/ConfigValuesUpdate.php | 6 +- .../JsonApi/Routes/ConfigValues/HelperTrait.php | 9 +- lib/models/ConfigValue.php | 10 ++ resources/vue/components/MyCourses.vue | 2 +- .../vue/components/MyCoursesNewContentToggle.vue | 9 +- .../vue/components/MyCoursesSidebarSwitch.vue | 25 +-- resources/vue/mixins/MyCoursesMixin.js | 21 ++- resources/vue/store/MyCoursesStore.js | 23 +-- 10 files changed, 225 insertions(+), 51 deletions(-) create mode 100644 db/migrations/5.4.3_combine_my_courses_view_settings.php diff --git a/app/controllers/my_courses.php b/app/controllers/my_courses.php index c0dbeb3..996ad87 100644 --- a/app/controllers/my_courses.php +++ b/app/controllers/my_courses.php @@ -793,9 +793,7 @@ class MyCoursesController extends AuthenticatedController 'allow_dozent_visibility' => Config::get()->ALLOW_DOZENT_VISIBILITY, 'open_groups' => array_values($GLOBALS['user']->cfg->MY_COURSES_OPEN_GROUPS), 'sem_number' => Config::get()->IMPORTANT_SEMNUMBER, - 'display_type' => $GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY ? 'tiles' : 'tables', - 'responsive_type' => $GLOBALS['user']->cfg->MY_COURSES_TILED_DISPLAY_RESPONSIVE ? 'tiles' : 'tables', - 'navigation_show_only_new' => $GLOBALS['user']->cfg->MY_COURSES_SHOW_NEW_ICONS_ONLY, + 'view_settings' => $GLOBALS['user']->cfg->MY_COURSES_VIEW_SETTINGS, 'group_by' => $this->getGroupField(), ], ]; diff --git a/db/migrations/5.4.3_combine_my_courses_view_settings.php b/db/migrations/5.4.3_combine_my_courses_view_settings.php new file mode 100644 index 0000000..f191a53 --- /dev/null +++ b/db/migrations/5.4.3_combine_my_courses_view_settings.php @@ -0,0 +1,167 @@ + false, + 'MY_COURSES_TILED_DISPLAY' => false, + 'MY_COURSES_TILED_DISPLAY_RESPONSIVE' => true, + ]; + + public function description() + { + return 'Combines the different view settings for my courses into a single configuration'; + } + + protected function up() + { + // Add new configuration + $query = "INSERT IGNORE INTO `config` ( + `field`, `value`, `type`, `range`, + `section`, `description`, + `mkdate`, `chdate` + ) VALUES ( + 'MY_COURSES_VIEW_SETTINGS', ?, 'array', 'user', + 'MeineVeranstaltungen', 'Konfiguration der Ansicht \"Meine Veranstaltungen\"', + UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + )"; + DBManager::get()->execute($query, [ + json_encode($this->convertOldConfig(['MY_COURSES_TILED_DISPLAY_RESPONSIVE' => true])) + ]); + + // Migrate old settings + $this->migrateOldConfigurations(); + + // Drop old configuration + $query = "DELETE `config`, `config_values` + FROM `config` + LEFT JOIN `config_values` USING (`field`) + WHERE `field` IN (?)"; + DBManager::get()->execute($query, [ + array_keys(self::OLD_FIELDS), + ]); + } + + protected function down() + { + // Restore old configuration + $query = "INSERT IGNORE INTO `config` ( + `field`, `value`, `type`, `range`, + `section`, `description`, + `mkdate`, `chdate` + ) VALUES ( + 'MY_COURSES_SHOW_NEW_ICONS_ONLY', 0, 'boolean', 'user', + 'MeineVeranstaltungen', 'Nur Icons für neue Inhalte sollen angezeigt werden', + UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + ), ( + 'MY_COURSES_TILED_DISPLAY', 0, 'boolean', 'user', + 'MeineVeranstaltungen', 'Hat die Kachelansicht unter \"Meine Veranstaltungen\" aktiviert', + UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + ), ( + 'MY_COURSES_TILED_DISPLAY_RESPONSIVE', 0, 'boolean', 'user', + 'MeineVeranstaltungen', 'Hat die Kachelansicht unter \"Meine Veranstaltungen\" aktiviert (responsiv)', + UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + )"; + DBManager::get()->exec($query); + + // Migrate new settings + $this->migrateNewConfigurations(); + + // Drop new configuration + $query = "DELETE `config`, `config_values` + FROM `config` + LEFT JOIN `config_values` USING (`field`) + WHERE `field` = 'MY_COURSES_VIEW_SETTINGS'"; + DBManager::get()->exec($query); + } + + private function migrateOldConfigurations(): void + { + $query = "SELECT `value` + FROM `config_values` + WHERE `range_id` = :user_id AND `field` = :field"; + $values_statement = DBManager::get()->prepare($query); + + $query = "INSERT IGNORE INTO `config_values` (`field`, `range_id`, `value`, `mkdate`, `chdate`) + VALUES ('MY_COURSES_VIEW_SETTINGS', :user_id, :value, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())"; + $insert_statement = DBManager::get()->prepare($query); + + $query = "SELECT DISTINCT `range_id` + FROM `config_values` + WHERE `field` IN (?)"; + $user_ids = DBManager::get()->fetchFirst($query, [ + array_keys(self::OLD_FIELDS), + ]); + foreach ($user_ids as $user_id) { + $values_statement->bindValue(':user_id', $user_id); + + $config = self::OLD_FIELDS; + foreach (array_keys(self::OLD_FIELDS) as $field) { + $values_statement->bindValue(':field', $field); + $values_statement->execute(); + + $config[$field] = $values_statement->fetchColumn(); + } + + $insert_statement->execute([ + ':user_id' => $user_id, + ':value' => json_encode($this->convertOldConfig($config)), + ]); + } + } + + private function convertOldConfig(array $config): array + { + return [ + 'regular' => [ + 'tiled' => (bool) $config['MY_COURSES_TILED_DISPLAY'], + 'only_new' => (bool) $config['MY_COURSES_SHOW_NEW_ICONS_ONLY'], + ], + 'responsive' => [ + 'tiled' => (bool) $config['MY_COURSES_TILED_DISPLAY_RESPONSIVE'], + 'only_new' => (bool) $config['MY_COURSES_SHOW_NEW_ICONS_ONLY'], + ], + ]; + } + + private function migrateNewConfigurations(): void + { + $query = "INSERT IGNORE INTO `config_values` (`field`, `range_id`, `value`, `mkdate`, `chdate`) + VALUES (:field, :user_id, :value, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())"; + $insert_statement = DBManager::get()->prepare($query); + + $query = "SELECT `range_id`, `value` + FROM `config_values` + WHERE `field` = 'MY_COURSES_VIEW_SETTINGS'"; + $statement = DBManager::get()->exec($query); + $statement->setFetchMode(PDO::FETCH_ASSOC); + foreach ($statement as $row) { + $config = json_decode($row['value'], true); + + $insert_statement->bindValue(':user_id', $row['user_id']); + + foreach ($this->convertNewConfig($config) as $field => $value) { + if ($value !== self::OLD_FIELDS[$field]) { + $insert_statement->bindValue(':field', $field); + $insert_statement->bindValue(':value', (int) $value); + $insert_statement->execute(); + } + } + } + } + + private function convertNewConfig(array $config): array + { + return [ + 'MY_COURSES_SHOW_NEW_ICONS_ONLY' => $config['regular']['only_new'] || $config['responsive']['only_new'], + 'MY_COURSES_TILED_DISPLAY' => $config['regular']['tiled'], + 'MY_COURSES_TILED_DISPLAY_RESPONSIVE' => $config['responsive']['tiled'], + ]; + } + +} diff --git a/lib/classes/JsonApi/Routes/ConfigValues/ConfigValuesUpdate.php b/lib/classes/JsonApi/Routes/ConfigValues/ConfigValuesUpdate.php index cf46134..c4fda2f 100644 --- a/lib/classes/JsonApi/Routes/ConfigValues/ConfigValuesUpdate.php +++ b/lib/classes/JsonApi/Routes/ConfigValues/ConfigValuesUpdate.php @@ -31,7 +31,11 @@ class ConfigValuesUpdate extends JsonApiController $resource = $this->findOrFakeConfigValue($range, $field); // TODO: zunächst kann diese Route nur Konfigurationseinstellungen vom Typ bool ändern - if ('boolean' !== $resource->entry['type'] && $resource->entry['field'] !== 'MY_COURSES_OPEN_GROUPS') { + if ( + 'boolean' !== $resource->entry['type'] + && $resource->entry['field'] !== 'MY_COURSES_OPEN_GROUPS' + && $resource->entry['field'] !== 'MY_COURSES_VIEW_SETTINGS' + ) { throw new NotImplementedException(); } diff --git a/lib/classes/JsonApi/Routes/ConfigValues/HelperTrait.php b/lib/classes/JsonApi/Routes/ConfigValues/HelperTrait.php index 3742fb5..eac1d37 100644 --- a/lib/classes/JsonApi/Routes/ConfigValues/HelperTrait.php +++ b/lib/classes/JsonApi/Routes/ConfigValues/HelperTrait.php @@ -2,11 +2,12 @@ namespace JsonApi\Routes\ConfigValues; +use ConfigValue; use JsonApi\Errors\RecordNotFoundException; trait HelperTrait { - private function generateId(\ConfigValue $resource): string + private function generateId(ConfigValue $resource): string { return join('_', [$resource['range_id'], $resource['field']]); } @@ -29,10 +30,10 @@ trait HelperTrait return $range; } - private function findOrFakeConfigValue(?\Range $range, string $field) + private function findOrFakeConfigValue(?\Range $range, string $field): ConfigValue { // first search optimistically for this config value - if ($configValue = \ConfigValue::find([$field, $range->id])) { + if ($configValue = ConfigValue::find([$field, $range->id])) { return $configValue; } @@ -41,7 +42,7 @@ trait HelperTrait throw new RecordNotFoundException(); } - return \ConfigValue::build([ + return ConfigValue::build([ 'field' => $field, 'range_id' => $range->id, 'value' => $configEntry->value, diff --git a/lib/models/ConfigValue.php b/lib/models/ConfigValue.php index 4e27ec7..829567d 100644 --- a/lib/models/ConfigValue.php +++ b/lib/models/ConfigValue.php @@ -12,6 +12,16 @@ * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 * * @category Stud.IP + * + * @property array $id + * @property string $field + * @property string $range_id + * @property string $value + * @property int $mkdate + * @property int $chdate + * @property string $comment + * + * @property ConfigEntry $entry */ class ConfigValue extends SimpleORMap diff --git a/resources/vue/components/MyCourses.vue b/resources/vue/components/MyCourses.vue index 769d315..4048025 100644 --- a/resources/vue/components/MyCourses.vue +++ b/resources/vue/components/MyCourses.vue @@ -55,7 +55,7 @@ export default { : MyCoursesTables; }, displayedType () { - return this.getConfig(this.viewConfig); + return this.getViewConfig('tiled') ? 'tiles' : 'table'; }, iconSize () { if (this.displayedType !== 'tiles' && !this.responsiveDisplay) { diff --git a/resources/vue/components/MyCoursesNewContentToggle.vue b/resources/vue/components/MyCoursesNewContentToggle.vue index 49bf7ed..3ced26d 100644 --- a/resources/vue/components/MyCoursesNewContentToggle.vue +++ b/resources/vue/components/MyCoursesNewContentToggle.vue @@ -2,7 +2,7 @@ @@ -17,15 +17,12 @@ export default { mixins: [MyCoursesMixin], computed: { showNewContents () { - return this.getConfig('navigation_show_only_new'); + return this.getViewConfig('only_new'); }, }, methods: { toggleNewContents() { - this.updateConfigValue({ - key: 'navigation_show_only_new', - value: !this.getConfig('navigation_show_only_new'), - }).then(() => { + this.updateViewConfig('only_new', !this.showNewContents).then(() => { Sidebar.close(); }); }, diff --git a/resources/vue/components/MyCoursesSidebarSwitch.vue b/resources/vue/components/MyCoursesSidebarSwitch.vue index 5abc1fb..788c053 100644 --- a/resources/vue/components/MyCoursesSidebarSwitch.vue +++ b/resources/vue/components/MyCoursesSidebarSwitch.vue @@ -1,13 +1,13 @@