Add script 'get-openssl-version.sh'.
This commit is contained in:
8
snippets/dokuwiki-2023-04-04/inc/.htaccess
Normal file
8
snippets/dokuwiki-2023-04-04/inc/.htaccess
Normal file
@ -0,0 +1,8 @@
|
||||
## no access to the inc directory
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfModule>
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAclRequiredException;
|
||||
|
||||
/**
|
||||
* Class AbstractAclAction
|
||||
*
|
||||
* An action that requires the ACL subsystem to be enabled (eg. useacl=1)
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
abstract class AbstractAclAction extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
global $conf;
|
||||
global $auth;
|
||||
if(!$conf['useacl']) throw new ActionAclRequiredException();
|
||||
if(!$auth) throw new ActionAclRequiredException();
|
||||
}
|
||||
|
||||
}
|
88
snippets/dokuwiki-2023-04-04/inc/Action/AbstractAction.php
Normal file
88
snippets/dokuwiki-2023-04-04/inc/Action/AbstractAction.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
|
||||
/**
|
||||
* Class AbstractAction
|
||||
*
|
||||
* Base class for all actions
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
abstract class AbstractAction {
|
||||
|
||||
/** @var string holds the name of the action (lowercase class name, no namespace) */
|
||||
protected $actionname;
|
||||
|
||||
/**
|
||||
* AbstractAction constructor.
|
||||
*
|
||||
* @param string $actionname the name of this action (see getActionName() for caveats)
|
||||
*/
|
||||
public function __construct($actionname = '') {
|
||||
if($actionname !== '') {
|
||||
$this->actionname = $actionname;
|
||||
} else {
|
||||
// http://stackoverflow.com/a/27457689/172068
|
||||
$this->actionname = strtolower(substr(strrchr(get_class($this), '\\'), 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum permission needed
|
||||
*
|
||||
* This needs to return one of the AUTH_* constants. It will be checked against
|
||||
* the current user and page after checkPermissions() ran through. If it fails,
|
||||
* the user will be shown the Denied action.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
abstract public function minimumPermission();
|
||||
|
||||
/**
|
||||
* Check conditions are met to run this action
|
||||
*
|
||||
* @throws ActionException
|
||||
* @return void
|
||||
*/
|
||||
public function checkPreconditions() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Process data
|
||||
*
|
||||
* This runs before any output is sent to the browser.
|
||||
*
|
||||
* Throw an Exception if a different action should be run after this step.
|
||||
*
|
||||
* @throws ActionException
|
||||
* @return void
|
||||
*/
|
||||
public function preProcess() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Output whatever content is wanted within tpl_content();
|
||||
*
|
||||
* @fixme we may want to return a Ui class here
|
||||
* @throws FatalException
|
||||
*/
|
||||
public function tplContent() {
|
||||
throw new FatalException('No content for Action ' . $this->actionname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this action
|
||||
*
|
||||
* This is usually the lowercased class name, but may differ for some actions.
|
||||
* eg. the export_ modes or for the Plugin action.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getActionName() {
|
||||
return $this->actionname;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
|
||||
/**
|
||||
* Class AbstractAliasAction
|
||||
*
|
||||
* An action that is an alias for another action. Skips the minimumPermission check
|
||||
*
|
||||
* Be sure to implement preProcess() and throw an ActionAbort exception
|
||||
* with the proper action.
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
abstract class AbstractAliasAction extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FatalException
|
||||
*/
|
||||
public function preProcess() {
|
||||
throw new FatalException('Alias Actions need to implement preProcess to load the aliased action');
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionUserRequiredException;
|
||||
|
||||
/**
|
||||
* Class AbstractUserAction
|
||||
*
|
||||
* An action that requires a logged in user
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
abstract class AbstractUserAction extends AbstractAclAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
global $INPUT;
|
||||
if($INPUT->server->str('REMOTE_USER') === '') {
|
||||
throw new ActionUserRequiredException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
snippets/dokuwiki-2023-04-04/inc/Action/Admin.php
Normal file
42
snippets/dokuwiki-2023-04-04/inc/Action/Admin.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Extension\AdminPlugin;
|
||||
|
||||
/**
|
||||
* Class Admin
|
||||
*
|
||||
* Action to show the admin interface or admin plugins
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Admin extends AbstractUserAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ; // let in check later
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function preProcess() {
|
||||
global $INPUT;
|
||||
|
||||
// retrieve admin plugin name from $_REQUEST['page']
|
||||
if($INPUT->str('page', '', true) != '') {
|
||||
/** @var AdminPlugin $plugin */
|
||||
if($plugin = plugin_getRequestAdminPlugin()) { // FIXME this method does also permission checking
|
||||
if(!$plugin->isAccessibleByCurrentUser()) {
|
||||
throw new ActionException('denied');
|
||||
}
|
||||
$plugin->handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function tplContent() {
|
||||
tpl_admin();
|
||||
}
|
||||
}
|
28
snippets/dokuwiki-2023-04-04/inc/Action/Backlink.php
Normal file
28
snippets/dokuwiki-2023-04-04/inc/Action/Backlink.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Backlink
|
||||
*
|
||||
* Shows which pages link to the current page
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Backlink extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Backlinks)->show();
|
||||
}
|
||||
|
||||
}
|
28
snippets/dokuwiki-2023-04-04/inc/Action/Cancel.php
Normal file
28
snippets/dokuwiki-2023-04-04/inc/Action/Cancel.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
|
||||
/**
|
||||
* Class Cancel
|
||||
*
|
||||
* Alias for show. Aborts editing
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Cancel extends AbstractAliasAction {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $ID;
|
||||
unlock($ID);
|
||||
|
||||
// continue with draftdel -> redirect -> show
|
||||
throw new ActionAbort('draftdel');
|
||||
}
|
||||
|
||||
}
|
26
snippets/dokuwiki-2023-04-04/inc/Action/Check.php
Normal file
26
snippets/dokuwiki-2023-04-04/inc/Action/Check.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
|
||||
/**
|
||||
* Class Check
|
||||
*
|
||||
* Adds some debugging info before aborting to show
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Check extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
public function preProcess() {
|
||||
check();
|
||||
throw new ActionAbort();
|
||||
}
|
||||
|
||||
}
|
39
snippets/dokuwiki-2023-04-04/inc/Action/Conflict.php
Normal file
39
snippets/dokuwiki-2023-04-04/inc/Action/Conflict.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Conflict
|
||||
*
|
||||
* Show the conflict resolution screen
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Conflict extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_EDIT;
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $PRE;
|
||||
global $TEXT;
|
||||
global $SUF;
|
||||
global $SUM;
|
||||
|
||||
$text = con($PRE, $TEXT, $SUF);
|
||||
(new Ui\PageConflict($text, $SUM))->show();
|
||||
}
|
||||
|
||||
}
|
52
snippets/dokuwiki-2023-04-04/inc/Action/Denied.php
Normal file
52
snippets/dokuwiki-2023-04-04/inc/Action/Denied.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Denied
|
||||
*
|
||||
* Show the access denied screen
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Denied extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
$this->showBanner();
|
||||
|
||||
$data = null;
|
||||
$event = new Event('ACTION_DENIED_TPLCONTENT', $data);
|
||||
if ($event->advise_before()) {
|
||||
global $INPUT;
|
||||
if (empty($INPUT->server->str('REMOTE_USER')) && actionOK('login')) {
|
||||
(new Ui\Login)->show();
|
||||
}
|
||||
}
|
||||
$event->advise_after();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error on denied pages
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showBanner()
|
||||
{
|
||||
// print intro
|
||||
print p_locale_xhtml('denied');
|
||||
}
|
||||
|
||||
}
|
41
snippets/dokuwiki-2023-04-04/inc/Action/Diff.php
Normal file
41
snippets/dokuwiki-2023-04-04/inc/Action/Diff.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Diff
|
||||
*
|
||||
* Show the differences between two revisions
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Diff extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
// store the selected diff type in cookie
|
||||
$difftype = $INPUT->str('difftype');
|
||||
if (!empty($difftype)) {
|
||||
set_doku_pref('difftype', $difftype);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $INFO;
|
||||
(new Ui\PageDiff($INFO['id']))->preference('showIntro', true)->show();
|
||||
}
|
||||
|
||||
}
|
43
snippets/dokuwiki-2023-04-04/inc/Action/Draft.php
Normal file
43
snippets/dokuwiki-2023-04-04/inc/Action/Draft.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Draft
|
||||
*
|
||||
* Screen to see and recover a draft
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
* @fixme combine with Recover?
|
||||
*/
|
||||
class Draft extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_EDIT;
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
global $INFO;
|
||||
if (!file_exists($INFO['draft'])) throw new ActionException('edit');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\PageDraft)->show();
|
||||
}
|
||||
|
||||
}
|
38
snippets/dokuwiki-2023-04-04/inc/Action/Draftdel.php
Normal file
38
snippets/dokuwiki-2023-04-04/inc/Action/Draftdel.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
|
||||
/**
|
||||
* Class Draftdel
|
||||
*
|
||||
* Delete a draft
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Draftdel extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_EDIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing draft for the current page and user if any
|
||||
*
|
||||
* Redirects to show, afterwards.
|
||||
*
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $INFO, $ID;
|
||||
$draft = new \dokuwiki\Draft($ID, $INFO['client']);
|
||||
if ($draft->isDraftAvailable() && checkSecurityToken()) {
|
||||
$draft->deleteDraft();
|
||||
}
|
||||
|
||||
throw new ActionAbort('redirect');
|
||||
}
|
||||
|
||||
}
|
96
snippets/dokuwiki-2023-04-04/inc/Action/Edit.php
Normal file
96
snippets/dokuwiki-2023-04-04/inc/Action/Edit.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Edit
|
||||
*
|
||||
* Handle editing
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Edit extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_READ; // we check again below
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc falls back to 'source' if page not writable
|
||||
*/
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
global $INFO;
|
||||
|
||||
// no edit permission? view source
|
||||
if ($INFO['exists'] && !$INFO['writable']) {
|
||||
throw new ActionAbort('source');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $ID;
|
||||
global $INFO;
|
||||
|
||||
global $TEXT;
|
||||
global $RANGE;
|
||||
global $PRE;
|
||||
global $SUF;
|
||||
global $REV;
|
||||
global $SUM;
|
||||
global $lang;
|
||||
global $DATE;
|
||||
|
||||
if (!isset($TEXT)) {
|
||||
if ($INFO['exists']) {
|
||||
if ($RANGE) {
|
||||
list($PRE, $TEXT, $SUF) = rawWikiSlices($RANGE, $ID, $REV);
|
||||
} else {
|
||||
$TEXT = rawWiki($ID, $REV);
|
||||
}
|
||||
} else {
|
||||
$TEXT = pageTemplate($ID);
|
||||
}
|
||||
}
|
||||
|
||||
//set summary default
|
||||
if (!$SUM) {
|
||||
if ($REV) {
|
||||
$SUM = sprintf($lang['restored'], dformat($REV));
|
||||
} elseif (!$INFO['exists']) {
|
||||
$SUM = $lang['created'];
|
||||
}
|
||||
}
|
||||
|
||||
// Use the date of the newest revision, not of the revision we edit
|
||||
// This is used for conflict detection
|
||||
if (!$DATE) $DATE = @filemtime(wikiFN($ID));
|
||||
|
||||
//check if locked by anyone - if not lock for my self
|
||||
$lockedby = checklock($ID);
|
||||
if ($lockedby) {
|
||||
throw new ActionAbort('locked');
|
||||
}
|
||||
lock($ID);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class ActionAbort
|
||||
*
|
||||
* Strictly speaking not an Exception but an expected execution path. Used to
|
||||
* signal when one action is done and another should take over.
|
||||
*
|
||||
* If you want to signal the same but under some error condition use ActionException
|
||||
* or one of it's decendants.
|
||||
*
|
||||
* The message will NOT be shown to the enduser
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class ActionAbort extends ActionException {
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class ActionAclRequiredException
|
||||
*
|
||||
* Thrown by AbstractACLAction when an action requires that the ACL subsystem is
|
||||
* enabled but it isn't. You should not use it
|
||||
*
|
||||
* The message will NOT be shown to the enduser
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class ActionAclRequiredException extends ActionException {
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class ActionDisabledException
|
||||
*
|
||||
* Thrown when the requested action has been disabled. Eg. through the 'disableactions'
|
||||
* config setting. You should probably not use it.
|
||||
*
|
||||
* The message will NOT be shown to the enduser, but a generic information will be shown.
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class ActionDisabledException extends ActionException {
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class ActionException
|
||||
*
|
||||
* This exception and its subclasses signal that the current action should be
|
||||
* aborted and a different action should be used instead. The new action can
|
||||
* be given as parameter in the constructor. Defaults to 'show'
|
||||
*
|
||||
* The message will NOT be shown to the enduser
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class ActionException extends \Exception {
|
||||
|
||||
/** @var string the new action */
|
||||
protected $newaction;
|
||||
|
||||
/** @var bool should the exception's message be shown to the user? */
|
||||
protected $displayToUser = false;
|
||||
|
||||
/**
|
||||
* ActionException constructor.
|
||||
*
|
||||
* When no new action is given 'show' is assumed. For requests that originated in a POST,
|
||||
* a 'redirect' is used which will cause a redirect to the 'show' action.
|
||||
*
|
||||
* @param string|null $newaction the action that should be used next
|
||||
* @param string $message optional message, will not be shown except for some dub classes
|
||||
*/
|
||||
public function __construct($newaction = null, $message = '') {
|
||||
global $INPUT;
|
||||
parent::__construct($message);
|
||||
if(is_null($newaction)) {
|
||||
if(strtolower($INPUT->server->str('REQUEST_METHOD')) == 'post') {
|
||||
$newaction = 'redirect';
|
||||
} else {
|
||||
$newaction = 'show';
|
||||
}
|
||||
}
|
||||
|
||||
$this->newaction = $newaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action to use next
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNewAction() {
|
||||
return $this->newaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this Exception's message be shown to the user?
|
||||
*
|
||||
* @param null|bool $set when null is given, the current setting is not changed
|
||||
* @return bool
|
||||
*/
|
||||
public function displayToUser($set = null) {
|
||||
if(!is_null($set)) $this->displayToUser = $set;
|
||||
return $set;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class ActionUserRequiredException
|
||||
*
|
||||
* Thrown by AbstractUserAction when an action requires that a user is logged
|
||||
* in but it isn't. You should not use it.
|
||||
*
|
||||
* The message will NOT be shown to the enduser
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class ActionUserRequiredException extends ActionException {
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class FatalException
|
||||
*
|
||||
* A fatal exception during handling the action
|
||||
*
|
||||
* Will abort all handling and display some info to the user. The HTTP status code
|
||||
* can be defined.
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class FatalException extends \Exception {
|
||||
/**
|
||||
* FatalException constructor.
|
||||
*
|
||||
* @param string $message the message to send
|
||||
* @param int $status the HTTP status to send
|
||||
* @param null|\Exception $previous previous exception
|
||||
*/
|
||||
public function __construct($message = 'A fatal error occured', $status = 500, $previous = null) {
|
||||
parent::__construct($message, $status, $previous);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action\Exception;
|
||||
|
||||
/**
|
||||
* Class NoActionException
|
||||
*
|
||||
* Thrown in the ActionRouter when a wanted action can not be found. Triggers
|
||||
* the unknown action event
|
||||
*
|
||||
* @package dokuwiki\Action\Exception
|
||||
*/
|
||||
class NoActionException extends \Exception {
|
||||
|
||||
}
|
113
snippets/dokuwiki-2023-04-04/inc/Action/Export.php
Normal file
113
snippets/dokuwiki-2023-04-04/inc/Action/Export.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Export
|
||||
*
|
||||
* Handle exporting by calling the appropriate renderer
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Export extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a wiki page for various formats
|
||||
*
|
||||
* Triggers ACTION_EXPORT_POSTPROCESS
|
||||
*
|
||||
* Event data:
|
||||
* data['id'] -- page id
|
||||
* data['mode'] -- requested export mode
|
||||
* data['headers'] -- export headers
|
||||
* data['output'] -- export output
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @author Michael Klier <chi@chimeric.de>
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $ID;
|
||||
global $REV;
|
||||
global $conf;
|
||||
global $lang;
|
||||
|
||||
$pre = '';
|
||||
$post = '';
|
||||
$headers = array();
|
||||
|
||||
// search engines: never cache exported docs! (Google only currently)
|
||||
$headers['X-Robots-Tag'] = 'noindex';
|
||||
|
||||
$mode = substr($this->actionname, 7);
|
||||
switch($mode) {
|
||||
case 'raw':
|
||||
$headers['Content-Type'] = 'text/plain; charset=utf-8';
|
||||
$headers['Content-Disposition'] = 'attachment; filename=' . noNS($ID) . '.txt';
|
||||
$output = rawWiki($ID, $REV);
|
||||
break;
|
||||
case 'xhtml':
|
||||
$pre .= '<!DOCTYPE html>' . DOKU_LF;
|
||||
$pre .= '<html lang="' . $conf['lang'] . '" dir="' . $lang['direction'] . '">' . DOKU_LF;
|
||||
$pre .= '<head>' . DOKU_LF;
|
||||
$pre .= ' <meta charset="utf-8" />' . DOKU_LF; // FIXME improve wrapper
|
||||
$pre .= ' <title>' . $ID . '</title>' . DOKU_LF;
|
||||
|
||||
// get metaheaders
|
||||
ob_start();
|
||||
tpl_metaheaders();
|
||||
$pre .= ob_get_clean();
|
||||
|
||||
$pre .= '</head>' . DOKU_LF;
|
||||
$pre .= '<body>' . DOKU_LF;
|
||||
$pre .= '<div class="dokuwiki export">' . DOKU_LF;
|
||||
|
||||
// get toc
|
||||
$pre .= tpl_toc(true);
|
||||
|
||||
$headers['Content-Type'] = 'text/html; charset=utf-8';
|
||||
$output = p_wiki_xhtml($ID, $REV, false);
|
||||
|
||||
$post .= '</div>' . DOKU_LF;
|
||||
$post .= '</body>' . DOKU_LF;
|
||||
$post .= '</html>' . DOKU_LF;
|
||||
break;
|
||||
case 'xhtmlbody':
|
||||
$headers['Content-Type'] = 'text/html; charset=utf-8';
|
||||
$output = p_wiki_xhtml($ID, $REV, false);
|
||||
break;
|
||||
default:
|
||||
$output = p_cached_output(wikiFN($ID, $REV), $mode, $ID);
|
||||
$headers = p_get_metadata($ID, "format $mode");
|
||||
break;
|
||||
}
|
||||
|
||||
// prepare event data
|
||||
$data = array();
|
||||
$data['id'] = $ID;
|
||||
$data['mode'] = $mode;
|
||||
$data['headers'] = $headers;
|
||||
$data['output'] =& $output;
|
||||
|
||||
Event::createAndTrigger('ACTION_EXPORT_POSTPROCESS', $data);
|
||||
|
||||
if(!empty($data['output'])) {
|
||||
if(is_array($data['headers'])) foreach($data['headers'] as $key => $val) {
|
||||
header("$key: $val");
|
||||
}
|
||||
print $pre . $data['output'] . $post;
|
||||
exit;
|
||||
}
|
||||
|
||||
throw new ActionAbort();
|
||||
}
|
||||
|
||||
}
|
29
snippets/dokuwiki-2023-04-04/inc/Action/Index.php
Normal file
29
snippets/dokuwiki-2023-04-04/inc/Action/Index.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Index
|
||||
*
|
||||
* Show the human readable sitemap. Do not confuse with Sitemap
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Index extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $IDX;
|
||||
(new Ui\Index($IDX))->show();
|
||||
}
|
||||
|
||||
}
|
56
snippets/dokuwiki-2023-04-04/inc/Action/Locked.php
Normal file
56
snippets/dokuwiki-2023-04-04/inc/Action/Locked.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Locked
|
||||
*
|
||||
* Show a locked screen when a page is locked
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Locked extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
$this->showBanner();
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error on locked pages
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showBanner()
|
||||
{
|
||||
global $ID;
|
||||
global $conf;
|
||||
global $lang;
|
||||
global $INFO;
|
||||
|
||||
$locktime = filemtime(wikiLockFN($ID));
|
||||
$expire = dformat($locktime + $conf['locktime']);
|
||||
$min = round(($conf['locktime'] - (time() - $locktime) )/60);
|
||||
|
||||
// print intro
|
||||
print p_locale_xhtml('locked');
|
||||
|
||||
print '<ul>';
|
||||
print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
|
||||
print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
|
||||
print '</ul>'.DOKU_LF;
|
||||
}
|
||||
|
||||
}
|
40
snippets/dokuwiki-2023-04-04/inc/Action/Login.php
Normal file
40
snippets/dokuwiki-2023-04-04/inc/Action/Login.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Login
|
||||
*
|
||||
* The login form. Actual logins are handled in inc/auth.php
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Login extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
global $INPUT;
|
||||
parent::checkPreconditions();
|
||||
if ($INPUT->server->has('REMOTE_USER')) {
|
||||
// nothing to do
|
||||
throw new ActionException();
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Login)->show();
|
||||
}
|
||||
|
||||
}
|
53
snippets/dokuwiki-2023-04-04/inc/Action/Logout.php
Normal file
53
snippets/dokuwiki-2023-04-04/inc/Action/Logout.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
|
||||
/**
|
||||
* Class Logout
|
||||
*
|
||||
* Log out a user
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Logout extends AbstractUserAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('logout')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
global $ID;
|
||||
global $INPUT;
|
||||
|
||||
if (!checkSecurityToken()) throw new ActionException();
|
||||
|
||||
// when logging out during an edit session, unlock the page
|
||||
$lockedby = checklock($ID);
|
||||
if($lockedby == $INPUT->server->str('REMOTE_USER')) {
|
||||
unlock($ID);
|
||||
}
|
||||
|
||||
// do the logout stuff and redirect to login
|
||||
auth_logoff();
|
||||
send_redirect(wl($ID, array('do' => 'login'), true, '&'));
|
||||
|
||||
// should never be reached
|
||||
throw new ActionException('login');
|
||||
}
|
||||
|
||||
}
|
24
snippets/dokuwiki-2023-04-04/inc/Action/Media.php
Normal file
24
snippets/dokuwiki-2023-04-04/inc/Action/Media.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
/**
|
||||
* Class Media
|
||||
*
|
||||
* The full screen media manager
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Media extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
tpl_media();
|
||||
}
|
||||
|
||||
}
|
34
snippets/dokuwiki-2023-04-04/inc/Action/Plugin.php
Normal file
34
snippets/dokuwiki-2023-04-04/inc/Action/Plugin.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Plugin
|
||||
*
|
||||
* Used to run action plugins
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Plugin extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs nothing but a warning unless an action plugin overwrites it
|
||||
*
|
||||
* @inheritdoc
|
||||
* @triggers TPL_ACT_UNKNOWN
|
||||
*/
|
||||
public function tplContent() {
|
||||
$evt = new Event('TPL_ACT_UNKNOWN', $this->actionname);
|
||||
if($evt->advise_before()) {
|
||||
msg('Failed to handle action: ' . hsc($this->actionname), -1);
|
||||
}
|
||||
$evt->advise_after();
|
||||
}
|
||||
}
|
47
snippets/dokuwiki-2023-04-04/inc/Action/Preview.php
Normal file
47
snippets/dokuwiki-2023-04-04/inc/Action/Preview.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Preview
|
||||
*
|
||||
* preview during editing
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Preview extends Edit
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
header('X-XSS-Protection: 0');
|
||||
$this->savedraft();
|
||||
parent::preProcess();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $TEXT;
|
||||
(new Ui\Editor)->show();
|
||||
(new Ui\PageView($TEXT))->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a draft on preview
|
||||
*/
|
||||
protected function savedraft()
|
||||
{
|
||||
global $ID, $INFO;
|
||||
$draft = new \dokuwiki\Draft($ID, $INFO['client']);
|
||||
if (!$draft->saveDraft()) {
|
||||
$errors = $draft->getErrors();
|
||||
foreach ($errors as $error) {
|
||||
msg(hsc($error), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
snippets/dokuwiki-2023-04-04/inc/Action/Profile.php
Normal file
51
snippets/dokuwiki-2023-04-04/inc/Action/Profile.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Profile
|
||||
*
|
||||
* Handle the profile form
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Profile extends AbstractUserAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('Profile')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $lang;
|
||||
if (updateprofile()) {
|
||||
msg($lang['profchanged'], 1);
|
||||
throw new ActionAbort('show');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserProfile)->show();
|
||||
}
|
||||
|
||||
}
|
43
snippets/dokuwiki-2023-04-04/inc/Action/ProfileDelete.php
Normal file
43
snippets/dokuwiki-2023-04-04/inc/Action/ProfileDelete.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
|
||||
/**
|
||||
* Class ProfileDelete
|
||||
*
|
||||
* Delete a user account
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class ProfileDelete extends AbstractUserAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('delUser')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
global $lang;
|
||||
if(auth_deleteprofile()) {
|
||||
msg($lang['profdeleted'], 1);
|
||||
throw new ActionAbort('show');
|
||||
} else {
|
||||
throw new ActionAbort('profile');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
snippets/dokuwiki-2023-04-04/inc/Action/Recent.php
Normal file
45
snippets/dokuwiki-2023-04-04/inc/Action/Recent.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Recent
|
||||
*
|
||||
* The recent changes view
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Recent extends AbstractAction
|
||||
{
|
||||
/** @var string what type of changes to show */
|
||||
protected $showType = 'both';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $INPUT;
|
||||
$show_changes = $INPUT->str('show_changes');
|
||||
if (!empty($show_changes)) {
|
||||
set_doku_pref('show_changes', $show_changes);
|
||||
$this->showType = $show_changes;
|
||||
} else {
|
||||
$this->showType = get_doku_pref('show_changes', 'both');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $INPUT;
|
||||
(new Ui\Recent($INPUT->extract('first')->int('first'), $this->showType))->show();
|
||||
}
|
||||
|
||||
}
|
24
snippets/dokuwiki-2023-04-04/inc/Action/Recover.php
Normal file
24
snippets/dokuwiki-2023-04-04/inc/Action/Recover.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
|
||||
/**
|
||||
* Class Recover
|
||||
*
|
||||
* Recover a draft
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Recover extends AbstractAliasAction {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
throw new ActionAbort('edit');
|
||||
}
|
||||
|
||||
}
|
65
snippets/dokuwiki-2023-04-04/inc/Action/Redirect.php
Normal file
65
snippets/dokuwiki-2023-04-04/inc/Action/Redirect.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Redirect
|
||||
*
|
||||
* Used to redirect to the current page with the last edited section as a target if found
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Redirect extends AbstractAliasAction {
|
||||
|
||||
/**
|
||||
* Redirect to the show action, trying to jump to the previously edited section
|
||||
*
|
||||
* @triggers ACTION_SHOW_REDIRECT
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $PRE;
|
||||
global $TEXT;
|
||||
global $INPUT;
|
||||
global $ID;
|
||||
global $ACT;
|
||||
|
||||
$opts = array(
|
||||
'id' => $ID,
|
||||
'preact' => $ACT
|
||||
);
|
||||
//get section name when coming from section edit
|
||||
if($INPUT->has('hid')) {
|
||||
// Use explicitly transmitted header id
|
||||
$opts['fragment'] = $INPUT->str('hid');
|
||||
} else if($PRE && preg_match('/^\s*==+([^=\n]+)/', $TEXT, $match)) {
|
||||
// Fallback to old mechanism
|
||||
$check = false; //Byref
|
||||
$opts['fragment'] = sectionID($match[0], $check);
|
||||
}
|
||||
|
||||
// execute the redirect
|
||||
Event::createAndTrigger('ACTION_SHOW_REDIRECT', $opts, array($this, 'redirect'));
|
||||
|
||||
// should never be reached
|
||||
throw new ActionAbort('show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the redirect
|
||||
*
|
||||
* Default action for ACTION_SHOW_REDIRECT
|
||||
*
|
||||
* @param array $opts id and fragment for the redirect and the preact
|
||||
*/
|
||||
public function redirect($opts) {
|
||||
$go = wl($opts['id'], '', true, '&');
|
||||
if(isset($opts['fragment'])) $go .= '#' . $opts['fragment'];
|
||||
|
||||
//show it
|
||||
send_redirect($go);
|
||||
}
|
||||
}
|
51
snippets/dokuwiki-2023-04-04/inc/Action/Register.php
Normal file
51
snippets/dokuwiki-2023-04-04/inc/Action/Register.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Register
|
||||
*
|
||||
* Self registering a new user
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Register extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $conf;
|
||||
if (isset($conf['openregister']) && !$conf['openregister']) throw new ActionDisabledException();
|
||||
if (!$auth->canDo('addUser')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
if (register()) { // FIXME could be moved from auth to here
|
||||
throw new ActionAbort('login');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserRegister)->show();
|
||||
}
|
||||
|
||||
}
|
185
snippets/dokuwiki-2023-04-04/inc/Action/Resendpwd.php
Normal file
185
snippets/dokuwiki-2023-04-04/inc/Action/Resendpwd.php
Normal file
@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Resendpwd
|
||||
*
|
||||
* Handle password recovery
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Resendpwd extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $conf;
|
||||
if (isset($conf['resendpasswd']) && !$conf['resendpasswd'])
|
||||
throw new ActionDisabledException(); //legacy option
|
||||
if (!$auth->canDo('modPass')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
if ($this->resendpwd()) {
|
||||
throw new ActionAbort('login');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserResendPwd)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a new password
|
||||
*
|
||||
* This function handles both phases of the password reset:
|
||||
*
|
||||
* - handling the first request of password reset
|
||||
* - validating the password reset auth token
|
||||
*
|
||||
* @author Benoit Chesneau <benoit@bchesneau.info>
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @fixme this should be split up into multiple methods
|
||||
* @return bool true on success, false on any error
|
||||
*/
|
||||
protected function resendpwd()
|
||||
{
|
||||
global $lang;
|
||||
global $conf;
|
||||
/* @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $INPUT;
|
||||
|
||||
if (!actionOK('resendpwd')) {
|
||||
msg($lang['resendna'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
$token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
|
||||
|
||||
if ($token) {
|
||||
// we're in token phase - get user info from token
|
||||
|
||||
$tfile = $conf['cachedir'] .'/'. $token[0] .'/'. $token . '.pwauth';
|
||||
if (!file_exists($tfile)) {
|
||||
msg($lang['resendpwdbadauth'], -1);
|
||||
$INPUT->remove('pwauth');
|
||||
return false;
|
||||
}
|
||||
// token is only valid for 3 days
|
||||
if ((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
|
||||
msg($lang['resendpwdbadauth'], -1);
|
||||
$INPUT->remove('pwauth');
|
||||
@unlink($tfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
$user = io_readfile($tfile);
|
||||
$userinfo = $auth->getUserData($user, $requireGroups = false);
|
||||
if (!$userinfo['mail']) {
|
||||
msg($lang['resendpwdnouser'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$conf['autopasswd']) { // we let the user choose a password
|
||||
$pass = $INPUT->str('pass');
|
||||
|
||||
// password given correctly?
|
||||
if (!$pass) return false;
|
||||
if ($pass != $INPUT->str('passchk')) {
|
||||
msg($lang['regbadpass'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// change it
|
||||
if (!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
|
||||
msg($lang['proffail'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else { // autogenerate the password and send by mail
|
||||
|
||||
$pass = auth_pwgen($user);
|
||||
if (!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
|
||||
msg($lang['proffail'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auth_sendPassword($user, $pass)) {
|
||||
msg($lang['resendpwdsuccess'], 1);
|
||||
} else {
|
||||
msg($lang['regmailfail'], -1);
|
||||
}
|
||||
}
|
||||
|
||||
@unlink($tfile);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// we're in request phase
|
||||
|
||||
if (!$INPUT->post->bool('save')) return false;
|
||||
|
||||
if (!$INPUT->post->str('login')) {
|
||||
msg($lang['resendpwdmissing'], -1);
|
||||
return false;
|
||||
} else {
|
||||
$user = trim($auth->cleanUser($INPUT->post->str('login')));
|
||||
}
|
||||
|
||||
$userinfo = $auth->getUserData($user, $requireGroups = false);
|
||||
if (!$userinfo['mail']) {
|
||||
msg($lang['resendpwdnouser'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// generate auth token
|
||||
$token = md5(auth_randombytes(16)); // random secret
|
||||
$tfile = $conf['cachedir'] .'/'. $token[0] .'/'. $token .'.pwauth';
|
||||
$url = wl('', array('do' => 'resendpwd', 'pwauth' => $token), true, '&');
|
||||
|
||||
io_saveFile($tfile, $user);
|
||||
|
||||
$text = rawLocale('pwconfirm');
|
||||
$trep = array(
|
||||
'FULLNAME' => $userinfo['name'],
|
||||
'LOGIN' => $user,
|
||||
'CONFIRM' => $url
|
||||
);
|
||||
|
||||
$mail = new \Mailer();
|
||||
$mail->to($userinfo['name'] .' <'. $userinfo['mail'] .'>');
|
||||
$mail->subject($lang['regpwmail']);
|
||||
$mail->setBody($text, $trep);
|
||||
if ($mail->send()) {
|
||||
msg($lang['resendpwdconfirm'], 1);
|
||||
} else {
|
||||
msg($lang['regmailfail'], -1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// never reached
|
||||
}
|
||||
|
||||
}
|
63
snippets/dokuwiki-2023-04-04/inc/Action/Revert.php
Normal file
63
snippets/dokuwiki-2023-04-04/inc/Action/Revert.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
|
||||
/**
|
||||
* Class Revert
|
||||
*
|
||||
* Quick revert to an old revision
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Revert extends AbstractUserAction
|
||||
{
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_EDIT;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @inheritdoc
|
||||
* @throws ActionAbort
|
||||
* @throws ActionException
|
||||
* @todo check for writability of the current page ($INFO might do it wrong and check the attic version)
|
||||
*/
|
||||
public function preProcess()
|
||||
{
|
||||
if (!checkSecurityToken()) throw new ActionException();
|
||||
|
||||
global $ID;
|
||||
global $REV;
|
||||
global $lang;
|
||||
|
||||
// when no revision is given, delete current one
|
||||
// FIXME this feature is not exposed in the GUI currently
|
||||
$text = '';
|
||||
$sum = $lang['deleted'];
|
||||
if ($REV) {
|
||||
$text = rawWiki($ID, $REV);
|
||||
if (!$text) throw new ActionException(); //something went wrong
|
||||
$sum = sprintf($lang['restored'], dformat($REV));
|
||||
}
|
||||
|
||||
// spam check
|
||||
if (checkwordblock($text)) {
|
||||
msg($lang['wordblock'], -1);
|
||||
throw new ActionException('edit');
|
||||
}
|
||||
|
||||
saveWikiText($ID, $text, $sum, false);
|
||||
msg($sum, 1);
|
||||
$REV = '';
|
||||
|
||||
// continue with draftdel -> redirect -> show
|
||||
throw new ActionAbort('draftdel');
|
||||
}
|
||||
|
||||
}
|
28
snippets/dokuwiki-2023-04-04/inc/Action/Revisions.php
Normal file
28
snippets/dokuwiki-2023-04-04/inc/Action/Revisions.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Revisions
|
||||
*
|
||||
* Show the list of old revisions of the current page
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Revisions extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
global $INFO, $INPUT;
|
||||
(new Ui\PageRevisions($INFO['id']))->show($INPUT->int('first', -1));
|
||||
}
|
||||
}
|
63
snippets/dokuwiki-2023-04-04/inc/Action/Save.php
Normal file
63
snippets/dokuwiki-2023-04-04/inc/Action/Save.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
|
||||
/**
|
||||
* Class Save
|
||||
*
|
||||
* Save at the end of an edit session
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Save extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
global $INFO;
|
||||
if($INFO['exists']) {
|
||||
return AUTH_EDIT;
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
if(!checkSecurityToken()) throw new ActionException('preview');
|
||||
|
||||
global $ID;
|
||||
global $DATE;
|
||||
global $PRE;
|
||||
global $TEXT;
|
||||
global $SUF;
|
||||
global $SUM;
|
||||
global $lang;
|
||||
global $INFO;
|
||||
global $INPUT;
|
||||
|
||||
//spam check
|
||||
if(checkwordblock()) {
|
||||
msg($lang['wordblock'], -1);
|
||||
throw new ActionException('edit');
|
||||
}
|
||||
//conflict check
|
||||
if($DATE != 0
|
||||
&& isset($INFO['meta']['date']['modified'])
|
||||
&& $INFO['meta']['date']['modified'] > $DATE
|
||||
) {
|
||||
throw new ActionException('conflict');
|
||||
}
|
||||
|
||||
//save it
|
||||
saveWikiText($ID, con($PRE, $TEXT, $SUF, true), $SUM, $INPUT->bool('minor')); //use pretty mode for con
|
||||
//unlock it
|
||||
unlock($ID);
|
||||
|
||||
// continue with draftdel -> redirect -> show
|
||||
throw new ActionAbort('draftdel');
|
||||
}
|
||||
|
||||
}
|
135
snippets/dokuwiki-2023-04-04/inc/Action/Search.php
Normal file
135
snippets/dokuwiki-2023-04-04/inc/Action/Search.php
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
|
||||
/**
|
||||
* Class Search
|
||||
*
|
||||
* Search for pages and content
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Search extends AbstractAction {
|
||||
|
||||
protected $pageLookupResults = array();
|
||||
protected $fullTextResults = array();
|
||||
protected $highlight = array();
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* we only search if a search word was given
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
}
|
||||
|
||||
public function preProcess()
|
||||
{
|
||||
global $QUERY, $ID, $conf, $INPUT;
|
||||
$s = cleanID($QUERY);
|
||||
|
||||
if ($ID !== $conf['start'] && !$INPUT->has('q')) {
|
||||
parse_str($INPUT->server->str('QUERY_STRING'), $urlParts);
|
||||
$urlParts['q'] = $urlParts['id'];
|
||||
unset($urlParts['id']);
|
||||
$url = wl($ID, $urlParts, true, '&');
|
||||
send_redirect($url);
|
||||
}
|
||||
|
||||
if ($s === '') throw new ActionAbort();
|
||||
$this->adjustGlobalQuery();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
$this->execute();
|
||||
|
||||
$search = new \dokuwiki\Ui\Search($this->pageLookupResults, $this->fullTextResults, $this->highlight);
|
||||
$search->show();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* run the search
|
||||
*/
|
||||
protected function execute()
|
||||
{
|
||||
global $INPUT, $QUERY;
|
||||
$after = $INPUT->str('min');
|
||||
$before = $INPUT->str('max');
|
||||
$this->pageLookupResults = ft_pageLookup($QUERY, true, useHeading('navigation'), $after, $before);
|
||||
$this->fullTextResults = ft_pageSearch($QUERY, $highlight, $INPUT->str('srt'), $after, $before);
|
||||
$this->highlight = $highlight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the global query accordingly to the config search_nslimit and search_fragment
|
||||
*
|
||||
* This will only do something if the search didn't originate from the form on the searchpage itself
|
||||
*/
|
||||
protected function adjustGlobalQuery()
|
||||
{
|
||||
global $conf, $INPUT, $QUERY, $ID;
|
||||
|
||||
if ($INPUT->bool('sf')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$Indexer = idx_get_indexer();
|
||||
$parsedQuery = ft_queryParser($Indexer, $QUERY);
|
||||
|
||||
if (empty($parsedQuery['ns']) && empty($parsedQuery['notns'])) {
|
||||
if ($conf['search_nslimit'] > 0) {
|
||||
if (getNS($ID) !== false) {
|
||||
$nsParts = explode(':', getNS($ID));
|
||||
$ns = implode(':', array_slice($nsParts, 0, $conf['search_nslimit']));
|
||||
$QUERY .= " @$ns";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($conf['search_fragment'] !== 'exact') {
|
||||
if (empty(array_diff($parsedQuery['words'], $parsedQuery['and']))) {
|
||||
if (strpos($QUERY, '*') === false) {
|
||||
$queryParts = explode(' ', $QUERY);
|
||||
$queryParts = array_map(function ($part) {
|
||||
if (strpos($part, '@') === 0) {
|
||||
return $part;
|
||||
}
|
||||
if (strpos($part, 'ns:') === 0) {
|
||||
return $part;
|
||||
}
|
||||
if (strpos($part, '^') === 0) {
|
||||
return $part;
|
||||
}
|
||||
if (strpos($part, '-ns:') === 0) {
|
||||
return $part;
|
||||
}
|
||||
|
||||
global $conf;
|
||||
|
||||
if ($conf['search_fragment'] === 'starts_with') {
|
||||
return $part . '*';
|
||||
}
|
||||
if ($conf['search_fragment'] === 'ends_with') {
|
||||
return '*' . $part;
|
||||
}
|
||||
|
||||
return '*' . $part . '*';
|
||||
|
||||
}, $queryParts);
|
||||
$QUERY = implode(' ', $queryParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
snippets/dokuwiki-2023-04-04/inc/Action/Show.php
Normal file
40
snippets/dokuwiki-2023-04-04/inc/Action/Show.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: andi
|
||||
* Date: 2/10/17
|
||||
* Time: 4:32 PM
|
||||
*/
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Show
|
||||
*
|
||||
* The default action of showing a page
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Show extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $ID;
|
||||
unlock($ID);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\PageView())->show();
|
||||
}
|
||||
|
||||
}
|
67
snippets/dokuwiki-2023-04-04/inc/Action/Sitemap.php
Normal file
67
snippets/dokuwiki-2023-04-04/inc/Action/Sitemap.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
use dokuwiki\Sitemap\Mapper;
|
||||
use dokuwiki\Utf8\PhpString;
|
||||
|
||||
/**
|
||||
* Class Sitemap
|
||||
*
|
||||
* Generate an XML sitemap for search engines. Do not confuse with Index
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Sitemap extends AbstractAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle sitemap delivery
|
||||
*
|
||||
* @author Michael Hamann <michael@content-space.de>
|
||||
* @throws FatalException
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $conf;
|
||||
|
||||
if($conf['sitemap'] < 1 || !is_numeric($conf['sitemap'])) {
|
||||
throw new FatalException('Sitemap generation is disabled', 404);
|
||||
}
|
||||
|
||||
$sitemap = Mapper::getFilePath();
|
||||
if(Mapper::sitemapIsCompressed()) {
|
||||
$mime = 'application/x-gzip';
|
||||
} else {
|
||||
$mime = 'application/xml; charset=utf-8';
|
||||
}
|
||||
|
||||
// Check if sitemap file exists, otherwise create it
|
||||
if(!is_readable($sitemap)) {
|
||||
Mapper::generate();
|
||||
}
|
||||
|
||||
if(is_readable($sitemap)) {
|
||||
// Send headers
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Content-Disposition: attachment; filename=' . PhpString::basename($sitemap));
|
||||
|
||||
http_conditionalRequest(filemtime($sitemap));
|
||||
|
||||
// Send file
|
||||
//use x-sendfile header to pass the delivery to compatible webservers
|
||||
http_sendfile($sitemap);
|
||||
|
||||
readfile($sitemap);
|
||||
exit;
|
||||
}
|
||||
|
||||
throw new FatalException('Could not read the sitemap file - bad permissions?');
|
||||
}
|
||||
|
||||
}
|
41
snippets/dokuwiki-2023-04-04/inc/Action/Source.php
Normal file
41
snippets/dokuwiki-2023-04-04/inc/Action/Source.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Source
|
||||
*
|
||||
* Show the source of a page
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Source extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
global $TEXT;
|
||||
global $INFO;
|
||||
global $ID;
|
||||
global $REV;
|
||||
|
||||
if ($INFO['exists']) {
|
||||
$TEXT = rawWiki($ID, $REV);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
}
|
176
snippets/dokuwiki-2023-04-04/inc/Action/Subscribe.php
Normal file
176
snippets/dokuwiki-2023-04-04/inc/Action/Subscribe.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Subscriptions\SubscriberManager;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Ui;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class Subscribe
|
||||
*
|
||||
* E-Mail subscription handling
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Subscribe extends AbstractUserAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
global $conf;
|
||||
if(isset($conf['subscribers']) && !$conf['subscribers']) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess()
|
||||
{
|
||||
try {
|
||||
$this->handleSubscribeData();
|
||||
} catch (ActionAbort $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
msg($e->getMessage(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Subscribe)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle page 'subscribe'
|
||||
*
|
||||
* @author Adrian Lang <lang@cosmocode.de>
|
||||
* @throws Exception if (un)subscribing fails
|
||||
* @throws ActionAbort when (un)subscribing worked
|
||||
*/
|
||||
protected function handleSubscribeData()
|
||||
{
|
||||
global $lang;
|
||||
global $INFO;
|
||||
global $INPUT;
|
||||
|
||||
// get and preprocess data.
|
||||
$params = array();
|
||||
foreach (array('target', 'style', 'action') as $param) {
|
||||
if ($INPUT->has("sub_$param")) {
|
||||
$params[$param] = $INPUT->str("sub_$param");
|
||||
}
|
||||
}
|
||||
|
||||
// any action given? if not just return and show the subscription page
|
||||
if (empty($params['action']) || !checkSecurityToken()) return;
|
||||
|
||||
// Handle POST data, may throw exception.
|
||||
Event::createAndTrigger('ACTION_HANDLE_SUBSCRIBE', $params, array($this, 'handlePostData'));
|
||||
|
||||
$target = $params['target'];
|
||||
$style = $params['style'];
|
||||
$action = $params['action'];
|
||||
|
||||
// Perform action.
|
||||
$subManager = new SubscriberManager();
|
||||
if ($action === 'unsubscribe') {
|
||||
$ok = $subManager->remove($target, $INPUT->server->str('REMOTE_USER'), $style);
|
||||
} else {
|
||||
$ok = $subManager->add($target, $INPUT->server->str('REMOTE_USER'), $style);
|
||||
}
|
||||
|
||||
if ($ok) {
|
||||
msg(
|
||||
sprintf(
|
||||
$lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
|
||||
prettyprint_id($target)
|
||||
), 1
|
||||
);
|
||||
throw new ActionAbort('redirect');
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
$lang["subscr_{$action}_error"],
|
||||
hsc($INFO['userinfo']['name']),
|
||||
prettyprint_id($target)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate POST data
|
||||
*
|
||||
* Validates POST data for a subscribe or unsubscribe request. This is the
|
||||
* default action for the event ACTION_HANDLE_SUBSCRIBE.
|
||||
*
|
||||
* @author Adrian Lang <lang@cosmocode.de>
|
||||
*
|
||||
* @param array &$params the parameters: target, style and action
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handlePostData(&$params)
|
||||
{
|
||||
global $INFO;
|
||||
global $lang;
|
||||
global $INPUT;
|
||||
|
||||
// Get and validate parameters.
|
||||
if (!isset($params['target'])) {
|
||||
throw new Exception('no subscription target given');
|
||||
}
|
||||
$target = $params['target'];
|
||||
$valid_styles = array('every', 'digest');
|
||||
if (substr($target, -1, 1) === ':') {
|
||||
// Allow “list” subscribe style since the target is a namespace.
|
||||
$valid_styles[] = 'list';
|
||||
}
|
||||
$style = valid_input_set(
|
||||
'style', $valid_styles, $params,
|
||||
'invalid subscription style given'
|
||||
);
|
||||
$action = valid_input_set(
|
||||
'action', array('subscribe', 'unsubscribe'),
|
||||
$params, 'invalid subscription action given'
|
||||
);
|
||||
|
||||
// Check other conditions.
|
||||
if ($action === 'subscribe') {
|
||||
if ($INFO['userinfo']['mail'] === '') {
|
||||
throw new Exception($lang['subscr_subscribe_noaddress']);
|
||||
}
|
||||
} elseif ($action === 'unsubscribe') {
|
||||
$is = false;
|
||||
foreach ($INFO['subscribed'] as $subscr) {
|
||||
if ($subscr['target'] === $target) {
|
||||
$is = true;
|
||||
}
|
||||
}
|
||||
if ($is === false) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
$lang['subscr_not_subscribed'],
|
||||
$INPUT->server->str('REMOTE_USER'),
|
||||
prettyprint_id($target)
|
||||
)
|
||||
);
|
||||
}
|
||||
// subscription_set deletes a subscription if style = null.
|
||||
$style = null;
|
||||
}
|
||||
|
||||
$params = compact('target', 'style', 'action');
|
||||
}
|
||||
|
||||
}
|
229
snippets/dokuwiki-2023-04-04/inc/ActionRouter.php
Normal file
229
snippets/dokuwiki-2023-04-04/inc/ActionRouter.php
Normal file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Action\AbstractAction;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
use dokuwiki\Action\Exception\NoActionException;
|
||||
use dokuwiki\Action\Plugin;
|
||||
|
||||
/**
|
||||
* Class ActionRouter
|
||||
* @package dokuwiki
|
||||
*/
|
||||
class ActionRouter {
|
||||
|
||||
/** @var AbstractAction */
|
||||
protected $action;
|
||||
|
||||
/** @var ActionRouter */
|
||||
protected static $instance = null;
|
||||
|
||||
/** @var int transition counter */
|
||||
protected $transitions = 0;
|
||||
|
||||
/** maximum loop */
|
||||
const MAX_TRANSITIONS = 5;
|
||||
|
||||
/** @var string[] the actions disabled in the configuration */
|
||||
protected $disabled;
|
||||
|
||||
/**
|
||||
* ActionRouter constructor. Singleton, thus protected!
|
||||
*
|
||||
* Sets up the correct action based on the $ACT global. Writes back
|
||||
* the selected action to $ACT
|
||||
*/
|
||||
protected function __construct() {
|
||||
global $ACT;
|
||||
global $conf;
|
||||
|
||||
$this->disabled = explode(',', $conf['disableactions']);
|
||||
$this->disabled = array_map('trim', $this->disabled);
|
||||
$this->transitions = 0;
|
||||
|
||||
$ACT = act_clean($ACT);
|
||||
$this->setupAction($ACT);
|
||||
$ACT = $this->action->getActionName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance
|
||||
*
|
||||
* @param bool $reinit
|
||||
* @return ActionRouter
|
||||
*/
|
||||
public static function getInstance($reinit = false) {
|
||||
if((self::$instance === null) || $reinit) {
|
||||
self::$instance = new ActionRouter();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the given action
|
||||
*
|
||||
* Instantiates the right class, runs permission checks and pre-processing and
|
||||
* sets $action
|
||||
*
|
||||
* @param string $actionname this is passed as a reference to $ACT, for plugin backward compatibility
|
||||
* @triggers ACTION_ACT_PREPROCESS
|
||||
*/
|
||||
protected function setupAction(&$actionname) {
|
||||
$presetup = $actionname;
|
||||
|
||||
try {
|
||||
// give plugins an opportunity to process the actionname
|
||||
$evt = new Extension\Event('ACTION_ACT_PREPROCESS', $actionname);
|
||||
if ($evt->advise_before()) {
|
||||
$this->action = $this->loadAction($actionname);
|
||||
$this->checkAction($this->action);
|
||||
$this->action->preProcess();
|
||||
} else {
|
||||
// event said the action should be kept, assume action plugin will handle it later
|
||||
$this->action = new Plugin($actionname);
|
||||
}
|
||||
$evt->advise_after();
|
||||
|
||||
} catch(ActionException $e) {
|
||||
// we should have gotten a new action
|
||||
$actionname = $e->getNewAction();
|
||||
|
||||
// this one should trigger a user message
|
||||
if(is_a($e, ActionDisabledException::class)) {
|
||||
msg('Action disabled: ' . hsc($presetup), -1);
|
||||
}
|
||||
|
||||
// some actions may request the display of a message
|
||||
if($e->displayToUser()) {
|
||||
msg(hsc($e->getMessage()), -1);
|
||||
}
|
||||
|
||||
// do setup for new action
|
||||
$this->transitionAction($presetup, $actionname);
|
||||
|
||||
} catch(NoActionException $e) {
|
||||
msg('Action unknown: ' . hsc($actionname), -1);
|
||||
$actionname = 'show';
|
||||
$this->transitionAction($presetup, $actionname);
|
||||
} catch(\Exception $e) {
|
||||
$this->handleFatalException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions from one action to another
|
||||
*
|
||||
* Basically just calls setupAction() again but does some checks before.
|
||||
*
|
||||
* @param string $from current action name
|
||||
* @param string $to new action name
|
||||
* @param null|ActionException $e any previous exception that caused the transition
|
||||
*/
|
||||
protected function transitionAction($from, $to, $e = null) {
|
||||
$this->transitions++;
|
||||
|
||||
// no infinite recursion
|
||||
if($from == $to) {
|
||||
$this->handleFatalException(new FatalException('Infinite loop in actions', 500, $e));
|
||||
}
|
||||
|
||||
// larger loops will be caught here
|
||||
if($this->transitions >= self::MAX_TRANSITIONS) {
|
||||
$this->handleFatalException(new FatalException('Maximum action transitions reached', 500, $e));
|
||||
}
|
||||
|
||||
// do the recursion
|
||||
$this->setupAction($to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts all processing with a message
|
||||
*
|
||||
* When a FataException instanc is passed, the code is treated as Status code
|
||||
*
|
||||
* @param \Exception|FatalException $e
|
||||
* @throws FatalException during unit testing
|
||||
*/
|
||||
protected function handleFatalException(\Exception $e) {
|
||||
if(is_a($e, FatalException::class)) {
|
||||
http_status($e->getCode());
|
||||
} else {
|
||||
http_status(500);
|
||||
}
|
||||
if(defined('DOKU_UNITTEST')) {
|
||||
throw $e;
|
||||
}
|
||||
ErrorHandler::logException($e);
|
||||
$msg = 'Something unforeseen has happened: ' . $e->getMessage();
|
||||
nice_die(hsc($msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the given action
|
||||
*
|
||||
* This translates the given name to a class name by uppercasing the first letter.
|
||||
* Underscores translate to camelcase names. For actions with underscores, the different
|
||||
* parts are removed beginning from the end until a matching class is found. The instatiated
|
||||
* Action will always have the full original action set as Name
|
||||
*
|
||||
* Example: 'export_raw' -> ExportRaw then 'export' -> 'Export'
|
||||
*
|
||||
* @param $actionname
|
||||
* @return AbstractAction
|
||||
* @throws NoActionException
|
||||
*/
|
||||
public function loadAction($actionname) {
|
||||
$actionname = strtolower($actionname); // FIXME is this needed here? should we run a cleanup somewhere else?
|
||||
$parts = explode('_', $actionname);
|
||||
while(!empty($parts)) {
|
||||
$load = join('_', $parts);
|
||||
$class = 'dokuwiki\\Action\\' . str_replace('_', '', ucwords($load, '_'));
|
||||
if(class_exists($class)) {
|
||||
return new $class($actionname);
|
||||
}
|
||||
array_pop($parts);
|
||||
}
|
||||
|
||||
throw new NoActionException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all the checks to see if this action can be executed
|
||||
*
|
||||
* @param AbstractAction $action
|
||||
* @throws ActionDisabledException
|
||||
* @throws ActionException
|
||||
*/
|
||||
public function checkAction(AbstractAction $action) {
|
||||
global $INFO;
|
||||
global $ID;
|
||||
|
||||
if(in_array($action->getActionName(), $this->disabled)) {
|
||||
throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
$action->checkPreconditions();
|
||||
|
||||
if(isset($INFO)) {
|
||||
$perm = $INFO['perm'];
|
||||
} else {
|
||||
$perm = auth_quickaclcheck($ID);
|
||||
}
|
||||
|
||||
if($perm < $action->minimumPermission()) {
|
||||
throw new ActionException('denied');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action handling the current request
|
||||
*
|
||||
* @return AbstractAction
|
||||
*/
|
||||
public function getAction() {
|
||||
return $this->action;
|
||||
}
|
||||
}
|
448
snippets/dokuwiki-2023-04-04/inc/Ajax.php
Normal file
448
snippets/dokuwiki-2023-04-04/inc/Ajax.php
Normal file
@ -0,0 +1,448 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
use dokuwiki\Utf8\Sort;
|
||||
|
||||
/**
|
||||
* Manage all builtin AJAX calls
|
||||
*
|
||||
* @todo The calls should be refactored out to their own proper classes
|
||||
* @package dokuwiki
|
||||
*/
|
||||
class Ajax {
|
||||
|
||||
/**
|
||||
* Execute the given call
|
||||
*
|
||||
* @param string $call name of the ajax call
|
||||
*/
|
||||
public function __construct($call) {
|
||||
$callfn = 'call' . ucfirst($call);
|
||||
if(method_exists($this, $callfn)) {
|
||||
$this->$callfn();
|
||||
} else {
|
||||
$evt = new Extension\Event('AJAX_CALL_UNKNOWN', $call);
|
||||
if($evt->advise_before()) {
|
||||
print "AJAX call '" . hsc($call) . "' unknown!\n";
|
||||
} else {
|
||||
$evt->advise_after();
|
||||
unset($evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for matching pagenames
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callQsearch() {
|
||||
global $lang;
|
||||
global $INPUT;
|
||||
|
||||
$maxnumbersuggestions = 50;
|
||||
|
||||
$query = $INPUT->post->str('q');
|
||||
if(empty($query)) $query = $INPUT->get->str('q');
|
||||
if(empty($query)) return;
|
||||
|
||||
$query = urldecode($query);
|
||||
|
||||
$data = ft_pageLookup($query, true, useHeading('navigation'));
|
||||
|
||||
if(!count($data)) return;
|
||||
|
||||
print '<strong>' . $lang['quickhits'] . '</strong>';
|
||||
print '<ul>';
|
||||
$counter = 0;
|
||||
foreach($data as $id => $title) {
|
||||
if(useHeading('navigation')) {
|
||||
$name = $title;
|
||||
} else {
|
||||
$ns = getNS($id);
|
||||
if($ns) {
|
||||
$name = noNS($id) . ' (' . $ns . ')';
|
||||
} else {
|
||||
$name = $id;
|
||||
}
|
||||
}
|
||||
echo '<li>' . html_wikilink(':' . $id, $name) . '</li>';
|
||||
|
||||
$counter++;
|
||||
if($counter > $maxnumbersuggestions) {
|
||||
echo '<li>...</li>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
print '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Support OpenSearch suggestions
|
||||
*
|
||||
* @link http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions/1.0
|
||||
* @author Mike Frysinger <vapier@gentoo.org>
|
||||
*/
|
||||
protected function callSuggestions() {
|
||||
global $INPUT;
|
||||
|
||||
$query = cleanID($INPUT->post->str('q'));
|
||||
if(empty($query)) $query = cleanID($INPUT->get->str('q'));
|
||||
if(empty($query)) return;
|
||||
|
||||
$data = ft_pageLookup($query);
|
||||
if(!count($data)) return;
|
||||
$data = array_keys($data);
|
||||
|
||||
// limit results to 15 hits
|
||||
$data = array_slice($data, 0, 15);
|
||||
$data = array_map('trim', $data);
|
||||
$data = array_map('noNS', $data);
|
||||
$data = array_unique($data);
|
||||
Sort::sort($data);
|
||||
|
||||
/* now construct a json */
|
||||
$suggestions = array(
|
||||
$query, // the original query
|
||||
$data, // some suggestions
|
||||
array(), // no description
|
||||
array() // no urls
|
||||
);
|
||||
|
||||
header('Content-Type: application/x-suggestions+json');
|
||||
print json_encode($suggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a page lock and save draft
|
||||
*
|
||||
* Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callLock() {
|
||||
global $ID;
|
||||
global $INFO;
|
||||
global $INPUT;
|
||||
|
||||
$ID = cleanID($INPUT->post->str('id'));
|
||||
if(empty($ID)) return;
|
||||
|
||||
$INFO = pageinfo();
|
||||
|
||||
$response = [
|
||||
'errors' => [],
|
||||
'lock' => '0',
|
||||
'draft' => '',
|
||||
];
|
||||
if(!$INFO['writable']) {
|
||||
$response['errors'][] = 'Permission to write this page has been denied.';
|
||||
echo json_encode($response);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!checklock($ID)) {
|
||||
lock($ID);
|
||||
$response['lock'] = '1';
|
||||
}
|
||||
|
||||
$draft = new Draft($ID, $INFO['client']);
|
||||
if ($draft->saveDraft()) {
|
||||
$response['draft'] = $draft->getDraftMessage();
|
||||
} else {
|
||||
$response['errors'] = array_merge($response['errors'], $draft->getErrors());
|
||||
}
|
||||
echo json_encode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a draft
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callDraftdel() {
|
||||
global $INPUT;
|
||||
$id = cleanID($INPUT->str('id'));
|
||||
if(empty($id)) return;
|
||||
|
||||
$client = $INPUT->server->str('REMOTE_USER');
|
||||
if(!$client) $client = clientIP(true);
|
||||
|
||||
$draft = new Draft($id, $client);
|
||||
if ($draft->isDraftAvailable() && checkSecurityToken()) {
|
||||
$draft->deleteDraft();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return subnamespaces for the Mediamanager
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callMedians() {
|
||||
global $conf;
|
||||
global $INPUT;
|
||||
|
||||
// wanted namespace
|
||||
$ns = cleanID($INPUT->post->str('ns'));
|
||||
$dir = utf8_encodeFN(str_replace(':', '/', $ns));
|
||||
|
||||
$lvl = count(explode(':', $ns));
|
||||
|
||||
$data = array();
|
||||
search($data, $conf['mediadir'], 'search_index', array('nofiles' => true), $dir);
|
||||
foreach(array_keys($data) as $item) {
|
||||
$data[$item]['level'] = $lvl + 1;
|
||||
}
|
||||
echo html_buildlist($data, 'idx', 'media_nstree_item', 'media_nstree_li');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of files for the Mediamanager
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callMedialist() {
|
||||
global $NS;
|
||||
global $INPUT;
|
||||
|
||||
$NS = cleanID($INPUT->post->str('ns'));
|
||||
$sort = $INPUT->post->bool('recent') ? 'date' : 'natural';
|
||||
if($INPUT->post->str('do') == 'media') {
|
||||
tpl_mediaFileList();
|
||||
} else {
|
||||
tpl_mediaContent(true, $sort);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the content of the right column
|
||||
* (image details) for the Mediamanager
|
||||
*
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
protected function callMediadetails() {
|
||||
global $IMG, $JUMPTO, $REV, $fullscreen, $INPUT;
|
||||
$fullscreen = true;
|
||||
require_once(DOKU_INC . 'lib/exe/mediamanager.php');
|
||||
|
||||
$image = '';
|
||||
if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
|
||||
if(isset($IMG)) $image = $IMG;
|
||||
if(isset($JUMPTO)) $image = $JUMPTO;
|
||||
$rev = false;
|
||||
if(isset($REV) && !$JUMPTO) $rev = $REV;
|
||||
|
||||
html_msgarea();
|
||||
tpl_mediaFileDetails($image, $rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image diff representation for mediamanager
|
||||
*
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
protected function callMediadiff() {
|
||||
global $INPUT;
|
||||
|
||||
$image = '';
|
||||
if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
|
||||
(new Ui\MediaDiff($image))->preference('fromAjax', true)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages file uploads
|
||||
*
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
protected function callMediaupload() {
|
||||
global $NS, $MSG, $INPUT;
|
||||
|
||||
$id = '';
|
||||
if(isset($_FILES['qqfile']['tmp_name'])) {
|
||||
$id = $INPUT->post->str('mediaid', $_FILES['qqfile']['name']);
|
||||
} elseif($INPUT->get->has('qqfile')) {
|
||||
$id = $INPUT->get->str('qqfile');
|
||||
}
|
||||
|
||||
$id = cleanID($id);
|
||||
|
||||
$NS = $INPUT->str('ns');
|
||||
$ns = $NS . ':' . getNS($id);
|
||||
|
||||
$AUTH = auth_quickaclcheck("$ns:*");
|
||||
if($AUTH >= AUTH_UPLOAD) {
|
||||
io_createNamespace("$ns:xxx", 'media');
|
||||
}
|
||||
|
||||
if(isset($_FILES['qqfile']['error']) && $_FILES['qqfile']['error']) unset($_FILES['qqfile']);
|
||||
|
||||
$res = false;
|
||||
if(isset($_FILES['qqfile']['tmp_name'])) $res = media_upload($NS, $AUTH, $_FILES['qqfile']);
|
||||
if($INPUT->get->has('qqfile')) $res = media_upload_xhr($NS, $AUTH);
|
||||
|
||||
if($res) {
|
||||
$result = array(
|
||||
'success' => true,
|
||||
'link' => media_managerURL(array('ns' => $ns, 'image' => $NS . ':' . $id), '&'),
|
||||
'id' => $NS . ':' . $id,
|
||||
'ns' => $NS
|
||||
);
|
||||
} else {
|
||||
$error = '';
|
||||
if(isset($MSG)) {
|
||||
foreach($MSG as $msg) {
|
||||
$error .= $msg['msg'];
|
||||
}
|
||||
}
|
||||
$result = array(
|
||||
'error' => $error,
|
||||
'ns' => $NS
|
||||
);
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sub index for index view
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
protected function callIndex() {
|
||||
global $conf;
|
||||
global $INPUT;
|
||||
|
||||
// wanted namespace
|
||||
$ns = cleanID($INPUT->post->str('idx'));
|
||||
$dir = utf8_encodeFN(str_replace(':', '/', $ns));
|
||||
|
||||
$lvl = count(explode(':', $ns));
|
||||
|
||||
$data = array();
|
||||
search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir);
|
||||
foreach (array_keys($data) as $item) {
|
||||
$data[$item]['level'] = $lvl + 1;
|
||||
}
|
||||
$idx = new Ui\Index;
|
||||
echo html_buildlist($data, 'idx', [$idx,'formatListItem'], [$idx,'tagListItem']);
|
||||
}
|
||||
|
||||
/**
|
||||
* List matching namespaces and pages for the link wizard
|
||||
*
|
||||
* @author Andreas Gohr <gohr@cosmocode.de>
|
||||
*/
|
||||
protected function callLinkwiz() {
|
||||
global $conf;
|
||||
global $lang;
|
||||
global $INPUT;
|
||||
|
||||
$q = ltrim(trim($INPUT->post->str('q')), ':');
|
||||
$id = noNS($q);
|
||||
$ns = getNS($q);
|
||||
|
||||
$ns = cleanID($ns);
|
||||
$id = cleanID($id);
|
||||
|
||||
$nsd = utf8_encodeFN(str_replace(':', '/', $ns));
|
||||
|
||||
$data = array();
|
||||
if($q !== '' && $ns === '') {
|
||||
|
||||
// use index to lookup matching pages
|
||||
$pages = ft_pageLookup($id, true);
|
||||
|
||||
// If 'useheading' option is 'always' or 'content',
|
||||
// search page titles with original query as well.
|
||||
if ($conf['useheading'] == '1' || $conf['useheading'] == 'content') {
|
||||
$pages = array_merge($pages, ft_pageLookup($q, true, true));
|
||||
asort($pages, SORT_STRING);
|
||||
}
|
||||
|
||||
// result contains matches in pages and namespaces
|
||||
// we now extract the matching namespaces to show
|
||||
// them seperately
|
||||
$dirs = array();
|
||||
|
||||
foreach($pages as $pid => $title) {
|
||||
if(strpos(getNS($pid), $id) !== false) {
|
||||
// match was in the namespace
|
||||
$dirs[getNS($pid)] = 1; // assoc array avoids dupes
|
||||
} else {
|
||||
// it is a matching page, add it to the result
|
||||
$data[] = array(
|
||||
'id' => $pid,
|
||||
'title' => $title,
|
||||
'type' => 'f',
|
||||
);
|
||||
}
|
||||
unset($pages[$pid]);
|
||||
}
|
||||
foreach($dirs as $dir => $junk) {
|
||||
$data[] = array(
|
||||
'id' => $dir,
|
||||
'type' => 'd',
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$opts = array(
|
||||
'depth' => 1,
|
||||
'listfiles' => true,
|
||||
'listdirs' => true,
|
||||
'pagesonly' => true,
|
||||
'firsthead' => true,
|
||||
'sneakyacl' => $conf['sneaky_index'],
|
||||
);
|
||||
if($id) $opts['filematch'] = '^.*\/' . $id;
|
||||
if($id) $opts['dirmatch'] = '^.*\/' . $id;
|
||||
search($data, $conf['datadir'], 'search_universal', $opts, $nsd);
|
||||
|
||||
// add back to upper
|
||||
if($ns) {
|
||||
array_unshift(
|
||||
$data, array(
|
||||
'id' => getNS($ns),
|
||||
'type' => 'u',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// fixme sort results in a useful way ?
|
||||
|
||||
if(!count($data)) {
|
||||
echo $lang['nothingfound'];
|
||||
exit;
|
||||
}
|
||||
|
||||
// output the found data
|
||||
$even = 1;
|
||||
foreach($data as $item) {
|
||||
$even *= -1; //zebra
|
||||
|
||||
if(($item['type'] == 'd' || $item['type'] == 'u') && $item['id'] !== '') $item['id'] .= ':';
|
||||
$link = wl($item['id']);
|
||||
|
||||
echo '<div class="' . (($even > 0) ? 'even' : 'odd') . ' type_' . $item['type'] . '">';
|
||||
|
||||
if($item['type'] == 'u') {
|
||||
$name = $lang['upperns'];
|
||||
} else {
|
||||
$name = hsc($item['id']);
|
||||
}
|
||||
|
||||
echo '<a href="' . $link . '" title="' . hsc($item['id']) . '" class="wikilink1">' . $name . '</a>';
|
||||
|
||||
if(!blank($item['title'])) {
|
||||
echo '<span>' . hsc($item['title']) . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
240
snippets/dokuwiki-2023-04-04/inc/Cache/Cache.php
Normal file
240
snippets/dokuwiki-2023-04-04/inc/Cache/Cache.php
Normal file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
use dokuwiki\Debug\PropertyDeprecationHelper;
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Generic handling of caching
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
use PropertyDeprecationHelper;
|
||||
|
||||
public $key = ''; // primary identifier for this item
|
||||
public $ext = ''; // file ext for cache data, secondary identifier for this item
|
||||
public $cache = ''; // cache file name
|
||||
public $depends = array(); // array containing cache dependency information,
|
||||
// used by makeDefaultCacheDecision to determine cache validity
|
||||
|
||||
// phpcs:disable
|
||||
/**
|
||||
* @deprecated since 2019-02-02 use the respective getters instead!
|
||||
*/
|
||||
protected $_event = ''; // event to be triggered during useCache
|
||||
protected $_time;
|
||||
protected $_nocache = false; // if set to true, cache will not be used or stored
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* @param string $key primary identifier
|
||||
* @param string $ext file extension
|
||||
*/
|
||||
public function __construct($key, $ext)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->ext = $ext;
|
||||
$this->cache = getCacheName($key, $ext);
|
||||
|
||||
/**
|
||||
* @deprecated since 2019-02-02 use the respective getters instead!
|
||||
*/
|
||||
$this->deprecatePublicProperty('_event');
|
||||
$this->deprecatePublicProperty('_time');
|
||||
$this->deprecatePublicProperty('_nocache');
|
||||
}
|
||||
|
||||
public function getTime()
|
||||
{
|
||||
return $this->_time;
|
||||
}
|
||||
|
||||
public function getEvent()
|
||||
{
|
||||
return $this->_event;
|
||||
}
|
||||
|
||||
public function setEvent($event)
|
||||
{
|
||||
$this->_event = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* public method to determine whether the cache can be used
|
||||
*
|
||||
* to assist in centralisation of event triggering and calculation of cache statistics,
|
||||
* don't override this function override makeDefaultCacheDecision()
|
||||
*
|
||||
* @param array $depends array of cache dependencies, support dependecies:
|
||||
* 'age' => max age of the cache in seconds
|
||||
* 'files' => cache must be younger than mtime of each file
|
||||
* (nb. dependency passes if file doesn't exist)
|
||||
*
|
||||
* @return bool true if cache can be used, false otherwise
|
||||
*/
|
||||
public function useCache($depends = array())
|
||||
{
|
||||
$this->depends = $depends;
|
||||
$this->addDependencies();
|
||||
|
||||
if ($this->getEvent()) {
|
||||
return $this->stats(
|
||||
Event::createAndTrigger(
|
||||
$this->getEvent(),
|
||||
$this,
|
||||
array($this, 'makeDefaultCacheDecision')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->stats($this->makeDefaultCacheDecision());
|
||||
}
|
||||
|
||||
/**
|
||||
* internal method containing cache use decision logic
|
||||
*
|
||||
* this function processes the following keys in the depends array
|
||||
* purge - force a purge on any non empty value
|
||||
* age - expire cache if older than age (seconds)
|
||||
* files - expire cache if any file in this array was updated more recently than the cache
|
||||
*
|
||||
* Note that this function needs to be public as it is used as callback for the event handler
|
||||
*
|
||||
* can be overridden
|
||||
*
|
||||
* @internal This method may only be called by the event handler! Call \dokuwiki\Cache\Cache::useCache instead!
|
||||
*
|
||||
* @return bool see useCache()
|
||||
*/
|
||||
public function makeDefaultCacheDecision()
|
||||
{
|
||||
if ($this->_nocache) {
|
||||
return false;
|
||||
} // caching turned off
|
||||
if (!empty($this->depends['purge'])) {
|
||||
return false;
|
||||
} // purge requested?
|
||||
if (!($this->_time = @filemtime($this->cache))) {
|
||||
return false;
|
||||
} // cache exists?
|
||||
|
||||
// cache too old?
|
||||
if (!empty($this->depends['age']) && ((time() - $this->_time) > $this->depends['age'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($this->depends['files'])) {
|
||||
foreach ($this->depends['files'] as $file) {
|
||||
if ($this->_time <= @filemtime($file)) {
|
||||
return false;
|
||||
} // cache older than files it depends on?
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* add dependencies to the depends array
|
||||
*
|
||||
* this method should only add dependencies,
|
||||
* it should not remove any existing dependencies and
|
||||
* it should only overwrite a dependency when the new value is more stringent than the old
|
||||
*/
|
||||
protected function addDependencies()
|
||||
{
|
||||
global $INPUT;
|
||||
if ($INPUT->has('purge')) {
|
||||
$this->depends['purge'] = true;
|
||||
} // purge requested
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve the cached data
|
||||
*
|
||||
* @param bool $clean true to clean line endings, false to leave line endings alone
|
||||
* @return string cache contents
|
||||
*/
|
||||
public function retrieveCache($clean = true)
|
||||
{
|
||||
return io_readFile($this->cache, $clean);
|
||||
}
|
||||
|
||||
/**
|
||||
* cache $data
|
||||
*
|
||||
* @param string $data the data to be cached
|
||||
* @return bool true on success, false otherwise
|
||||
*/
|
||||
public function storeCache($data)
|
||||
{
|
||||
if ($this->_nocache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return io_saveFile($this->cache, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove any cached data associated with this cache instance
|
||||
*/
|
||||
public function removeCache()
|
||||
{
|
||||
@unlink($this->cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record cache hits statistics.
|
||||
* (Only when debugging allowed, to reduce overhead.)
|
||||
*
|
||||
* @param bool $success result of this cache use attempt
|
||||
* @return bool pass-thru $success value
|
||||
*/
|
||||
protected function stats($success)
|
||||
{
|
||||
global $conf;
|
||||
static $stats = null;
|
||||
static $file;
|
||||
|
||||
if (!$conf['allowdebug']) {
|
||||
return $success;
|
||||
}
|
||||
|
||||
if (is_null($stats)) {
|
||||
$file = $conf['cachedir'] . '/cache_stats.txt';
|
||||
$lines = explode("\n", io_readFile($file));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$i = strpos($line, ',');
|
||||
$stats[substr($line, 0, $i)] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($stats[$this->ext])) {
|
||||
list($ext, $count, $hits) = explode(',', $stats[$this->ext]);
|
||||
} else {
|
||||
$ext = $this->ext;
|
||||
$count = 0;
|
||||
$hits = 0;
|
||||
}
|
||||
|
||||
$count++;
|
||||
if ($success) {
|
||||
$hits++;
|
||||
}
|
||||
$stats[$this->ext] = "$ext,$count,$hits";
|
||||
|
||||
io_saveFile($file, join("\n", $stats));
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isNoCache()
|
||||
{
|
||||
return $this->_nocache;
|
||||
}
|
||||
}
|
56
snippets/dokuwiki-2023-04-04/inc/Cache/CacheImageMod.php
Normal file
56
snippets/dokuwiki-2023-04-04/inc/Cache/CacheImageMod.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
/**
|
||||
* Handle the caching of modified (resized/cropped) images
|
||||
*/
|
||||
class CacheImageMod extends Cache
|
||||
{
|
||||
|
||||
/** @var string source file */
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* @param string $file Original source file
|
||||
* @param int $w new width in pixel
|
||||
* @param int $h new height in pixel
|
||||
* @param string $ext Image extension - no leading dot
|
||||
* @param bool $crop Is this a crop?
|
||||
*/
|
||||
public function __construct($file, $w, $h, $ext, $crop)
|
||||
{
|
||||
$fullext = '.media.' . $w . 'x' . $h;
|
||||
$fullext .= $crop ? '.crop' : '';
|
||||
$fullext .= ".$ext";
|
||||
|
||||
$this->file = $file;
|
||||
|
||||
$this->setEvent('IMAGEMOD_CACHE_USE');
|
||||
parent::__construct($file, $fullext);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function makeDefaultCacheDecision()
|
||||
{
|
||||
if (!file_exists($this->file)) {
|
||||
return false;
|
||||
}
|
||||
return parent::makeDefaultCacheDecision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caching depends on the source and the wiki config
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function addDependencies()
|
||||
{
|
||||
parent::addDependencies();
|
||||
|
||||
$this->depends['files'] = array_merge(
|
||||
[$this->file],
|
||||
getConfigFiles('main')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
46
snippets/dokuwiki-2023-04-04/inc/Cache/CacheInstructions.php
Normal file
46
snippets/dokuwiki-2023-04-04/inc/Cache/CacheInstructions.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
/**
|
||||
* Caching of parser instructions
|
||||
*/
|
||||
class CacheInstructions extends \dokuwiki\Cache\CacheParser
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $id page id
|
||||
* @param string $file source file for cache
|
||||
*/
|
||||
public function __construct($id, $file)
|
||||
{
|
||||
parent::__construct($id, $file, 'i');
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve the cached data
|
||||
*
|
||||
* @param bool $clean true to clean line endings, false to leave line endings alone
|
||||
* @return array cache contents
|
||||
*/
|
||||
public function retrieveCache($clean = true)
|
||||
{
|
||||
$contents = io_readFile($this->cache, false);
|
||||
return !empty($contents) ? unserialize($contents) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* cache $instructions
|
||||
*
|
||||
* @param array $instructions the instruction to be cached
|
||||
* @return bool true on success, false otherwise
|
||||
*/
|
||||
public function storeCache($instructions)
|
||||
{
|
||||
if ($this->_nocache) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return io_saveFile($this->cache, serialize($instructions));
|
||||
}
|
||||
}
|
66
snippets/dokuwiki-2023-04-04/inc/Cache/CacheParser.php
Normal file
66
snippets/dokuwiki-2023-04-04/inc/Cache/CacheParser.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
/**
|
||||
* Parser caching
|
||||
*/
|
||||
class CacheParser extends Cache
|
||||
{
|
||||
|
||||
public $file = ''; // source file for cache
|
||||
public $mode = ''; // input mode (represents the processing the input file will undergo)
|
||||
public $page = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $id page id
|
||||
* @param string $file source file for cache
|
||||
* @param string $mode input mode
|
||||
*/
|
||||
public function __construct($id, $file, $mode)
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
if ($id) {
|
||||
$this->page = $id;
|
||||
}
|
||||
$this->file = $file;
|
||||
$this->mode = $mode;
|
||||
|
||||
$this->setEvent('PARSER_CACHE_USE');
|
||||
parent::__construct($file . $INPUT->server->str('HTTP_HOST') . $INPUT->server->str('SERVER_PORT'), '.' . $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* method contains cache use decision logic
|
||||
*
|
||||
* @return bool see useCache()
|
||||
*/
|
||||
public function makeDefaultCacheDecision()
|
||||
{
|
||||
|
||||
if (!file_exists($this->file)) {
|
||||
return false;
|
||||
} // source exists?
|
||||
return parent::makeDefaultCacheDecision();
|
||||
}
|
||||
|
||||
protected function addDependencies()
|
||||
{
|
||||
|
||||
// parser cache file dependencies ...
|
||||
$files = array(
|
||||
$this->file, // ... source
|
||||
DOKU_INC . 'inc/Parsing/Parser.php', // ... parser
|
||||
DOKU_INC . 'inc/parser/handler.php', // ... handler
|
||||
);
|
||||
$files = array_merge($files, getConfigFiles('main')); // ... wiki settings
|
||||
|
||||
$this->depends['files'] = !empty($this->depends['files']) ?
|
||||
array_merge($files, $this->depends['files']) :
|
||||
$files;
|
||||
parent::addDependencies();
|
||||
}
|
||||
|
||||
}
|
94
snippets/dokuwiki-2023-04-04/inc/Cache/CacheRenderer.php
Normal file
94
snippets/dokuwiki-2023-04-04/inc/Cache/CacheRenderer.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
/**
|
||||
* Caching of data of renderer
|
||||
*/
|
||||
class CacheRenderer extends CacheParser
|
||||
{
|
||||
|
||||
/**
|
||||
* method contains cache use decision logic
|
||||
*
|
||||
* @return bool see useCache()
|
||||
*/
|
||||
public function makeDefaultCacheDecision()
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (!parent::makeDefaultCacheDecision()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($this->page)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// meta cache older than file it depends on?
|
||||
if ($this->_time < @filemtime(metaFN($this->page, '.meta'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check current link existence is consistent with cache version
|
||||
// first check the purgefile
|
||||
// - if the cache is more recent than the purgefile we know no links can have been updated
|
||||
if ($this->_time >= @filemtime($conf['cachedir'] . '/purgefile')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// for wiki pages, check metadata dependencies
|
||||
$metadata = p_get_metadata($this->page);
|
||||
|
||||
if (!isset($metadata['relation']['references']) ||
|
||||
empty($metadata['relation']['references'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($metadata['relation']['references'] as $id => $exists) {
|
||||
if ($exists != page_exists($id, '', false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function addDependencies()
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// default renderer cache file 'age' is dependent on 'cachetime' setting, two special values:
|
||||
// -1 : do not cache (should not be overridden)
|
||||
// 0 : cache never expires (can be overridden) - no need to set depends['age']
|
||||
if ($conf['cachetime'] == -1) {
|
||||
$this->_nocache = true;
|
||||
return;
|
||||
} elseif ($conf['cachetime'] > 0) {
|
||||
$this->depends['age'] = isset($this->depends['age']) ?
|
||||
min($this->depends['age'], $conf['cachetime']) : $conf['cachetime'];
|
||||
}
|
||||
|
||||
// renderer cache file dependencies ...
|
||||
$files = array(
|
||||
DOKU_INC . 'inc/parser/' . $this->mode . '.php', // ... the renderer
|
||||
);
|
||||
|
||||
// page implies metadata and possibly some other dependencies
|
||||
if (isset($this->page)) {
|
||||
|
||||
// for xhtml this will render the metadata if needed
|
||||
$valid = p_get_metadata($this->page, 'date valid');
|
||||
if (!empty($valid['age'])) {
|
||||
$this->depends['age'] = isset($this->depends['age']) ?
|
||||
min($this->depends['age'], $valid['age']) : $valid['age'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->depends['files'] = !empty($this->depends['files']) ?
|
||||
array_merge($files, $this->depends['files']) :
|
||||
$files;
|
||||
|
||||
parent::addDependencies();
|
||||
}
|
||||
}
|
678
snippets/dokuwiki-2023-04-04/inc/ChangeLog/ChangeLog.php
Normal file
678
snippets/dokuwiki-2023-04-04/inc/ChangeLog/ChangeLog.php
Normal file
@ -0,0 +1,678 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
use dokuwiki\Logger;
|
||||
|
||||
/**
|
||||
* ChangeLog Prototype; methods for handling changelog
|
||||
*/
|
||||
abstract class ChangeLog
|
||||
{
|
||||
use ChangeLogTrait;
|
||||
|
||||
/** @var string */
|
||||
protected $id;
|
||||
/** @var false|int */
|
||||
protected $currentRevision;
|
||||
/** @var array */
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $id page id
|
||||
* @param int $chunk_size maximum block size read from file
|
||||
*/
|
||||
public function __construct($id, $chunk_size = 8192)
|
||||
{
|
||||
global $cache_revinfo;
|
||||
|
||||
$this->cache =& $cache_revinfo;
|
||||
if (!isset($this->cache[$id])) {
|
||||
$this->cache[$id] = array();
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
$this->setChunkSize($chunk_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
abstract protected function getFilename();
|
||||
|
||||
/**
|
||||
* Check whether given revision is the current page
|
||||
*
|
||||
* @param int $rev timestamp of current page
|
||||
* @return bool true if $rev is current revision, otherwise false
|
||||
*/
|
||||
public function isCurrentRevision($rev)
|
||||
{
|
||||
return $rev == $this->currentRevision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the revision is last revision
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @return bool true if $rev is last revision, otherwise false
|
||||
*/
|
||||
public function isLastRevision($rev = null)
|
||||
{
|
||||
return $rev === $this->lastRevision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current revision identifier
|
||||
*
|
||||
* The "current" revision means current version of the page or media file. It is either
|
||||
* identical with or newer than the "last" revision, that depends on whether the file
|
||||
* has modified, created or deleted outside of DokuWiki.
|
||||
* The value of identifier can be determined by timestamp as far as the file exists,
|
||||
* otherwise it must be assigned larger than any other revisions to keep them sortable.
|
||||
*
|
||||
* @return int|false revision timestamp
|
||||
*/
|
||||
public function currentRevision()
|
||||
{
|
||||
if (!isset($this->currentRevision)) {
|
||||
// set ChangeLog::currentRevision property
|
||||
$this->getCurrentRevisionInfo();
|
||||
}
|
||||
return $this->currentRevision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last revision identifier, date value of the last entry of the changelog
|
||||
*
|
||||
* @return int|false revision timestamp
|
||||
*/
|
||||
public function lastRevision()
|
||||
{
|
||||
$revs = $this->getRevisions(-1, 1);
|
||||
return empty($revs) ? false : $revs[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save revision info to the cache pool
|
||||
*
|
||||
* @param array $info Revision info structure
|
||||
* @return bool
|
||||
*/
|
||||
protected function cacheRevisionInfo($info)
|
||||
{
|
||||
if (!is_array($info)) return false;
|
||||
//$this->cache[$this->id][$info['date']] ??= $info; // since php 7.4
|
||||
$this->cache[$this->id][$info['date']] = $this->cache[$this->id][$info['date']] ?? $info;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the changelog information for a specific revision (timestamp)
|
||||
*
|
||||
* Adjacent changelog lines are optimistically parsed and cached to speed up
|
||||
* consecutive calls to getRevisionInfo. For large changelog files, only the chunk
|
||||
* containing the requested changelog line is read.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @param bool $retrieveCurrentRevInfo allows to skip for getting other revision info in the
|
||||
* getCurrentRevisionInfo() where $currentRevision is not yet determined
|
||||
* @return bool|array false or array with entries:
|
||||
* - date: unix timestamp
|
||||
* - ip: IPv4 address (127.0.0.1)
|
||||
* - type: log line type
|
||||
* - id: page id
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
*
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
public function getRevisionInfo($rev, $retrieveCurrentRevInfo = true)
|
||||
{
|
||||
$rev = max(0, $rev);
|
||||
if (!$rev) return false;
|
||||
|
||||
//ensure the external edits are cached as well
|
||||
if (!isset($this->currentRevision) && $retrieveCurrentRevInfo) {
|
||||
$this->getCurrentRevisionInfo();
|
||||
}
|
||||
|
||||
// check if it's already in the memory cache
|
||||
if (isset($this->cache[$this->id]) && isset($this->cache[$this->id][$rev])) {
|
||||
return $this->cache[$this->id][$rev];
|
||||
}
|
||||
|
||||
//read lines from changelog
|
||||
list($fp, $lines) = $this->readloglines($rev);
|
||||
if ($fp) {
|
||||
fclose($fp);
|
||||
}
|
||||
if (empty($lines)) return false;
|
||||
|
||||
// parse and cache changelog lines
|
||||
foreach ($lines as $value) {
|
||||
$info = $this->parseLogLine($value);
|
||||
$this->cacheRevisionInfo($info);
|
||||
}
|
||||
if (!isset($this->cache[$this->id][$rev])) {
|
||||
return false;
|
||||
}
|
||||
return $this->cache[$this->id][$rev];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of page revisions numbers
|
||||
*
|
||||
* Does not guarantee that the revision exists in the attic,
|
||||
* only that a line with the date exists in the changelog.
|
||||
* By default the current revision is skipped.
|
||||
*
|
||||
* The current revision is automatically skipped when the page exists.
|
||||
* See $INFO['meta']['last_change'] for the current revision.
|
||||
* A negative $first let read the current revision too.
|
||||
*
|
||||
* For efficiency, the log lines are parsed and cached for later
|
||||
* calls to getRevisionInfo. Large changelog files are read
|
||||
* backwards in chunks until the requested number of changelog
|
||||
* lines are received.
|
||||
*
|
||||
* @param int $first skip the first n changelog lines
|
||||
* @param int $num number of revisions to return
|
||||
* @return array with the revision timestamps
|
||||
*
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
public function getRevisions($first, $num)
|
||||
{
|
||||
$revs = array();
|
||||
$lines = array();
|
||||
$count = 0;
|
||||
|
||||
$logfile = $this->getChangelogFilename();
|
||||
if (!file_exists($logfile)) return $revs;
|
||||
|
||||
$num = max($num, 0);
|
||||
if ($num == 0) {
|
||||
return $revs;
|
||||
}
|
||||
|
||||
if ($first < 0) {
|
||||
$first = 0;
|
||||
} else {
|
||||
$fileLastMod = $this->getFilename();
|
||||
if (file_exists($fileLastMod) && $this->isLastRevision(filemtime($fileLastMod))) {
|
||||
// skip last revision if the page exists
|
||||
$first = max($first + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (filesize($logfile) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
// read whole file
|
||||
$lines = file($logfile);
|
||||
if ($lines === false) {
|
||||
return $revs;
|
||||
}
|
||||
} else {
|
||||
// read chunks backwards
|
||||
$fp = fopen($logfile, 'rb'); // "file pointer"
|
||||
if ($fp === false) {
|
||||
return $revs;
|
||||
}
|
||||
fseek($fp, 0, SEEK_END);
|
||||
$tail = ftell($fp);
|
||||
|
||||
// chunk backwards
|
||||
$finger = max($tail - $this->chunk_size, 0);
|
||||
while ($count < $num + $first) {
|
||||
$nl = $this->getNewlinepointer($fp, $finger);
|
||||
|
||||
// was the chunk big enough? if not, take another bite
|
||||
if ($nl > 0 && $tail <= $nl) {
|
||||
$finger = max($finger - $this->chunk_size, 0);
|
||||
continue;
|
||||
} else {
|
||||
$finger = $nl;
|
||||
}
|
||||
|
||||
// read chunk
|
||||
$chunk = '';
|
||||
$read_size = max($tail - $finger, 0); // found chunk size
|
||||
$got = 0;
|
||||
while ($got < $read_size && !feof($fp)) {
|
||||
$tmp = @fread($fp, max(min($this->chunk_size, $read_size - $got), 0));
|
||||
if ($tmp === false) {
|
||||
break;
|
||||
} //error state
|
||||
$got += strlen($tmp);
|
||||
$chunk .= $tmp;
|
||||
}
|
||||
$tmp = explode("\n", $chunk);
|
||||
array_pop($tmp); // remove trailing newline
|
||||
|
||||
// combine with previous chunk
|
||||
$count += count($tmp);
|
||||
$lines = array_merge($tmp, $lines);
|
||||
|
||||
// next chunk
|
||||
if ($finger == 0) {
|
||||
break;
|
||||
} else { // already read all the lines
|
||||
$tail = $finger;
|
||||
$finger = max($tail - $this->chunk_size, 0);
|
||||
}
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
// skip parsing extra lines
|
||||
$num = max(min(count($lines) - $first, $num), 0);
|
||||
if ($first > 0 && $num > 0) {
|
||||
$lines = array_slice($lines, max(count($lines) - $first - $num, 0), $num);
|
||||
} elseif ($first > 0 && $num == 0) {
|
||||
$lines = array_slice($lines, 0, max(count($lines) - $first, 0));
|
||||
} elseif ($first == 0 && $num > 0) {
|
||||
$lines = array_slice($lines, max(count($lines) - $num, 0));
|
||||
}
|
||||
|
||||
// handle lines in reverse order
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
}
|
||||
}
|
||||
|
||||
return $revs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth revision left or right-hand side for a specific page id and revision (timestamp)
|
||||
*
|
||||
* For large changelog files, only the chunk containing the
|
||||
* reference revision $rev is read and sometimes a next chunk.
|
||||
*
|
||||
* Adjacent changelog lines are optimistically parsed and cached to speed up
|
||||
* consecutive calls to getRevisionInfo.
|
||||
*
|
||||
* @param int $rev revision timestamp used as start date
|
||||
* (doesn't need to be exact revision number)
|
||||
* @param int $direction give position of returned revision with respect to $rev;
|
||||
positive=next, negative=prev
|
||||
* @return bool|int
|
||||
* timestamp of the requested revision
|
||||
* otherwise false
|
||||
*/
|
||||
public function getRelativeRevision($rev, $direction)
|
||||
{
|
||||
$rev = max($rev, 0);
|
||||
$direction = (int)$direction;
|
||||
|
||||
//no direction given or last rev, so no follow-up
|
||||
if (!$direction || ($direction > 0 && $this->isCurrentRevision($rev))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//get lines from changelog
|
||||
list($fp, $lines, $head, $tail, $eof) = $this->readloglines($rev);
|
||||
if (empty($lines)) return false;
|
||||
|
||||
// look for revisions later/earlier than $rev, when founded count till the wanted revision is reached
|
||||
// also parse and cache changelog lines for getRevisionInfo().
|
||||
$revCounter = 0;
|
||||
$relativeRev = false;
|
||||
$checkOtherChunk = true; //always runs once
|
||||
while (!$relativeRev && $checkOtherChunk) {
|
||||
$info = array();
|
||||
//parse in normal or reverse order
|
||||
$count = count($lines);
|
||||
if ($direction > 0) {
|
||||
$start = 0;
|
||||
$step = 1;
|
||||
} else {
|
||||
$start = $count - 1;
|
||||
$step = -1;
|
||||
}
|
||||
for ($i = $start; $i >= 0 && $i < $count; $i = $i + $step) {
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
//look for revs older/earlier then reference $rev and select $direction-th one
|
||||
if (($direction > 0 && $info['date'] > $rev) || ($direction < 0 && $info['date'] < $rev)) {
|
||||
$revCounter++;
|
||||
if ($revCounter == abs($direction)) {
|
||||
$relativeRev = $info['date'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//true when $rev is found, but not the wanted follow-up.
|
||||
$checkOtherChunk = $fp
|
||||
&& ($info['date'] == $rev || ($revCounter > 0 && !$relativeRev))
|
||||
&& !(($tail == $eof && $direction > 0) || ($head == 0 && $direction < 0));
|
||||
|
||||
if ($checkOtherChunk) {
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, $direction);
|
||||
|
||||
if (empty($lines)) break;
|
||||
}
|
||||
}
|
||||
if ($fp) {
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $relativeRev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns revisions around rev1 and rev2
|
||||
* When available it returns $max entries for each revision
|
||||
*
|
||||
* @param int $rev1 oldest revision timestamp
|
||||
* @param int $rev2 newest revision timestamp (0 looks up last revision)
|
||||
* @param int $max maximum number of revisions returned
|
||||
* @return array with two arrays with revisions surrounding rev1 respectively rev2
|
||||
*/
|
||||
public function getRevisionsAround($rev1, $rev2, $max = 50)
|
||||
{
|
||||
$max = intval(abs($max) / 2) * 2 + 1;
|
||||
$rev1 = max($rev1, 0);
|
||||
$rev2 = max($rev2, 0);
|
||||
|
||||
if ($rev2) {
|
||||
if ($rev2 < $rev1) {
|
||||
$rev = $rev2;
|
||||
$rev2 = $rev1;
|
||||
$rev1 = $rev;
|
||||
}
|
||||
} else {
|
||||
//empty right side means a removed page. Look up last revision.
|
||||
$rev2 = $this->currentRevision();
|
||||
}
|
||||
//collect revisions around rev2
|
||||
list($revs2, $allRevs, $fp, $lines, $head, $tail) = $this->retrieveRevisionsAround($rev2, $max);
|
||||
|
||||
if (empty($revs2)) return array(array(), array());
|
||||
|
||||
//collect revisions around rev1
|
||||
$index = array_search($rev1, $allRevs);
|
||||
if ($index === false) {
|
||||
//no overlapping revisions
|
||||
list($revs1, , , , ,) = $this->retrieveRevisionsAround($rev1, $max);
|
||||
if (empty($revs1)) $revs1 = array();
|
||||
} else {
|
||||
//revisions overlaps, reuse revisions around rev2
|
||||
$lastRev = array_pop($allRevs); //keep last entry that could be external edit
|
||||
$revs1 = $allRevs;
|
||||
while ($head > 0) {
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs1[] = $info['date'];
|
||||
$index++;
|
||||
|
||||
if ($index > intval($max / 2)) break 2;
|
||||
}
|
||||
}
|
||||
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1);
|
||||
}
|
||||
sort($revs1);
|
||||
$revs1[] = $lastRev; //push back last entry
|
||||
|
||||
//return wanted selection
|
||||
$revs1 = array_slice($revs1, max($index - intval($max / 2), 0), $max);
|
||||
}
|
||||
|
||||
return array(array_reverse($revs1), array_reverse($revs2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an existing revision for a specific date which is
|
||||
* the current one or younger or equal then the date
|
||||
*
|
||||
* @param number $date_at timestamp
|
||||
* @return string revision ('' for current)
|
||||
*/
|
||||
public function getLastRevisionAt($date_at)
|
||||
{
|
||||
$fileLastMod = $this->getFilename();
|
||||
//requested date_at(timestamp) younger or equal then modified_time($this->id) => load current
|
||||
if (file_exists($fileLastMod) && $date_at >= @filemtime($fileLastMod)) {
|
||||
return '';
|
||||
} else {
|
||||
if ($rev = $this->getRelativeRevision($date_at + 1, -1)) { //+1 to get also the requested date revision
|
||||
return $rev;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the $max revisions near to the timestamp $rev
|
||||
*
|
||||
* Ideally, half of retrieved timestamps are older than $rev, another half are newer.
|
||||
* The returned array $requestedRevs may not contain the reference timestamp $rev
|
||||
* when it does not match any revision value recorded in changelog.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @param int $max maximum number of revisions to be returned
|
||||
* @return bool|array
|
||||
* return array with entries:
|
||||
* - $requestedRevs: array of with $max revision timestamps
|
||||
* - $revs: all parsed revision timestamps
|
||||
* - $fp: file pointer only defined for chuck reading, needs closing.
|
||||
* - $lines: non-parsed changelog lines before the parsed revisions
|
||||
* - $head: position of first read changelog line
|
||||
* - $lastTail: position of end of last read changelog line
|
||||
* otherwise false
|
||||
*/
|
||||
protected function retrieveRevisionsAround($rev, $max)
|
||||
{
|
||||
$revs = array();
|
||||
$afterCount = $beforeCount = 0;
|
||||
|
||||
//get lines from changelog
|
||||
list($fp, $lines, $startHead, $startTail, $eof) = $this->readloglines($rev);
|
||||
if (empty($lines)) return false;
|
||||
|
||||
//parse changelog lines in chunk, and read forward more chunks until $max/2 is reached
|
||||
$head = $startHead;
|
||||
$tail = $startTail;
|
||||
while (count($lines) > 0) {
|
||||
foreach ($lines as $line) {
|
||||
$info = $this->parseLogLine($line);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
if ($info['date'] >= $rev) {
|
||||
//count revs after reference $rev
|
||||
$afterCount++;
|
||||
if ($afterCount == 1) $beforeCount = count($revs);
|
||||
}
|
||||
//enough revs after reference $rev?
|
||||
if ($afterCount > intval($max / 2)) break 2;
|
||||
}
|
||||
}
|
||||
//retrieve next chunk
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, 1);
|
||||
}
|
||||
$lastTail = $tail;
|
||||
|
||||
// add a possible revision of external edit, create or deletion
|
||||
if ($lastTail == $eof && $afterCount <= intval($max / 2) &&
|
||||
count($revs) && !$this->isCurrentRevision($revs[count($revs)-1])
|
||||
) {
|
||||
$revs[] = $this->currentRevision;
|
||||
$afterCount++;
|
||||
}
|
||||
|
||||
if ($afterCount == 0) {
|
||||
//given timestamp $rev is newer than the most recent line in chunk
|
||||
return false; //FIXME: or proceed to collect older revisions?
|
||||
}
|
||||
|
||||
//read more chunks backward until $max/2 is reached and total number of revs is equal to $max
|
||||
$lines = array();
|
||||
$i = 0;
|
||||
if ($afterCount > 0) {
|
||||
$head = $startHead;
|
||||
$tail = $startTail;
|
||||
while ($head > 0) {
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1);
|
||||
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
$beforeCount++;
|
||||
//enough revs before reference $rev?
|
||||
if ($beforeCount > max(intval($max / 2), $max - $afterCount)) break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//keep only non-parsed lines
|
||||
$lines = array_slice($lines, 0, $i);
|
||||
|
||||
sort($revs);
|
||||
|
||||
//trunk desired selection
|
||||
$requestedRevs = array_slice($revs, -$max, $max);
|
||||
|
||||
return array($requestedRevs, $revs, $fp, $lines, $head, $lastTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current revision information, considering external edit, create or deletion
|
||||
*
|
||||
* When the file has not modified since its last revision, the information of the last
|
||||
* change that had already recorded in the changelog is returned as current change info.
|
||||
* Otherwise, the change information since the last revision caused outside DokuWiki
|
||||
* should be returned, which is referred as "external revision".
|
||||
*
|
||||
* The change date of the file can be determined by timestamp as far as the file exists,
|
||||
* however this is not possible when the file has already deleted outside of DokuWiki.
|
||||
* In such case we assign 1 sec before current time() for the external deletion.
|
||||
* As a result, the value of current revision identifier may change each time because:
|
||||
* 1) the file has again modified outside of DokuWiki, or
|
||||
* 2) the value is essentially volatile for deleted but once existed files.
|
||||
*
|
||||
* @return bool|array false when page had never existed or array with entries:
|
||||
* - date: revision identifier (timestamp or last revision +1)
|
||||
* - ip: IPv4 address (127.0.0.1)
|
||||
* - type: log line type
|
||||
* - id: id of page or media
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
* - timestamp: unix timestamp or false (key set only for external edit occurred)
|
||||
*
|
||||
* @author Satoshi Sahara <sahara.satoshi@gmail.com>
|
||||
*/
|
||||
public function getCurrentRevisionInfo()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (isset($this->currentRevision)) return $this->getRevisionInfo($this->currentRevision);
|
||||
|
||||
// get revision id from the item file timestamp and changelog
|
||||
$fileLastMod = $this->getFilename();
|
||||
$fileRev = @filemtime($fileLastMod); // false when the file not exist
|
||||
$lastRev = $this->lastRevision(); // false when no changelog
|
||||
|
||||
if (!$fileRev && !$lastRev) { // has never existed
|
||||
$this->currentRevision = false;
|
||||
return false;
|
||||
} elseif ($fileRev === $lastRev) { // not external edit
|
||||
$this->currentRevision = $lastRev;
|
||||
return $this->getRevisionInfo($lastRev);
|
||||
}
|
||||
|
||||
if (!$fileRev && $lastRev) { // item file does not exist
|
||||
// check consistency against changelog
|
||||
$revInfo = $this->getRevisionInfo($lastRev, false);
|
||||
if ($revInfo['type'] == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$this->currentRevision = $lastRev;
|
||||
return $revInfo;
|
||||
}
|
||||
|
||||
// externally deleted, set revision date as late as possible
|
||||
$revInfo = [
|
||||
'date' => max($lastRev +1, time() -1), // 1 sec before now or new page save
|
||||
'ip' => '127.0.0.1',
|
||||
'type' => DOKU_CHANGE_TYPE_DELETE,
|
||||
'id' => $this->id,
|
||||
'user' => '',
|
||||
'sum' => $lang['deleted'].' - '.$lang['external_edit'].' ('.$lang['unknowndate'].')',
|
||||
'extra' => '',
|
||||
'sizechange' => -io_getSizeFile($this->getFilename($lastRev)),
|
||||
'timestamp' => false,
|
||||
];
|
||||
|
||||
} else { // item file exists, with timestamp $fileRev
|
||||
// here, file timestamp $fileRev is different with last revision timestamp $lastRev in changelog
|
||||
$isJustCreated = $lastRev === false || (
|
||||
$fileRev > $lastRev &&
|
||||
$this->getRevisionInfo($lastRev, false)['type'] == DOKU_CHANGE_TYPE_DELETE
|
||||
);
|
||||
$filesize_new = filesize($this->getFilename());
|
||||
$filesize_old = $isJustCreated ? 0 : io_getSizeFile($this->getFilename($lastRev));
|
||||
$sizechange = $filesize_new - $filesize_old;
|
||||
|
||||
if ($isJustCreated) {
|
||||
$timestamp = $fileRev;
|
||||
$sum = $lang['created'].' - '.$lang['external_edit'];
|
||||
} elseif ($fileRev > $lastRev) {
|
||||
$timestamp = $fileRev;
|
||||
$sum = $lang['external_edit'];
|
||||
} else {
|
||||
// $fileRev is older than $lastRev, that is erroneous/incorrect occurrence.
|
||||
$msg = "Warning: current file modification time is older than last revision date";
|
||||
$details = 'File revision: '.$fileRev.' '.dformat($fileRev, "%Y-%m-%d %H:%M:%S")."\n"
|
||||
.'Last revision: '.$lastRev.' '.dformat($lastRev, "%Y-%m-%d %H:%M:%S");
|
||||
Logger::error($msg, $details, $this->getFilename());
|
||||
$timestamp = false;
|
||||
$sum = $lang['external_edit'].' ('.$lang['unknowndate'].')';
|
||||
}
|
||||
|
||||
// externally created or edited
|
||||
$revInfo = [
|
||||
'date' => $timestamp ?: $lastRev +1,
|
||||
'ip' => '127.0.0.1',
|
||||
'type' => $isJustCreated ? DOKU_CHANGE_TYPE_CREATE : DOKU_CHANGE_TYPE_EDIT,
|
||||
'id' => $this->id,
|
||||
'user' => '',
|
||||
'sum' => $sum,
|
||||
'extra' => '',
|
||||
'sizechange' => $sizechange,
|
||||
'timestamp' => $timestamp,
|
||||
];
|
||||
}
|
||||
|
||||
// cache current revision information of external edition
|
||||
$this->currentRevision = $revInfo['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $revInfo;
|
||||
return $this->getRevisionInfo($this->currentRevision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mechanism to trace no-actual external current revision
|
||||
* @param int $rev
|
||||
*/
|
||||
public function traceCurrentRevision($rev)
|
||||
{
|
||||
if ($rev > $this->lastRevision()) {
|
||||
$rev = $this->currentRevision();
|
||||
}
|
||||
return $rev;
|
||||
}
|
||||
}
|
269
snippets/dokuwiki-2023-04-04/inc/ChangeLog/ChangeLogTrait.php
Normal file
269
snippets/dokuwiki-2023-04-04/inc/ChangeLog/ChangeLogTrait.php
Normal file
@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
use dokuwiki\Utf8\PhpString;
|
||||
|
||||
/**
|
||||
* Provides methods for handling of changelog
|
||||
*/
|
||||
trait ChangeLogTrait
|
||||
{
|
||||
/**
|
||||
* Adds an entry to the changelog file
|
||||
*
|
||||
* @return array added log line as revision info
|
||||
*/
|
||||
abstract public function addLogEntry(array $info, $timestamp = null);
|
||||
|
||||
/**
|
||||
* Parses a changelog line into it's components
|
||||
*
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
*
|
||||
* @param string $line changelog line
|
||||
* @return array|bool parsed line or false
|
||||
*/
|
||||
public static function parseLogLine($line)
|
||||
{
|
||||
$info = explode("\t", rtrim($line, "\n"));
|
||||
if ($info !== false && count($info) > 1) {
|
||||
return [
|
||||
'date' => (int)$info[0], // unix timestamp
|
||||
'ip' => $info[1], // IPv4 address (127.0.0.1)
|
||||
'type' => $info[2], // log line type
|
||||
'id' => $info[3], // page id
|
||||
'user' => $info[4], // user name
|
||||
'sum' => $info[5], // edit summary (or action reason)
|
||||
'extra' => $info[6], // extra data (varies by line type)
|
||||
'sizechange' => (isset($info[7]) && $info[7] !== '') ? (int)$info[7] : null, //
|
||||
];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a changelog line from it's components
|
||||
*
|
||||
* @param array $info Revision info structure
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return string changelog line
|
||||
*/
|
||||
public static function buildLogLine(array &$info, $timestamp = null)
|
||||
{
|
||||
$strip = ["\t", "\n"];
|
||||
$entry = array(
|
||||
'date' => $timestamp ?? $info['date'],
|
||||
'ip' => $info['ip'],
|
||||
'type' => str_replace($strip, '', $info['type']),
|
||||
'id' => $info['id'],
|
||||
'user' => $info['user'],
|
||||
'sum' => PhpString::substr(str_replace($strip, '', $info['sum']), 0, 255),
|
||||
'extra' => str_replace($strip, '', $info['extra']),
|
||||
'sizechange' => $info['sizechange'],
|
||||
);
|
||||
$info = $entry;
|
||||
return implode("\t", $entry) ."\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to changelog
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
abstract protected function getChangelogFilename();
|
||||
|
||||
/**
|
||||
* Checks if the ID has old revisions
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasRevisions()
|
||||
{
|
||||
$logfile = $this->getChangelogFilename();
|
||||
return file_exists($logfile);
|
||||
}
|
||||
|
||||
|
||||
/** @var int */
|
||||
protected $chunk_size;
|
||||
|
||||
/**
|
||||
* Set chunk size for file reading
|
||||
* Chunk size zero let read whole file at once
|
||||
*
|
||||
* @param int $chunk_size maximum block size read from file
|
||||
*/
|
||||
public function setChunkSize($chunk_size)
|
||||
{
|
||||
if (!is_numeric($chunk_size)) $chunk_size = 0;
|
||||
|
||||
$this->chunk_size = (int)max($chunk_size, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines from changelog.
|
||||
* If file larger than $chunk_size, only chunk is read that could contain $rev.
|
||||
*
|
||||
* When reference timestamp $rev is outside time range of changelog, readloglines() will return
|
||||
* lines in first or last chunk, but they obviously does not contain $rev.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @return array|false
|
||||
* if success returns array(fp, array(changeloglines), $head, $tail, $eof)
|
||||
* where fp only defined for chuck reading, needs closing.
|
||||
* otherwise false
|
||||
*/
|
||||
protected function readloglines($rev)
|
||||
{
|
||||
$file = $this->getChangelogFilename();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fp = null;
|
||||
$head = 0;
|
||||
$tail = 0;
|
||||
$eof = 0;
|
||||
|
||||
if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
// read whole file
|
||||
$lines = file($file);
|
||||
if ($lines === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// read by chunk
|
||||
$fp = fopen($file, 'rb'); // "file pointer"
|
||||
if ($fp === false) {
|
||||
return false;
|
||||
}
|
||||
fseek($fp, 0, SEEK_END);
|
||||
$eof = ftell($fp);
|
||||
$tail = $eof;
|
||||
|
||||
// find chunk
|
||||
while ($tail - $head > $this->chunk_size) {
|
||||
$finger = $head + intval(($tail - $head) / 2);
|
||||
$finger = $this->getNewlinepointer($fp, $finger);
|
||||
$tmp = fgets($fp);
|
||||
if ($finger == $head || $finger == $tail) {
|
||||
break;
|
||||
}
|
||||
$info = $this->parseLogLine($tmp);
|
||||
$finger_rev = $info['date'];
|
||||
|
||||
if ($finger_rev > $rev) {
|
||||
$tail = $finger;
|
||||
} else {
|
||||
$head = $finger;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tail - $head < 1) {
|
||||
// could not find chunk, assume requested rev is missing
|
||||
fclose($fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
}
|
||||
return array(
|
||||
$fp,
|
||||
$lines,
|
||||
$head,
|
||||
$tail,
|
||||
$eof,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read chunk and return array with lines of given chunk.
|
||||
* Has no check if $head and $tail are really at a new line
|
||||
*
|
||||
* @param resource $fp resource file pointer
|
||||
* @param int $head start point chunk
|
||||
* @param int $tail end point chunk
|
||||
* @return array lines read from chunk
|
||||
*/
|
||||
protected function readChunk($fp, $head, $tail)
|
||||
{
|
||||
$chunk = '';
|
||||
$chunk_size = max($tail - $head, 0); // found chunk size
|
||||
$got = 0;
|
||||
fseek($fp, $head);
|
||||
while ($got < $chunk_size && !feof($fp)) {
|
||||
$tmp = @fread($fp, max(min($this->chunk_size, $chunk_size - $got), 0));
|
||||
if ($tmp === false) { //error state
|
||||
break;
|
||||
}
|
||||
$got += strlen($tmp);
|
||||
$chunk .= $tmp;
|
||||
}
|
||||
$lines = explode("\n", $chunk);
|
||||
array_pop($lines); // remove trailing newline
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pointer to first new line after $finger and return its position
|
||||
*
|
||||
* @param resource $fp file pointer
|
||||
* @param int $finger a pointer
|
||||
* @return int pointer
|
||||
*/
|
||||
protected function getNewlinepointer($fp, $finger)
|
||||
{
|
||||
fseek($fp, $finger);
|
||||
$nl = $finger;
|
||||
if ($finger > 0) {
|
||||
fgets($fp); // slip the finger forward to a new line
|
||||
$nl = ftell($fp);
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next lines of the changelog of the chunk before head or after tail
|
||||
*
|
||||
* @param resource $fp file pointer
|
||||
* @param int $head position head of last chunk
|
||||
* @param int $tail position tail of last chunk
|
||||
* @param int $direction positive forward, negative backward
|
||||
* @return array with entries:
|
||||
* - $lines: changelog lines of read chunk
|
||||
* - $head: head of chunk
|
||||
* - $tail: tail of chunk
|
||||
*/
|
||||
protected function readAdjacentChunk($fp, $head, $tail, $direction)
|
||||
{
|
||||
if (!$fp) return array(array(), $head, $tail);
|
||||
|
||||
if ($direction > 0) {
|
||||
//read forward
|
||||
$head = $tail;
|
||||
$tail = $head + intval($this->chunk_size * (2 / 3));
|
||||
$tail = $this->getNewlinepointer($fp, $tail);
|
||||
} else {
|
||||
//read backward
|
||||
$tail = $head;
|
||||
$head = max($tail - $this->chunk_size, 0);
|
||||
while (true) {
|
||||
$nl = $this->getNewlinepointer($fp, $head);
|
||||
// was the chunk big enough? if not, take another bite
|
||||
if ($nl > 0 && $tail <= $nl) {
|
||||
$head = max($head - $this->chunk_size, 0);
|
||||
} else {
|
||||
$head = $nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load next chunk
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
return array($lines, $head, $tail);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* Class MediaChangeLog; handles changelog of a media file
|
||||
*/
|
||||
class MediaChangeLog extends ChangeLog
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns path to changelog
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getChangelogFilename()
|
||||
{
|
||||
return mediaMetaFN($this->id, '.changes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
* @param string|int $rev empty string or revision timestamp
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getFilename($rev = '')
|
||||
{
|
||||
return mediaFN($this->id, $rev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds an entry to the changelog
|
||||
*
|
||||
* @param array $info Revision info structure of a media file
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return array revision info of added log line
|
||||
*
|
||||
* @see also addMediaLogEntry() in inc/changelog.php file
|
||||
*/
|
||||
public function addLogEntry(array $info, $timestamp = null)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (isset($timestamp)) unset($this->cache[$this->id][$info['date']]);
|
||||
|
||||
// add changelog lines
|
||||
$logline = $this->buildLogLine($info, $timestamp);
|
||||
io_saveFile(mediaMetaFN($this->id,'.changes'), $logline, $append = true);
|
||||
io_saveFile($conf['media_changelog'], $logline, $append = true); //global changelog cache
|
||||
|
||||
// update cache
|
||||
$this->currentRevision = $info['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $info;
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
60
snippets/dokuwiki-2023-04-04/inc/ChangeLog/PageChangeLog.php
Normal file
60
snippets/dokuwiki-2023-04-04/inc/ChangeLog/PageChangeLog.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* Class PageChangeLog; handles changelog of a wiki page
|
||||
*/
|
||||
class PageChangeLog extends ChangeLog
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns path to changelog
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getChangelogFilename()
|
||||
{
|
||||
return metaFN($this->id, '.changes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
* @param string|int $rev empty string or revision timestamp
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getFilename($rev = '')
|
||||
{
|
||||
return wikiFN($this->id, $rev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds an entry to the changelog
|
||||
*
|
||||
* @param array $info Revision info structure of a page
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return array revision info of added log line
|
||||
*
|
||||
* @see also addLogEntry() in inc/changelog.php file
|
||||
*/
|
||||
public function addLogEntry(array $info, $timestamp = null)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (isset($timestamp)) unset($this->cache[$this->id][$info['date']]);
|
||||
|
||||
// add changelog lines
|
||||
$logline = $this->buildLogLine($info, $timestamp);
|
||||
io_saveFile(metaFN($this->id,'.changes'), $logline, true);
|
||||
io_saveFile($conf['changelog'], $logline, true); //global changelog cache
|
||||
|
||||
// update cache
|
||||
$this->currentRevision = $info['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $info;
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
396
snippets/dokuwiki-2023-04-04/inc/ChangeLog/RevisionInfo.php
Normal file
396
snippets/dokuwiki-2023-04-04/inc/ChangeLog/RevisionInfo.php
Normal file
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* Class RevisionInfo
|
||||
*
|
||||
* Provides methods to show Revision Information in DokuWiki Ui components:
|
||||
* - Ui\Recent
|
||||
* - Ui\PageRevisions
|
||||
* - Ui\MediaRevisions
|
||||
*/
|
||||
class RevisionInfo
|
||||
{
|
||||
/* @var array */
|
||||
protected $info;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $info Revision Information structure with entries:
|
||||
* - date: unix timestamp
|
||||
* - ip: IPv4 or IPv6 address
|
||||
* - type: change type (log line type)
|
||||
* - id: page id
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
* additionally,
|
||||
* - current: (optional) whether current revision or not
|
||||
* - timestamp: (optional) set only when external edits occurred
|
||||
* - mode: (internal use) ether "media" or "page"
|
||||
*/
|
||||
public function __construct($info = null)
|
||||
{
|
||||
if (is_array($info) && isset($info['id'])) {
|
||||
// define strategy context
|
||||
$info['mode'] = $info['media'] ? 'media' : 'page';
|
||||
} else {
|
||||
$info = [
|
||||
'mode' => 'page',
|
||||
'date' => false,
|
||||
];
|
||||
}
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or return whether this revision is current page or media file
|
||||
*
|
||||
* This method does not check exactly whether the revision is current or not. Instead,
|
||||
* set value of associated "current" key for internal use. Some UI element like diff
|
||||
* link button depend on relation to current page or media file. A changelog line does
|
||||
* not indicate whether it corresponds to current page or media file.
|
||||
*
|
||||
* @param bool $value true if the revision is current, otherwise false
|
||||
* @return bool
|
||||
*/
|
||||
public function isCurrent($value = null)
|
||||
{
|
||||
return (bool) $this->val('current', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return or set a value of associated key of revision information
|
||||
* but does not allow to change values of existing keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public function val($key, $value = null)
|
||||
{
|
||||
if (isset($value) && !array_key_exists($key, $this->info)) {
|
||||
// setter, only for new keys
|
||||
$this->info[$key] = $value;
|
||||
}
|
||||
if (array_key_exists($key, $this->info)) {
|
||||
// getter
|
||||
return $this->info[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra key-value to the revision information
|
||||
* but does not allow to change values of existing keys
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
public function append(array $info)
|
||||
{
|
||||
foreach ($info as $key => $value) {
|
||||
$this->val($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* file icon of the page or media file
|
||||
* used in [Ui\recent]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showFileIcon()
|
||||
{
|
||||
$id = $this->val('id');
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
return media_printicon($id);
|
||||
case 'page': // page revision
|
||||
return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* edit date and time of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @param bool $checkTimestamp enable timestamp check, alter formatted string when timestamp is false
|
||||
* @return string
|
||||
*/
|
||||
public function showEditDate($checkTimestamp = false)
|
||||
{
|
||||
$formatted = dformat($this->val('date'));
|
||||
if ($checkTimestamp && $this->val('timestamp') === false) {
|
||||
// exact date is unknown for externally deleted file
|
||||
// when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__"
|
||||
$formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted);
|
||||
}
|
||||
return '<span class="date">'. $formatted .'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* edit summary
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showEditSummary()
|
||||
{
|
||||
return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* editor of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showEditor()
|
||||
{
|
||||
if ($this->val('user')) {
|
||||
$html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>';
|
||||
if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>';
|
||||
} else {
|
||||
$html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>';
|
||||
}
|
||||
return '<span class="user">'. $html. '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* name of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showFileName()
|
||||
{
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id];
|
||||
if ($rev) $params += ['rev'=> $rev];
|
||||
$href = media_managerURL($params, '&');
|
||||
$display_name = $id;
|
||||
$exists = file_exists(mediaFN($id, $rev));
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$params = $rev ? ['rev'=> $rev] : [];
|
||||
$href = wl($id, $params, false, '&');
|
||||
$display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
|
||||
if (!$display_name) $display_name = $id;
|
||||
$exists = page_exists($id, $rev);
|
||||
}
|
||||
|
||||
if($exists) {
|
||||
$class = 'wikilink1';
|
||||
} else {
|
||||
if($this->isCurrent()) {
|
||||
//show only not-existing link for current page, which allows for directly create a new page/upload
|
||||
$class = 'wikilink2';
|
||||
} else {
|
||||
//revision is not in attic
|
||||
return $display_name;
|
||||
}
|
||||
}
|
||||
if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$class = 'wikilink2';
|
||||
}
|
||||
return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Revision Title for PageDiff table headline
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showRevisionTitle()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (!$this->val('date')) return '—';
|
||||
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
$params = ($rev) ? ['rev'=> $rev] : [];
|
||||
|
||||
// revision info may have timestamp key when external edits occurred
|
||||
$date = ($this->val('timestamp') === false)
|
||||
? $lang['unknowndate']
|
||||
: dformat($this->val('date'));
|
||||
|
||||
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$href = ml($id, $params, false, '&');
|
||||
$exists = file_exists(mediaFN($id, $rev));
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$href = wl($id, $params, false, '&');
|
||||
$exists = page_exists($id, $rev);
|
||||
}
|
||||
if($exists) {
|
||||
$class = 'wikilink1';
|
||||
} else {
|
||||
if($this->isCurrent()) {
|
||||
//show only not-existing link for current page, which allows for directly create a new page/upload
|
||||
$class = 'wikilink2';
|
||||
} else {
|
||||
//revision is not in attic
|
||||
return $id.' ['.$date.']';
|
||||
}
|
||||
}
|
||||
if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$class = 'wikilink2';
|
||||
}
|
||||
return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>';
|
||||
}
|
||||
|
||||
/**
|
||||
* diff link icon in recent changes list, to compare (this) current revision with previous one
|
||||
* all items in "recent changes" are current revision of the page or media
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconCompareWithPrevious()
|
||||
{
|
||||
global $lang;
|
||||
$id = $this->val('id');
|
||||
|
||||
$href = '';
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
// unlike page, media file does not copied to media_attic when uploaded.
|
||||
// diff icon will not be shown when external edit occurred
|
||||
// because no attic file to be compared with current.
|
||||
$revs = (new MediaChangeLog($id))->getRevisions(0, 1);
|
||||
$showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id)));
|
||||
if ($showLink) {
|
||||
$param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id];
|
||||
$href = media_managerURL($param, '&');
|
||||
}
|
||||
break;
|
||||
case 'page': // page revision
|
||||
// when a page just created anyway, it is natural to expect no older revisions
|
||||
// even if it had once existed but deleted before. Simply ignore to check changelog.
|
||||
if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) {
|
||||
$href = wl($id, ['do'=>'diff'], false, '&');
|
||||
}
|
||||
}
|
||||
|
||||
if ($href) {
|
||||
return '<a href="'.$href.'" class="diff_link">'
|
||||
.'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
|
||||
.' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
|
||||
.'</a>';
|
||||
} else {
|
||||
return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* diff link icon in revisions list, compare this revision with current one
|
||||
* the icon does not displayed for the current revision
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconCompareWithCurrent()
|
||||
{
|
||||
global $lang;
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
|
||||
$href = '';
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) {
|
||||
$param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev];
|
||||
$href = media_managerURL($param, '&');
|
||||
}
|
||||
break;
|
||||
case 'page': // page revision
|
||||
if (!$this->isCurrent()) {
|
||||
$href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&');
|
||||
}
|
||||
}
|
||||
|
||||
if ($href) {
|
||||
return '<a href="'.$href.'" class="diff_link">'
|
||||
.'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
|
||||
.' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
|
||||
.'</a>';
|
||||
} else {
|
||||
return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* icon for revision action
|
||||
* used in [Ui\recent]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconRevisions()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (!actionOK('revisions')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$id = $this->val('id');
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$param = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id];
|
||||
$href = media_managerURL($param, '&');
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$href = wl($id, ['do'=>'revisions'], false, '&');
|
||||
}
|
||||
return '<a href="'.$href.'" class="revisions_link">'
|
||||
. '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"'
|
||||
. ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />'
|
||||
. '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* size change
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showSizeChange()
|
||||
{
|
||||
$class = 'sizechange';
|
||||
$value = filesize_h(abs($this->val('sizechange')));
|
||||
if ($this->val('sizechange') > 0) {
|
||||
$class .= ' positive';
|
||||
$value = '+' . $value;
|
||||
} elseif ($this->val('sizechange') < 0) {
|
||||
$class .= ' negative';
|
||||
$value = '-' . $value;
|
||||
} else {
|
||||
$value = '±' . $value;
|
||||
}
|
||||
return '<span class="'.$class.'">'.$value.'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* current indicator, used in revision list
|
||||
* not used in Ui\Recent because recent files are always current one
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showCurrentIndicator()
|
||||
{
|
||||
global $lang;
|
||||
return $this->isCurrent() ? '('.$lang['current'].')' : '';
|
||||
}
|
||||
|
||||
|
||||
}
|
175
snippets/dokuwiki-2023-04-04/inc/Debug/DebugHelper.php
Normal file
175
snippets/dokuwiki-2023-04-04/inc/Debug/DebugHelper.php
Normal file
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace dokuwiki\Debug;
|
||||
|
||||
use Doku_Event;
|
||||
use dokuwiki\Extension\EventHandler;
|
||||
use dokuwiki\Logger;
|
||||
|
||||
class DebugHelper
|
||||
{
|
||||
const INFO_DEPRECATION_LOG_EVENT = 'INFO_DEPRECATION_LOG';
|
||||
|
||||
/**
|
||||
* Check if deprecation messages shall be handled
|
||||
*
|
||||
* This is either because its logging is not disabled or a deprecation handler was registered
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isEnabled()
|
||||
{
|
||||
/** @var EventHandler $EVENT_HANDLER */
|
||||
global $EVENT_HANDLER;
|
||||
if (
|
||||
!Logger::getInstance(Logger::LOG_DEPRECATED)->isLogging() &&
|
||||
($EVENT_HANDLER === null || !$EVENT_HANDLER->hasHandlerForEvent('INFO_DEPRECATION_LOG'))
|
||||
) {
|
||||
// avoid any work if no one cares
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log accesses to deprecated fucntions to the debug log
|
||||
*
|
||||
* @param string $alternative (optional) The function or method that should be used instead
|
||||
* @param int $callerOffset (optional) How far the deprecated method is removed from this one
|
||||
* @param string $thing (optional) The deprecated thing, defaults to the calling method
|
||||
* @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
|
||||
*/
|
||||
public static function dbgDeprecatedFunction($alternative = '', $callerOffset = 1, $thing = '')
|
||||
{
|
||||
if (!self::isEnabled()) return;
|
||||
|
||||
$backtrace = debug_backtrace();
|
||||
for ($i = 0; $i < $callerOffset; $i += 1) {
|
||||
if(count($backtrace) > 1) array_shift($backtrace);
|
||||
}
|
||||
|
||||
list($self, $call) = $backtrace;
|
||||
|
||||
if (!$thing) {
|
||||
$thing = trim(
|
||||
(!empty($self['class']) ? ($self['class'] . '::') : '') .
|
||||
$self['function'] . '()', ':');
|
||||
}
|
||||
|
||||
self::triggerDeprecationEvent(
|
||||
$backtrace,
|
||||
$alternative,
|
||||
$thing,
|
||||
trim(
|
||||
(!empty($call['class']) ? ($call['class'] . '::') : '') .
|
||||
$call['function'] . '()', ':'),
|
||||
$self['file'] ?? $call['file'] ?? '',
|
||||
$self['line'] ?? $call['line'] ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This marks logs a deprecation warning for a property that should no longer be used
|
||||
*
|
||||
* This is usually called withing a magic getter or setter.
|
||||
* For logging deprecated functions or methods see dbgDeprecatedFunction()
|
||||
*
|
||||
* @param string $class The class with the deprecated property
|
||||
* @param string $propertyName The name of the deprecated property
|
||||
*
|
||||
* @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
|
||||
*/
|
||||
public static function dbgDeprecatedProperty($class, $propertyName)
|
||||
{
|
||||
if (!self::isEnabled()) return;
|
||||
|
||||
$backtrace = debug_backtrace();
|
||||
array_shift($backtrace);
|
||||
$call = $backtrace[1];
|
||||
$caller = trim($call['class'] . '::' . $call['function'] . '()', ':');
|
||||
$qualifiedName = $class . '::$' . $propertyName;
|
||||
self::triggerDeprecationEvent(
|
||||
$backtrace,
|
||||
'',
|
||||
$qualifiedName,
|
||||
$caller,
|
||||
$backtrace[0]['file'],
|
||||
$backtrace[0]['line']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a custom deprecation event
|
||||
*
|
||||
* Usually dbgDeprecatedFunction() or dbgDeprecatedProperty() should be used instead.
|
||||
* This method is intended only for those situation where they are not applicable.
|
||||
*
|
||||
* @param string $alternative
|
||||
* @param string $deprecatedThing
|
||||
* @param string $caller
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
* @param int $callerOffset How many lines should be removed from the beginning of the backtrace
|
||||
*/
|
||||
public static function dbgCustomDeprecationEvent(
|
||||
$alternative,
|
||||
$deprecatedThing,
|
||||
$caller,
|
||||
$file,
|
||||
$line,
|
||||
$callerOffset = 1
|
||||
)
|
||||
{
|
||||
if (!self::isEnabled()) return;
|
||||
|
||||
$backtrace = array_slice(debug_backtrace(), $callerOffset);
|
||||
|
||||
self::triggerDeprecationEvent(
|
||||
$backtrace,
|
||||
$alternative,
|
||||
$deprecatedThing,
|
||||
$caller,
|
||||
$file,
|
||||
$line
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $backtrace
|
||||
* @param string $alternative
|
||||
* @param string $deprecatedThing
|
||||
* @param string $caller
|
||||
* @param string $file
|
||||
* @param int $line
|
||||
*/
|
||||
private static function triggerDeprecationEvent(
|
||||
array $backtrace,
|
||||
$alternative,
|
||||
$deprecatedThing,
|
||||
$caller,
|
||||
$file,
|
||||
$line
|
||||
)
|
||||
{
|
||||
$data = [
|
||||
'trace' => $backtrace,
|
||||
'alternative' => $alternative,
|
||||
'called' => $deprecatedThing,
|
||||
'caller' => $caller,
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
];
|
||||
$event = new Doku_Event(self::INFO_DEPRECATION_LOG_EVENT, $data);
|
||||
if ($event->advise_before()) {
|
||||
$msg = $event->data['called'] . ' is deprecated. It was called from ';
|
||||
$msg .= $event->data['caller'] . ' in ' . $event->data['file'] . ':' . $event->data['line'];
|
||||
if ($event->data['alternative']) {
|
||||
$msg .= ' ' . $event->data['alternative'] . ' should be used instead!';
|
||||
}
|
||||
Logger::getInstance(Logger::LOG_DEPRECATED)->log($msg);
|
||||
}
|
||||
$event->advise_after();
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* Trait for issuing warnings on deprecated access.
|
||||
*
|
||||
* Adapted from https://github.com/wikimedia/mediawiki/blob/4aedefdbfd193f323097354bf581de1c93f02715/includes/debug/DeprecationHelper.php
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
namespace dokuwiki\Debug;
|
||||
|
||||
/**
|
||||
* Use this trait in classes which have properties for which public access
|
||||
* is deprecated. Set the list of properties in $deprecatedPublicProperties
|
||||
* and make the properties non-public. The trait will preserve public access
|
||||
* but issue deprecation warnings when it is needed.
|
||||
*
|
||||
* Example usage:
|
||||
* class Foo {
|
||||
* use DeprecationHelper;
|
||||
* protected $bar;
|
||||
* public function __construct() {
|
||||
* $this->deprecatePublicProperty( 'bar', '1.21', __CLASS__ );
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* $foo = new Foo;
|
||||
* $foo->bar; // works but logs a warning
|
||||
*
|
||||
* Cannot be used with classes that have their own __get/__set methods.
|
||||
*
|
||||
*/
|
||||
trait PropertyDeprecationHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* List of deprecated properties, in <property name> => <class> format
|
||||
* where <class> is the the name of the class defining the property
|
||||
*
|
||||
* E.g. [ '_event' => '\dokuwiki\Cache\Cache' ]
|
||||
* @var string[]
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [];
|
||||
|
||||
/**
|
||||
* Mark a property as deprecated. Only use this for properties that used to be public and only
|
||||
* call it in the constructor.
|
||||
*
|
||||
* @param string $property The name of the property.
|
||||
* @param null $class name of the class defining the property
|
||||
* @see DebugHelper::dbgDeprecatedProperty
|
||||
*/
|
||||
protected function deprecatePublicProperty(
|
||||
$property,
|
||||
$class = null
|
||||
) {
|
||||
$this->deprecatedPublicProperties[$property] = $class ?: get_class();
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->deprecatedPublicProperties[$name])) {
|
||||
$class = $this->deprecatedPublicProperties[$name];
|
||||
DebugHelper::dbgDeprecatedProperty($class, $name);
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
$qualifiedName = get_class() . '::$' . $name;
|
||||
if ($this->deprecationHelperGetPropertyOwner($name)) {
|
||||
// Someone tried to access a normal non-public property. Try to behave like PHP would.
|
||||
trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR);
|
||||
} else {
|
||||
// Non-existing property. Try to behave like PHP would.
|
||||
trigger_error("Undefined property: $qualifiedName", E_USER_NOTICE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if (isset($this->deprecatedPublicProperties[$name])) {
|
||||
$class = $this->deprecatedPublicProperties[$name];
|
||||
DebugHelper::dbgDeprecatedProperty($class, $name);
|
||||
$this->$name = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
$qualifiedName = get_class() . '::$' . $name;
|
||||
if ($this->deprecationHelperGetPropertyOwner($name)) {
|
||||
// Someone tried to access a normal non-public property. Try to behave like PHP would.
|
||||
trigger_error("Cannot access non-public property $qualifiedName", E_USER_ERROR);
|
||||
} else {
|
||||
// Non-existing property. Try to behave like PHP would.
|
||||
$this->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like property_exists but also check for non-visible private properties and returns which
|
||||
* class in the inheritance chain declared the property.
|
||||
* @param string $property
|
||||
* @return string|bool Best guess for the class in which the property is defined.
|
||||
*/
|
||||
private function deprecationHelperGetPropertyOwner($property)
|
||||
{
|
||||
// Easy branch: check for protected property / private property of the current class.
|
||||
if (property_exists($this, $property)) {
|
||||
// The class name is not necessarily correct here but getting the correct class
|
||||
// name would be expensive, this will work most of the time and getting it
|
||||
// wrong is not a big deal.
|
||||
return __CLASS__;
|
||||
}
|
||||
// property_exists() returns false when the property does exist but is private (and not
|
||||
// defined by the current class, for some value of "current" that differs slightly
|
||||
// between engines).
|
||||
// Since PHP triggers an error on public access of non-public properties but happily
|
||||
// allows public access to undefined properties, we need to detect this case as well.
|
||||
// Reflection is slow so use array cast hack to check for that:
|
||||
$obfuscatedProps = array_keys((array)$this);
|
||||
$obfuscatedPropTail = "\0$property";
|
||||
foreach ($obfuscatedProps as $obfuscatedProp) {
|
||||
// private props are in the form \0<classname>\0<propname>
|
||||
if (strpos($obfuscatedProp, $obfuscatedPropTail, 1) !== false) {
|
||||
$classname = substr($obfuscatedProp, 1, -strlen($obfuscatedPropTail));
|
||||
if ($classname === '*') {
|
||||
// sanity; this shouldn't be possible as protected properties were handled earlier
|
||||
$classname = __CLASS__;
|
||||
}
|
||||
return $classname;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
1544
snippets/dokuwiki-2023-04-04/inc/DifferenceEngine.php
Normal file
1544
snippets/dokuwiki-2023-04-04/inc/DifferenceEngine.php
Normal file
File diff suppressed because it is too large
Load Diff
165
snippets/dokuwiki-2023-04-04/inc/Draft.php
Normal file
165
snippets/dokuwiki-2023-04-04/inc/Draft.php
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
/**
|
||||
* Class Draft
|
||||
*
|
||||
* @package dokuwiki
|
||||
*/
|
||||
class Draft
|
||||
{
|
||||
|
||||
protected $errors = [];
|
||||
protected $cname;
|
||||
protected $id;
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* Draft constructor.
|
||||
*
|
||||
* @param string $ID the page id for this draft
|
||||
* @param string $client the client identification (username or ip or similar) for this draft
|
||||
*/
|
||||
public function __construct($ID, $client)
|
||||
{
|
||||
$this->id = $ID;
|
||||
$this->client = $client;
|
||||
$this->cname = getCacheName("$client\n$ID", '.draft');
|
||||
if(file_exists($this->cname) && file_exists(wikiFN($ID))) {
|
||||
if (filemtime($this->cname) < filemtime(wikiFN($ID))) {
|
||||
// remove stale draft
|
||||
$this->deleteDraft();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename for this draft (whether or not it exists)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDraftFilename()
|
||||
{
|
||||
return $this->cname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this draft exists on the filesystem
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDraftAvailable()
|
||||
{
|
||||
return file_exists($this->cname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a draft of a current edit session
|
||||
*
|
||||
* The draft will not be saved if
|
||||
* - drafts are deactivated in the config
|
||||
* - or the editarea is empty and there are no event handlers registered
|
||||
* - or the event is prevented
|
||||
*
|
||||
* @triggers DRAFT_SAVE
|
||||
*
|
||||
* @return bool whether has the draft been saved
|
||||
*/
|
||||
public function saveDraft()
|
||||
{
|
||||
global $INPUT, $INFO, $EVENT_HANDLER, $conf;
|
||||
if (!$conf['usedraft']) {
|
||||
return false;
|
||||
}
|
||||
if (!$INPUT->post->has('wikitext') &&
|
||||
!$EVENT_HANDLER->hasHandlerForEvent('DRAFT_SAVE')) {
|
||||
return false;
|
||||
}
|
||||
$draft = [
|
||||
'id' => $this->id,
|
||||
'prefix' => substr($INPUT->post->str('prefix'), 0, -1),
|
||||
'text' => $INPUT->post->str('wikitext'),
|
||||
'suffix' => $INPUT->post->str('suffix'),
|
||||
'date' => $INPUT->post->int('date'),
|
||||
'client' => $this->client,
|
||||
'cname' => $this->cname,
|
||||
'errors' => [],
|
||||
];
|
||||
$event = new Extension\Event('DRAFT_SAVE', $draft);
|
||||
if ($event->advise_before()) {
|
||||
$draft['hasBeenSaved'] = io_saveFile($draft['cname'], serialize($draft));
|
||||
if ($draft['hasBeenSaved']) {
|
||||
$INFO['draft'] = $draft['cname'];
|
||||
}
|
||||
} else {
|
||||
$draft['hasBeenSaved'] = false;
|
||||
}
|
||||
$event->advise_after();
|
||||
|
||||
$this->errors = $draft['errors'];
|
||||
|
||||
return $draft['hasBeenSaved'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text from the draft file
|
||||
*
|
||||
* @throws \RuntimeException if the draft file doesn't exist
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDraftText()
|
||||
{
|
||||
if (!file_exists($this->cname)) {
|
||||
throw new \RuntimeException(
|
||||
"Draft for page $this->id and user $this->client doesn't exist at $this->cname."
|
||||
);
|
||||
}
|
||||
$draft = unserialize(io_readFile($this->cname,false));
|
||||
return cleanText(con($draft['prefix'],$draft['text'],$draft['suffix'],true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the draft from the filesystem
|
||||
*
|
||||
* Also sets $INFO['draft'] to null
|
||||
*/
|
||||
public function deleteDraft()
|
||||
{
|
||||
global $INFO;
|
||||
@unlink($this->cname);
|
||||
$INFO['draft'] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted message stating when the draft was saved
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDraftMessage()
|
||||
{
|
||||
global $lang;
|
||||
return $lang['draftdate'] . ' ' . dformat(filemtime($this->cname));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the errors that occured when saving the draft
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp when this draft was saved
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDraftDate()
|
||||
{
|
||||
return filemtime($this->cname);
|
||||
}
|
||||
}
|
205
snippets/dokuwiki-2023-04-04/inc/ErrorHandler.php
Normal file
205
snippets/dokuwiki-2023-04-04/inc/ErrorHandler.php
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Exception\FatalException;
|
||||
|
||||
/**
|
||||
* Manage the global handling of errors and exceptions
|
||||
*
|
||||
* Developer may use this to log and display exceptions themselves
|
||||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
/**
|
||||
* Standard error codes used in PHP errors
|
||||
* @see https://www.php.net/manual/en/errorfunc.constants.php
|
||||
*/
|
||||
const ERRORCODES = [
|
||||
1 => 'E_ERROR',
|
||||
2 => 'E_WARNING',
|
||||
4 => 'E_PARSE',
|
||||
8 => 'E_NOTICE',
|
||||
16 => 'E_CORE_ERROR',
|
||||
32 => 'E_CORE_WARNING',
|
||||
64 => 'E_COMPILE_ERROR',
|
||||
128 => 'E_COMPILE_WARNING',
|
||||
256 => 'E_USER_ERROR',
|
||||
512 => 'E_USER_WARNING',
|
||||
1024 => 'E_USER_NOTICE',
|
||||
2048 => 'E_STRICT',
|
||||
4096 => 'E_RECOVERABLE_ERROR',
|
||||
8192 => 'E_DEPRECATED',
|
||||
16384 => 'E_USER_DEPRECATED',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the default error handling
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
if (!defined('DOKU_UNITTEST')) {
|
||||
set_exception_handler([ErrorHandler::class, 'fatalException']);
|
||||
register_shutdown_function([ErrorHandler::class, 'fatalShutdown']);
|
||||
set_error_handler(
|
||||
[ErrorHandler::class, 'errorHandler'],
|
||||
E_WARNING|E_USER_ERROR|E_USER_WARNING|E_RECOVERABLE_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Exception handler to show a nice user message before dieing
|
||||
*
|
||||
* The exception is logged to the error log
|
||||
*
|
||||
* @param \Throwable $e
|
||||
*/
|
||||
public static function fatalException($e)
|
||||
{
|
||||
$plugin = self::guessPlugin($e);
|
||||
$title = hsc(get_class($e) . ': ' . $e->getMessage());
|
||||
$msg = 'An unforeseen error has occured. This is most likely a bug somewhere.';
|
||||
if ($plugin) $msg .= ' It might be a problem in the ' . $plugin . ' plugin.';
|
||||
$logged = self::logException($e)
|
||||
? 'More info has been written to the DokuWiki error log.'
|
||||
: $e->getFile() . ':' . $e->getLine();
|
||||
|
||||
echo <<<EOT
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>$title</title></head>
|
||||
<body style="font-family: Arial, sans-serif">
|
||||
<div style="width:60%; margin: auto; background-color: #fcc;
|
||||
border: 1px solid #faa; padding: 0.5em 1em;">
|
||||
<h1 style="font-size: 120%">$title</h1>
|
||||
<p>$msg</p>
|
||||
<p>$logged</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to display an error message for the given Exception
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @param string $intro
|
||||
*/
|
||||
public static function showExceptionMsg($e, $intro = 'Error!')
|
||||
{
|
||||
$msg = hsc($intro) . '<br />' . hsc(get_class($e) . ': ' . $e->getMessage());
|
||||
if (self::logException($e)) $msg .= '<br />More info is available in the error log.';
|
||||
msg($msg, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Last resort to handle fatal errors that still can't be caught
|
||||
*/
|
||||
public static function fatalShutdown()
|
||||
{
|
||||
$error = error_get_last();
|
||||
// Check if it's a core/fatal error, otherwise it's a normal shutdown
|
||||
if (
|
||||
$error !== null &&
|
||||
in_array(
|
||||
$error['type'],
|
||||
[
|
||||
E_ERROR,
|
||||
E_CORE_ERROR,
|
||||
E_COMPILE_ERROR,
|
||||
]
|
||||
)
|
||||
) {
|
||||
self::fatalException(
|
||||
new FatalException($error['message'], 0, $error['type'], $error['file'], $error['line'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the given exception to the error log
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return bool false if the logging failed
|
||||
*/
|
||||
public static function logException($e)
|
||||
{
|
||||
if (is_a($e, \ErrorException::class)) {
|
||||
$prefix = self::ERRORCODES[$e->getSeverity()];
|
||||
} else {
|
||||
$prefix = get_class($e);
|
||||
}
|
||||
|
||||
return Logger::getInstance()->log(
|
||||
$prefix . ': ' . $e->getMessage(),
|
||||
$e->getTraceAsString(),
|
||||
$e->getFile(),
|
||||
$e->getLine()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler to log non-exception errors
|
||||
*
|
||||
* @param int $errno
|
||||
* @param string $errstr
|
||||
* @param string $errfile
|
||||
* @param int $errline
|
||||
* @return bool
|
||||
*/
|
||||
public static function errorHandler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// ignore supressed warnings
|
||||
if (!(error_reporting() & $errno)) return false;
|
||||
|
||||
$ex = new \ErrorException(
|
||||
$errstr,
|
||||
0,
|
||||
$errno,
|
||||
$errfile,
|
||||
$errline
|
||||
);
|
||||
self::logException($ex);
|
||||
|
||||
if($ex->getSeverity() === E_WARNING && $conf['hidewarnings']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the the stacktrace for plugin files
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return false|string
|
||||
*/
|
||||
protected static function guessPlugin($e)
|
||||
{
|
||||
if (preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $e->getFile()), $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
foreach ($e->getTrace() as $line) {
|
||||
if (
|
||||
isset($line['class']) &&
|
||||
preg_match('/\w+?_plugin_(\w+)/', $line['class'], $match)
|
||||
) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
if (
|
||||
isset($line['file']) &&
|
||||
preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $line['file']), $match)
|
||||
) {
|
||||
return $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Exception;
|
||||
|
||||
/**
|
||||
* Fatal Errors are converted into this Exception in out Shutdown handler
|
||||
*/
|
||||
class FatalException extends \ErrorException
|
||||
{
|
||||
|
||||
}
|
22
snippets/dokuwiki-2023-04-04/inc/Extension/ActionPlugin.php
Normal file
22
snippets/dokuwiki-2023-04-04/inc/Extension/ActionPlugin.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* Action Plugin Prototype
|
||||
*
|
||||
* Handles action hooks within a plugin
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
*/
|
||||
abstract class ActionPlugin extends Plugin
|
||||
{
|
||||
|
||||
/**
|
||||
* Registers a callback function for a given event
|
||||
*
|
||||
* @param \Doku_Event_Handler $controller
|
||||
*/
|
||||
abstract public function register(\Doku_Event_Handler $controller);
|
||||
}
|
123
snippets/dokuwiki-2023-04-04/inc/Extension/AdminPlugin.php
Normal file
123
snippets/dokuwiki-2023-04-04/inc/Extension/AdminPlugin.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* Admin Plugin Prototype
|
||||
*
|
||||
* Implements an admin interface in a plugin
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
*/
|
||||
abstract class AdminPlugin extends Plugin
|
||||
{
|
||||
|
||||
/**
|
||||
* Return the text that is displayed at the main admin menu
|
||||
* (Default localized language string 'menu' is returned, override this function for setting another name)
|
||||
*
|
||||
* @param string $language language code
|
||||
* @return string menu string
|
||||
*/
|
||||
public function getMenuText($language)
|
||||
{
|
||||
$menutext = $this->getLang('menu');
|
||||
if (!$menutext) {
|
||||
$info = $this->getInfo();
|
||||
$menutext = $info['name'] . ' ...';
|
||||
}
|
||||
return $menutext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the icon being displayed in the main admin menu.
|
||||
* By default it tries to find an 'admin.svg' file in the plugin directory.
|
||||
* (Override this function for setting another image)
|
||||
*
|
||||
* Important: you have to return a single path, monochrome SVG icon! It has to be
|
||||
* under 2 Kilobytes!
|
||||
*
|
||||
* We recommend icons from https://materialdesignicons.com/ or to use a matching
|
||||
* style.
|
||||
*
|
||||
* @return string full path to the icon file
|
||||
*/
|
||||
public function getMenuIcon()
|
||||
{
|
||||
$plugin = $this->getPluginName();
|
||||
return DOKU_PLUGIN . $plugin . '/admin.svg';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine position in list in admin window
|
||||
* Lower values are sorted up
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMenuSort()
|
||||
{
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carry out required processing
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// some plugins might not need this
|
||||
}
|
||||
|
||||
/**
|
||||
* Output html of the admin page
|
||||
*/
|
||||
abstract public function html();
|
||||
|
||||
/**
|
||||
* Checks if access should be granted to this admin plugin
|
||||
*
|
||||
* @return bool true if the current user may access this admin plugin
|
||||
*/
|
||||
public function isAccessibleByCurrentUser() {
|
||||
$data = [];
|
||||
$data['instance'] = $this;
|
||||
$data['hasAccess'] = false;
|
||||
|
||||
$event = new Event('ADMINPLUGIN_ACCESS_CHECK', $data);
|
||||
if($event->advise_before()) {
|
||||
if ($this->forAdminOnly()) {
|
||||
$data['hasAccess'] = auth_isadmin();
|
||||
} else {
|
||||
$data['hasAccess'] = auth_ismanager();
|
||||
}
|
||||
}
|
||||
$event->advise_after();
|
||||
|
||||
return $data['hasAccess'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true for access only by admins (config:superuser) or false if managers are allowed as well
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function forAdminOnly()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array with ToC items. Items can be created with the html_mktocitem()
|
||||
*
|
||||
* @see html_mktocitem()
|
||||
* @see tpl_toc()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTOC()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
|
461
snippets/dokuwiki-2023-04-04/inc/Extension/AuthPlugin.php
Normal file
461
snippets/dokuwiki-2023-04-04/inc/Extension/AuthPlugin.php
Normal file
@ -0,0 +1,461 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* Auth Plugin Prototype
|
||||
*
|
||||
* allows to authenticate users in a plugin
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @author Jan Schumann <js@jschumann-it.com>
|
||||
*/
|
||||
abstract class AuthPlugin extends Plugin
|
||||
{
|
||||
public $success = true;
|
||||
|
||||
/**
|
||||
* Possible things an auth backend module may be able to
|
||||
* do. The things a backend can do need to be set to true
|
||||
* in the constructor.
|
||||
*/
|
||||
protected $cando = array(
|
||||
'addUser' => false, // can Users be created?
|
||||
'delUser' => false, // can Users be deleted?
|
||||
'modLogin' => false, // can login names be changed?
|
||||
'modPass' => false, // can passwords be changed?
|
||||
'modName' => false, // can real names be changed?
|
||||
'modMail' => false, // can emails be changed?
|
||||
'modGroups' => false, // can groups be changed?
|
||||
'getUsers' => false, // can a (filtered) list of users be retrieved?
|
||||
'getUserCount' => false, // can the number of users be retrieved?
|
||||
'getGroups' => false, // can a list of available groups be retrieved?
|
||||
'external' => false, // does the module do external auth checking?
|
||||
'logout' => true, // can the user logout again? (eg. not possible with HTTP auth)
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Carry out sanity checks to ensure the object is
|
||||
* able to operate. Set capabilities in $this->cando
|
||||
* array here
|
||||
*
|
||||
* For future compatibility, sub classes should always include a call
|
||||
* to parent::__constructor() in their constructors!
|
||||
*
|
||||
* Set $this->success to false if checks fail
|
||||
*
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// the base class constructor does nothing, derived class
|
||||
// constructors do the real work
|
||||
}
|
||||
|
||||
/**
|
||||
* Available Capabilities. [ DO NOT OVERRIDE ]
|
||||
*
|
||||
* For introspection/debugging
|
||||
*
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
* @return array
|
||||
*/
|
||||
public function getCapabilities()
|
||||
{
|
||||
return array_keys($this->cando);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capability check. [ DO NOT OVERRIDE ]
|
||||
*
|
||||
* Checks the capabilities set in the $this->cando array and
|
||||
* some pseudo capabilities (shortcutting access to multiple
|
||||
* ones)
|
||||
*
|
||||
* ususal capabilities start with lowercase letter
|
||||
* shortcut capabilities start with uppercase letter
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $cap the capability to check
|
||||
* @return bool
|
||||
*/
|
||||
public function canDo($cap)
|
||||
{
|
||||
switch ($cap) {
|
||||
case 'Profile':
|
||||
// can at least one of the user's properties be changed?
|
||||
return ($this->cando['modPass'] ||
|
||||
$this->cando['modName'] ||
|
||||
$this->cando['modMail']);
|
||||
break;
|
||||
case 'UserMod':
|
||||
// can at least anything be changed?
|
||||
return ($this->cando['modPass'] ||
|
||||
$this->cando['modName'] ||
|
||||
$this->cando['modMail'] ||
|
||||
$this->cando['modLogin'] ||
|
||||
$this->cando['modGroups'] ||
|
||||
$this->cando['modMail']);
|
||||
break;
|
||||
default:
|
||||
// print a helping message for developers
|
||||
if (!isset($this->cando[$cap])) {
|
||||
msg("Check for unknown capability '$cap' - Do you use an outdated Plugin?", -1);
|
||||
}
|
||||
return $this->cando[$cap];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the AUTH_USERDATA_CHANGE event and call the modification function. [ DO NOT OVERRIDE ]
|
||||
*
|
||||
* You should use this function instead of calling createUser, modifyUser or
|
||||
* deleteUsers directly. The event handlers can prevent the modification, for
|
||||
* example for enforcing a user name schema.
|
||||
*
|
||||
* @author Gabriel Birke <birke@d-scribe.de>
|
||||
* @param string $type Modification type ('create', 'modify', 'delete')
|
||||
* @param array $params Parameters for the createUser, modifyUser or deleteUsers method.
|
||||
* The content of this array depends on the modification type
|
||||
* @return bool|null|int Result from the modification function or false if an event handler has canceled the action
|
||||
*/
|
||||
public function triggerUserMod($type, $params)
|
||||
{
|
||||
$validTypes = array(
|
||||
'create' => 'createUser',
|
||||
'modify' => 'modifyUser',
|
||||
'delete' => 'deleteUsers',
|
||||
);
|
||||
if (empty($validTypes[$type])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$eventdata = array('type' => $type, 'params' => $params, 'modification_result' => null);
|
||||
$evt = new Event('AUTH_USER_CHANGE', $eventdata);
|
||||
if ($evt->advise_before(true)) {
|
||||
$result = call_user_func_array(array($this, $validTypes[$type]), $evt->data['params']);
|
||||
$evt->data['modification_result'] = $result;
|
||||
}
|
||||
$evt->advise_after();
|
||||
unset($evt);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log off the current user [ OPTIONAL ]
|
||||
*
|
||||
* Is run in addition to the ususal logoff method. Should
|
||||
* only be needed when trustExternal is implemented.
|
||||
*
|
||||
* @see auth_logoff()
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
public function logOff()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Do all authentication [ OPTIONAL ]
|
||||
*
|
||||
* Set $this->cando['external'] = true when implemented
|
||||
*
|
||||
* If this function is implemented it will be used to
|
||||
* authenticate a user - all other DokuWiki internals
|
||||
* will not be used for authenticating (except this
|
||||
* function returns null, in which case, DokuWiki will
|
||||
* still run auth_login as a fallback, which may call
|
||||
* checkPass()). If this function is not returning null,
|
||||
* implementing checkPass() is not needed here anymore.
|
||||
*
|
||||
* The function can be used to authenticate against third
|
||||
* party cookies or Apache auth mechanisms and replaces
|
||||
* the auth_login() function
|
||||
*
|
||||
* The function will be called with or without a set
|
||||
* username. If the Username is given it was called
|
||||
* from the login form and the given credentials might
|
||||
* need to be checked. If no username was given it
|
||||
* the function needs to check if the user is logged in
|
||||
* by other means (cookie, environment).
|
||||
*
|
||||
* The function needs to set some globals needed by
|
||||
* DokuWiki like auth_login() does.
|
||||
*
|
||||
* @see auth_login()
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Cleartext Password
|
||||
* @param bool $sticky Cookie should not expire
|
||||
* @return bool true on successful auth,
|
||||
* null on unknown result (fallback to checkPass)
|
||||
*/
|
||||
public function trustExternal($user, $pass, $sticky = false)
|
||||
{
|
||||
/* some example:
|
||||
|
||||
global $USERINFO;
|
||||
global $conf;
|
||||
$sticky ? $sticky = true : $sticky = false; //sanity check
|
||||
|
||||
// do the checking here
|
||||
|
||||
// set the globals if authed
|
||||
$USERINFO['name'] = 'FIXME';
|
||||
$USERINFO['mail'] = 'FIXME';
|
||||
$USERINFO['grps'] = array('FIXME');
|
||||
$_SERVER['REMOTE_USER'] = $user;
|
||||
$_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
|
||||
$_SESSION[DOKU_COOKIE]['auth']['pass'] = $pass;
|
||||
$_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
|
||||
return true;
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user+password [ MUST BE OVERRIDDEN ]
|
||||
*
|
||||
* Checks if the given user exists and the given
|
||||
* plaintext password is correct
|
||||
*
|
||||
* May be ommited if trustExternal is used.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $user the user name
|
||||
* @param string $pass the clear text password
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPass($user, $pass)
|
||||
{
|
||||
msg("no valid authorisation system in use", -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user info [ MUST BE OVERRIDDEN ]
|
||||
*
|
||||
* Returns info about the given user needs to contain
|
||||
* at least these fields:
|
||||
*
|
||||
* name string full name of the user
|
||||
* mail string email address of the user
|
||||
* grps array list of groups the user is in
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $user the user name
|
||||
* @param bool $requireGroups whether or not the returned data must include groups
|
||||
* @return false|array containing user data or false
|
||||
*/
|
||||
public function getUserData($user, $requireGroups = true)
|
||||
{
|
||||
if (!$this->cando['external']) msg("no valid authorisation system in use", -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new User [implement only where required/possible]
|
||||
*
|
||||
* Returns false if the user already exists, null when an error
|
||||
* occurred and true if everything went well.
|
||||
*
|
||||
* The new user HAS TO be added to the default group by this
|
||||
* function!
|
||||
*
|
||||
* Set addUser capability when implemented
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $user
|
||||
* @param string $pass
|
||||
* @param string $name
|
||||
* @param string $mail
|
||||
* @param null|array $grps
|
||||
* @return bool|null
|
||||
*/
|
||||
public function createUser($user, $pass, $name, $mail, $grps = null)
|
||||
{
|
||||
msg("authorisation method does not allow creation of new users", -1);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify user data [implement only where required/possible]
|
||||
*
|
||||
* Set the mod* capabilities according to the implemented features
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param string $user nick of the user to be changed
|
||||
* @param array $changes array of field/value pairs to be changed (password will be clear text)
|
||||
* @return bool
|
||||
*/
|
||||
public function modifyUser($user, $changes)
|
||||
{
|
||||
msg("authorisation method does not allow modifying of user data", -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one or more users [implement only where required/possible]
|
||||
*
|
||||
* Set delUser capability when implemented
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param array $users
|
||||
* @return int number of users deleted
|
||||
*/
|
||||
public function deleteUsers($users)
|
||||
{
|
||||
msg("authorisation method does not allow deleting of users", -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a count of the number of user which meet $filter criteria
|
||||
* [should be implemented whenever retrieveUsers is implemented]
|
||||
*
|
||||
* Set getUserCount capability when implemented
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param array $filter array of field/pattern pairs, empty array for no filter
|
||||
* @return int
|
||||
*/
|
||||
public function getUserCount($filter = array())
|
||||
{
|
||||
msg("authorisation method does not provide user counts", -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk retrieval of user data [implement only where required/possible]
|
||||
*
|
||||
* Set getUsers capability when implemented
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param int $start index of first user to be returned
|
||||
* @param int $limit max number of users to be returned, 0 for unlimited
|
||||
* @param array $filter array of field/pattern pairs, null for no filter
|
||||
* @return array list of userinfo (refer getUserData for internal userinfo details)
|
||||
*/
|
||||
public function retrieveUsers($start = 0, $limit = 0, $filter = null)
|
||||
{
|
||||
msg("authorisation method does not support mass retrieval of user data", -1);
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a group [implement only where required/possible]
|
||||
*
|
||||
* Set addGroup capability when implemented
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param string $group
|
||||
* @return bool
|
||||
*/
|
||||
public function addGroup($group)
|
||||
{
|
||||
msg("authorisation method does not support independent group creation", -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve groups [implement only where required/possible]
|
||||
*
|
||||
* Set getGroups capability when implemented
|
||||
*
|
||||
* @author Chris Smith <chris@jalakai.co.uk>
|
||||
* @param int $start
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function retrieveGroups($start = 0, $limit = 0)
|
||||
{
|
||||
msg("authorisation method does not support group list retrieval", -1);
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return case sensitivity of the backend [OPTIONAL]
|
||||
*
|
||||
* When your backend is caseinsensitive (eg. you can login with USER and
|
||||
* user) then you need to overwrite this method and return false
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCaseSensitive()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a given username [OPTIONAL]
|
||||
*
|
||||
* This function is applied to any user name that is given to
|
||||
* the backend and should also be applied to any user name within
|
||||
* the backend before returning it somewhere.
|
||||
*
|
||||
* This should be used to enforce username restrictions.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $user username
|
||||
* @return string the cleaned username
|
||||
*/
|
||||
public function cleanUser($user)
|
||||
{
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize a given groupname [OPTIONAL]
|
||||
*
|
||||
* This function is applied to any groupname that is given to
|
||||
* the backend and should also be applied to any groupname within
|
||||
* the backend before returning it somewhere.
|
||||
*
|
||||
* This should be used to enforce groupname restrictions.
|
||||
*
|
||||
* Groupnames are to be passed without a leading '@' here.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param string $group groupname
|
||||
* @return string the cleaned groupname
|
||||
*/
|
||||
public function cleanGroup($group)
|
||||
{
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Session Cache validity [implement only where required/possible]
|
||||
*
|
||||
* DokuWiki caches user info in the user's session for the timespan defined
|
||||
* in $conf['auth_security_timeout'].
|
||||
*
|
||||
* This makes sure slow authentication backends do not slow down DokuWiki.
|
||||
* This also means that changes to the user database will not be reflected
|
||||
* on currently logged in users.
|
||||
*
|
||||
* To accommodate for this, the user manager plugin will touch a reference
|
||||
* file whenever a change is submitted. This function compares the filetime
|
||||
* of this reference file with the time stored in the session.
|
||||
*
|
||||
* This reference file mechanism does not reflect changes done directly in
|
||||
* the backend's database through other means than the user manager plugin.
|
||||
*
|
||||
* Fast backends might want to return always false, to force rechecks on
|
||||
* each page load. Others might want to use their own checking here. If
|
||||
* unsure, do not override.
|
||||
*
|
||||
* @param string $user - The username
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @return bool
|
||||
*/
|
||||
public function useSessionCache($user)
|
||||
{
|
||||
global $conf;
|
||||
return ($_SESSION[DOKU_COOKIE]['auth']['time'] >= @filemtime($conf['cachedir'] . '/sessionpurge'));
|
||||
}
|
||||
}
|
13
snippets/dokuwiki-2023-04-04/inc/Extension/CLIPlugin.php
Normal file
13
snippets/dokuwiki-2023-04-04/inc/Extension/CLIPlugin.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* CLI plugin prototype
|
||||
*
|
||||
* Provides DokuWiki plugin functionality on top of php-cli
|
||||
*/
|
||||
abstract class CLIPlugin extends \splitbrain\phpcli\CLI implements PluginInterface
|
||||
{
|
||||
use PluginTrait;
|
||||
}
|
201
snippets/dokuwiki-2023-04-04/inc/Extension/Event.php
Normal file
201
snippets/dokuwiki-2023-04-04/inc/Extension/Event.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use dokuwiki\Logger;
|
||||
|
||||
/**
|
||||
* The Action plugin event
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/** @var string READONLY event name, objects must register against this name to see the event */
|
||||
public $name = '';
|
||||
/** @var mixed|null READWRITE data relevant to the event, no standardised format, refer to event docs */
|
||||
public $data = null;
|
||||
/**
|
||||
* @var mixed|null READWRITE the results of the event action, only relevant in "_AFTER" advise
|
||||
* event handlers may modify this if they are preventing the default action
|
||||
* to provide the after event handlers with event results
|
||||
*/
|
||||
public $result = null;
|
||||
/** @var bool READONLY if true, event handlers can prevent the events default action */
|
||||
public $canPreventDefault = true;
|
||||
|
||||
/** @var bool whether or not to carry out the default action associated with the event */
|
||||
protected $runDefault = true;
|
||||
/** @var bool whether or not to continue propagating the event to other handlers */
|
||||
protected $mayContinue = true;
|
||||
|
||||
/**
|
||||
* event constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function __construct($name, &$data)
|
||||
{
|
||||
|
||||
$this->name = $name;
|
||||
$this->data =& $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* advise all registered BEFORE handlers of this event
|
||||
*
|
||||
* if these methods are used by functions outside of this object, they must
|
||||
* properly handle correct processing of any default action and issue an
|
||||
* advise_after() signal. e.g.
|
||||
* $evt = new dokuwiki\Plugin\Doku_Event(name, data);
|
||||
* if ($evt->advise_before(canPreventDefault) {
|
||||
* // default action code block
|
||||
* }
|
||||
* $evt->advise_after();
|
||||
* unset($evt);
|
||||
*
|
||||
* @param bool $enablePreventDefault
|
||||
* @return bool results of processing the event, usually $this->runDefault
|
||||
*/
|
||||
public function advise_before($enablePreventDefault = true)
|
||||
{
|
||||
global $EVENT_HANDLER;
|
||||
|
||||
$this->canPreventDefault = $enablePreventDefault;
|
||||
if ($EVENT_HANDLER !== null) {
|
||||
$EVENT_HANDLER->process_event($this, 'BEFORE');
|
||||
} else {
|
||||
Logger::getInstance(Logger::LOG_DEBUG)
|
||||
->log($this->name . ':BEFORE event triggered before event system was initialized');
|
||||
}
|
||||
|
||||
return (!$enablePreventDefault || $this->runDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* advise all registered AFTER handlers of this event
|
||||
*
|
||||
* @param bool $enablePreventDefault
|
||||
* @see advise_before() for details
|
||||
*/
|
||||
public function advise_after()
|
||||
{
|
||||
global $EVENT_HANDLER;
|
||||
|
||||
$this->mayContinue = true;
|
||||
|
||||
if ($EVENT_HANDLER !== null) {
|
||||
$EVENT_HANDLER->process_event($this, 'AFTER');
|
||||
} else {
|
||||
Logger::getInstance(Logger::LOG_DEBUG)->
|
||||
log($this->name . ':AFTER event triggered before event system was initialized');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger
|
||||
*
|
||||
* - advise all registered (<event>_BEFORE) handlers that this event is about to take place
|
||||
* - carry out the default action using $this->data based on $enablePrevent and
|
||||
* $this->_default, all of which may have been modified by the event handlers.
|
||||
* - advise all registered (<event>_AFTER) handlers that the event has taken place
|
||||
*
|
||||
* @param null|callable $action
|
||||
* @param bool $enablePrevent
|
||||
* @return mixed $event->results
|
||||
* the value set by any <event>_before or <event> handlers if the default action is prevented
|
||||
* or the results of the default action (as modified by <event>_after handlers)
|
||||
* or NULL no action took place and no handler modified the value
|
||||
*/
|
||||
public function trigger($action = null, $enablePrevent = true)
|
||||
{
|
||||
|
||||
if (!is_callable($action)) {
|
||||
$enablePrevent = false;
|
||||
if ($action !== null) {
|
||||
trigger_error(
|
||||
'The default action of ' . $this .
|
||||
' is not null but also not callable. Maybe the method is not public?',
|
||||
E_USER_WARNING
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->advise_before($enablePrevent) && is_callable($action)) {
|
||||
$this->result = call_user_func_array($action, [&$this->data]);
|
||||
}
|
||||
|
||||
$this->advise_after();
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* stopPropagation
|
||||
*
|
||||
* stop any further processing of the event by event handlers
|
||||
* this function does not prevent the default action taking place
|
||||
*/
|
||||
public function stopPropagation()
|
||||
{
|
||||
$this->mayContinue = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* may the event propagate to the next handler?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mayPropagate()
|
||||
{
|
||||
return $this->mayContinue;
|
||||
}
|
||||
|
||||
/**
|
||||
* preventDefault
|
||||
*
|
||||
* prevent the default action taking place
|
||||
*/
|
||||
public function preventDefault()
|
||||
{
|
||||
$this->runDefault = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* should the default action be executed?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function mayRunDefault()
|
||||
{
|
||||
return $this->runDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to trigger an event
|
||||
*
|
||||
* Creates, triggers and destroys an event in one go
|
||||
*
|
||||
* @param string $name name for the event
|
||||
* @param mixed $data event data
|
||||
* @param callable $action (optional, default=NULL) default action, a php callback function
|
||||
* @param bool $canPreventDefault (optional, default=true) can hooks prevent the default action
|
||||
*
|
||||
* @return mixed the event results value after all event processing is complete
|
||||
* by default this is the return value of the default action however
|
||||
* it can be set or modified by event handler hooks
|
||||
*/
|
||||
static public function createAndTrigger($name, &$data, $action = null, $canPreventDefault = true)
|
||||
{
|
||||
$evt = new Event($name, $data);
|
||||
return $evt->trigger($action, $canPreventDefault);
|
||||
}
|
||||
}
|
119
snippets/dokuwiki-2023-04-04/inc/Extension/EventHandler.php
Normal file
119
snippets/dokuwiki-2023-04-04/inc/Extension/EventHandler.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* Controls the registration and execution of all events,
|
||||
*/
|
||||
class EventHandler
|
||||
{
|
||||
|
||||
// public properties: none
|
||||
|
||||
// private properties
|
||||
protected $hooks = array(); // array of events and their registered handlers
|
||||
|
||||
/**
|
||||
* event_handler
|
||||
*
|
||||
* constructor, loads all action plugins and calls their register() method giving them
|
||||
* an opportunity to register any hooks they require
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
// load action plugins
|
||||
/** @var ActionPlugin $plugin */
|
||||
$plugin = null;
|
||||
$pluginlist = plugin_list('action');
|
||||
|
||||
foreach ($pluginlist as $plugin_name) {
|
||||
$plugin = plugin_load('action', $plugin_name);
|
||||
|
||||
if ($plugin !== null) $plugin->register($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* register_hook
|
||||
*
|
||||
* register a hook for an event
|
||||
*
|
||||
* @param string $event name used by the event
|
||||
* @param string $advise BEFORE|AFTER
|
||||
* @param object $obj scope for the method be executed on, NULL for global function or callable
|
||||
* @param string|callable $method event handler function
|
||||
* @param mixed $param data passed to the event handler
|
||||
* @param int $seq sequence number for ordering hook execution (ascending)
|
||||
*/
|
||||
public function register_hook($event, $advise, $obj, $method, $param = null, $seq = 0)
|
||||
{
|
||||
$seq = (int)$seq;
|
||||
$doSort = !isset($this->hooks[$event . '_' . $advise][$seq]);
|
||||
$this->hooks[$event . '_' . $advise][$seq][] = array($obj, $method, $param);
|
||||
|
||||
if ($doSort) {
|
||||
ksort($this->hooks[$event . '_' . $advise]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* process the before/after event
|
||||
*
|
||||
* @param Event $event
|
||||
* @param string $advise BEFORE or AFTER
|
||||
*/
|
||||
public function process_event($event, $advise = '')
|
||||
{
|
||||
|
||||
$evt_name = $event->name . ($advise ? '_' . $advise : '_BEFORE');
|
||||
|
||||
if (!empty($this->hooks[$evt_name])) {
|
||||
foreach ($this->hooks[$evt_name] as $sequenced_hooks) {
|
||||
foreach ($sequenced_hooks as $hook) {
|
||||
list($obj, $method, $param) = $hook;
|
||||
|
||||
if ($obj === null) {
|
||||
$method($event, $param);
|
||||
} else {
|
||||
$obj->$method($event, $param);
|
||||
}
|
||||
|
||||
if (!$event->mayPropagate()) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an event has any registered handlers
|
||||
*
|
||||
* When $advise is empty, both BEFORE and AFTER events will be considered,
|
||||
* otherwise only the given advisory is checked
|
||||
*
|
||||
* @param string $name Name of the event
|
||||
* @param string $advise BEFORE, AFTER or empty
|
||||
* @return bool
|
||||
*/
|
||||
public function hasHandlerForEvent($name, $advise = '')
|
||||
{
|
||||
if ($advise) {
|
||||
return isset($this->hooks[$name . '_' . $advise]);
|
||||
}
|
||||
|
||||
return isset($this->hooks[$name . '_BEFORE']) || isset($this->hooks[$name . '_AFTER']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all hooks and their currently registered handlers
|
||||
*
|
||||
* The handlers are sorted by sequence, then by register time
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEventHandlers()
|
||||
{
|
||||
return $this->hooks;
|
||||
}
|
||||
}
|
13
snippets/dokuwiki-2023-04-04/inc/Extension/Plugin.php
Normal file
13
snippets/dokuwiki-2023-04-04/inc/Extension/Plugin.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* DokuWiki Base Plugin
|
||||
*
|
||||
* Most plugin types inherit from this class
|
||||
*/
|
||||
abstract class Plugin implements PluginInterface
|
||||
{
|
||||
use PluginTrait;
|
||||
}
|
402
snippets/dokuwiki-2023-04-04/inc/Extension/PluginController.php
Normal file
402
snippets/dokuwiki-2023-04-04/inc/Extension/PluginController.php
Normal file
@ -0,0 +1,402 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use dokuwiki\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Class to encapsulate access to dokuwiki plugins
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
*/
|
||||
class PluginController
|
||||
{
|
||||
/** @var array the types of plugins DokuWiki supports */
|
||||
const PLUGIN_TYPES = ['auth', 'admin', 'syntax', 'action', 'renderer', 'helper', 'remote', 'cli'];
|
||||
|
||||
protected $listByType = [];
|
||||
/** @var array all installed plugins and their enabled state [plugin=>enabled] */
|
||||
protected $masterList = [];
|
||||
protected $pluginCascade = ['default' => [], 'local' => [], 'protected' => []];
|
||||
protected $lastLocalConfigFile = '';
|
||||
|
||||
/**
|
||||
* Populates the master list of plugins
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->loadConfig();
|
||||
$this->populateMasterList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of available plugins of given type
|
||||
*
|
||||
* @param $type string, plugin_type name;
|
||||
* the type of plugin to return,
|
||||
* use empty string for all types
|
||||
* @param $all bool;
|
||||
* false to only return enabled plugins,
|
||||
* true to return both enabled and disabled plugins
|
||||
*
|
||||
* @return array of
|
||||
* - plugin names when $type = ''
|
||||
* - or plugin component names when a $type is given
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
public function getList($type = '', $all = false)
|
||||
{
|
||||
|
||||
// request the complete list
|
||||
if (!$type) {
|
||||
return $all ? array_keys($this->masterList) : array_keys(array_filter($this->masterList));
|
||||
}
|
||||
|
||||
if (!isset($this->listByType[$type]['enabled'])) {
|
||||
$this->listByType[$type]['enabled'] = $this->getListByType($type, true);
|
||||
}
|
||||
if ($all && !isset($this->listByType[$type]['disabled'])) {
|
||||
$this->listByType[$type]['disabled'] = $this->getListByType($type, false);
|
||||
}
|
||||
|
||||
return $all
|
||||
? array_merge($this->listByType[$type]['enabled'], $this->listByType[$type]['disabled'])
|
||||
: $this->listByType[$type]['enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given plugin and creates an object of it
|
||||
*
|
||||
* @param $type string type of plugin to load
|
||||
* @param $name string name of the plugin to load
|
||||
* @param $new bool true to return a new instance of the plugin, false to use an already loaded instance
|
||||
* @param $disabled bool true to load even disabled plugins
|
||||
* @return PluginInterface|null the plugin object or null on failure
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
*/
|
||||
public function load($type, $name, $new = false, $disabled = false)
|
||||
{
|
||||
|
||||
//we keep all loaded plugins available in global scope for reuse
|
||||
global $DOKU_PLUGINS;
|
||||
|
||||
list($plugin, /* $component */) = $this->splitName($name);
|
||||
|
||||
// check if disabled
|
||||
if (!$disabled && !$this->isEnabled($plugin)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$class = $type . '_plugin_' . $name;
|
||||
|
||||
try {
|
||||
//plugin already loaded?
|
||||
if (!empty($DOKU_PLUGINS[$type][$name])) {
|
||||
if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
|
||||
|
||||
return class_exists($class, true) ? new $class : null;
|
||||
}
|
||||
|
||||
return $DOKU_PLUGINS[$type][$name];
|
||||
}
|
||||
|
||||
//construct class and instantiate
|
||||
if (!class_exists($class, true)) {
|
||||
# the plugin might be in the wrong directory
|
||||
$inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
|
||||
if ($inf['base'] && $inf['base'] != $plugin) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
|
||||
hsc($plugin),
|
||||
hsc(
|
||||
$inf['base']
|
||||
)
|
||||
), -1
|
||||
);
|
||||
} elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin name '%s' is not a valid plugin name, only the characters a-z ".
|
||||
"and 0-9 are allowed. " .
|
||||
'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
|
||||
), -1
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
$DOKU_PLUGINS[$type][$name] = new $class;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
ErrorHandler::showExceptionMsg($e, sprintf('Failed to load plugin %s', $plugin));
|
||||
return null;
|
||||
}
|
||||
|
||||
return $DOKU_PLUGINS[$type][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether plugin is disabled
|
||||
*
|
||||
* @param string $plugin name of plugin
|
||||
* @return bool true disabled, false enabled
|
||||
* @deprecated in favor of the more sensible isEnabled where the return value matches the enabled state
|
||||
*/
|
||||
public function isDisabled($plugin)
|
||||
{
|
||||
dbg_deprecated('isEnabled()');
|
||||
return !$this->isEnabled($plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether plugin is disabled
|
||||
*
|
||||
* @param string $plugin name of plugin
|
||||
* @return bool true enabled, false disabled
|
||||
*/
|
||||
public function isEnabled($plugin)
|
||||
{
|
||||
return !empty($this->masterList[$plugin]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the plugin
|
||||
*
|
||||
* @param string $plugin name of plugin
|
||||
* @return bool true saving succeed, false saving failed
|
||||
*/
|
||||
public function disable($plugin)
|
||||
{
|
||||
if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
|
||||
$this->masterList[$plugin] = 0;
|
||||
return $this->saveList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the plugin
|
||||
*
|
||||
* @param string $plugin name of plugin
|
||||
* @return bool true saving succeed, false saving failed
|
||||
*/
|
||||
public function enable($plugin)
|
||||
{
|
||||
if (array_key_exists($plugin, $this->pluginCascade['protected'])) return false;
|
||||
$this->masterList[$plugin] = 1;
|
||||
return $this->saveList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cascade of the config files
|
||||
*
|
||||
* @return array with arrays of plugin configs
|
||||
*/
|
||||
public function getCascade()
|
||||
{
|
||||
return $this->pluginCascade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all installed plugins and their current enabled state
|
||||
*/
|
||||
protected function populateMasterList()
|
||||
{
|
||||
if ($dh = @opendir(DOKU_PLUGIN)) {
|
||||
$all_plugins = array();
|
||||
while (false !== ($plugin = readdir($dh))) {
|
||||
if ($plugin[0] === '.') continue; // skip hidden entries
|
||||
if (is_file(DOKU_PLUGIN . $plugin)) continue; // skip files, we're only interested in directories
|
||||
|
||||
if (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 0) {
|
||||
$all_plugins[$plugin] = 0;
|
||||
|
||||
} elseif (array_key_exists($plugin, $this->masterList) && $this->masterList[$plugin] == 1) {
|
||||
$all_plugins[$plugin] = 1;
|
||||
} else {
|
||||
$all_plugins[$plugin] = 1;
|
||||
}
|
||||
}
|
||||
$this->masterList = $all_plugins;
|
||||
if (!file_exists($this->lastLocalConfigFile)) {
|
||||
$this->saveList(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the plugin config $files
|
||||
* and returns the entries of the $plugins array set in these files
|
||||
*
|
||||
* @param array $files list of files to include, latter overrides previous
|
||||
* @return array with entries of the $plugins arrays of the included files
|
||||
*/
|
||||
protected function checkRequire($files)
|
||||
{
|
||||
$plugins = array();
|
||||
foreach ($files as $file) {
|
||||
if (file_exists($file)) {
|
||||
include_once($file);
|
||||
}
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current list of plugins
|
||||
*
|
||||
* @param bool $forceSave ;
|
||||
* false to save only when config changed
|
||||
* true to always save
|
||||
* @return bool true saving succeed, false saving failed
|
||||
*/
|
||||
protected function saveList($forceSave = false)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (empty($this->masterList)) return false;
|
||||
|
||||
// Rebuild list of local settings
|
||||
$local_plugins = $this->rebuildLocal();
|
||||
if ($local_plugins != $this->pluginCascade['local'] || $forceSave) {
|
||||
$file = $this->lastLocalConfigFile;
|
||||
$out = "<?php\n/*\n * Local plugin enable/disable settings\n" .
|
||||
" * Auto-generated through plugin/extension manager\n *\n" .
|
||||
" * NOTE: Plugins will not be added to this file unless there " .
|
||||
"is a need to override a default setting. Plugins are\n" .
|
||||
" * enabled by default.\n */\n";
|
||||
foreach ($local_plugins as $plugin => $value) {
|
||||
$out .= "\$plugins['$plugin'] = $value;\n";
|
||||
}
|
||||
// backup current file (remove any existing backup)
|
||||
if (file_exists($file)) {
|
||||
$backup = $file . '.bak';
|
||||
if (file_exists($backup)) @unlink($backup);
|
||||
if (!@copy($file, $backup)) return false;
|
||||
if ($conf['fperm']) chmod($backup, $conf['fperm']);
|
||||
}
|
||||
//check if can open for writing, else restore
|
||||
return io_saveFile($file, $out);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild the set of local plugins
|
||||
*
|
||||
* @return array array of plugins to be saved in end($config_cascade['plugins']['local'])
|
||||
*/
|
||||
protected function rebuildLocal()
|
||||
{
|
||||
//assign to local variable to avoid overwriting
|
||||
$backup = $this->masterList;
|
||||
//Can't do anything about protected one so rule them out completely
|
||||
$local_default = array_diff_key($backup, $this->pluginCascade['protected']);
|
||||
//Diff between local+default and default
|
||||
//gives us the ones we need to check and save
|
||||
$diffed_ones = array_diff_key($local_default, $this->pluginCascade['default']);
|
||||
//The ones which we are sure of (list of 0s not in default)
|
||||
$sure_plugins = array_filter($diffed_ones, array($this, 'negate'));
|
||||
//the ones in need of diff
|
||||
$conflicts = array_diff_key($local_default, $diffed_ones);
|
||||
//The final list
|
||||
return array_merge($sure_plugins, array_diff_assoc($conflicts, $this->pluginCascade['default']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the list of plugins and cascade
|
||||
*
|
||||
*/
|
||||
protected function loadConfig()
|
||||
{
|
||||
global $config_cascade;
|
||||
foreach (array('default', 'protected') as $type) {
|
||||
if (array_key_exists($type, $config_cascade['plugins'])) {
|
||||
$this->pluginCascade[$type] = $this->checkRequire($config_cascade['plugins'][$type]);
|
||||
}
|
||||
}
|
||||
$local = $config_cascade['plugins']['local'];
|
||||
$this->lastLocalConfigFile = array_pop($local);
|
||||
$this->pluginCascade['local'] = $this->checkRequire(array($this->lastLocalConfigFile));
|
||||
if (is_array($local)) {
|
||||
$this->pluginCascade['default'] = array_merge(
|
||||
$this->pluginCascade['default'],
|
||||
$this->checkRequire($local)
|
||||
);
|
||||
}
|
||||
$this->masterList = array_merge(
|
||||
$this->pluginCascade['default'],
|
||||
$this->pluginCascade['local'],
|
||||
$this->pluginCascade['protected']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of available plugin components of given type
|
||||
*
|
||||
* @param string $type plugin_type name; the type of plugin to return,
|
||||
* @param bool $enabled true to return enabled plugins,
|
||||
* false to return disabled plugins
|
||||
* @return array of plugin components of requested type
|
||||
*/
|
||||
protected function getListByType($type, $enabled)
|
||||
{
|
||||
$master_list = $enabled
|
||||
? array_keys(array_filter($this->masterList))
|
||||
: array_keys(array_filter($this->masterList, array($this, 'negate')));
|
||||
$plugins = array();
|
||||
|
||||
foreach ($master_list as $plugin) {
|
||||
|
||||
if (file_exists(DOKU_PLUGIN . "$plugin/$type.php")) {
|
||||
$plugins[] = $plugin;
|
||||
continue;
|
||||
}
|
||||
|
||||
$typedir = DOKU_PLUGIN . "$plugin/$type/";
|
||||
if (is_dir($typedir)) {
|
||||
if ($dp = opendir($typedir)) {
|
||||
while (false !== ($component = readdir($dp))) {
|
||||
if (strpos($component, '.') === 0 || strtolower(substr($component, -4)) !== '.php') continue;
|
||||
if (is_file($typedir . $component)) {
|
||||
$plugins[] = $plugin . '_' . substr($component, 0, -4);
|
||||
}
|
||||
}
|
||||
closedir($dp);
|
||||
}
|
||||
}
|
||||
|
||||
}//foreach
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split name in a plugin name and a component name
|
||||
*
|
||||
* @param string $name
|
||||
* @return array with
|
||||
* - plugin name
|
||||
* - and component name when available, otherwise empty string
|
||||
*/
|
||||
protected function splitName($name)
|
||||
{
|
||||
if (!isset($this->masterList[$name])) {
|
||||
return sexplode('_', $name, 2, '');
|
||||
}
|
||||
|
||||
return array($name, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns inverse boolean value of the input
|
||||
*
|
||||
* @param mixed $input
|
||||
* @return bool inversed boolean value of input
|
||||
*/
|
||||
protected function negate($input)
|
||||
{
|
||||
return !(bool)$input;
|
||||
}
|
||||
}
|
162
snippets/dokuwiki-2023-04-04/inc/Extension/PluginInterface.php
Normal file
162
snippets/dokuwiki-2023-04-04/inc/Extension/PluginInterface.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* DokuWiki Plugin Interface
|
||||
*
|
||||
* Defines the public contract all DokuWiki plugins will adhere to. The actual code
|
||||
* to do so is defined in dokuwiki\Extension\PluginTrait
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Christopher Smith <chris@jalakai.co.uk>
|
||||
*/
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* General Info
|
||||
*
|
||||
* Needs to return a associative array with the following values:
|
||||
*
|
||||
* base - the plugin's base name (eg. the directory it needs to be installed in)
|
||||
* author - Author of the plugin
|
||||
* email - Email address to contact the author
|
||||
* date - Last modified date of the plugin in YYYY-MM-DD format
|
||||
* name - Name of the plugin
|
||||
* desc - Short description of the plugin (Text only)
|
||||
* url - Website with more information on the plugin (eg. syntax description)
|
||||
*/
|
||||
public function getInfo();
|
||||
|
||||
/**
|
||||
* The type of the plugin inferred from the class name
|
||||
*
|
||||
* @return string plugin type
|
||||
*/
|
||||
public function getPluginType();
|
||||
|
||||
/**
|
||||
* The name of the plugin inferred from the class name
|
||||
*
|
||||
* @return string plugin name
|
||||
*/
|
||||
public function getPluginName();
|
||||
|
||||
/**
|
||||
* The component part of the plugin inferred from the class name
|
||||
*
|
||||
* @return string component name
|
||||
*/
|
||||
public function getPluginComponent();
|
||||
|
||||
/**
|
||||
* Access plugin language strings
|
||||
*
|
||||
* to try to minimise unnecessary loading of the strings when the plugin doesn't require them
|
||||
* e.g. when info plugin is querying plugins for information about themselves.
|
||||
*
|
||||
* @param string $id id of the string to be retrieved
|
||||
* @return string in appropriate language or english if not available
|
||||
*/
|
||||
public function getLang($id);
|
||||
|
||||
/**
|
||||
* retrieve a language dependent file and pass to xhtml renderer for display
|
||||
* plugin equivalent of p_locale_xhtml()
|
||||
*
|
||||
* @param string $id id of language dependent wiki page
|
||||
* @return string parsed contents of the wiki page in xhtml format
|
||||
*/
|
||||
public function locale_xhtml($id);
|
||||
|
||||
/**
|
||||
* Prepends appropriate path for a language dependent filename
|
||||
* plugin equivalent of localFN()
|
||||
*
|
||||
* @param string $id id of localization file
|
||||
* @param string $ext The file extension (usually txt)
|
||||
* @return string wiki text
|
||||
*/
|
||||
public function localFN($id, $ext = 'txt');
|
||||
|
||||
/**
|
||||
* Reads all the plugins language dependent strings into $this->lang
|
||||
* this function is automatically called by getLang()
|
||||
*
|
||||
* @todo this could be made protected and be moved to the trait only
|
||||
*/
|
||||
public function setupLocale();
|
||||
|
||||
/**
|
||||
* use this function to access plugin configuration variables
|
||||
*
|
||||
* @param string $setting the setting to access
|
||||
* @param mixed $notset what to return if the setting is not available
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConf($setting, $notset = false);
|
||||
|
||||
/**
|
||||
* merges the plugin's default settings with any local settings
|
||||
* this function is automatically called through getConf()
|
||||
*
|
||||
* @todo this could be made protected and be moved to the trait only
|
||||
*/
|
||||
public function loadConfig();
|
||||
|
||||
/**
|
||||
* Loads a given helper plugin (if enabled)
|
||||
*
|
||||
* @author Esther Brunner <wikidesign@gmail.com>
|
||||
*
|
||||
* @param string $name name of plugin to load
|
||||
* @param bool $msg if a message should be displayed in case the plugin is not available
|
||||
* @return PluginInterface|null helper plugin object
|
||||
*/
|
||||
public function loadHelper($name, $msg = true);
|
||||
|
||||
/**
|
||||
* email
|
||||
* standardised function to generate an email link according to obfuscation settings
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $name
|
||||
* @param string $class
|
||||
* @param string $more
|
||||
* @return string html
|
||||
*/
|
||||
public function email($email, $name = '', $class = '', $more = '');
|
||||
|
||||
/**
|
||||
* external_link
|
||||
* standardised function to generate an external link according to conf settings
|
||||
*
|
||||
* @param string $link
|
||||
* @param string $title
|
||||
* @param string $class
|
||||
* @param string $target
|
||||
* @param string $more
|
||||
* @return string
|
||||
*/
|
||||
public function external_link($link, $title = '', $class = '', $target = '', $more = '');
|
||||
|
||||
/**
|
||||
* output text string through the parser, allows dokuwiki markup to be used
|
||||
* very ineffecient for small pieces of data - try not to use
|
||||
*
|
||||
* @param string $text wiki markup to parse
|
||||
* @param string $format output format
|
||||
* @return null|string
|
||||
*/
|
||||
public function render_text($text, $format = 'xhtml');
|
||||
|
||||
/**
|
||||
* Allow the plugin to prevent DokuWiki from reusing an instance
|
||||
*
|
||||
* @return bool false if the plugin has to be instantiated
|
||||
*/
|
||||
public function isSingleton();
|
||||
}
|
||||
|
||||
|
||||
|
256
snippets/dokuwiki-2023-04-04/inc/Extension/PluginTrait.php
Normal file
256
snippets/dokuwiki-2023-04-04/inc/Extension/PluginTrait.php
Normal file
@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
/**
|
||||
* Provides standard DokuWiki plugin behaviour
|
||||
*/
|
||||
trait PluginTrait
|
||||
{
|
||||
|
||||
protected $localised = false; // set to true by setupLocale() after loading language dependent strings
|
||||
protected $lang = array(); // array to hold language dependent strings, best accessed via ->getLang()
|
||||
protected $configloaded = false; // set to true by loadConfig() after loading plugin configuration variables
|
||||
protected $conf = array(); // array to hold plugin settings, best accessed via ->getConf()
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getInfo()
|
||||
*/
|
||||
public function getInfo()
|
||||
{
|
||||
$parts = explode('_', get_class($this));
|
||||
$info = DOKU_PLUGIN . '/' . $parts[2] . '/plugin.info.txt';
|
||||
if (file_exists($info)) return confToHash($info);
|
||||
|
||||
msg(
|
||||
'getInfo() not implemented in ' . get_class($this) . ' and ' . $info . ' not found.<br />' .
|
||||
'Verify you\'re running the latest version of the plugin. If the problem persists, send a ' .
|
||||
'bug report to the author of the ' . $parts[2] . ' plugin.', -1
|
||||
);
|
||||
return array(
|
||||
'date' => '0000-00-00',
|
||||
'name' => $parts[2] . ' plugin',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::isSingleton()
|
||||
*/
|
||||
public function isSingleton()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::loadHelper()
|
||||
*/
|
||||
public function loadHelper($name, $msg = true)
|
||||
{
|
||||
$obj = plugin_load('helper', $name);
|
||||
if (is_null($obj) && $msg) msg("Helper plugin $name is not available or invalid.", -1);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
// region introspection methods
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getPluginType()
|
||||
*/
|
||||
public function getPluginType()
|
||||
{
|
||||
list($t) = explode('_', get_class($this), 2);
|
||||
return $t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getPluginName()
|
||||
*/
|
||||
public function getPluginName()
|
||||
{
|
||||
list(/* $t */, /* $p */, $n) = sexplode('_', get_class($this), 4, '');
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getPluginComponent()
|
||||
*/
|
||||
public function getPluginComponent()
|
||||
{
|
||||
list(/* $t */, /* $p */, /* $n */, $c) = sexplode('_', get_class($this), 4, '');
|
||||
return $c;
|
||||
}
|
||||
|
||||
// endregion
|
||||
// region localization methods
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getLang()
|
||||
*/
|
||||
public function getLang($id)
|
||||
{
|
||||
if (!$this->localised) $this->setupLocale();
|
||||
|
||||
return (isset($this->lang[$id]) ? $this->lang[$id] : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::locale_xhtml()
|
||||
*/
|
||||
public function locale_xhtml($id)
|
||||
{
|
||||
return p_cached_output($this->localFN($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::localFN()
|
||||
*/
|
||||
public function localFN($id, $ext = 'txt')
|
||||
{
|
||||
global $conf;
|
||||
$plugin = $this->getPluginName();
|
||||
$file = DOKU_CONF . 'plugin_lang/' . $plugin . '/' . $conf['lang'] . '/' . $id . '.' . $ext;
|
||||
if (!file_exists($file)) {
|
||||
$file = DOKU_PLUGIN . $plugin . '/lang/' . $conf['lang'] . '/' . $id . '.' . $ext;
|
||||
if (!file_exists($file)) {
|
||||
//fall back to english
|
||||
$file = DOKU_PLUGIN . $plugin . '/lang/en/' . $id . '.' . $ext;
|
||||
}
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::setupLocale()
|
||||
*/
|
||||
public function setupLocale()
|
||||
{
|
||||
if ($this->localised) return;
|
||||
|
||||
global $conf, $config_cascade; // definitely don't invoke "global $lang"
|
||||
$path = DOKU_PLUGIN . $this->getPluginName() . '/lang/';
|
||||
|
||||
$lang = array();
|
||||
|
||||
// don't include once, in case several plugin components require the same language file
|
||||
@include($path . 'en/lang.php');
|
||||
foreach ($config_cascade['lang']['plugin'] as $config_file) {
|
||||
if (file_exists($config_file . $this->getPluginName() . '/en/lang.php')) {
|
||||
include($config_file . $this->getPluginName() . '/en/lang.php');
|
||||
}
|
||||
}
|
||||
|
||||
if ($conf['lang'] != 'en') {
|
||||
@include($path . $conf['lang'] . '/lang.php');
|
||||
foreach ($config_cascade['lang']['plugin'] as $config_file) {
|
||||
if (file_exists($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php')) {
|
||||
include($config_file . $this->getPluginName() . '/' . $conf['lang'] . '/lang.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->lang = $lang;
|
||||
$this->localised = true;
|
||||
}
|
||||
|
||||
// endregion
|
||||
// region configuration methods
|
||||
|
||||
/**
|
||||
* @see PluginInterface::getConf()
|
||||
*/
|
||||
public function getConf($setting, $notset = false)
|
||||
{
|
||||
|
||||
if (!$this->configloaded) {
|
||||
$this->loadConfig();
|
||||
}
|
||||
|
||||
if (isset($this->conf[$setting])) {
|
||||
return $this->conf[$setting];
|
||||
} else {
|
||||
return $notset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::loadConfig()
|
||||
*/
|
||||
public function loadConfig()
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$defaults = $this->readDefaultSettings();
|
||||
$plugin = $this->getPluginName();
|
||||
|
||||
foreach ($defaults as $key => $value) {
|
||||
if (isset($conf['plugin'][$plugin][$key])) continue;
|
||||
$conf['plugin'][$plugin][$key] = $value;
|
||||
}
|
||||
|
||||
$this->configloaded = true;
|
||||
$this->conf =& $conf['plugin'][$plugin];
|
||||
}
|
||||
|
||||
/**
|
||||
* read the plugin's default configuration settings from conf/default.php
|
||||
* this function is automatically called through getConf()
|
||||
*
|
||||
* @return array setting => value
|
||||
*/
|
||||
protected function readDefaultSettings()
|
||||
{
|
||||
|
||||
$path = DOKU_PLUGIN . $this->getPluginName() . '/conf/';
|
||||
$conf = array();
|
||||
|
||||
if (file_exists($path . 'default.php')) {
|
||||
include($path . 'default.php');
|
||||
}
|
||||
|
||||
return $conf;
|
||||
}
|
||||
|
||||
// endregion
|
||||
// region output methods
|
||||
|
||||
/**
|
||||
* @see PluginInterface::email()
|
||||
*/
|
||||
public function email($email, $name = '', $class = '', $more = '')
|
||||
{
|
||||
if (!$email) return $name;
|
||||
$email = obfuscate($email);
|
||||
if (!$name) $name = $email;
|
||||
$class = "class='" . ($class ? $class : 'mail') . "'";
|
||||
return "<a href='mailto:$email' $class title='$email' $more>$name</a>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::external_link()
|
||||
*/
|
||||
public function external_link($link, $title = '', $class = '', $target = '', $more = '')
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$link = htmlentities($link);
|
||||
if (!$title) $title = $link;
|
||||
if (!$target) $target = $conf['target']['extern'];
|
||||
if ($conf['relnofollow']) $more .= ' rel="nofollow"';
|
||||
|
||||
if ($class) $class = " class='$class'";
|
||||
if ($target) $target = " target='$target'";
|
||||
if ($more) $more = " " . trim($more);
|
||||
|
||||
return "<a href='$link'$class$target$more>$title</a>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PluginInterface::render_text()
|
||||
*/
|
||||
public function render_text($text, $format = 'xhtml')
|
||||
{
|
||||
return p_render($format, p_get_instructions($text), $info);
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
122
snippets/dokuwiki-2023-04-04/inc/Extension/RemotePlugin.php
Normal file
122
snippets/dokuwiki-2023-04-04/inc/Extension/RemotePlugin.php
Normal file
@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use dokuwiki\Remote\Api;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Remote Plugin prototype
|
||||
*
|
||||
* Add functionality to the remote API in a plugin
|
||||
*/
|
||||
abstract class RemotePlugin extends Plugin
|
||||
{
|
||||
|
||||
private $api;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->api = new Api();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available methods with remote access.
|
||||
*
|
||||
* By default it exports all public methods of a remote plugin. Methods beginning
|
||||
* with an underscore are skipped.
|
||||
*
|
||||
* @return array Information about all provided methods. {@see dokuwiki\Remote\RemoteAPI}.
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function _getMethods()
|
||||
{
|
||||
$result = array();
|
||||
|
||||
$reflection = new \ReflectionClass($this);
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
// skip parent methods, only methods further down are exported
|
||||
$declaredin = $method->getDeclaringClass()->name;
|
||||
if ($declaredin === 'dokuwiki\Extension\Plugin' || $declaredin === 'dokuwiki\Extension\RemotePlugin') {
|
||||
continue;
|
||||
}
|
||||
$method_name = $method->name;
|
||||
if (strpos($method_name, '_') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// strip asterisks
|
||||
$doc = $method->getDocComment();
|
||||
$doc = preg_replace(
|
||||
array('/^[ \t]*\/\*+[ \t]*/m', '/[ \t]*\*+[ \t]*/m', '/\*+\/\s*$/m', '/\s*\/\s*$/m'),
|
||||
array('', '', '', ''),
|
||||
$doc
|
||||
);
|
||||
|
||||
// prepare data
|
||||
$data = array();
|
||||
$data['name'] = $method_name;
|
||||
$data['public'] = 0;
|
||||
$data['doc'] = $doc;
|
||||
$data['args'] = array();
|
||||
|
||||
// get parameter type from doc block type hint
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$name = $parameter->name;
|
||||
$type = 'string'; // we default to string
|
||||
if (preg_match('/^@param[ \t]+([\w|\[\]]+)[ \t]\$' . $name . '/m', $doc, $m)) {
|
||||
$type = $this->cleanTypeHint($m[1]);
|
||||
}
|
||||
$data['args'][] = $type;
|
||||
}
|
||||
|
||||
// get return type from doc block type hint
|
||||
if (preg_match('/^@return[ \t]+([\w|\[\]]+)/m', $doc, $m)) {
|
||||
$data['return'] = $this->cleanTypeHint($m[1]);
|
||||
} else {
|
||||
$data['return'] = 'string';
|
||||
}
|
||||
|
||||
// add to result
|
||||
$result[$method_name] = $data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given type hint against the valid options for the remote API
|
||||
*
|
||||
* @param string $hint
|
||||
* @return string
|
||||
*/
|
||||
protected function cleanTypeHint($hint)
|
||||
{
|
||||
$types = explode('|', $hint);
|
||||
foreach ($types as $t) {
|
||||
if (substr($t, -2) === '[]') {
|
||||
return 'array';
|
||||
}
|
||||
if ($t === 'boolean') {
|
||||
return 'bool';
|
||||
}
|
||||
if (in_array($t, array('array', 'string', 'int', 'double', 'bool', 'null', 'date', 'file'))) {
|
||||
return $t;
|
||||
}
|
||||
}
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Api
|
||||
*/
|
||||
protected function getApi()
|
||||
{
|
||||
return $this->api;
|
||||
}
|
||||
|
||||
}
|
132
snippets/dokuwiki-2023-04-04/inc/Extension/SyntaxPlugin.php
Normal file
132
snippets/dokuwiki-2023-04-04/inc/Extension/SyntaxPlugin.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use Doku_Handler;
|
||||
use Doku_Renderer;
|
||||
|
||||
/**
|
||||
* Syntax Plugin Prototype
|
||||
*
|
||||
* All DokuWiki plugins to extend the parser/rendering mechanism
|
||||
* need to inherit from this class
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
abstract class SyntaxPlugin extends \dokuwiki\Parsing\ParserMode\Plugin
|
||||
{
|
||||
use PluginTrait;
|
||||
|
||||
protected $allowedModesSetup = false;
|
||||
|
||||
/**
|
||||
* Syntax Type
|
||||
*
|
||||
* Needs to return one of the mode types defined in $PARSER_MODES in Parser.php
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getType();
|
||||
|
||||
/**
|
||||
* Allowed Mode Types
|
||||
*
|
||||
* Defines the mode types for other dokuwiki markup that maybe nested within the
|
||||
* plugin's own markup. Needs to return an array of one or more of the mode types
|
||||
* defined in $PARSER_MODES in Parser.php
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedTypes()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Paragraph Type
|
||||
*
|
||||
* Defines how this syntax is handled regarding paragraphs. This is important
|
||||
* for correct XHTML nesting. Should return one of the following:
|
||||
*
|
||||
* 'normal' - The plugin can be used inside paragraphs
|
||||
* 'block' - Open paragraphs need to be closed before plugin output
|
||||
* 'stack' - Special case. Plugin wraps other paragraphs.
|
||||
*
|
||||
* @see Doku_Handler_Block
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPType()
|
||||
{
|
||||
return 'normal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler to prepare matched data for the rendering process
|
||||
*
|
||||
* This function can only pass data to render() via its return value - render()
|
||||
* may be not be run during the object's current life.
|
||||
*
|
||||
* Usually you should only need the $match param.
|
||||
*
|
||||
* @param string $match The text matched by the patterns
|
||||
* @param int $state The lexer state for the match
|
||||
* @param int $pos The character position of the matched text
|
||||
* @param Doku_Handler $handler The Doku_Handler object
|
||||
* @return bool|array Return an array with all data you want to use in render, false don't add an instruction
|
||||
*/
|
||||
abstract public function handle($match, $state, $pos, Doku_Handler $handler);
|
||||
|
||||
/**
|
||||
* Handles the actual output creation.
|
||||
*
|
||||
* The function must not assume any other of the classes methods have been run
|
||||
* during the object's current life. The only reliable data it receives are its
|
||||
* parameters.
|
||||
*
|
||||
* The function should always check for the given output format and return false
|
||||
* when a format isn't supported.
|
||||
*
|
||||
* $renderer contains a reference to the renderer object which is
|
||||
* currently handling the rendering. You need to use it for writing
|
||||
* the output. How this is done depends on the renderer used (specified
|
||||
* by $format
|
||||
*
|
||||
* The contents of the $data array depends on what the handler() function above
|
||||
* created
|
||||
*
|
||||
* @param string $format output format being rendered
|
||||
* @param Doku_Renderer $renderer the current renderer object
|
||||
* @param array $data data created by handler()
|
||||
* @return boolean rendered correctly? (however, returned value is not used at the moment)
|
||||
*/
|
||||
abstract public function render($format, Doku_Renderer $renderer, $data);
|
||||
|
||||
/**
|
||||
* There should be no need to override this function
|
||||
*
|
||||
* @param string $mode
|
||||
* @return bool
|
||||
*/
|
||||
public function accepts($mode)
|
||||
{
|
||||
|
||||
if (!$this->allowedModesSetup) {
|
||||
global $PARSER_MODES;
|
||||
|
||||
$allowedModeTypes = $this->getAllowedTypes();
|
||||
foreach ($allowedModeTypes as $mt) {
|
||||
$this->allowedModes = array_merge($this->allowedModes, $PARSER_MODES[$mt]);
|
||||
}
|
||||
|
||||
$idx = array_search(substr(get_class($this), 7), (array)$this->allowedModes);
|
||||
if ($idx !== false) {
|
||||
unset($this->allowedModes[$idx]);
|
||||
}
|
||||
$this->allowedModesSetup = true;
|
||||
}
|
||||
|
||||
return parent::accepts($mode);
|
||||
}
|
||||
}
|
31
snippets/dokuwiki-2023-04-04/inc/FeedParser.php
Normal file
31
snippets/dokuwiki-2023-04-04/inc/FeedParser.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use dokuwiki\FeedParserFile;
|
||||
use SimplePie\File;
|
||||
|
||||
/**
|
||||
* We override some methods of the original SimplePie class here
|
||||
*/
|
||||
class FeedParser extends \SimplePie\SimplePie {
|
||||
|
||||
/**
|
||||
* Constructor. Set some defaults
|
||||
*/
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
$this->enable_cache(false);
|
||||
$this->registry->register(File::class, FeedParserFile::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward compatibility for older plugins
|
||||
*
|
||||
* phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
* @param string $url
|
||||
*/
|
||||
public function feed_url($url){
|
||||
$this->set_feed_url($url);
|
||||
}
|
||||
}
|
||||
|
||||
|
62
snippets/dokuwiki-2023-04-04/inc/FeedParserFile.php
Normal file
62
snippets/dokuwiki-2023-04-04/inc/FeedParserFile.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\HTTP\DokuHTTPClient;
|
||||
|
||||
/**
|
||||
* Fetch an URL using our own HTTPClient
|
||||
*
|
||||
* Replaces SimplePie's own class
|
||||
*/
|
||||
class FeedParserFile extends \SimplePie\File
|
||||
{
|
||||
protected $http;
|
||||
/** @noinspection PhpMissingParentConstructorInspection */
|
||||
|
||||
/**
|
||||
* Inititializes the HTTPClient
|
||||
*
|
||||
* We ignore all given parameters - they are set in DokuHTTPClient
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct(
|
||||
$url,
|
||||
$timeout = 10,
|
||||
$redirects = 5,
|
||||
$headers = null,
|
||||
$useragent = null,
|
||||
$force_fsockopen = false,
|
||||
$curl_options = array()
|
||||
) {
|
||||
$this->http = new DokuHTTPClient();
|
||||
$this->success = $this->http->sendRequest($url);
|
||||
|
||||
$this->headers = $this->http->resp_headers;
|
||||
$this->body = $this->http->resp_body;
|
||||
$this->error = $this->http->error;
|
||||
|
||||
$this->method = \SimplePie\SimplePie::FILE_SOURCE_REMOTE | \SimplePie\SimplePie::FILE_SOURCE_FSOCKOPEN;
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function headers()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function body()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
166
snippets/dokuwiki-2023-04-04/inc/File/MediaFile.php
Normal file
166
snippets/dokuwiki-2023-04-04/inc/File/MediaFile.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
use JpegMeta;
|
||||
|
||||
class MediaFile
|
||||
{
|
||||
protected $id;
|
||||
protected $path;
|
||||
|
||||
protected $mime;
|
||||
protected $ext;
|
||||
protected $downloadable;
|
||||
|
||||
protected $width;
|
||||
protected $height;
|
||||
protected $meta;
|
||||
|
||||
/**
|
||||
* MediaFile constructor.
|
||||
* @param string $id
|
||||
* @param string|int $rev optional revision
|
||||
*/
|
||||
public function __construct($id, $rev = '')
|
||||
{
|
||||
$this->id = $id; //FIXME should it be cleaned?
|
||||
$this->path = mediaFN($id, $rev);
|
||||
|
||||
list($this->ext, $this->mime, $this->downloadable) = mimetype($this->path, false);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID without namespace, used for display purposes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName()
|
||||
{
|
||||
return noNS($this->id);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getMime()
|
||||
{
|
||||
if (!$this->mime) return 'application/octet-stream';
|
||||
return $this->mime;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getExtension()
|
||||
{
|
||||
return (string)$this->ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the extesion but does some clean up
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIcoClass()
|
||||
{
|
||||
$ext = $this->getExtension();
|
||||
if ($ext === '') $ext = 'file';
|
||||
return preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this file be downloaded instead being displayed inline?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDownloadable()
|
||||
{
|
||||
return $this->downloadable;
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getFileSize()
|
||||
{
|
||||
return filesize($this->path);
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getLastModified()
|
||||
{
|
||||
return filemtime($this->path);
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function isWritable()
|
||||
{
|
||||
return is_writable($this->path);
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function isImage()
|
||||
{
|
||||
return (substr($this->mime, 0, 6) === 'image/');
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes width and height for images when requested
|
||||
*/
|
||||
protected function initSizes()
|
||||
{
|
||||
$this->width = 0;
|
||||
$this->height = 0;
|
||||
if (!$this->isImage()) return;
|
||||
$info = getimagesize($this->path);
|
||||
if ($info === false) return;
|
||||
list($this->width, $this->height) = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width if this is a supported image, 0 otherwise
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
if ($this->width === null) $this->initSizes();
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height if this is a supported image, 0 otherwise
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
if ($this->height === null) $this->initSizes();
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permissions the current user has on the file
|
||||
*
|
||||
* @todo doing this for each file within a namespace is a waste, we need to cache this somehow
|
||||
* @return int
|
||||
*/
|
||||
public function userPermission()
|
||||
{
|
||||
return auth_quickaclcheck(getNS($this->id).':*');
|
||||
}
|
||||
|
||||
/** @return JpegMeta */
|
||||
public function getMeta()
|
||||
{
|
||||
if($this->meta === null) $this->meta = new JpegMeta($this->path);
|
||||
return $this->meta;
|
||||
}
|
||||
}
|
15
snippets/dokuwiki-2023-04-04/inc/File/MediaResolver.php
Normal file
15
snippets/dokuwiki-2023-04-04/inc/File/MediaResolver.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Creates an absolute media ID from a relative one
|
||||
*/
|
||||
class MediaResolver extends Resolver {
|
||||
|
||||
/** @inheritDoc */
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
return cleanID(parent::resolveId($id, $rev, $isDateAt));
|
||||
}
|
||||
}
|
334
snippets/dokuwiki-2023-04-04/inc/File/PageFile.php
Normal file
334
snippets/dokuwiki-2023-04-04/inc/File/PageFile.php
Normal file
@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
use dokuwiki\Cache\CacheInstructions;
|
||||
use dokuwiki\ChangeLog\PageChangeLog;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Input\Input;
|
||||
use dokuwiki\Logger;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PageFile : handles wiki text file and its change management for specific page
|
||||
*/
|
||||
class PageFile
|
||||
{
|
||||
protected $id;
|
||||
|
||||
/* @var PageChangeLog $changelog */
|
||||
public $changelog;
|
||||
|
||||
/* @var array $data initial data when event COMMON_WIKIPAGE_SAVE triggered */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* PageFile constructor.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function __construct($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->changelog = new PageChangeLog($this->id);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getPath($rev = '')
|
||||
{
|
||||
return wikiFN($this->id, $rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw WikiText of the page, considering change type at revision date
|
||||
* similar to function rawWiki($id, $rev = '')
|
||||
*
|
||||
* @param int|false $rev timestamp when a revision of wikitext is desired
|
||||
* @return string
|
||||
*/
|
||||
public function rawWikiText($rev = null)
|
||||
{
|
||||
if ($rev !== null) {
|
||||
$revInfo = $rev ? $this->changelog->getRevisionInfo($rev) : false;
|
||||
return (!$revInfo || $revInfo['type'] == DOKU_CHANGE_TYPE_DELETE)
|
||||
? '' // attic stores complete last page version for a deleted page
|
||||
: io_readWikiPage($this->getPath($rev), $this->id, $rev); // retrieve from attic
|
||||
} else {
|
||||
return io_readWikiPage($this->getPath(), $this->id, '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a wikitext by calling io_writeWikiPage.
|
||||
* Also directs changelog and attic updates.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
*
|
||||
* @param string $text wikitext being saved
|
||||
* @param string $summary summary of text update
|
||||
* @param bool $minor mark this saved version as minor update
|
||||
* @return array|void data of event COMMON_WIKIPAGE_SAVE
|
||||
*/
|
||||
public function saveWikiText($text, $summary, $minor = false)
|
||||
{
|
||||
/* Note to developers:
|
||||
This code is subtle and delicate. Test the behavior of
|
||||
the attic and changelog with dokuwiki and external edits
|
||||
after any changes. External edits change the wiki page
|
||||
directly without using php or dokuwiki.
|
||||
*/
|
||||
global $conf;
|
||||
global $lang;
|
||||
global $REV;
|
||||
/* @var Input $INPUT */
|
||||
global $INPUT;
|
||||
|
||||
// prevent recursive call
|
||||
if (isset($this->data)) return;
|
||||
|
||||
$pagefile = $this->getPath();
|
||||
$currentRevision = @filemtime($pagefile); // int or false
|
||||
$currentContent = $this->rawWikiText();
|
||||
$currentSize = file_exists($pagefile) ? filesize($pagefile) : 0;
|
||||
|
||||
// prepare data for event COMMON_WIKIPAGE_SAVE
|
||||
$data = array(
|
||||
'id' => $this->id, // should not be altered by any handlers
|
||||
'file' => $pagefile, // same above
|
||||
'changeType' => null, // set prior to event, and confirm later
|
||||
'revertFrom' => $REV,
|
||||
'oldRevision' => $currentRevision,
|
||||
'oldContent' => $currentContent,
|
||||
'newRevision' => 0, // only available in the after hook
|
||||
'newContent' => $text,
|
||||
'summary' => $summary,
|
||||
'contentChanged' => ($text != $currentContent), // confirm later
|
||||
'changeInfo' => '', // automatically determined by revertFrom
|
||||
'sizechange' => strlen($text) - strlen($currentContent), // TBD
|
||||
);
|
||||
|
||||
// determine tentatively change type and relevant elements of event data
|
||||
if ($data['revertFrom']) {
|
||||
// new text may differ from exact revert revision
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_REVERT;
|
||||
$data['changeInfo'] = $REV;
|
||||
} elseif (trim($data['newContent']) == '') {
|
||||
// empty or whitespace only content deletes
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_DELETE;
|
||||
} elseif (!file_exists($pagefile)) {
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_CREATE;
|
||||
} else {
|
||||
// minor edits allowable only for logged in users
|
||||
$is_minor_change = ($minor && $conf['useacl'] && $INPUT->server->str('REMOTE_USER'));
|
||||
$data['changeType'] = $is_minor_change
|
||||
? DOKU_CHANGE_TYPE_MINOR_EDIT
|
||||
: DOKU_CHANGE_TYPE_EDIT;
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$data['page'] = $this; // allow event handlers to use this class methods
|
||||
|
||||
$event = new Event('COMMON_WIKIPAGE_SAVE', $data);
|
||||
if (!$event->advise_before()) return;
|
||||
|
||||
// if the content has not been changed, no save happens (plugins may override this)
|
||||
if (!$data['contentChanged']) return;
|
||||
|
||||
// Check whether the pagefile has modified during $event->advise_before()
|
||||
clearstatcache();
|
||||
$fileRev = @filemtime($pagefile);
|
||||
if ($fileRev === $currentRevision) {
|
||||
// pagefile has not touched by plugin's event handler
|
||||
// add a potential external edit entry to changelog and store it into attic
|
||||
$this->detectExternalEdit();
|
||||
$filesize_old = $currentSize;
|
||||
} else {
|
||||
// pagefile has modified by plugin's event handler, confirm sizechange
|
||||
$filesize_old = (
|
||||
$data['changeType'] == DOKU_CHANGE_TYPE_CREATE || (
|
||||
$data['changeType'] == DOKU_CHANGE_TYPE_REVERT && !file_exists($pagefile))
|
||||
) ? 0 : filesize($pagefile);
|
||||
}
|
||||
|
||||
// make change to the current file
|
||||
if ($data['changeType'] == DOKU_CHANGE_TYPE_DELETE) {
|
||||
// nothing to do when the file has already deleted
|
||||
if (!file_exists($pagefile)) return;
|
||||
// autoset summary on deletion
|
||||
if (blank($data['summary'])) {
|
||||
$data['summary'] = $lang['deleted'];
|
||||
}
|
||||
// send "update" event with empty data, so plugins can react to page deletion
|
||||
$ioData = array([$pagefile, '', false], getNS($this->id), noNS($this->id), false);
|
||||
Event::createAndTrigger('IO_WIKIPAGE_WRITE', $ioData);
|
||||
// pre-save deleted revision
|
||||
@touch($pagefile);
|
||||
clearstatcache();
|
||||
$data['newRevision'] = $this->saveOldRevision();
|
||||
// remove empty file
|
||||
@unlink($pagefile);
|
||||
$filesize_new = 0;
|
||||
// don't remove old meta info as it should be saved, plugins can use
|
||||
// IO_WIKIPAGE_WRITE for removing their metadata...
|
||||
// purge non-persistant meta data
|
||||
p_purge_metadata($this->id);
|
||||
// remove empty namespaces
|
||||
io_sweepNS($this->id, 'datadir');
|
||||
io_sweepNS($this->id, 'mediadir');
|
||||
} else {
|
||||
// save file (namespace dir is created in io_writeWikiPage)
|
||||
io_writeWikiPage($pagefile, $data['newContent'], $this->id);
|
||||
// pre-save the revision, to keep the attic in sync
|
||||
$data['newRevision'] = $this->saveOldRevision();
|
||||
$filesize_new = filesize($pagefile);
|
||||
}
|
||||
$data['sizechange'] = $filesize_new - $filesize_old;
|
||||
|
||||
$event->advise_after();
|
||||
|
||||
unset($data['page']);
|
||||
|
||||
// adds an entry to the changelog and saves the metadata for the page
|
||||
$logEntry = $this->changelog->addLogEntry([
|
||||
'date' => $data['newRevision'],
|
||||
'ip' => clientIP(true),
|
||||
'type' => $data['changeType'],
|
||||
'id' => $this->id,
|
||||
'user' => $INPUT->server->str('REMOTE_USER'),
|
||||
'sum' => $data['summary'],
|
||||
'extra' => $data['changeInfo'],
|
||||
'sizechange' => $data['sizechange'],
|
||||
]);
|
||||
// update metadata
|
||||
$this->updateMetadata($logEntry);
|
||||
|
||||
// update the purgefile (timestamp of the last time anything within the wiki was changed)
|
||||
io_saveFile($conf['cachedir'].'/purgefile', time());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page version is newer than the last entry in the page's changelog.
|
||||
* If so, we assume it has been an external edit and we create an attic copy and add a proper
|
||||
* changelog line.
|
||||
*
|
||||
* This check is only executed when the page is about to be saved again from the wiki,
|
||||
* triggered in @see saveWikiText()
|
||||
*/
|
||||
public function detectExternalEdit()
|
||||
{
|
||||
$revInfo = $this->changelog->getCurrentRevisionInfo();
|
||||
|
||||
// only interested in external revision
|
||||
if (empty($revInfo) || !array_key_exists('timestamp', $revInfo)) return;
|
||||
|
||||
if ($revInfo['type'] != DOKU_CHANGE_TYPE_DELETE && !$revInfo['timestamp']) {
|
||||
// file is older than last revision, that is erroneous/incorrect occurence.
|
||||
// try to change file modification time
|
||||
$fileLastMod = $this->getPath();
|
||||
$wrong_timestamp = filemtime($fileLastMod);
|
||||
if (touch($fileLastMod, $revInfo['date'])) {
|
||||
clearstatcache();
|
||||
$msg = "PageFile($this->id)::detectExternalEdit(): timestamp successfully modified";
|
||||
$details = '('.$wrong_timestamp.' -> '.$revInfo['date'].')';
|
||||
Logger::error($msg, $details, $fileLastMod);
|
||||
} else {
|
||||
// runtime error
|
||||
$msg = "PageFile($this->id)::detectExternalEdit(): page file should be newer than last revision "
|
||||
.'('.filemtime($fileLastMod).' < '. $this->changelog->lastRevision() .')';
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
}
|
||||
|
||||
// keep at least 1 sec before new page save
|
||||
if ($revInfo['date'] == time()) sleep(1); // wait a tick
|
||||
|
||||
// store externally edited file to the attic folder
|
||||
$this->saveOldRevision();
|
||||
// add a changelog entry for externally edited file
|
||||
$this->changelog->addLogEntry($revInfo);
|
||||
// remove soon to be stale instructions
|
||||
$cache = new CacheInstructions($this->id, $this->getPath());
|
||||
$cache->removeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current version to the attic and returns its revision date
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return int|string revision timestamp
|
||||
*/
|
||||
public function saveOldRevision()
|
||||
{
|
||||
$oldfile = $this->getPath();
|
||||
if (!file_exists($oldfile)) return '';
|
||||
$date = filemtime($oldfile);
|
||||
$newfile = $this->getPath($date);
|
||||
io_writeWikiPage($newfile, $this->rawWikiText(), $this->id, $date);
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update metadata of changed page
|
||||
*
|
||||
* @param array $logEntry changelog entry
|
||||
*/
|
||||
public function updateMetadata(array $logEntry)
|
||||
{
|
||||
global $INFO;
|
||||
|
||||
list(
|
||||
'date' => $date,
|
||||
'type' => $changeType,
|
||||
'user' => $user,
|
||||
) = $logEntry;
|
||||
|
||||
$wasRemoved = ($changeType === DOKU_CHANGE_TYPE_DELETE);
|
||||
$wasCreated = ($changeType === DOKU_CHANGE_TYPE_CREATE);
|
||||
$wasReverted = ($changeType === DOKU_CHANGE_TYPE_REVERT);
|
||||
$wasMinorEdit = ($changeType === DOKU_CHANGE_TYPE_MINOR_EDIT);
|
||||
|
||||
$createdDate = @filectime($this->getPath());
|
||||
|
||||
if ($wasRemoved) return;
|
||||
|
||||
$oldmeta = p_read_metadata($this->id)['persistent'];
|
||||
$meta = array();
|
||||
|
||||
if ($wasCreated &&
|
||||
(empty($oldmeta['date']['created']) || $oldmeta['date']['created'] === $createdDate)
|
||||
) {
|
||||
// newly created
|
||||
$meta['date']['created'] = $createdDate;
|
||||
if ($user) {
|
||||
$meta['creator'] = $INFO['userinfo']['name'] ?? null;
|
||||
$meta['user'] = $user;
|
||||
}
|
||||
} elseif (($wasCreated || $wasReverted) && !empty($oldmeta['date']['created'])) {
|
||||
// re-created / restored
|
||||
$meta['date']['created'] = $oldmeta['date']['created'];
|
||||
$meta['date']['modified'] = $createdDate; // use the files ctime here
|
||||
$meta['creator'] = $oldmeta['creator'] ?? null;
|
||||
if ($user) {
|
||||
$meta['contributor'][$user] = $INFO['userinfo']['name'] ?? null;
|
||||
}
|
||||
} elseif (!$wasMinorEdit) { // non-minor modification
|
||||
$meta['date']['modified'] = $date;
|
||||
if ($user) {
|
||||
$meta['contributor'][$user] = $INFO['userinfo']['name'] ?? null;
|
||||
}
|
||||
}
|
||||
$meta['last_change'] = $logEntry;
|
||||
p_set_metadata($this->id, $meta);
|
||||
}
|
||||
|
||||
}
|
99
snippets/dokuwiki-2023-04-04/inc/File/PageResolver.php
Normal file
99
snippets/dokuwiki-2023-04-04/inc/File/PageResolver.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Creates an absolute page ID from a relative one
|
||||
*/
|
||||
class PageResolver extends Resolver
|
||||
{
|
||||
|
||||
/**
|
||||
* Resolves a given ID to be absolute
|
||||
*
|
||||
* This handles all kinds of relative shortcuts, startpages and autoplurals
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
global $conf;
|
||||
$id = (string) $id;
|
||||
|
||||
// pages may have a hash attached, we separate it on resolving
|
||||
if (strpos($id, '#') !== false) {
|
||||
list($id, $hash) = sexplode('#', $id, 2);
|
||||
$hash = cleanID($hash);
|
||||
} else {
|
||||
$hash = '';
|
||||
}
|
||||
|
||||
if ($id !== '') {
|
||||
$id = parent::resolveId($id, $rev, $isDateAt);
|
||||
$id = $this->resolveStartPage($id, $rev, $isDateAt);
|
||||
if ($conf['autoplural']) {
|
||||
$id = $this->resolveAutoPlural($id, $rev, $isDateAt);
|
||||
}
|
||||
} else {
|
||||
$id = $this->contextID;
|
||||
}
|
||||
|
||||
$id = cleanID($id); // FIXME always? or support parameter
|
||||
// readd hash if any
|
||||
if ($hash !== '') $id .= "#$hash";
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs ending in :
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|int|false $rev
|
||||
* @param bool $isDateAt
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStartPage($id, $rev, $isDateAt)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if ($id[-1] !== ':') return $id;
|
||||
|
||||
if (page_exists($id . $conf['start'], $rev, true, $isDateAt)) {
|
||||
// start page inside namespace
|
||||
return $id . $conf['start'];
|
||||
} elseif (page_exists($id . noNS(cleanID($id)), $rev, true, $isDateAt)) {
|
||||
// page named like the NS inside the NS
|
||||
return $id . noNS(cleanID($id));
|
||||
} elseif (page_exists(substr($id, 0, -1), $rev, true, $isDateAt)) {
|
||||
// page named like the NS outside the NS
|
||||
return substr($id, 0, -1);
|
||||
}
|
||||
|
||||
// fall back to default start page
|
||||
return $id . $conf['start'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try alternative plural/singular form
|
||||
*
|
||||
* @param string $id
|
||||
* @param int $rev
|
||||
* @param bool $isDateAt
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveAutoPlural($id, $rev, $isDateAt)
|
||||
{
|
||||
if (page_exists($id, $rev, $isDateAt)) return $id;
|
||||
|
||||
if ($id[-1] === 's') {
|
||||
$try = substr($id, 0, -1);
|
||||
} else {
|
||||
$try = $id . 's';
|
||||
}
|
||||
|
||||
if (page_exists($try, $rev, true, $isDateAt)) {
|
||||
return $try;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
106
snippets/dokuwiki-2023-04-04/inc/File/Resolver.php
Normal file
106
snippets/dokuwiki-2023-04-04/inc/File/Resolver.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Resolving relative IDs to absolute ones
|
||||
*/
|
||||
abstract class Resolver
|
||||
{
|
||||
|
||||
/** @var string context page ID */
|
||||
protected $contextID;
|
||||
/** @var string namespace of context page ID */
|
||||
protected $contextNS;
|
||||
|
||||
/**
|
||||
* @param string $contextID the current pageID that's the context to resolve relative IDs to
|
||||
*/
|
||||
public function __construct($contextID)
|
||||
{
|
||||
$this->contextID = $contextID;
|
||||
$this->contextNS = (string)getNS($contextID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a given ID to be absolute
|
||||
*
|
||||
* @param string $id The ID to resolve
|
||||
* @param string|int|false $rev The revision time to use when resolving
|
||||
* @param bool $isDateAt Is the given revision only a datetime hint not an exact revision?
|
||||
* @return string
|
||||
*/
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// some pre cleaning for useslash:
|
||||
if ($conf['useslash']) $id = str_replace('/', ':', $id);
|
||||
// on some systems, semicolons might be used instead of colons:
|
||||
$id = str_replace(';', ':', $id);
|
||||
|
||||
$id = $this->resolvePrefix($id);
|
||||
return $this->resolveRelatives($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle IDs starting with . or ~ and prepend the proper prefix
|
||||
*
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
protected function resolvePrefix($id)
|
||||
{
|
||||
if($id === '') return $id;
|
||||
|
||||
// relative to current page (makes the current page a start page)
|
||||
if ($id[0] === '~') {
|
||||
$id = $this->contextID . ':' . substr($id, 1);
|
||||
}
|
||||
|
||||
// relative to current namespace
|
||||
if ($id[0] === '.') {
|
||||
// normalize initial dots without a colon
|
||||
$id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id);
|
||||
$id = $this->contextNS . ':' . $id;
|
||||
}
|
||||
|
||||
// auto-relative, because there is a context namespace but no namespace in the ID
|
||||
if ($this->contextID !== '' && strpos($id, ':') === false) {
|
||||
$id = $this->contextNS . ':' . $id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle . and .. within IDs
|
||||
*
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveRelatives($id)
|
||||
{
|
||||
if ($id === '') return '';
|
||||
$trail = ($id[-1] === ':') ? ':' : ''; // keep trailing colon
|
||||
|
||||
$result = [];
|
||||
$parts = explode(':', $id);
|
||||
|
||||
foreach ($parts as $dir) {
|
||||
if ($dir === '.') continue;
|
||||
if ($dir === '') continue;
|
||||
if ($dir === '..') {
|
||||
array_pop($result);
|
||||
continue;
|
||||
}
|
||||
array_push($result, $dir);
|
||||
}
|
||||
|
||||
$id = implode(':', $result);
|
||||
$id .= $trail;
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
37
snippets/dokuwiki-2023-04-04/inc/Form/ButtonElement.php
Normal file
37
snippets/dokuwiki-2023-04-04/inc/Form/ButtonElement.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class ButtonElement
|
||||
*
|
||||
* Represents a simple button
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class ButtonElement extends Element
|
||||
{
|
||||
/** @var string HTML content */
|
||||
protected $content = '';
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $content HTML content of the button. You have to escape it yourself.
|
||||
*/
|
||||
public function __construct($name, $content = '')
|
||||
{
|
||||
parent::__construct('button', array('name' => $name, 'value' => 1));
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '<button ' . buildAttributes($this->attrs(), true) . '>'.$this->content.'</button>';
|
||||
}
|
||||
|
||||
}
|
83
snippets/dokuwiki-2023-04-04/inc/Form/CheckableElement.php
Normal file
83
snippets/dokuwiki-2023-04-04/inc/Form/CheckableElement.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class CheckableElement
|
||||
*
|
||||
* For Radio- and Checkboxes
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class CheckableElement extends InputElement
|
||||
{
|
||||
/**
|
||||
* @param string $type The type of this element
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element
|
||||
*/
|
||||
public function __construct($type, $name, $label)
|
||||
{
|
||||
parent::__construct($type, $name, $label);
|
||||
// default value is 1
|
||||
$this->attr('value', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the useInput flag and sets the checked attribute accordingly
|
||||
*/
|
||||
protected function prefillInput()
|
||||
{
|
||||
global $INPUT;
|
||||
list($name, $key) = $this->getInputName();
|
||||
$myvalue = $this->val();
|
||||
|
||||
if (!$INPUT->has($name)) return;
|
||||
|
||||
if ($key === null) {
|
||||
// no key - single value
|
||||
$value = $INPUT->str($name);
|
||||
if ($value == $myvalue) {
|
||||
$this->attr('checked', 'checked');
|
||||
} else {
|
||||
$this->rmattr('checked');
|
||||
}
|
||||
} else {
|
||||
// we have an array, there might be several values in it
|
||||
$input = $INPUT->arr($name);
|
||||
if (isset($input[$key])) {
|
||||
$this->rmattr('checked');
|
||||
|
||||
// values seem to be in another sub array
|
||||
if (is_array($input[$key])) {
|
||||
$input = $input[$key];
|
||||
}
|
||||
|
||||
foreach ($input as $value) {
|
||||
if ($value == $myvalue) {
|
||||
$this->attr('checked', 'checked');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element wrapped in a label
|
||||
* Note: allow HTML tags in label text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->label) {
|
||||
return '<label '. buildAttributes($this->label->attrs()) .'>'.DOKU_LF
|
||||
. $this->mainElementHTML() .DOKU_LF
|
||||
.'<span>'. $this->label->val() .'</span>'.DOKU_LF
|
||||
.'</label>';
|
||||
} else {
|
||||
return $this->mainElementHTML();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
202
snippets/dokuwiki-2023-04-04/inc/Form/DropdownElement.php
Normal file
202
snippets/dokuwiki-2023-04-04/inc/Form/DropdownElement.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class DropdownElement
|
||||
*
|
||||
* Represents a HTML select. Please not that prefilling with input data only works for single values.
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class DropdownElement extends InputElement
|
||||
{
|
||||
/** @var array OptGroup[] */
|
||||
protected $optGroups = [];
|
||||
|
||||
/** @var string[] the currently set values */
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
* @param string $name The name of this form element
|
||||
* @param array $options The available options
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
*/
|
||||
public function __construct($name, $options, $label = '')
|
||||
{
|
||||
parent::__construct('dropdown', $name, $label);
|
||||
$this->rmattr('type');
|
||||
$this->optGroups[''] = new OptGroup(null, $options);
|
||||
$this->val('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an `<optgroup>` and respective options
|
||||
*
|
||||
* @param string $label
|
||||
* @param array $options
|
||||
* @return OptGroup a reference to the added optgroup
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addOptGroup($label, $options)
|
||||
{
|
||||
if (empty($label)) {
|
||||
throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
|
||||
}
|
||||
$this->optGroups[$label] = new OptGroup($label, $options);
|
||||
return end($this->optGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get the optgroups of an Dropdown-Element.
|
||||
*
|
||||
* optgroups have to be given as associative array
|
||||
* * the key being the label of the group
|
||||
* * the value being an array of options as defined in @param null|array $optGroups
|
||||
* @return OptGroup[]|DropdownElement
|
||||
* @see OptGroup::options()
|
||||
*
|
||||
*/
|
||||
public function optGroups($optGroups = null)
|
||||
{
|
||||
if ($optGroups === null) {
|
||||
return $this->optGroups;
|
||||
}
|
||||
if (!is_array($optGroups)) {
|
||||
throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!'));
|
||||
}
|
||||
$this->optGroups = array();
|
||||
foreach ($optGroups as $label => $options) {
|
||||
$this->addOptGroup($label, $options);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the options of the Dropdown
|
||||
*
|
||||
* Options can be given as associative array (value => label) or as an
|
||||
* indexd array (label = value) or as an array of arrays. In the latter
|
||||
* case an element has to look as follows:
|
||||
* option-value => array (
|
||||
* 'label' => option-label,
|
||||
* 'attrs' => array (
|
||||
* attr-key => attr-value, ...
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @param null|array $options
|
||||
* @return $this|array
|
||||
*/
|
||||
public function options($options = null)
|
||||
{
|
||||
if ($options === null) {
|
||||
return $this->optGroups['']->options();
|
||||
}
|
||||
$this->optGroups[''] = new OptGroup(null, $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the current value
|
||||
*
|
||||
* When setting a value that is not defined in the options, the value is ignored
|
||||
* and the first option's value is selected instead
|
||||
*
|
||||
* @param null|string|string[] $value The value to set
|
||||
* @return $this|string|string[]
|
||||
*/
|
||||
public function val($value = null)
|
||||
{
|
||||
// getter
|
||||
if ($value === null) {
|
||||
if (isset($this->attributes['multiple'])) {
|
||||
return $this->values;
|
||||
} else {
|
||||
return $this->values[0];
|
||||
}
|
||||
}
|
||||
|
||||
// setter
|
||||
$this->values = $this->setValuesInOptGroups((array) $value);
|
||||
if(!$this->values) {
|
||||
// unknown value set, select first option instead
|
||||
$this->values = $this->setValuesInOptGroups((array) $this->getFirstOptionKey());
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first option's key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFirstOptionKey()
|
||||
{
|
||||
$options = $this->options();
|
||||
if (!empty($options)) {
|
||||
$keys = array_keys($options);
|
||||
return (string)array_shift($keys);
|
||||
}
|
||||
foreach ($this->optGroups as $optGroup) {
|
||||
$options = $optGroup->options();
|
||||
if (!empty($options)) {
|
||||
$keys = array_keys($options);
|
||||
return (string)array_shift($keys);
|
||||
}
|
||||
}
|
||||
|
||||
return ''; // should not happen
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value in the OptGroups, including the optgroup for the options without optgroup.
|
||||
*
|
||||
* @param string[] $values The values to be set
|
||||
* @return string[] The values actually set
|
||||
*/
|
||||
protected function setValuesInOptGroups($values)
|
||||
{
|
||||
$valueset = [];
|
||||
|
||||
/** @var OptGroup $optGroup */
|
||||
foreach ($this->optGroups as $optGroup) {
|
||||
$found = $optGroup->storeValues($values);
|
||||
$values = array_diff($values, $found);
|
||||
$valueset = array_merge($valueset, $found);
|
||||
}
|
||||
|
||||
return $valueset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the HTML for the select it self
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
$attr = $this->attrs();
|
||||
if (isset($attr['multiple'])) {
|
||||
// use array notation when multiple values are allowed
|
||||
$attr['name'] .= '[]';
|
||||
} elseif ($this->useInput) {
|
||||
// prefilling is only supported for non-multi fields
|
||||
$this->prefillInput();
|
||||
}
|
||||
|
||||
$html = '<select ' . buildAttributes($attr) . '>';
|
||||
$html = array_reduce(
|
||||
$this->optGroups,
|
||||
function ($html, OptGroup $optGroup) {
|
||||
return $html . $optGroup->toHTML();
|
||||
},
|
||||
$html
|
||||
);
|
||||
$html .= '</select>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
}
|
160
snippets/dokuwiki-2023-04-04/inc/Form/Element.php
Normal file
160
snippets/dokuwiki-2023-04-04/inc/Form/Element.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class Element
|
||||
*
|
||||
* The basic building block of a form
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
abstract class Element
|
||||
{
|
||||
/**
|
||||
* @var array the attributes of this element
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* @var string The type of this element
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @param string $type The type of this element
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($type, $attributes = array())
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or sets an attribute
|
||||
*
|
||||
* When no $value is given, the current content of the attribute is returned.
|
||||
* An empty string is returned for unset attributes.
|
||||
*
|
||||
* When a $value is given, the content is set to that value and the Element
|
||||
* itself is returned for easy chaining
|
||||
*
|
||||
* @param string $name Name of the attribute to access
|
||||
* @param null|string $value New value to set
|
||||
* @return string|$this
|
||||
*/
|
||||
public function attr($name, $value = null)
|
||||
{
|
||||
// set
|
||||
if ($value !== null) {
|
||||
$this->attributes[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// get
|
||||
if (isset($this->attributes[$name])) {
|
||||
return $this->attributes[$name];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given attribute if it exists
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function rmattr($name)
|
||||
{
|
||||
if (isset($this->attributes[$name])) {
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or adds a all given attributes at once
|
||||
*
|
||||
* @param array|null $attributes
|
||||
* @return array|$this
|
||||
*/
|
||||
public function attrs($attributes = null)
|
||||
{
|
||||
// set
|
||||
if ($attributes) {
|
||||
foreach ((array) $attributes as $key => $val) {
|
||||
$this->attr($key, $val);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
// get
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class to the class attribute
|
||||
*
|
||||
* This is the preferred method of setting the element's class
|
||||
*
|
||||
* @param string $class the new class to add
|
||||
* @return $this
|
||||
*/
|
||||
public function addClass($class)
|
||||
{
|
||||
$classes = explode(' ', $this->attr('class'));
|
||||
$classes[] = $class;
|
||||
$classes = array_unique($classes);
|
||||
$classes = array_filter($classes);
|
||||
$this->attr('class', join(' ', $classes));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the element's ID
|
||||
*
|
||||
* This is the preferred way of setting the element's ID
|
||||
*
|
||||
* @param null|string $id
|
||||
* @return string|$this
|
||||
*/
|
||||
public function id($id = null)
|
||||
{
|
||||
if (strpos($id, '__') === false) {
|
||||
throw new \InvalidArgumentException('IDs in DokuWiki have to contain two subsequent underscores');
|
||||
}
|
||||
|
||||
return $this->attr('id', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the element's value
|
||||
*
|
||||
* This is the preferred way of setting the element's value
|
||||
*
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null)
|
||||
{
|
||||
return $this->attr('value', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function toHTML();
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class FieldsetCloseElement
|
||||
*
|
||||
* Closes an open Fieldset
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class FieldsetCloseElement extends TagCloseElement
|
||||
{
|
||||
/**
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($attributes = array())
|
||||
{
|
||||
parent::__construct('', $attributes);
|
||||
$this->type = 'fieldsetclose';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '</fieldset>';
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class FieldsetOpenElement
|
||||
*
|
||||
* Opens a Fieldset with an optional legend
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class FieldsetOpenElement extends TagOpenElement
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $legend
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($legend='', $attributes = array())
|
||||
{
|
||||
// this is a bit messy and we just do it for the nicer class hierarchy
|
||||
// the parent would expect the tag in $value but we're storing the
|
||||
// legend there, so we have to set the type manually
|
||||
parent::__construct($legend, $attributes);
|
||||
$this->type = 'fieldsetopen';
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
$html = '<fieldset '.buildAttributes($this->attrs()).'>';
|
||||
$legend = $this->val();
|
||||
if ($legend) $html .= DOKU_LF.'<legend>'.hsc($legend).'</legend>';
|
||||
return $html;
|
||||
}
|
||||
}
|
499
snippets/dokuwiki-2023-04-04/inc/Form/Form.php
Normal file
499
snippets/dokuwiki-2023-04-04/inc/Form/Form.php
Normal file
@ -0,0 +1,499 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Form
|
||||
*
|
||||
* Represents the whole Form. This is what you work on, and add Elements to
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class Form extends Element
|
||||
{
|
||||
/**
|
||||
* @var array name value pairs for hidden values
|
||||
*/
|
||||
protected $hidden = array();
|
||||
|
||||
/**
|
||||
* @var Element[] the elements of the form
|
||||
*/
|
||||
protected $elements = array();
|
||||
|
||||
/**
|
||||
* Creates a new, empty form with some default attributes
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param bool $unsafe if true, then the security token is ommited
|
||||
*/
|
||||
public function __construct($attributes = array(), $unsafe = false)
|
||||
{
|
||||
global $ID;
|
||||
|
||||
parent::__construct('form', $attributes);
|
||||
|
||||
// use the current URL as default action
|
||||
if (!$this->attr('action')) {
|
||||
$get = $_GET;
|
||||
if (isset($get['id'])) unset($get['id']);
|
||||
$self = wl($ID, $get, false, '&'); //attributes are escaped later
|
||||
$this->attr('action', $self);
|
||||
}
|
||||
|
||||
// post is default
|
||||
if (!$this->attr('method')) {
|
||||
$this->attr('method', 'post');
|
||||
}
|
||||
|
||||
// we like UTF-8
|
||||
if (!$this->attr('accept-charset')) {
|
||||
$this->attr('accept-charset', 'utf-8');
|
||||
}
|
||||
|
||||
// add the security token by default
|
||||
if (!$unsafe) {
|
||||
$this->setHiddenField('sectok', getSecurityToken());
|
||||
}
|
||||
|
||||
// identify this as a new form based form in HTML
|
||||
$this->addClass('doku_form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a hidden field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setHiddenField($name, $value)
|
||||
{
|
||||
$this->hidden[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
#region element query function
|
||||
|
||||
/**
|
||||
* Returns the numbers of elements in the form
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function elementCount()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of the element in the form or false if it is not in the form
|
||||
*
|
||||
* Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
|
||||
* to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
|
||||
* return value of this function.
|
||||
*
|
||||
* @param Element $element
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
public function getElementPosition(Element $element)
|
||||
{
|
||||
return array_search($element, $this->elements, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the element at a position.
|
||||
* A position out-of-bounds will return either the
|
||||
* first (underflow) or last (overflow) element.
|
||||
*
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function getElementAt($pos)
|
||||
{
|
||||
if ($pos < 0) $pos = count($this->elements) + $pos;
|
||||
if ($pos < 0) $pos = 0;
|
||||
if ($pos >= count($this->elements)) $pos = count($this->elements) - 1;
|
||||
return $this->elements[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the first of a type of element
|
||||
*
|
||||
* @param string $type Element type to look for.
|
||||
* @param int $offset search from this position onward
|
||||
* @return false|int position of element if found, otherwise false
|
||||
*/
|
||||
public function findPositionByType($type, $offset = 0)
|
||||
{
|
||||
$len = $this->elementCount();
|
||||
for ($pos = $offset; $pos < $len; $pos++) {
|
||||
if ($this->elements[$pos]->getType() == $type) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the first element matching the attribute
|
||||
*
|
||||
* @param string $name Name of the attribute
|
||||
* @param string $value Value the attribute should have
|
||||
* @param int $offset search from this position onward
|
||||
* @return false|int position of element if found, otherwise false
|
||||
*/
|
||||
public function findPositionByAttribute($name, $value, $offset = 0)
|
||||
{
|
||||
$len = $this->elementCount();
|
||||
for ($pos = $offset; $pos < $len; $pos++) {
|
||||
if ($this->elements[$pos]->attr($name) == $value) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Element positioning functions
|
||||
|
||||
/**
|
||||
* Adds or inserts an element to the form
|
||||
*
|
||||
* @param Element $element
|
||||
* @param int $pos 0-based position in the form, -1 for at the end
|
||||
* @return Element
|
||||
*/
|
||||
public function addElement(Element $element, $pos = -1)
|
||||
{
|
||||
if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
'You can\'t add a form to a form'
|
||||
);
|
||||
if ($pos < 0) {
|
||||
$this->elements[] = $element;
|
||||
} else {
|
||||
array_splice($this->elements, $pos, 0, array($element));
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an existing element with a new one
|
||||
*
|
||||
* @param Element $element the new element
|
||||
* @param int $pos 0-based position of the element to replace
|
||||
*/
|
||||
public function replaceElement(Element $element, $pos)
|
||||
{
|
||||
if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
'You can\'t add a form to a form'
|
||||
);
|
||||
array_splice($this->elements, $pos, 1, array($element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element from the form completely
|
||||
*
|
||||
* @param int $pos 0-based position of the element to remove
|
||||
*/
|
||||
public function removeElement($pos)
|
||||
{
|
||||
array_splice($this->elements, $pos, 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Element adding functions
|
||||
|
||||
/**
|
||||
* Adds a text input field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return InputElement
|
||||
*/
|
||||
public function addTextInput($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new InputElement('text', $name, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a password input field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return InputElement
|
||||
*/
|
||||
public function addPasswordInput($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new InputElement('password', $name, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a radio button field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return CheckableElement
|
||||
*/
|
||||
public function addRadioButton($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a checkbox field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return CheckableElement
|
||||
*/
|
||||
public function addCheckbox($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dropdown field
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $options
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return DropdownElement
|
||||
*/
|
||||
public function addDropdown($name, $options, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new DropdownElement($name, $options, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a textarea field
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param int $pos
|
||||
* @return TextareaElement
|
||||
*/
|
||||
public function addTextarea($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TextareaElement($name, $label), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a simple button, escapes the content for you
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $content
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addButton($name, $content, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a simple button, allows HTML for content
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $html
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addButtonHTML($name, $html, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new ButtonElement($name, $html), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a label referencing another input element, escapes the label for you
|
||||
*
|
||||
* @param string $label
|
||||
* @param string $for
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addLabel($label, $for='', $pos = -1)
|
||||
{
|
||||
return $this->addLabelHTML(hsc($label), $for, $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a label referencing another input element, allows HTML for content
|
||||
*
|
||||
* @param string $content
|
||||
* @param string|Element $for
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addLabelHTML($content, $for='', $pos = -1)
|
||||
{
|
||||
$element = new LabelElement(hsc($content));
|
||||
|
||||
if (is_a($for, '\dokuwiki\Form\Element')) {
|
||||
/** @var Element $for */
|
||||
$for = $for->id();
|
||||
}
|
||||
$for = (string) $for;
|
||||
if ($for !== '') {
|
||||
$element->attr('for', $for);
|
||||
}
|
||||
|
||||
return $this->addElement($element, $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fixed HTML to the form
|
||||
*
|
||||
* @param string $html
|
||||
* @param int $pos
|
||||
* @return HTMLElement
|
||||
*/
|
||||
public function addHTML($html, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new HTMLElement($html), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a closed HTML tag to the form
|
||||
*
|
||||
* @param string $tag
|
||||
* @param int $pos
|
||||
* @return TagElement
|
||||
*/
|
||||
public function addTag($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagElement($tag), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an open HTML tag to the form
|
||||
*
|
||||
* Be sure to close it again!
|
||||
*
|
||||
* @param string $tag
|
||||
* @param int $pos
|
||||
* @return TagOpenElement
|
||||
*/
|
||||
public function addTagOpen($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagOpenElement($tag), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a closing HTML tag to the form
|
||||
*
|
||||
* Be sure it had been opened before
|
||||
*
|
||||
* @param string $tag
|
||||
* @param int $pos
|
||||
* @return TagCloseElement
|
||||
*/
|
||||
public function addTagClose($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagCloseElement($tag), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a Fieldset
|
||||
*
|
||||
* @param string $legend
|
||||
* @param int $pos
|
||||
* @return FieldsetOpenElement
|
||||
*/
|
||||
public function addFieldsetOpen($legend = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new FieldsetOpenElement($legend), $pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a fieldset
|
||||
*
|
||||
* @param int $pos
|
||||
* @return TagCloseElement
|
||||
*/
|
||||
public function addFieldsetClose($pos = -1)
|
||||
{
|
||||
return $this->addElement(new FieldsetCloseElement(), $pos);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/**
|
||||
* Adjust the elements so that fieldset open and closes are matching
|
||||
*/
|
||||
protected function balanceFieldsets()
|
||||
{
|
||||
$lastclose = 0;
|
||||
$isopen = false;
|
||||
$len = count($this->elements);
|
||||
|
||||
for ($pos = 0; $pos < $len; $pos++) {
|
||||
$type = $this->elements[$pos]->getType();
|
||||
if ($type == 'fieldsetopen') {
|
||||
if ($isopen) {
|
||||
//close previous fieldset
|
||||
$this->addFieldsetClose($pos);
|
||||
$lastclose = $pos + 1;
|
||||
$pos++;
|
||||
$len++;
|
||||
}
|
||||
$isopen = true;
|
||||
} elseif ($type == 'fieldsetclose') {
|
||||
if (!$isopen) {
|
||||
// make sure there was a fieldsetopen
|
||||
// either right after the last close or at the begining
|
||||
$this->addFieldsetOpen('', $lastclose);
|
||||
$len++;
|
||||
$pos++;
|
||||
}
|
||||
$lastclose = $pos;
|
||||
$isopen = false;
|
||||
}
|
||||
}
|
||||
|
||||
// close open fieldset at the end
|
||||
if ($isopen) {
|
||||
$this->addFieldsetClose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of the whole form
|
||||
*
|
||||
* @param string $eventName (optional) name of the event: FORM_{$name}_OUTPUT
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML($eventName = null)
|
||||
{
|
||||
$this->balanceFieldsets();
|
||||
|
||||
// trigger event to provide an opportunity to modify this form
|
||||
if (isset($eventName)) {
|
||||
$eventName = 'FORM_'.strtoupper($eventName).'_OUTPUT';
|
||||
Event::createAndTrigger($eventName, $this, null, false);
|
||||
}
|
||||
|
||||
$html = '<form '. buildAttributes($this->attrs()) .'>';
|
||||
|
||||
foreach ($this->hidden as $name => $value) {
|
||||
$html .= '<input type="hidden" name="'. $name .'" value="'. formText($value) .'" />';
|
||||
}
|
||||
|
||||
foreach ($this->elements as $element) {
|
||||
$html .= $element->toHTML();
|
||||
}
|
||||
|
||||
$html .= '</form>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
31
snippets/dokuwiki-2023-04-04/inc/Form/HTMLElement.php
Normal file
31
snippets/dokuwiki-2023-04-04/inc/Form/HTMLElement.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class HTMLElement
|
||||
*
|
||||
* Holds arbitrary HTML that is added as is to the Form
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class HTMLElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $html
|
||||
*/
|
||||
public function __construct($html)
|
||||
{
|
||||
parent::__construct('html', $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return $this->val();
|
||||
}
|
||||
}
|
170
snippets/dokuwiki-2023-04-04/inc/Form/InputElement.php
Normal file
170
snippets/dokuwiki-2023-04-04/inc/Form/InputElement.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class InputElement
|
||||
*
|
||||
* Base class for all input elements. Uses a wrapping label when label
|
||||
* text is given.
|
||||
*
|
||||
* @todo figure out how to make wrapping or related label configurable
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class InputElement extends Element
|
||||
{
|
||||
/**
|
||||
* @var LabelElement
|
||||
*/
|
||||
protected $label = null;
|
||||
|
||||
/**
|
||||
* @var bool if the element should reflect posted values
|
||||
*/
|
||||
protected $useInput = true;
|
||||
|
||||
/**
|
||||
* @param string $type The type of this element
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
*/
|
||||
public function __construct($type, $name, $label = '')
|
||||
{
|
||||
parent::__construct($type, array('name' => $name));
|
||||
$this->attr('name', $name);
|
||||
$this->attr('type', $type);
|
||||
if ($label) $this->label = new LabelElement($label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label element if there's one set
|
||||
*
|
||||
* @return LabelElement|null
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the user sent input be used to initialize the input field
|
||||
*
|
||||
* The default is true. Any set values will be overwritten by the INPUT
|
||||
* provided values.
|
||||
*
|
||||
* @param bool $useinput
|
||||
* @return $this
|
||||
*/
|
||||
public function useInput($useinput)
|
||||
{
|
||||
$this->useInput = (bool) $useinput;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the element's ID
|
||||
*
|
||||
* @param null|string $id
|
||||
* @return string|$this
|
||||
*/
|
||||
public function id($id = null)
|
||||
{
|
||||
if ($this->label) $this->label->attr('for', $id);
|
||||
return parent::id($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a class to the class attribute
|
||||
*
|
||||
* This is the preferred method of setting the element's class
|
||||
*
|
||||
* @param string $class the new class to add
|
||||
* @return $this
|
||||
*/
|
||||
public function addClass($class)
|
||||
{
|
||||
if ($this->label) $this->label->addClass($class);
|
||||
return parent::addClass($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out how to access the value for this field from INPUT data
|
||||
*
|
||||
* The element's name could have been given as a simple string ('foo')
|
||||
* or in array notation ('foo[bar]').
|
||||
*
|
||||
* Note: this function only handles one level of arrays. If your data
|
||||
* is nested deeper, you should call useInput(false) and set the
|
||||
* correct value yourself
|
||||
*
|
||||
* @return array name and array key (null if not an array)
|
||||
*/
|
||||
protected function getInputName()
|
||||
{
|
||||
$name = $this->attr('name');
|
||||
parse_str("$name=1", $parsed);
|
||||
|
||||
$name = array_keys($parsed);
|
||||
$name = array_shift($name);
|
||||
|
||||
if (isset($parsed[$name]) && is_array($parsed[$name])) {
|
||||
$key = array_keys($parsed[$name]);
|
||||
$key = array_shift($key);
|
||||
} else {
|
||||
$key = null;
|
||||
}
|
||||
|
||||
return array($name, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the useInput flag and set the value attribute accordingly
|
||||
*/
|
||||
protected function prefillInput()
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
list($name, $key) = $this->getInputName();
|
||||
if (!$INPUT->has($name)) return;
|
||||
|
||||
if ($key === null) {
|
||||
$value = $INPUT->str($name);
|
||||
} else {
|
||||
$value = $INPUT->arr($name);
|
||||
if (isset($value[$key])) {
|
||||
$value = $value[$key];
|
||||
} else {
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
$this->val($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
if ($this->useInput) $this->prefillInput();
|
||||
return '<input '. buildAttributes($this->attrs()) .' />';
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element wrapped in a label
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->label) {
|
||||
return '<label '. buildAttributes($this->label->attrs()) .'>'.DOKU_LF
|
||||
.'<span>'. hsc($this->label->val()) .'</span>'.DOKU_LF
|
||||
. $this->mainElementHTML() .DOKU_LF
|
||||
.'</label>';
|
||||
} else {
|
||||
return $this->mainElementHTML();
|
||||
}
|
||||
}
|
||||
}
|
30
snippets/dokuwiki-2023-04-04/inc/Form/LabelElement.php
Normal file
30
snippets/dokuwiki-2023-04-04/inc/Form/LabelElement.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class Label
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class LabelElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* Creates a new Label
|
||||
*
|
||||
* @param string $label This is is raw HTML and will not be escaped
|
||||
*/
|
||||
public function __construct($label)
|
||||
{
|
||||
parent::__construct('label', $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '<label ' . buildAttributes($this->attrs()) . '>' . $this->val() . '</label>';
|
||||
}
|
||||
}
|
186
snippets/dokuwiki-2023-04-04/inc/Form/LegacyForm.php
Normal file
186
snippets/dokuwiki-2023-04-04/inc/Form/LegacyForm.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class LegacyForm
|
||||
*
|
||||
* Provides a compatibility layer to the old Doku_Form API
|
||||
*
|
||||
* This can be used to work with the modern API on forms provided by old events for
|
||||
* example. When you start new forms, just use Form\Form
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class LegacyForm extends Form
|
||||
{
|
||||
/**
|
||||
* Creates a new modern form from an old legacy Doku_Form
|
||||
*
|
||||
* @param \Doku_Form $oldform
|
||||
*/
|
||||
public function __construct(\Doku_Form $oldform)
|
||||
{
|
||||
parent::__construct($oldform->params);
|
||||
|
||||
$this->hidden = $oldform->_hidden;
|
||||
|
||||
foreach ($oldform->_content as $element) {
|
||||
list($ctl, $attr) = $this->parseLegacyAttr($element);
|
||||
|
||||
if (is_array($element)) {
|
||||
switch ($ctl['elem']) {
|
||||
case 'wikitext':
|
||||
$this->addTextarea('wikitext')
|
||||
->attrs($attr)
|
||||
->id('wiki__text')
|
||||
->val($ctl['text'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'textfield':
|
||||
$this->addTextInput($ctl['name'], $ctl['text'])
|
||||
->attrs($attr)
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'passwordfield':
|
||||
$this->addPasswordInput($ctl['name'], $ctl['text'])
|
||||
->attrs($attr)
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'checkboxfield':
|
||||
$this->addCheckbox($ctl['name'], $ctl['text'])
|
||||
->attrs($attr)
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'radiofield':
|
||||
$this->addRadioButton($ctl['name'], $ctl['text'])
|
||||
->attrs($attr)
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'tag':
|
||||
$this->addTag($ctl['tag'])
|
||||
->attrs($attr)
|
||||
->attr('name', $ctl['name'])
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'opentag':
|
||||
$this->addTagOpen($ctl['tag'])
|
||||
->attrs($attr)
|
||||
->attr('name', $ctl['name'])
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'closetag':
|
||||
$this->addTagClose($ctl['tag']);
|
||||
break;
|
||||
case 'openfieldset':
|
||||
$this->addFieldsetOpen($ctl['legend'])
|
||||
->attrs($attr)
|
||||
->attr('name', $ctl['name'])
|
||||
->id($ctl['id'])
|
||||
->addClass($ctl['class']);
|
||||
break;
|
||||
case 'closefieldset':
|
||||
$this->addFieldsetClose();
|
||||
break;
|
||||
case 'button':
|
||||
case 'field':
|
||||
case 'fieldright':
|
||||
case 'filefield':
|
||||
case 'menufield':
|
||||
case 'listboxfield':
|
||||
throw new \UnexpectedValueException('Unsupported legacy field ' . $ctl['elem']);
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException('Unknown legacy field ' . $ctl['elem']);
|
||||
|
||||
}
|
||||
} else {
|
||||
$this->addHTML($element);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses out what is the elements attributes and what is control info
|
||||
*
|
||||
* @param array $legacy
|
||||
* @return array
|
||||
*/
|
||||
protected function parseLegacyAttr($legacy)
|
||||
{
|
||||
$attributes = array();
|
||||
$control = array();
|
||||
|
||||
foreach ($legacy as $key => $val) {
|
||||
if ($key[0] == '_') {
|
||||
$control[substr($key, 1)] = $val;
|
||||
} elseif($key == 'name') {
|
||||
$control[$key] = $val;
|
||||
} elseif($key == 'id') {
|
||||
$control[$key] = $val;
|
||||
} else {
|
||||
$attributes[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return array($control, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates our types to the legacy types
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function legacyType($type)
|
||||
{
|
||||
static $types = array(
|
||||
'text' => 'textfield',
|
||||
'password' => 'passwordfield',
|
||||
'checkbox' => 'checkboxfield',
|
||||
'radio' => 'radiofield',
|
||||
'tagopen' => 'opentag',
|
||||
'tagclose' => 'closetag',
|
||||
'fieldsetopen' => 'openfieldset',
|
||||
'fieldsetclose' => 'closefieldset',
|
||||
);
|
||||
if (isset($types[$type])) return $types[$type];
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an old legacy form from this modern form's data
|
||||
*
|
||||
* @return \Doku_Form
|
||||
*/
|
||||
public function toLegacy()
|
||||
{
|
||||
$this->balanceFieldsets();
|
||||
|
||||
$legacy = new \Doku_Form($this->attrs());
|
||||
$legacy->_hidden = $this->hidden;
|
||||
foreach ($this->elements as $element) {
|
||||
if (is_a($element, 'dokuwiki\Form\HTMLElement')) {
|
||||
$legacy->_content[] = $element->toHTML();
|
||||
} elseif (is_a($element, 'dokuwiki\Form\InputElement')) {
|
||||
/** @var InputElement $element */
|
||||
$data = $element->attrs();
|
||||
$data['_elem'] = $this->legacyType($element->getType());
|
||||
$label = $element->getLabel();
|
||||
if ($label) {
|
||||
$data['_class'] = $label->attr('class');
|
||||
}
|
||||
$legacy->_content[] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
return $legacy;
|
||||
}
|
||||
}
|
118
snippets/dokuwiki-2023-04-04/inc/Form/OptGroup.php
Normal file
118
snippets/dokuwiki-2023-04-04/inc/Form/OptGroup.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
class OptGroup extends Element
|
||||
{
|
||||
protected $options = [];
|
||||
protected $values = [];
|
||||
|
||||
/**
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
* @param array $options The available options
|
||||
*/
|
||||
public function __construct($label, $options)
|
||||
{
|
||||
parent::__construct('optGroup', array('label' => $label));
|
||||
$this->options($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given values so they can be used during rendering
|
||||
*
|
||||
* This is intended to be only called from within DropdownElement::val()
|
||||
*
|
||||
* @param string[] $values the values to set
|
||||
* @return string[] the values that have been set (options exist)
|
||||
* @see DropdownElement::val()
|
||||
*/
|
||||
public function storeValues($values)
|
||||
{
|
||||
$this->values = [];
|
||||
foreach ($values as $value) {
|
||||
if(isset($this->options[$value])) {
|
||||
$this->values[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the options of the optgroup
|
||||
*
|
||||
* Options can be given as associative array (value => label) or as an
|
||||
* indexd array (label = value) or as an array of arrays. In the latter
|
||||
* case an element has to look as follows:
|
||||
* option-value => array (
|
||||
* 'label' => option-label,
|
||||
* 'attrs' => array (
|
||||
* attr-key => attr-value, ...
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @param null|array $options
|
||||
* @return $this|array
|
||||
*/
|
||||
public function options($options = null)
|
||||
{
|
||||
if ($options === null) return $this->options;
|
||||
if (!is_array($options)) throw new \InvalidArgumentException('Options have to be an array');
|
||||
$this->options = array();
|
||||
foreach ($options as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
if (!key_exists('label', $val)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'If option is given as array, it has to have a "label"-key!'
|
||||
);
|
||||
}
|
||||
if (key_exists('attrs', $val) && is_array($val['attrs']) && key_exists('selected', $val['attrs'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Please use function "DropdownElement::val()" to set the selected option'
|
||||
);
|
||||
}
|
||||
$this->options[$key] = $val;
|
||||
} elseif (is_int($key)) {
|
||||
$this->options[$val] = array('label' => (string)$val);
|
||||
} else {
|
||||
$this->options[$key] = array('label' => (string)$val);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->attributes['label'] === null) {
|
||||
return $this->renderOptions();
|
||||
}
|
||||
$html = '<optgroup ' . buildAttributes($this->attrs()) . '>';
|
||||
$html .= $this->renderOptions();
|
||||
$html .= '</optgroup>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function renderOptions()
|
||||
{
|
||||
$html = '';
|
||||
foreach ($this->options as $key => $val) {
|
||||
$selected = in_array((string)$key, $this->values) ? ' selected="selected"' : '';
|
||||
$attrs = '';
|
||||
if (!empty($val['attrs']) && is_array($val['attrs'])) {
|
||||
$attrs = buildAttributes($val['attrs']);
|
||||
}
|
||||
$html .= '<option' . $selected . ' value="' . hsc($key) . '" ' . $attrs . '>';
|
||||
$html .= hsc($val['label']);
|
||||
$html .= '</option>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
95
snippets/dokuwiki-2023-04-04/inc/Form/TagCloseElement.php
Normal file
95
snippets/dokuwiki-2023-04-04/inc/Form/TagCloseElement.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class TagCloseElement
|
||||
*
|
||||
* Creates an HTML close tag. You have to make sure it has been opened
|
||||
* before or this will produce invalid HTML
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagCloseElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tagclose', $tag, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* do not call this
|
||||
*
|
||||
* @param string $class
|
||||
* @return void
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function addClass($class)
|
||||
{
|
||||
throw new \BadMethodCallException('You can\t add classes to closing tag');
|
||||
}
|
||||
|
||||
/**
|
||||
* do not call this
|
||||
*
|
||||
* @param null|string $id
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function id($id = null)
|
||||
{
|
||||
if ($id === null) {
|
||||
return '';
|
||||
} else {
|
||||
throw new \BadMethodCallException('You can\t add ID to closing tag');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* do not call this
|
||||
*
|
||||
* @param string $name
|
||||
* @param null|string $value
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function attr($name, $value = null)
|
||||
{
|
||||
if ($value === null) {
|
||||
return '';
|
||||
} else {
|
||||
throw new \BadMethodCallException('You can\t add attributes to closing tag');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* do not call this
|
||||
*
|
||||
* @param array|null $attributes
|
||||
* @return array
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function attrs($attributes = null)
|
||||
{
|
||||
if ($attributes === null) {
|
||||
return array();
|
||||
} else {
|
||||
throw new \BadMethodCallException('You can\t add attributes to closing tag');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '</'.$this->val().'>';
|
||||
}
|
||||
|
||||
}
|
32
snippets/dokuwiki-2023-04-04/inc/Form/TagElement.php
Normal file
32
snippets/dokuwiki-2023-04-04/inc/Form/TagElement.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class TagElement
|
||||
*
|
||||
* Creates a self closing HTML tag
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tag', $tag, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '<'.$this->val().' '.buildAttributes($this->attrs()).' />';
|
||||
}
|
||||
}
|
33
snippets/dokuwiki-2023-04-04/inc/Form/TagOpenElement.php
Normal file
33
snippets/dokuwiki-2023-04-04/inc/Form/TagOpenElement.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class TagOpenElement
|
||||
*
|
||||
* Creates an open HTML tag. You have to make sure you close it
|
||||
* again or this will produce invalid HTML
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagOpenElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tagopen', $tag, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
return '<'.$this->val().' '.buildAttributes($this->attrs()).'>';
|
||||
}
|
||||
}
|
55
snippets/dokuwiki-2023-04-04/inc/Form/TextareaElement.php
Normal file
55
snippets/dokuwiki-2023-04-04/inc/Form/TextareaElement.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class TextareaElement
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TextareaElement extends InputElement
|
||||
{
|
||||
/**
|
||||
* @var string the actual text within the area
|
||||
*/
|
||||
protected $text;
|
||||
|
||||
/**
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element
|
||||
*/
|
||||
public function __construct($name, $label)
|
||||
{
|
||||
parent::__construct('textarea', $name, $label);
|
||||
$this->attr('dir', 'auto');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the element's value
|
||||
*
|
||||
* This is the preferred way of setting the element's value
|
||||
*
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null)
|
||||
{
|
||||
if ($value !== null) {
|
||||
$this->text = cleanText($value);
|
||||
return $this;
|
||||
}
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
if ($this->useInput) $this->prefillInput();
|
||||
return '<textarea ' . buildAttributes($this->attrs()) . '>' .
|
||||
formText($this->val()) . '</textarea>';
|
||||
}
|
||||
|
||||
}
|
47
snippets/dokuwiki-2023-04-04/inc/Form/ValueElement.php
Normal file
47
snippets/dokuwiki-2023-04-04/inc/Form/ValueElement.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class ValueElement
|
||||
*
|
||||
* Just like an Element but it's value is not part of its attributes
|
||||
*
|
||||
* What the value is (tag name, content, etc) is defined by the actual implementations
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
abstract class ValueElement extends Element
|
||||
{
|
||||
/**
|
||||
* @var string holds the element's value
|
||||
*/
|
||||
protected $value = '';
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($type, $value, $attributes = array())
|
||||
{
|
||||
parent::__construct($type, $attributes);
|
||||
$this->val($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set the element's value
|
||||
*
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null)
|
||||
{
|
||||
if ($value !== null) {
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
}
|
77
snippets/dokuwiki-2023-04-04/inc/HTTP/DokuHTTPClient.php
Normal file
77
snippets/dokuwiki-2023-04-04/inc/HTTP/DokuHTTPClient.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace dokuwiki\HTTP;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds DokuWiki specific configs to the HTTP client
|
||||
*
|
||||
* @author Andreas Goetz <cpuidle@gmx.de>
|
||||
*/
|
||||
class DokuHTTPClient extends HTTPClient {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
public function __construct(){
|
||||
global $conf;
|
||||
|
||||
// call parent constructor
|
||||
parent::__construct();
|
||||
|
||||
// set some values from the config
|
||||
$this->proxy_host = $conf['proxy']['host'];
|
||||
$this->proxy_port = $conf['proxy']['port'];
|
||||
$this->proxy_user = $conf['proxy']['user'];
|
||||
$this->proxy_pass = conf_decodeString($conf['proxy']['pass']);
|
||||
$this->proxy_ssl = $conf['proxy']['ssl'];
|
||||
$this->proxy_except = $conf['proxy']['except'];
|
||||
|
||||
// allow enabling debugging via URL parameter (if debugging allowed)
|
||||
if($conf['allowdebug']) {
|
||||
if(
|
||||
isset($_REQUEST['httpdebug']) ||
|
||||
(
|
||||
isset($_SERVER['HTTP_REFERER']) &&
|
||||
strpos($_SERVER['HTTP_REFERER'], 'httpdebug') !== false
|
||||
)
|
||||
) {
|
||||
$this->debug = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps an event around the parent function
|
||||
*
|
||||
* @triggers HTTPCLIENT_REQUEST_SEND
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string|array $data the post data either as array or raw data
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function sendRequest($url,$data='',$method='GET'){
|
||||
$httpdata = array('url' => $url,
|
||||
'data' => $data,
|
||||
'method' => $method);
|
||||
$evt = new \Doku_Event('HTTPCLIENT_REQUEST_SEND',$httpdata);
|
||||
if($evt->advise_before()){
|
||||
$url = $httpdata['url'];
|
||||
$data = $httpdata['data'];
|
||||
$method = $httpdata['method'];
|
||||
}
|
||||
$evt->advise_after();
|
||||
unset($evt);
|
||||
return parent::sendRequest($url,$data,$method);
|
||||
}
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user