Initial commit

This commit is contained in:
2021-10-26 13:02:53 +02:00
commit 73843b66ce
4678 changed files with 319494 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionDisabledException;
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
*/
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;
}
}

View File

@@ -0,0 +1,28 @@
<?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;
}
public function preProcess() {
throw new FatalException('Alias Actions need to implement preProcess to load the aliased action');
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionException;
/**
* 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
}
public function checkPreconditions() {
parent::checkPreconditions();
}
public function preProcess() {
global $INPUT;
global $INFO;
// retrieve admin plugin name from $_REQUEST['page']
if(($page = $INPUT->str('page', '', true)) != '') {
/** @var $plugin \dokuwiki\Extension\AdminPlugin */
if($plugin = plugin_getRequestAdminPlugin()) { // FIXME this method does also permission checking
if(!$plugin->isAccessibleByCurrentUser()) {
throw new ActionException('denied');
}
$plugin->handle();
}
}
}
public function tplContent() {
tpl_admin();
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace dokuwiki\Action;
/**
* 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() {
html_backlinks();
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
/**
* Class Cancel
*
* Alias for show. Aborts editing
*
* @package dokuwiki\Action
*/
class Cancel extends AbstractAliasAction {
/** @inheritdoc */
public function preProcess() {
global $ID;
unlock($ID);
// continue with draftdel -> redirect -> show
throw new ActionAbort('draftdel');
}
}

View 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();
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace dokuwiki\Action;
/**
* 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;
}
}
public function tplContent() {
global $PRE;
global $TEXT;
global $SUF;
global $SUM;
html_conflict(con($PRE, $TEXT, $SUF), $SUM);
html_diff(con($PRE, $TEXT, $SUF), false);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace dokuwiki\Action;
/**
* Class Denied
*
* Show the access denied screen
*
* @package dokuwiki\Action
*/
class Denied extends AbstractAclAction {
/** @inheritdoc */
public function minimumPermission() {
return AUTH_NONE;
}
public function tplContent() {
html_denied();
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace dokuwiki\Action;
/**
* 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() {
html_diff();
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionException;
/**
* 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() {
html_draft();
}
}

View 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()) {
$draft->deleteDraft();
}
throw new ActionAbort('redirect');
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
/**
* 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() {
html_edit();
}
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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);
}
}

View File

@@ -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 {
}

View 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();
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace dokuwiki\Action;
/**
* 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;
html_index($IDX);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace dokuwiki\Action;
/**
* 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() {
html_locked();
html_edit();
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionException;
/**
* 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() {
html_login();
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionDisabledException;
use dokuwiki\Action\Exception\ActionException;
/**
* 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 \dokuwiki\Extension\AuthPlugin $auth */
global $auth;
if(!$auth->canDo('logout')) throw new ActionDisabledException();
}
/** @inheritdoc */
public function preProcess() {
global $ID;
global $INPUT;
// 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');
}
}

View 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();
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace dokuwiki\Action;
/**
* 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 \dokuwiki\Extension\Event('TPL_ACT_UNKNOWN', $this->actionname);
if($evt->advise_before()) {
msg('Failed to handle action: ' . hsc($this->actionname), -1);
}
$evt->advise_after();
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace dokuwiki\Action;
/**
* 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;
html_edit();
html_show($TEXT);
}
/**
* 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);
}
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
use dokuwiki\Action\Exception\ActionDisabledException;
/**
* 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 \dokuwiki\Extension\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() {
html_updateprofile();
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
use dokuwiki\Action\Exception\ActionDisabledException;
/**
* 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 \dokuwiki\Extension\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');
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace dokuwiki\Action;
/**
* 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;
html_recent((int) $INPUT->extract('first')->int('first'), $this->showType);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
/**
* Class Recover
*
* Recover a draft
*
* @package dokuwiki\Action
*/
class Recover extends AbstractAliasAction {
/** @inheritdoc */
public function preProcess() {
throw new ActionAbort('edit');
}
}

View 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);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
use dokuwiki\Action\Exception\ActionDisabledException;
/**
* 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 \dokuwiki\Extension\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() {
html_register();
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
use dokuwiki\Action\Exception\ActionDisabledException;
/**
* 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 \dokuwiki\Extension\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() {
html_resendpwd();
}
/**
* 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 \dokuwiki\Extension\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
}
}

View File

@@ -0,0 +1,60 @@
<?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 AbstractAction {
/** @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');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace dokuwiki\Action;
/**
* 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 $INPUT;
html_revisions($INPUT->int('first'));
}
}

View File

@@ -0,0 +1,60 @@
<?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 && $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');
}
}

View 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);
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Created by IntelliJ IDEA.
* User: andi
* Date: 2/10/17
* Time: 4:32 PM
*/
namespace dokuwiki\Action;
/**
* 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() {
html_show();
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\FatalException;
use dokuwiki\Sitemap\Mapper;
/**
* 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=' . \dokuwiki\Utf8\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?');
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace dokuwiki\Action;
/**
* 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() {
html_edit();
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace dokuwiki\Action;
use dokuwiki\Action\Exception\ActionAbort;
use dokuwiki\Action\Exception\ActionDisabledException;
use dokuwiki\Subscriptions\SubscriberManager;
use dokuwiki\Extension\Event;
/**
* 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() {
tpl_subscribe();
}
/**
* 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');
}
}