Skip to content
Snippets Groups Projects
Commit 2df806ab authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms
Browse files

complete separate regular and responsive view settings for my courses, fixes #2071

Closes #2071

Merge request studip/studip!1557
parent 19ea9438
No related branches found
No related tags found
No related merge requests found
......@@ -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(),
],
];
......
<?php
/**
* - MY_COURSES_TILED_DISPLAY
* - MY_COURSES_TILED_DISPLAY_RESPONSIVE
* - MY_COURSES_SHOW_NEW_ICONS_ONLY
*/
final class CombineMyCoursesViewSettings extends Migration
{
const OLD_FIELDS = [
'MY_COURSES_SHOW_NEW_ICONS_ONLY' => 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'],
];
}
}
......@@ -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();
}
......
......@@ -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,
......
......@@ -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
......
......@@ -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) {
......
......@@ -2,7 +2,7 @@
<ul class="widget-list widget-options">
<li>
<a href="#" class="options-checkbox" :class="showNewContents ? 'options-checked' : 'options-unchecked'" @click.prevent="toggleNewContents">
<translate>Nur neue Inhalte anzeigen</translate>
{{ $gettext('Nur neue Inhalte anzeigen') }}
</a>
</li>
</ul>
......@@ -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();
});
},
......
<template>
<ul class="widget-list widget-links sidebar-views">
<li :class="{ active: tableView }">
<a href="#" @click.prevent="setTableView">
<translate>Tabellarische Ansicht</translate>
<a href="#" @click.prevent="setTiledView(false)">
{{ $gettext('Tabellarische Ansicht') }}
</a>
</li>
<li :class="{ active: tilesView }">
<a href="#" @click.prevent="setTilesView">
<translate>Kachelansicht</translate>
<a href="#" @click.prevent="setTiledView(true)">
{{ $gettext('Kachelansicht') }}
</a>
</li>
</ul>
......@@ -22,24 +22,15 @@ export default {
mixins: [MyCoursesMixin],
computed: {
tableView () {
return this.getConfig(this.viewConfig) === 'tables';
return !this.getViewConfig('tiled');
},
tilesView () {
return this.getConfig(this.viewConfig) === 'tiles';
return this.getViewConfig('tiled');
},
},
methods: {
setTableView () {
this.setView('tables');
},
setTilesView () {
this.setView('tiles');
},
setView (view) {
this.updateConfigValue({
key: this.viewConfig,
value: view
}).then(() => {
setTiledView (state) {
this.updateViewConfig('tiled', state).then(() => {
Sidebar.close();
});
}
......
......@@ -16,6 +16,22 @@ export default {
'updateConfigValue',
]),
getViewConfig(key) {
return this.getConfig(
'view_settings',
this.responsiveDisplay ? 'responsive' : 'regular',
key
);
},
updateViewConfig(key, value) {
let config = this.getConfig('view_settings');
config[this.responsiveDisplay ? 'responsive' : 'regular'][key] = value;
return this.updateConfigValue({
key: 'view_settings',
value: config
});
},
getCourseName(course, include_number = false) {
let name = course.name;
if (include_number) {
......@@ -110,7 +126,7 @@ export default {
return;
}
if (this.getConfig('navigation_show_only_new') && !nav.important) {
if (this.getViewConfig('only_new') && !nav.important) {
return;
}
......@@ -150,9 +166,6 @@ export default {
'getConfig',
]),
viewConfig () {
return this.responsiveDisplay ? 'responsive_type' : 'display_type';
},
numberOfNavElements () {
return Math.max(
...Object.values(this.courses).map(course => {
......
const configMapping = {
display_type: value => {
view_settings: value => {
return {
MY_COURSES_TILED_DISPLAY: value === 'tiles',
}
},
responsive_type: value => {
return {
MY_COURSES_TILED_DISPLAY_RESPONSIVE: value === 'tiles',
}
},
navigation_show_only_new: value => {
return {
MY_COURSES_SHOW_NEW_ICONS_ONLY: value,
MY_COURSES_VIEW_SETTINGS: value
};
},
open_groups: value => {
......@@ -19,7 +9,6 @@ const configMapping = {
MY_COURSES_OPEN_GROUPS: value,
};
},
};
export default {
......@@ -39,8 +28,12 @@ export default {
}
return state.config.open_groups.includes(group.id);
},
getConfig: (state) => (key) => {
return state.config[key];
getConfig: (state) => (...keys) => {
let config = state.config;
for (const key of keys) {
config = config[key];
}
return config;
},
},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment