Skip to content
Snippets Groups Projects
Commit 3acd8f79 authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms Committed by David Siegfried
Browse files

refactor wiki to vue sfc, fixes #4307

Closes #4307

Merge request studip/studip!3204
parent a9e8a820
No related branches found
No related tags found
No related merge requests found
Showing
with 423 additions and 385 deletions
......@@ -446,16 +446,10 @@ class Course_WikiController extends AuthenticatedController
}
Navigation::activateItem('/course/wiki/start');
$user = User::findCurrent();
WikiOnlineEditingUser::deleteBySQL(
"`page_id` = :page_id AND `chdate` < UNIX_TIMESTAMP() - :threshold",
[
'page_id' => $page->id,
'threshold' => WikiOnlineEditingUser::$threshold
]
);
WikiOnlineEditingUser::purge($page);
$pageData = [
'page_id' => $page->id,
'user_id' => $user ? $user->id : null,
'user_id' => $user?->id,
];
$online_user = WikiOnlineEditingUser::findOneBySQL(
'`page_id` = :page_id AND `user_id` = :user_id',
......@@ -466,11 +460,12 @@ class Course_WikiController extends AuthenticatedController
}
$editingUsers = WikiOnlineEditingUser::countBySQL(
"`page_id` = ? AND `editing` = 1 AND `user_id` != ?",
[$page->id, $user ? $user->id : null]
[$page->id, $user?->id]
);
$online_user->editing = $editingUsers === 0 ? 1 : 0;
$online_user->editing = $editingUsers === 0;
$online_user->chdate = time();
$online_user->store();
$this->me_online = $online_user;
$this->online_users = WikiOnlineEditingUser::findBySQL(
"JOIN `auth_user_md5` USING (`user_id`)
......@@ -478,11 +473,10 @@ class Course_WikiController extends AuthenticatedController
ORDER BY Nachname, Vorname",
[$page->id]
);
$startPage = WikiPage::find($this->range->getConfiguration()->WIKI_STARTPAGE_ID);
$this->contentbar = ContentBar::get()
->setTOC(CoreWiki::getTOC($page))
->setIcon(Icon::create('wiki'))
->setInfo(_('Zuletzt gespeichert') .': '. '<studip-date-time :timestamp="Math.floor(lastSaveDate / 1000)" :relative="true"></studip-date-time>');
->setInfo(_('Zuletzt gespeichert') .': '. '<span class="wiki-last-edited-' . $this->page->id . '"></span>');
}
public function apply_editing_action(WikiPage $page)
......@@ -513,12 +507,37 @@ class Course_WikiController extends AuthenticatedController
}
$online_user->store();
$output = [
'me_online' => $online_user->toArray(),
'me_online' => ['editing' => (bool) $online_user->editing],
'users' => $page->getOnlineUsers()
];
$this->render_json($output);
}
public function cancel_apply_editing_action(WikiPage $page)
{
if (!$page->isEditable() || !Request::isPost()) {
throw new AccessDeniedException();
}
$user = User::findCurrent();
$pageData = [
'page_id' => $page->id,
'user_id' => $user->id
];
$online_user = WikiOnlineEditingUser::findOneBySQL(
'`page_id` = :page_id AND `user_id` = :user_id',
$pageData
);
if (!$online_user) {
$online_user = WikiOnlineEditingUser::build($pageData);
}
$online_user->editing_request = false;
$online_user->store();
$this->render_json([
'users' => $page->getOnlineUsers()
]);
}
public function leave_editing_action(WikiPage $page)
{
if (!$page->isEditable()) {
......@@ -554,6 +573,7 @@ class Course_WikiController extends AuthenticatedController
$this->render_json([
'error' => 'not_in_edit_mode'
]);
return;
}
$online_user_them = WikiOnlineEditingUser::findOneBySQL(
'`page_id` = :page_id AND `user_id` = :user_id',
......@@ -566,11 +586,11 @@ class Course_WikiController extends AuthenticatedController
return;
}
$online_user_me->editing = 0;
$online_user_me->editing = false;
$online_user_me->store();
$online_user_them->editing_request = 1; //that will be set to 0 by the user themself
$online_user_them->editing = 1;
$online_user_them->editing_request = true; //that will be set to 0 by the user themself
$online_user_them->editing = true;
$online_user_them->store();
$this->render_json([
......
......@@ -117,7 +117,6 @@ class JsupdaterController extends AuthenticatedController
'messages' => $this->getMessagesUpdates($pageInfo['messages'] ?? null),
'personalnotifications' => $this->getPersonalNotificationUpdates(),
'questionnaire' => $this->getQuestionnaireUpdates($pageInfo['questionnaire'] ?? null),
'wiki_page_content' => $this->getWikiPageContents($pageInfo['wiki_page_content'] ?? null),
'wiki_editor_status' => $this->getWikiEditorStatus($pageInfo['wiki_editor_status'] ?? null),
];
......@@ -258,105 +257,79 @@ class JsupdaterController extends AuthenticatedController
return $data;
}
private function getWikiPageContents($pageInfo): array
private function getWikiEditorStatus($pageInfo): array
{
$data = [];
if (!empty($pageInfo)) {
foreach ($pageInfo as $page_id) {
$page = WikiPage::find($page_id);
if ($page && $page->isReadable() && ($page->chdate >= Request::int('server_timestamp'))) {
$data['contents'][$page_id] = wikiReady($page->content, true, $page->range_id, $page->id);
}
}
}
return $data;
}
$id = $pageInfo['id'];
private function getWikiEditorStatus($pageInfo): array
{
$data = [];
if (!empty($pageInfo['page_ids'])) {
$user = User::findCurrent();
foreach ((array) $pageInfo['page_ids'] as $page_id) {
WikiOnlineEditingUser::deleteBySQL(
"`page_id` = :page_id AND `chdate` < UNIX_TIMESTAMP() - :threshold",
[
'page_id' => $page_id,
'threshold' => WikiOnlineEditingUser::$threshold
]
);
$page = WikiPage::find($page_id);
$page = WikiPage::find($id);
if ($page) {
WikiOnlineEditingUser::purge($page);
if ($page->isEditable()) {
if (
$pageInfo['focussed'] == $page_id
&& !empty($pageInfo['page_content'])
) {
$page->content = \Studip\Markup::markAsHtml(
$pageInfo['page_content']
);
if ($page->isDirty()) {
$page['user_id'] = User::findCurrent()->id;
$page->store();
}
}
$onlineData = [
'user_id' => $user->id,
'page_id' => $page_id
'page_id' => $page->id
];
$online = WikiOnlineEditingUser::findOneBySQL(
"`user_id` = :user_id AND `page_id` = :page_id",
'`user_id` = :user_id AND `page_id` = :page_id',
$onlineData
);
if (!$online) {
$online = WikiOnlineEditingUser::build($onlineData);
} elseif ($online->editing && isset($pageInfo['content'])) {
$page->content = \Studip\Markup::markAsHtml($pageInfo['content']);
if ($page->isDirty()) {
$page->user_id = $user->id;
$page->store();
}
} else {
$editingUsers = WikiOnlineEditingUser::countBySQL(
"`page_id` = ? AND `editing` = 1 AND `user_id` != ?",
'`page_id` = ? AND `editing` = 1 AND `user_id` != ?',
[$page->id, $user->id]
);
if ($editingUsers > 0) {
$online->editing = 0;
$online->editing = false;
} elseif ($online->editing && $online->editing_request) {
// this is the mode that this user requested the editing mode and was granted to get it:
$online->editing_request = 0;
$online->editing_request = false;
} elseif ($online->editing_request) {
$other_requests = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `editing_request` = 1 AND `user_id` != ?", [
$other_requests = WikiOnlineEditingUser::countBySql('`page_id` = ? AND `editing_request` = 1 AND `user_id` != ?', [
$page->id,
$user->id,
]);
if ($other_requests === 0) {
$online->editing_request = 0;
$online->editing = 1;
$online->editing_request = false;
$online->editing = true;
}
} else {
if ($pageInfo['focussed'] == $page_id) {
$online->editing = 1;
} else {
$other_users = WikiOnlineEditingUser::countBySql("`page_id` = ? AND `user_id` != ?", [
} elseif (!$pageInfo['online']) {
$other_users = WikiOnlineEditingUser::countBySql('`page_id` = ? AND `user_id` != ?', [
$page->id,
$user->id,
]);
if ($other_users === 0) {
// if I'm the only user I don't need to lose the edit mode
$online->editing = 1;
} else {
$online->editing = 0;
}
$online->editing = $other_users === 0;
}
}
$online->chdate = time();
$online->store();
$data['contents'][$page_id] = wikiReady($page->content, true, $page->range_id, $page_id);
$data['wysiwyg_contents'][$page_id] = $page->content;
$data['pages'][$page_id]['editing'] = $online->editing;
}
else {
$data['pages'][$page_id]['editing'] = 0;
$data['editing'] = (bool) $online->editing;
}
$data['pages'][$page_id]['chdate'] = $page->chdate;
$data['users'][$page_id] = $page->getOnlineUsers();
if (
$page->isReadable()
&& $page->chdate >= Request::int('server_timestamp')
) {
$data['content'] = wikiReady($page->content, true, $page->range_id, $page->id);
$data['wysiwyg'] = $page->content;
$data['chdate'] = date('c', $page->chdate);
}
$data['users'] = $page->getOnlineUsers();
}
}
return $data;
......
......@@ -3,61 +3,20 @@
* @var WikiPage $page
* @var Course_WikiController $controller
* @var WikiOnlineEditingUser $me_online
* @var ContentBar $contentbar
*/
?>
<div class="wiki-editor-container"
data-page_id="<?= htmlReady($page->id) ?>"
data-editing="<?= htmlReady($me_online->editing) ?>"
data-content="<?= htmlReady(wikiReady($page->content, true, $page->range_id, $page->id)) ?>"
data-chdate="<?= htmlReady($page->chdate) ?>"
data-users="<?= htmlReady(json_encode($page->getOnlineUsers())) ?>">
<?= $contentbar ?>
<form action="<?= $controller->save($page) ?>" method="post" class="default" v-show="editing">
<?= CSRFProtection::tokenTag() ?>
<textarea class="wiki-editor size-l"
ref="wiki_editor"
data-editor="extraPlugins=WikiLink"
name="content"><?= wysiwygReady($page->content) ?></textarea>
<div></div>
<label>
<input type="checkbox" v-model="autosave">
<?= _('Automatisches Speichern aktivieren.') ?>
</label>
<div>
<?= _('Zuletzt gespeichert') .': ' ?>
<studip-date-time :timestamp="Math.floor(lastSaveDate / 1000)" :relative="true"></studip-date-time>
</div>
<div data-dialog-button="">
<button class="button" :title="isChanged ? '<?= _('Den aktuellen Stand speichern.') ?>' : '<?= _('Der aktuelle Stand wurde bereits gespeichert.') ?>'">
<?= _('Speichern') ?>
</button>
<?= \Studip\LinkButton::create(_('Verlassen'), $controller->leave_editing($page))?>
<button v-for="user in requestingUsers"
:key="user.user_id"
@click.prevent="delegateEditMode(user.user_id)"
class="button">
{{ $gettextInterpolate($gettext('Schreibmodus an %{name} übergeben'), { name: user.fullname }) }}
</button>
</div>
</form>
<div v-if="!editing" class="">
<div v-html="content"></div>
<div data-dialog-button="">
<button class="button"
title="<?= _('Beantragen Sie, dass Sie den Text jetzt bearbeiten wollen.') ?>"
@click.prevent="applyEditing">
<?= _('Bearbeiten beantragen') ?>
</button>
<?= \Studip\LinkButton::create(_('Verlassen'), $controller->leave_editing($page))?>
</div>
</div>
<wiki-editor-online-users :users="users"></wiki-editor-online-users>
</div>
<?= Studip\VueApp::create('WikiEditor')
->withProps([
'cancel-url' => $controller->leave_editingURL($page),
'chdate' => date('c', $page->chdate),
'page-content' => wikiReady($page->content, true, $page->range_id, $page->id),
'editing' => (bool) $me_online->editing,
'page-id' => (int) $page->id,
'save-url' => $controller->saveURL($page),
'users' => $page->getOnlineUsers(),
])
?>
......@@ -40,6 +40,7 @@ final class VueApp implements Stringable
private array $plugins = [];
private array $props = [];
private array $slots = [];
private array $stores = [];
private array $storeData = [];
......@@ -86,8 +87,9 @@ final class VueApp implements Stringable
*/
public function withSlot(string $name, string|Template $content): VueApp
{
$this->slots[$name] = $content instanceof Template ? $content->render() : $content;
return $this;
$clone = clone $this;
$clone->slots[$name] = $content instanceof Template ? $content->render() : $content;
return $clone;
}
/**
......@@ -139,7 +141,6 @@ final class VueApp implements Stringable
{
$clone = clone $this;
$clone->plugins[$plugin] = $filename ?? $plugin;
return $clone;
}
......
<?php
/**
* WikiOnlineEditingUser.php
*
......@@ -13,14 +12,20 @@
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*
* @property string page_id database column
* @property string user_id database column
* @property string id alias column for user_id
* @property string last_lifesign computed column read/write
* @property int $id pk
* @property string $user_id database column
* @property int $page_id database column
* @property bool $editing database column
* @property bool $editing_request database column
* @property int $chdate database column
* @property int $mkdate database column
*
* @property WikiPage $page
* @property User $user
*/
class WikiOnlineEditingUser extends SimpleORMap
{
public static $threshold = 60 * 1;
public static int $threshold = 1 * 60;
protected static function configure($config = [])
{
......@@ -35,4 +40,15 @@ class WikiOnlineEditingUser extends SimpleORMap
];
parent::configure($config);
}
public static function purge(WikiPage $page): void
{
WikiOnlineEditingUser::deleteBySQL(
'`page_id` = :page_id AND `chdate` < UNIX_TIMESTAMP() - :threshold',
[
'page_id' => $page->id,
'threshold' => self::$threshold
]
);
}
}
......@@ -11,7 +11,8 @@
* @author mlunzena
* @copyright (c) Authors
*
* @property array $id alias for pk
* @property int $id alias for pk
* @property int $page_id database column
* @property string $course_id database column
* @property string|null $user_id database column
* @property string $name database column
......@@ -289,14 +290,9 @@ class WikiPage extends SimpleORMap implements PrivacyObject
*/
public function getOnlineUsers(): array
{
$users = [];
WikiOnlineEditingUser::deleteBySQL(
"`page_id` = :page_id AND `chdate` < UNIX_TIMESTAMP() - :threshold",
[
'page_id' => $this->id,
'threshold' => WikiOnlineEditingUser::$threshold
]
);
WikiOnlineEditingUser::purge($this);
$this->resetRelation('onlineeditingusers');
return $this->onlineeditingusers->map(function (WikiOnlineEditingUser $editing_user) {
return [
'user_id' => $editing_user->user_id,
......
......@@ -38,8 +38,11 @@ STUDIP.ready(() => {
};
});
STUDIP.Vue.load().then(async ({createApp, store, Vue}) => {
STUDIP.Vue.load().then(({createApp, store, Vue}) => {
const promises = [Promise.resolve()];
for (const [index, name] of Object.entries(config.stores)) {
promises.push(
import(`../../../vue/store/${name}.js`).then(storeConfig => {
store.registerModule(index, storeConfig.default);
......@@ -52,15 +55,18 @@ STUDIP.ready(() => {
dataElement.remove();
}
});
})
);
}
for (const [plugin, filename] of Object.entries(config.plugins)) {
promises.push(
import(`../../../vue/plugins/${filename}.js`)
.then((temp) => Vue.use(temp[plugin], { store }));
.then((temp) => Vue.use(temp[plugin], { store }))
);
}
Promise.all(promises).then(() => {
createApp({
components,
store,
......@@ -91,6 +97,7 @@ STUDIP.ready(() => {
},
}).$mount(node);
});
});
node.dataset.vueAppCreated = 'true';
});
......
/**
* This file contains all wiki related javascript.
*
* For now this is the "submit and edit" functionality via ajax.
*
* @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
* @copyright Stud.IP Core Group
* @license GPL2 or any later version
* @since Stud.IP 3.3
*/
STUDIP.domReady(() => {
STUDIP.JSUpdater.register('wiki_page_content', STUDIP.Wiki.updatePageContent, function () {
//update the wiki page for readers:
return Array.from(document.getElementsByClassName('wiki_page_content')).map(node => {
return node.dataset.page_id;
});
});
if (document.querySelector('.wiki-editor-container') !== null) {
STUDIP.Wiki.initEditor();
}
STUDIP.JSUpdater.register('wiki_editor_status', STUDIP.Wiki.updateEditorStatus, function () {
let info = {
page_ids: [],
focussed: null
};
for (let page_id in STUDIP.Wiki.Editors) {
info.page_ids.push(page_id);
let editor = STUDIP.Wiki.Editors[page_id].editor;
if (STUDIP.Wiki.Editors[page_id].isChanged && STUDIP.Wiki.Editors[page_id].autosave) {
//if either the textarea or the wysiwyg has focus:
info.page_content = editor.getData();
STUDIP.Wiki.Editors[page_id].isChanged = false;
STUDIP.Wiki.Editors[page_id].lastSaveDate = new Date();
}
if (editor.editing.view.document.isFocused) {
STUDIP.Wiki.Editors[page_id].lastFocussedDate = new Date();
}
if (new Date() - STUDIP.Wiki.Editors[page_id].lastFocussedDate < 1000 * 60) { //time after inactivity
info.focussed = page_id;
} else {
if (STUDIP.Wiki.Editors[page_id].users.length !== 1) {
//then I will likely lose my edit mode so others can obtain it
STUDIP.Wiki.Editors[page_id].editing = false;
}
}
}
return info;
});
});
......@@ -44,7 +44,6 @@ import "./bootstrap/tour.js"
import "./bootstrap/questionnaire.js"
import "./bootstrap/qr_code.js"
import "./bootstrap/startpage.js"
import "./bootstrap/wiki.js"
import "./bootstrap/course_wizard.js"
import "./bootstrap/big_image_handler.js"
import "./bootstrap/opengraph.js"
......
......@@ -81,7 +81,6 @@ import * as Gettext from './lib/gettext';
import UserFilter from './lib/user_filter.js';
import wysiwyg from './lib/wysiwyg.js';
import ScrollToTop from './lib/scroll_to_top.js';
import Wiki from './lib/wiki.js';
const configURLHelper = _.get(window, 'STUDIP.URLHelper', {});
const URLHelper = createURLHelper(configURLHelper);
......@@ -173,5 +172,4 @@ window.STUDIP = _.assign(window.STUDIP || {}, {
dialogReady,
ScrollToTop,
Vue,
Wiki
});
......@@ -84,7 +84,7 @@ function collectData() {
for (const [index, handler] of Object.entries(registeredHandlers)) {
if (handler.data) {
const thisData = $.isFunction(handler.data) ? handler.data() : handler.data;
if (thisData !== null && !$.isEmptyObject(thisData)) {
if (thisData !== null && !(typeof thisData === 'object' && $.isEmptyObject(thisData))) {
data[index] = thisData;
}
}
......
const Wiki = {
updatePageContent(pageContents) {
if (!pageContents) {
return;
}
for (let page_id in pageContents.contents) {
$('.wiki_page_content_' + page_id).html(pageContents.contents[page_id]);
}
},
updateEditorStatus(editorStatus) {
if (!editorStatus) {
return;
}
for (let page_id in STUDIP.Wiki.Editors) {
STUDIP.Wiki.Editors[page_id].users = editorStatus.users[page_id];
if (!STUDIP.Wiki.Editors[page_id].editing) {
STUDIP.Wiki.Editors[page_id].content = editorStatus.contents[page_id];
STUDIP.Wiki.Editors[page_id].editor.setData(editorStatus.wysiwyg_contents[page_id]);
}
if (
!STUDIP.Wiki.Editors[page_id].editing
&& editorStatus.pages[page_id].editing > 0
) {
STUDIP.Wiki.Editors[page_id].editing = true;
STUDIP.Wiki.Editors[page_id].focusEditor();
} else {
STUDIP.Wiki.Editors[page_id].editing = editorStatus.pages[page_id].editing > 0;
}
STUDIP.Wiki.Editors[page_id].lastSaveDate = new Date(editorStatus.pages[page_id].chdate * 1000);
}
},
Editors: {},
initEditor() {
let wiki_edit_container = document.querySelectorAll( '.wiki-editor-container');
for (let edit_container of wiki_edit_container) {
let page_id = edit_container.dataset.page_id;
Promise.all([
STUDIP.Vue.load(),
import('../../../vue/components/WikiEditorOnlineUsers.vue').then((config) => config.default),
]).then(([{ createApp }, WikiEditorOnlineUsers]) => {
return createApp({
el: edit_container,
data() {
return {
page_id: page_id,
editing: edit_container.dataset.editing > 0,
content: edit_container.dataset.content,
users: JSON.parse(edit_container.dataset.users),
editor: null,
isChanged: false,
lastSaveDate: new Date(edit_container.dataset.chdate * 1000),
lastChangeDate: 0,
lastFocussedDate: 0,
autosave: true
};
},
methods: {
applyEditing() {
const url = STUDIP.URLHelper.getURL('dispatch.php/course/wiki/apply_editing/' + this.page_id)
$.post(url).done(output => {
if (output.me_online.editing > 0) {
this.editing = true;
this.focusEditor();
}
this.users = output.users;
});
},
delegateEditMode(user_id) {
const url = STUDIP.URLHelper.getURL('dispatch.php/course/wiki/delegate_edit_mode/' + this.page_id + '/' + user_id);
$.post(url).done(() => this.editing = false);
},
focusEditor() {
this.$nextTick(() => {
this.editor.editing.view.focus();
});
}
},
mounted() {
let textarea = this.$refs['wiki_editor'];
let promise = STUDIP.wysiwyg.replace(textarea);
promise.then((editor) => {
if (this.editing) {
editor.editing.view.focus();
}
editor.model.document.on('change:data',() => {
this.isChanged = true;
this.lastChangeDate = new Date();
});
this.editor = editor;
});
},
computed: {
requestingUsers() {
return this.users
.filter(u => u.editing_request)
.sort((a, b) => a.fullname.localeCompare(b.fullname));
}
},
components: { WikiEditorOnlineUsers }
});
}).then((app) => {
STUDIP.Wiki.Editors[page_id] = app;
});
}
}
};
export default Wiki;
<template>
<div>
<form :action="saveUrl" method="post" class="default" v-show="isEditing">
<input type="hidden" :name="csrf.name" :value="csrf.value">
<textarea class="wiki-editor size-l"
ref="wiki_editor"
data-editor="extraPlugins=WikiLink"
name="content"
v-model="content"
></textarea>
<div></div>
<label>
<input type="checkbox" v-model="autosave">
{{ $gettext('Automatisches Speichern aktivieren.') }}
</label>
<div>
{{ $gettext('Zuletzt gespeichert') }}:
<studip-date-time :timestamp="Math.floor(lastSaveDate / 1000)"
:relative="true"
></studip-date-time>
</div>
<div data-dialog-button="">
<button class="button" :title="isChanged ? $gettext('Den aktuellen Stand speichern.') : $gettext('Der aktuelle Stand wurde bereits gespeichert.')">
{{ $gettext('Speichern') }}
</button>
<a :href="cancelUrl" class="button">
{{ $gettext('Verlassen') }}
</a>
<button v-for="user in requestingUsers"
:key="user.user_id"
@click.prevent="delegateEditMode(user.user_id)"
class="button"
>
{{ $gettextInterpolate($gettext('Schreibmodus an %{name} übergeben'), { name: user.fullname }, true) }}
</button>
</div>
</form>
<div v-if="!isEditing">
<div v-html="content"></div>
<div data-dialog-button>
<button class="button"
v-if="!editingWasRequested"
:title="$gettext('Beantragen Sie, dass Sie den Text jetzt bearbeiten wollen.')"
@click.prevent="applyEditing()"
>
{{ $gettext('Bearbeiten beantragen') }}
</button>
<button class="cancel button"
v-else
:title="$gettext('Klicken Sie, um die Anfrage zum Bearbeiten abzubrechen')"
@click.prevent="cancelApplyEditing()"
>
{{ $gettext('Bearbeiten beantragt') }}
</button>
<a :href="cancelUrl" class="button">
{{ $gettext('Verlassen') }}
</a>
</div>
</div>
<wiki-editor-online-users :users="onlineUsers"></wiki-editor-online-users>
<mounting-portal :mount-to="`.wiki-last-edited-${pageId}`">
<studip-date-time :timestamp="Math.floor(lastSaveDate / 1000)"
:relative="true"
></studip-date-time>
</mounting-portal>
</div>
</template>
<script>
import WikiEditorOnlineUsers from "./WikiEditorOnlineUsers.vue";
import StudipDateTime from "./StudipDateTime.vue";
import JSUpdater from "@/assets/javascripts/lib/jsupdater";
export default {
name: 'wiki-editor',
components: {StudipDateTime, WikiEditorOnlineUsers },
props: {
cancelUrl: {
type: String,
required: true,
},
chdate: {
type: String,
required: true,
},
editing: {
type: Boolean,
default: true
},
offlineThreshold: {
type: Number,
default: 60 * 1000
},
pageContent: {
type: String,
default: ''
},
pageId: {
type: Number,
required: true
},
saveUrl: {
type: String,
required: true
},
users: {
type: Array,
default: () => []
}
},
data() {
return {
autosave: true,
content: this.pageContent,
editor: null,
isChanged: false,
isEditing: this.editing,
lastFocussedDate: null,
lastSaveDate: new Date(this.chdate),
onlineUsers: this.users,
};
},
computed: {
csrf() {
return STUDIP.CSRF_TOKEN;
},
editingWasRequested() {
return this.onlineUsers
.filter(u => u.user_id === STUDIP.USER_ID)
.some(u => u.editing_request);
},
isOnlineAndEditing() {
return this.isEditing
&& new Date() - this.lastFocussedDate < this.offlineThreshold;
},
requestingUsers() {
return this.onlineUsers
.filter(u => u.editing_request)
.sort((a, b) => a.fullname.localeCompare(b.fullname));
}
},
methods: {
applyEditing() {
const url = STUDIP.URLHelper.getURL(`dispatch.php/course/wiki/apply_editing/${this.pageId}`)
$.post(url).done(output => {
if (output.me_online.editing > 0) {
this.isEditing = true;
this.focusEditor();
}
this.onlineUsers = output.users;
});
},
cancelApplyEditing() {
const url = STUDIP.URLHelper.getURL(`dispatch.php/course/wiki/cancel_apply_editing/${this.pageId}`)
$.post(url).done(output => {
this.onlineUsers = output.users;
});
},
delegateEditMode(user_id) {
const url = STUDIP.URLHelper.getURL(`dispatch.php/course/wiki/delegate_edit_mode/${this.pageId}/${user_id}`);
$.post(url).done(() => {
this.isEditing = false;
});
},
focusEditor() {
this.$nextTick(() => {
this.editor.editing.view.focus();
});
},
getUpdaterData() {
if (this.editor.editing.view.document.isFocused) {
this.lastFocussedDate = new Date();
}
const data = {
id: this.pageId,
online: this.isOnlineAndEditing
};
if (this.isChanged) {
data.content = this.editor.getData();
this.isChanged = false;
}
return data;
}
},
mounted() {
const textarea = this.$refs['wiki_editor'];
STUDIP.wysiwyg.replace(textarea).then((editor) => {
editor.model.document.on('change:data', () => {
if (this.autosave) {
this.isChanged = true;
}
});
if (this.isEditing) {
this.focusEditor();
}
this.editor = editor;
});
JSUpdater.register(
'wiki_editor_status',
(content) => {
this.onlineUsers = content.users;
this.isEditing = content.editing;
if ('chdate' in content) {
this.lastSaveDate = new Date(content.chdate);
}
if ('content' in content) {
this.content = content.content;
}
if (!this.isEditing && 'wysiwyg' in content) {
this.editor.setData(content.wysiwyg);
}
},
() => this.getUpdaterData()
)
}
}
</script>
......@@ -4,7 +4,7 @@
<template #content>
<ol class="clean">
<li v-for="user in users" :key="user.user_id">
<img class="avatar-small" :src="user.avatar">
<img class="avatar-small" :src="user.avatar" alt="">
{{ user.fullname }}
<span v-if="user.editing" :title="$gettext('Diese Person hat den Bearbeitungsmodus.')">
......@@ -19,10 +19,13 @@
</SidebarWidget>
</MountingPortal>
</template>
<script>
import SidebarWidget from "./SidebarWidget.vue";
import StudipIcon from "./StudipIcon.vue";
export default {
name: 'WikiEditorOnlineUsers',
components: {StudipIcon, SidebarWidget},
props: {
users: Array
},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment