diff --git a/controllers/PageController.php b/controllers/PageController.php
index 387f726b..2c448e2b 100644
--- a/controllers/PageController.php
+++ b/controllers/PageController.php
@@ -15,6 +15,7 @@
use humhub\modules\wiki\permissions\CreatePage;
use humhub\modules\wiki\permissions\EditPages;
use humhub\modules\wiki\permissions\ViewHistory;
+use humhub\modules\user\models\User;
use Throwable;
use Yii;
use yii\base\Exception;
@@ -22,6 +23,7 @@
use yii\db\StaleObjectException;
use yii\web\HttpException;
use yii\web\Response;
+use DateTime;
/**
* PageController
@@ -29,7 +31,9 @@
* @author luke
*/
class PageController extends BaseController
-{
+{
+ public const TTL = 300;
+
/**
* @return $this|Response
* @throws Exception
@@ -211,6 +215,7 @@ public function actionEdit($id = null, $title = null, $categoryId = null)
]);
}
+ $form->page->updateIsEditing();
return $this->renderSidebarContent('edit', $params);
}
@@ -335,7 +340,7 @@ public function actionDelete(int $id)
}
$page->delete();
-
+ $page->doneEditing();
return $this->redirect($this->contentContainer->createUrl('index'));
}
@@ -439,5 +444,160 @@ protected function getAccessRules()
];
}
+ /**
+ * @param int $id
+ * @return $this|Response
+ * @throws HttpException
+ */
+ public function actionMerge(int $id)
+ {
+ $dateTime = new DateTime();
+ $userIdentity = Yii::$app->user->identity->username;
+
+ $page = $this->getWikiPage($id);
+ if (!$page) {
+ throw new HttpException(404, 'Wiki page not found!');
+ }
+
+ $form = (new PageEditForm(['container' => $this->contentContainer]))->forPage($id);
+ if (!$form->load(Yii::$app->request->post())) {
+ throw new HttpException(404);
+ }
+
+ $submittedRevision = new WikiPageRevision();
+ $submittedRevision->revision = time();
+ $submittedRevision->content = $form->revision->content;
+ $submittedRevision->isCurrentlyEditing = true;
+
+ $mergedRevision = $page->createRevision();
+ $changedContentSepeartor = '**conflicting changes of '. $userIdentity .' from '. $dateTime->format('Y-m-d H:i:s').'**';
+ $mergedRevision->content = $page->latestRevision->content.'
'.$changedContentSepeartor.'
'.$submittedRevision->content;
+ $mergedRevision->save();
+ $page->doneEditing();
+
+ return $this->redirect(Url::toWiki($page));
+ }
+
+ /**
+ * @param int $id
+ * @return $this|Response
+ * @throws HttpException
+ */
+ public function actionCreateCopy(int $id)
+ {
+ $userIdentity = Yii::$app->user->identity->username;
+ $dateTime = new DateTime();
+
+ $page = $this->getWikiPage($id);
+ if (!$page) {
+ throw new HttpException(404, 'Wiki page not found!');
+ }
+
+ $parentId = $page->parent_page_id;
+
+ $form = (new PageEditForm(['container' => $this->contentContainer]))->forPage($id);
+ if (!$form->load(Yii::$app->request->post())) {
+ throw new HttpException(404);
+ }
+
+ $childPage = new WikiPage();
+ $childPage->title = $page->title.' conflicting copy of '. $userIdentity .' from '. $dateTime->format('Y-m-d H:i:s');
+ $childPage->parent_page_id = $parentId;
+ $childPage->content->contentcontainer_id= $page->content->contentcontainer_id;
+
+ if (!$childPage->save()) {
+ throw new HttpException(500, 'Failed to create the child page!');
+ }
+
+ if (!$childPage->id) {
+ throw new HttpException('Child page ID is not available after saving.');
+ }
+
+ $revision = new WikiPageRevision();
+ $revision->content = $form->revision->content;
+ $revision->wiki_page_id = $childPage->id;
+ $revision->revision = 1;
+ $revision->user_id = Yii::$app->user->id;
+
+ if (!$revision->save()) {
+ throw new HttpException('Failed to add content to the child page.');
+ }
+
+ $page->doneEditing();
+
+ return $this->redirect(Url::toWiki($page));
+ }
+
+ /**
+ * @param int $id
+ * @return $this|Response
+ * @throws HttpException
+ */
+ public function actionEditingStatus(int $id)
+ {
+ $page = $this->getWikiPage($id);
+ if (!$page) {
+ throw new HttpException(404, 'Wiki page not found!');
+ }
+
+ $user = User::find()->where(['username' => $page->is_currently_editing])->one();
+ if($user) {
+ $firstName = $user->profile->firstname;
+ $lastName = $user->profile->lastname;
+ $fullName = $firstName.' '.$lastName.' ('.$page->is_currently_editing.')';
+ }
+ else { $fullName = ''; }
+
+ return $this->asJson([
+ 'success' => true,
+ 'isEditing' => $page->isEditing(),
+ 'body' => $fullName .' '. Yii::t('WikiModule.base', 'is already editing.
Editing it would cause conflict. Do you really want to continue?'),
+ ]);
+ }
+
+ /**
+ * @return $this|Response
+ */
+ public function actionEditingTimerUpdate(int $id = null)
+ {
+ $conflictingEditing = false;
+ $page = $this->getWikiPage($id);
+ if (!$page) {
+ return $this->asJson([
+ 'sucess' => false,
+ ]);
+ }
+
+ $user = Yii::$app->user->identity->username;
+
+ $editingUser = User::find()->where(['username' => $page->is_currently_editing])->one();
+ if($editingUser) {
+ $firstName = $editingUser->profile->firstname;
+ $lastName = $editingUser->profile->lastname;
+ $fullName = $firstName.' '.$lastName.' ('.$page->is_currently_editing.')';
+ }
+ else { $fullName = ''; }
+
+ if ($page->is_currently_editing == NULL) {
+ $page->updateIsEditing();
+ }
+
+ if ($page->is_currently_editing == $user) {
+ $page->updateEditingTime();
+ }
+ elseif (time() - $page->editing_started_at < self::TTL) {
+ $conflictingEditing = true;
+ }
+
+ return $this->asJson([
+ 'success' => true,
+ 'conflictingEditing' => $conflictingEditing,
+ 'url' => Url::toWiki($page),
+ 'header' => Yii::t('WikiModule.base', 'Confirm Edit'),
+ 'body' => $fullName .' '. Yii::t('WikiModule.base', 'is already editing.
Editing it would cause conflict. Do you really want to continue?'),
+ 'confirmText' => Yii::t('WikiModule.base', 'Cancel'),
+ 'cancelText' => Yii::t('WikiModule.base', 'Continue'),
+ ]);
+ }
}
diff --git a/helpers/Url.php b/helpers/Url.php
index 8aba1d31..45755d3b 100644
--- a/helpers/Url.php
+++ b/helpers/Url.php
@@ -31,6 +31,10 @@ class Url extends \yii\helpers\Url
public const ROUTE_WIKI_DIFF_EDITING = '/wiki/page/diff-editing';
public const ROUTE_WIKI_REVERT = '/wiki/page/revert';
public const ROUTE_EXTRACT_TITLES = '/wiki/page/headlines';
+ public const ROUTE_WIKI_MERGE = '/wiki/page/merge';
+ public const ROUTE_WIKI_CREATE_COPY = '/wiki/page/create-copy';
+ public const ROUTE_WIKI_EDITING_STATUS = '/wiki/page/editing-status';
+ public const ROUTE_WIKI_EDITING_TIMER_UPDATE = '/wiki/page/editing-timer-update';
public static function toHome(ContentContainerActiveRecord $container)
{
@@ -129,4 +133,24 @@ public static function toWikiEntry(WikiPage $page)
return static::to([static::ROUTE_WIKI_ENTRY, 'id' => $page->id, 'container' => $page->content->container]);
}
+ public static function toWikiMerge(WikiPage $page)
+ {
+ return static::to([static::ROUTE_WIKI_MERGE, 'id' => $page->id, 'container' => $page->content->container]);
+ }
+
+ public static function toWikiCreateCopy(WikiPage $page)
+ {
+ return static::to([static::ROUTE_WIKI_CREATE_COPY, 'id' => $page->id, 'container' => $page->content->container]);
+ }
+
+ public static function toWikiEditingStatus(WikiPage $page)
+ {
+ return static::to([static::ROUTE_WIKI_EDITING_STATUS, 'id' => $page->id, 'container' => $page->content->container]);
+ }
+
+ public static function toWikiEditingTimerUpdate(WikiPage $page)
+ {
+ return static::to([static::ROUTE_WIKI_EDITING_TIMER_UPDATE, 'id' => $page->id, 'container' => $page->content->container]);
+ }
+
}
diff --git a/messages/de/base.php b/messages/de/base.php
index b7c00dad..12ebd921 100644
--- a/messages/de/base.php
+++ b/messages/de/base.php
@@ -3,7 +3,7 @@
return [
'({n,plural,=1{+1 subpage}other{+{count} subpages}})' => '({n,plural,=1{+1 Unterseite}other{+{count} Unterseiten}})',
'Confirm page reverting' => 'Seite rückgängig machen bestätigen',
- 'Warning!
Another user has updated this page since you have started editing it. Please confirm that you want to overwrite those changes.
:linkToCompare' => 'Warnung!
Ein anderer Benutzer hat diese Seite gespeichert, seit mit der Bearbeitung begonnen wurde. Sollen diese Änderungen überschreiben werden?
:linkToCompare',
+ 'Warning!
Another user has updated this page since you have started editing it. Please confirm that you want to overwrite those changes.
:linkToCompare' => 'Warnung!
Ein anderer Benutzer bearbeitet gerade diese Seite. Durch eine parallele Bearbeitung kann es zu Konflikten oder Datenverlust kommen. Möchten Sie trotzdem fortfahren?
:linkZuVergleichen',
'Wiki link' => 'Wiki-Verweis',
'Wiki module configuration' => 'Wiki Modulkonfiguration',
'Add Page' => 'Seite hinzufügen',
@@ -86,4 +86,10 @@
'Hide Navigation Entries of this module globally by default' => '',
'Hide Navigation Entry' => '',
'No wiki pages found.' => '',
+ 'Merge' => 'Zusammenführen',
+ 'Create Copy' => 'Kopie erstellen',
+ 'Confirm Page Edit' => 'Seite wird gerade bearbeitet',
+ 'Continue' => 'Weiter',
+ 'Confirm Edit' => 'Bearbeitung bestätigen',
+ 'is already editing.
Editing it would cause conflict. Do you really want to continue?' => 'bearbeitet diese Seite.
Durch die Bearbeitung kommt es zu einem Dateikonflikt. Trotzdem fortfahren?',
];
diff --git a/migrations/m250203_094213_is_currently_editing.php b/migrations/m250203_094213_is_currently_editing.php
new file mode 100644
index 00000000..c6ba194c
--- /dev/null
+++ b/migrations/m250203_094213_is_currently_editing.php
@@ -0,0 +1,26 @@
+addColumn('wiki_page', 'is_currently_editing', $this->string(50)->null());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeDown()
+ {
+ $this->dropColumn('wiki_page', 'is_currently_editing');
+
+ }
+}
diff --git a/migrations/m250219_192950_editing_started_at.php b/migrations/m250219_192950_editing_started_at.php
new file mode 100644
index 00000000..60bac647
--- /dev/null
+++ b/migrations/m250219_192950_editing_started_at.php
@@ -0,0 +1,26 @@
+addColumn('wiki_page', 'editing_started_at', $this->integer()->null());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function safeDown()
+ {
+ $this->dropColumn('wiki_page', 'editing_started_at');
+ }
+
+}
diff --git a/models/WikiPage.php b/models/WikiPage.php
index ea6ce769..85308a9e 100644
--- a/models/WikiPage.php
+++ b/models/WikiPage.php
@@ -28,6 +28,8 @@
* @property int $sort_order
* @property int $is_container_menu
* @property int $container_menu_order
+ * @property string|null $is_currently_editing
+ * @property int|null $editing_started_at
*
* @property-read WikiPage|null $categoryPage
* @property-read WikiPageRevision $latestRevision
@@ -42,6 +44,7 @@ class WikiPage extends ContentActiveRecord implements Searchable
public const SCENARIO_EDIT = 'edit';
public const CACHE_CHILDREN_COUNT_KEY = 'wikiChildrenCount_%s';
public $moduleId = 'wiki';
+ public const TTL = 300;
/**
* @inheritdoc
*/
@@ -540,4 +543,48 @@ public function getIsCategory(): bool
return $this->_isCategory;
}
+
+ /**
+ * Function to check if any other user is currently editing the page
+ *
+ * @return bool
+ */
+ public function isEditing() {
+ $user = Yii::$app->user->identity->username;
+
+ if ($this->is_currently_editing == NULL || $this->is_currently_editing == $user) {
+ return false;
+ }
+
+ if (time() - $this->editing_started_at > self::TTL) {
+ $this->doneEditing();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Function to update the Attribute value to the new user in the edit page
+ */
+ public function updateIsEditing() {
+ $user = Yii::$app->user->identity->username;
+ if ($this->is_currently_editing == NULL) {
+ $this->updateAttributes(['is_currently_editing' => $user, 'editing_started_at' => time()]);
+ }
+ }
+
+ /**
+ * Function to make the editing attributes null to show that no user is currently editing the page.
+ */
+ public function doneEditing() {
+ $this->updateAttributes(['is_currently_editing' => new Expression('NULL'), 'editing_started_at' => new Expression('NULL')]);
+ }
+
+ /**
+ * Function to update the time stamp i.e editing_started_at
+ */
+ public function updateEditingTime() {
+ $this->updateAttributes(['editing_started_at' => time()]);
+ }
}
diff --git a/models/forms/PageEditForm.php b/models/forms/PageEditForm.php
index d6c7a831..112d6be8 100644
--- a/models/forms/PageEditForm.php
+++ b/models/forms/PageEditForm.php
@@ -241,6 +241,8 @@ public function save()
$this->page->content->hidden = $this->hidden;
}
+ $this->page->doneEditing();
+
return WikiPage::getDb()->transaction(function ($db) {
if ($this->page->save()) {
$this->revision->wiki_page_id = $this->page->id;
diff --git a/resources/js/humhub.wiki.Form.js b/resources/js/humhub.wiki.Form.js
index a4ab0b97..2a988b99 100644
--- a/resources/js/humhub.wiki.Form.js
+++ b/resources/js/humhub.wiki.Form.js
@@ -2,6 +2,11 @@ humhub.module('wiki.Form', function(module, require, $) {
var Widget = require('ui.widget').Widget;
var wikiView = require('wiki');
var additions = require('ui.additions');
+ var client = require('client');
+ var modal = require('ui.modal');
+
+ var editPollingInterval = 5000;
+ var editPollingTimer = null;
/**
* This widget represents the wiki form
@@ -39,6 +44,8 @@ humhub.module('wiki.Form', function(module, require, $) {
}
});
}
+ checkValidUser();
+ startEditPolling();
};
Form.prototype.getRichtextMenu = function() {
@@ -74,9 +81,56 @@ humhub.module('wiki.Form', function(module, require, $) {
}, 500);
};
+ Form.prototype.openUrlLink = function(evt) {
+ var form = this.$;
+ form.attr('action', evt.$trigger.data('action-click-url'))
+ .submit();
+ }
+
Form.submit = function () {
+ if (editPollingTimer) {
+ clearInterval(editPollingTimer);
+ }
$('form[data-ui-widget="wiki.Form"]').submit();
};
+ function pollTimerEditingStatus() {
+ var url = document.querySelector('[data-url-editing-timer-update]').getAttribute('data-url-editing-timer-update');
+
+ client.get(url).then(function(response) {
+ }).catch(function(e) {
+ module.log.error(e, true);
+ });
+ }
+
+ function startEditPolling() {
+ if (editPollingTimer) {
+ clearInterval(editPollingTimer);
+ }
+ editPollingTimer = setInterval(pollTimerEditingStatus, editPollingInterval);
+ }
+
+ function checkValidUser() {
+ var url = document.querySelector('[data-url-editing-timer-update]').getAttribute('data-url-editing-timer-update');
+ client.get(url).then(function(response) {
+ if(response.success&&response.conflictingEditing) {
+ var options = {
+ 'header': response.header,
+ 'body': response.body,
+ 'confirmText': response.confirmText,
+ 'cancelText' : response.cancelText,
+ };
+
+ modal.confirm(options).then(function ($confirmed) {
+ if ($confirmed) {
+ client.redirect(response.url);
+ }
+ });
+ }
+ }).catch(function(e) {
+ module.log.error(e, true);
+ })
+ }
+
module.export = Form;
});
diff --git a/resources/js/humhub.wiki.js b/resources/js/humhub.wiki.js
index 8a7ab3f6..6ab6de39 100644
--- a/resources/js/humhub.wiki.js
+++ b/resources/js/humhub.wiki.js
@@ -4,8 +4,11 @@ humhub.module('wiki', function(module, require, $) {
var view = require('ui.view');
var client = require('client');
var loader = require('ui.loader');
+ var modal = require('ui.modal');
var stickyElementSettings = [];
+ var pollingInterval = 5000;
+ var pollingTimer = null;
var registerStickyElement = function($node, $trigger, condition) {
stickyElementSettings.push({$node: $node, $trigger: $trigger, condition: condition});
@@ -50,6 +53,7 @@ humhub.module('wiki', function(module, require, $) {
call: () => $(window).width() < 768
});
}
+ startPolling();
};
Content.prototype.loader = function (show) {
@@ -126,12 +130,60 @@ humhub.module('wiki', function(module, require, $) {
event.off('humhub:content:afterMove.wiki');
};
+ var confirmEditing = function(evt) {
+ var editUrl = evt.$trigger.data('action-click-url');
+ client.redirect(editUrl);
+ };
+
+ function pollEditingStatus() {
+ if (document.querySelector('[data-url-editing-status]') == null) {
+ if (pollingTimer) {
+ clearInterval(pollingTimer);
+ }
+ return;
+ }
+ var url = document.querySelector('[data-url-editing-status]').getAttribute('data-url-editing-status');
+
+ client.get(url).then(function(response) {
+ if (response.success && response.isEditing) {
+ openEditingDialog(response.body);
+ } else {
+ closeEditingDialog();
+ }
+ }).catch(function(e) {
+ module.log.error(e, true);
+ });
+ }
+
+ function openEditingDialog(body) {
+ var button = document.querySelector('[data-url-editing-status]');
+ button.setAttribute('data-action-confirm', body);
+
+ }
+
+ function closeEditingDialog() {
+ var button = document.querySelector('[data-url-editing-status]');
+ if (button.getAttribute('data-action-confirm') != null) {
+ button.removeAttribute('data-action-confirm');
+ client.reload();
+ }
+ }
+
+ function startPolling() {
+ if (pollingTimer) {
+ clearInterval(pollingTimer);
+ }
+ pollingTimer = setInterval(pollEditingStatus, pollingInterval);
+ }
+
module.export({
Content: Content,
toAnchor: toAnchor,
revertRevision: revertRevision,
actionDelete: actionDelete,
registerStickyElement: registerStickyElement,
- unload: unload
+ unload: unload,
+ confirmEditing: confirmEditing,
+ startPolling: startPolling,
})
});
diff --git a/tests/codeception/functional/EditingConflictCest.php b/tests/codeception/functional/EditingConflictCest.php
new file mode 100644
index 00000000..f1bd3d0e
--- /dev/null
+++ b/tests/codeception/functional/EditingConflictCest.php
@@ -0,0 +1,190 @@
+wantTo('Ensure merging wiki page revisions works correctly');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $I->sendAjaxPostRequest(Url::toWikiMerge($page), [
+ 'WikiPage' => ['title' => 'Updated title'],
+ 'WikiPageRevision' => ['content' => 'Updated content.'],
+ 'PageEditForm' => ['latestRevisionNumber' => '1234567890'],]);
+
+ $I->amOnSpace($space->guid, '/wiki/page/view', ['id' => $page->id, 'title' => $page->title]);
+ $I->see('Initial content');
+ $I->see('conflicting changes');
+ $I->see('Updated content');
+
+ }
+
+ public function testCreateCopyFunctionality(FunctionalTester $I)
+ {
+ $I->wantTo('Ensure Creating copy of wiki page revisions works correctly');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $I->sendAjaxPostRequest(Url::toWikiCreateCopy($page), [
+ 'WikiPage' => ['title' => 'Test Page'],
+ 'WikiPageRevision' => ['content' => 'Updated content.'],
+ 'PageEditForm' => ['latestRevisionNumber' => '1234567890'],]);
+
+ $I->amOnSpace($space->guid, '/wiki/overview');
+ $I->see('Test page conflicting copy of');
+
+ $I->amOnSpace($space->guid, '/wiki/page/view', ['id' => 2]);
+ $I->see('Test page conflicting copy of');
+ $I->see('Updated content.');
+ $I->dontSee('Initial content');
+ }
+
+
+ public function testEditingStatusSameUser(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing status of an existing Wiki page when access by same user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => $page->id]);
+
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"isEditing":false');
+ $I->seeInSource('"user":null');
+
+ $I->amOnSpace($space->guid, '/wiki/page/edit', ['id' => $page->id]);
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => $page->id]);
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"isEditing":false');
+ $I->seeInSource('"user":"admin"');
+
+ $I->amOnSpace($space->guid, '/wiki/overview');
+ $I->sendAjaxPostRequest(Url::toWikiDelete($page));
+ $I->seeSuccessResponseCode();
+
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => $page->id]);
+
+ $I->seeResponseCodeIs(404);
+
+ }
+
+ public function testEditingStatusSave(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing status of an existing Wiki page when access by same user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $I->amOnSpace($space->guid, '/wiki/page/edit', ['id' => $page->id]);
+ $I->saveWiki();
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => $page->id]);
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"isEditing":false');
+ $I->seeInSource('"user":null');
+
+ }
+
+ public function testEditingStatusDiffUser(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing status of an existing Wiki page when access by different user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_MODERATOR);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $page->updateAttributes(['is_currently_editing' => 'admin', 'editing_started_at' => time()]);
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => $page->id]);
+
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"isEditing":true');
+ $I->seeInSource('"user":"admin"');
+
+ }
+
+ public function testEditingStatusPageNotFound(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing status of an non-existing Wiki page when access by different user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $I->amOnSpace($space->guid, '/wiki/page/editing-status', ['id' => 123456789]);
+ $I->seeResponseCodeIs(404);
+ }
+
+ public function testEditingTTLSameUser(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing TTL status of a Wiki page when access by same user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_ADMIN);
+ $I->amAdmin(true);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+
+ $I->amOnSpace($space->guid, '/wiki/page/editing-timer-update', ['id' => $page->id]);
+
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"user":"admin"');
+ $I->seeInSource('"unAuthorizedLogin":false');
+
+ }
+
+ public function testEditingTTLDiffUser(FunctionalTester $I)
+ {
+ $I->wantTo('Check the editing TTL status of a Wiki page when access by different user');
+ $space = $I->loginBySpaceUserGroup(Space::USERGROUP_MODERATOR);
+ $I->enableModule($space->guid, 'wiki');
+ $page = $I->createWiki($space, 'Test Page', 'Initial content');
+
+ $page->updateAttributes(['is_currently_editing' => 'admin', 'editing_started_at' => time()]);
+ $I->amOnSpace($space->guid, '/wiki/page/editing-timer-update', ['id' => $page->id]);
+
+ $I->seeResponseCodeIs(200);
+
+ $I->seeInSource('"success":true');
+ $I->seeInSource('"user":"admin"');
+ $I->seeInSource('"unAuthorizedLogin":true');
+
+ }
+
+}
\ No newline at end of file
diff --git a/views/page/edit.php b/views/page/edit.php
index 28e31293..9cd9e848 100644
--- a/views/page/edit.php
+++ b/views/page/edit.php
@@ -15,6 +15,7 @@
use humhub\modules\wiki\widgets\WikiPagePicker;
use humhub\modules\wiki\widgets\WikiPath;
use humhub\widgets\Button;
+use humhub\modules\wiki\helpers\Url;
use humhub\modules\topic\widgets\TopicPicker;
/* @var $this View */
@@ -47,7 +48,7 @@
]) ?>
-