From f21dad3906d4ec6b3d86685599409894630abdc1 Mon Sep 17 00:00:00 2001 From: Andreas Gohr <andi@splitbrain.org> Date: Sat, 11 Feb 2017 12:34:11 +0100 Subject: [PATCH] all actions should have a class now Lots of FIXMEs and the routing isn't integrated, yet --- _test/tests/inc/Action/general.test.php | 176 ++++++++++++++++++ .../inc/Action/minimumPermissions.test.php | 49 ----- inc/Action/AbstractAction.php | 12 +- inc/Action/AbstractAliasAction.php | 15 ++ inc/Action/AbstractUserAction.php | 4 +- inc/Action/Admin.php | 55 ++++++ inc/Action/Cancel.php | 19 ++ inc/Action/Conflict.php | 33 ++++ inc/Action/Draft.php | 29 +++ inc/Action/Draftdel.php | 25 +++ inc/Action/Edit.php | 85 +++++++++ .../Exception/ActionNoUserException.php | 7 - .../Exception/ActionUserRequiredException.php | 7 + inc/Action/Export.php | 112 +++++++++++ inc/Action/Index.php | 23 +++ inc/Action/Locked.php | 22 +++ inc/Action/Logout.php | 4 +- inc/Action/Media.php | 22 +++ inc/Action/Preview.php | 26 +++ inc/Action/Profile.php | 32 ++++ inc/Action/Profiledel.php | 35 ++++ inc/Action/Recover.php | 19 ++ inc/Action/Register.php | 30 +++ inc/Action/Resendpwd.php | 159 ++++++++++++++++ inc/Action/Revert.php | 66 +++++++ inc/Action/Save.php | 62 ++++++ inc/Action/Source.php | 22 +++ inc/ActionRouter.php | 2 + 28 files changed, 1091 insertions(+), 61 deletions(-) create mode 100644 _test/tests/inc/Action/general.test.php delete mode 100644 _test/tests/inc/Action/minimumPermissions.test.php create mode 100644 inc/Action/AbstractAliasAction.php create mode 100644 inc/Action/Admin.php create mode 100644 inc/Action/Cancel.php create mode 100644 inc/Action/Conflict.php create mode 100644 inc/Action/Draft.php create mode 100644 inc/Action/Draftdel.php create mode 100644 inc/Action/Edit.php delete mode 100644 inc/Action/Exception/ActionNoUserException.php create mode 100644 inc/Action/Exception/ActionUserRequiredException.php create mode 100644 inc/Action/Export.php create mode 100644 inc/Action/Index.php create mode 100644 inc/Action/Locked.php create mode 100644 inc/Action/Media.php create mode 100644 inc/Action/Preview.php create mode 100644 inc/Action/Profile.php create mode 100644 inc/Action/Profiledel.php create mode 100644 inc/Action/Recover.php create mode 100644 inc/Action/Register.php create mode 100644 inc/Action/Resendpwd.php create mode 100644 inc/Action/Revert.php create mode 100644 inc/Action/Save.php create mode 100644 inc/Action/Source.php diff --git a/_test/tests/inc/Action/general.test.php b/_test/tests/inc/Action/general.test.php new file mode 100644 index 000000000..3aa6681ab --- /dev/null +++ b/_test/tests/inc/Action/general.test.php @@ -0,0 +1,176 @@ +<?php + +use dokuwiki\Action\AbstractAclAction; +use dokuwiki\Action\AbstractUserAction; +use dokuwiki\Action\Exception\ActionAclRequiredException; +use dokuwiki\Action\Exception\ActionDisabledException; +use dokuwiki\Action\Exception\ActionUserRequiredException; + +class action_general extends DokuWikiTest { + + public function dataProvider() { + return array( + array('Login', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Logout', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Search', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Recent', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Profile', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Profiledel', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Index', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Sitemap', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Denied', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Register', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Resendpwd', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + + array('Revert', AUTH_ADMIN, array('exists' => true, 'ismanager' => false)), + array('Revert', AUTH_EDIT, array('exists' => true, 'ismanager' => true)), + + array('Admin', AUTH_ADMIN, array('exists' => true, 'ismanager' => false)), + array('Admin', AUTH_READ, array('exists' => true, 'ismanager' => true)), // let in, check later again + + array('Check', AUTH_READ, array('exists' => true, 'ismanager' => false)), // sensible? + array('Diff', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Show', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Subscribe', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Locked', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Source', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Export', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Media', AUTH_READ, array('exists' => true, 'ismanager' => false)), + + array('Draftdel', AUTH_EDIT, array('exists' => true, 'ismanager' => false)), + + // aliases + array('Cancel', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + array('Recover', AUTH_NONE, array('exists' => true, 'ismanager' => false)), + + // EDITING existing page + array('Save', AUTH_EDIT, array('exists' => true, 'ismanager' => false)), + array('Conflict', AUTH_EDIT, array('exists' => true, 'ismanager' => false)), + array('Draft', AUTH_EDIT, array('exists' => true, 'ismanager' => false)), + //the edit function will check again and do a source show + //when no AUTH_EDIT available: + array('Edit', AUTH_READ, array('exists' => true, 'ismanager' => false)), + array('Preview', AUTH_READ, array('exists' => true, 'ismanager' => false)), + + // EDITING new page + array('Save', AUTH_CREATE, array('exists' => false, 'ismanager' => false)), + array('Conflict', AUTH_CREATE, array('exists' => false, 'ismanager' => false)), + array('Draft', AUTH_CREATE, array('exists' => false, 'ismanager' => false)), + array('Edit', AUTH_CREATE, array('exists' => false, 'ismanager' => false)), + array('Preview', AUTH_CREATE, array('exists' => false, 'ismanager' => false)), + + + ); + } + + /** + * @dataProvider dataProvider + * @param $name + * @param $expected + * @param $info + */ + public function testMinimumPermissions($name, $expected, $info) { + global $INFO; + $INFO = $info; + + $classname = 'dokuwiki\\Action\\' . $name; + /** @var \dokuwiki\Action\AbstractAction $class */ + $class = new $classname(); + + $this->assertSame($expected, $class->minimumPermission()); + } + + /** + * All actions should handle the disableactions setting + * + * @dataProvider dataProvider + * @param $name + */ + public function testBaseClassActionOkPermission($name) { + $classname = 'dokuwiki\\Action\\' . $name; + /** @var \dokuwiki\Action\AbstractAction $class */ + $class = new $classname(); + + global $conf; + $conf['useacl'] = 1; + $conf['subscribers'] = 1; + $conf['disableactions'] = ''; + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertNotSame(ActionDisabledException::class, get_class($e)); + } + + $conf['disableactions'] = $class->getActionName(); + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertSame(ActionDisabledException::class, get_class($e)); + } + } + + /** + * Actions inheriting from AbstractAclAction should have an ACL enabled check + * + * @dataProvider dataProvider + * @param $name + */ + public function testBaseClassAclPermission($name) { + $classname = 'dokuwiki\\Action\\' . $name; + /** @var \dokuwiki\Action\AbstractAction $class */ + $class = new $classname(); + if(!is_a($class, AbstractAclAction::class)) return; + + global $conf; + $conf['useacl'] = 1; + $conf['subscribers'] = 1; + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertNotSame(ActionAclRequiredException::class, get_class($e)); + } + + $conf['useacl'] = 0; + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertSame(ActionAclRequiredException::class, get_class($e)); + } + } + + /** + * Actions inheriting from AbstractUserAction should have user check + * + * @dataProvider dataProvider + * @param $name + */ + public function testBaseClassUserPermission($name) { + $classname = 'dokuwiki\\Action\\' . $name; + /** @var \dokuwiki\Action\AbstractAction $class */ + $class = new $classname(); + if(!is_a($class, AbstractUserAction::class)) return; + + global $conf; + $conf['useacl'] = 1; + $conf['subscribers'] = 1; + $_SERVER['REMOTE_USER'] = 'test'; + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertNotSame(ActionUserRequiredException::class, get_class($e)); + } + + unset($_SERVER['REMOTE_USER']); + + try { + $class->checkPermissions(); + } catch(\Exception $e) { + $this->assertSame(ActionUserRequiredException::class, get_class($e)); + } + } +} diff --git a/_test/tests/inc/Action/minimumPermissions.test.php b/_test/tests/inc/Action/minimumPermissions.test.php deleted file mode 100644 index e667b302b..000000000 --- a/_test/tests/inc/Action/minimumPermissions.test.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -class action_minimumPermissions extends DokuWikiTest { - - - - public function dataProvider() { - return array ( - array('Login', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - array('Logout', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - array('Search', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - array('Recent', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - //array('Profile', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - //array('Profile_delete', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - //array('Index', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - array('Sitemap', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - array('Denied', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - - array('Check', AUTH_READ, array('exists' => true, 'ismanager' => false)), - array('Diff', AUTH_READ, array('exists' => true, 'ismanager' => false)), - array('Show', AUTH_READ, array('exists' => true, 'ismanager' => false)), - array('Subscribe', AUTH_READ, array('exists' => true, 'ismanager' => false)), - - - - /* - array('', AUTH_NONE, array('exists' => true, 'ismanager' => false)), - */ - - ); - } - - /** - * @dataProvider dataProvider - * @param $name - * @param $expected - * @param $info - */ - public function testMinimumPermissions($name, $expected, $info) { - global $INFO; - $INFO = $info; - - $classname = 'dokuwiki\\Action\\'.$name; - /** @var \dokuwiki\Action\AbstractAction $class */ - $class = new $classname(); - - $this->assertSame($expected, $class->minimumPermission()); - } -} diff --git a/inc/Action/AbstractAction.php b/inc/Action/AbstractAction.php index ecee86523..9094a7cde 100644 --- a/inc/Action/AbstractAction.php +++ b/inc/Action/AbstractAction.php @@ -4,6 +4,7 @@ namespace dokuwiki\Action; use dokuwiki\Action\Exception\ActionDisabledException; use dokuwiki\Action\Exception\ActionException; +use dokuwiki\Action\Exception\FatalException; abstract class AbstractAction { @@ -54,8 +55,17 @@ abstract class AbstractAction { /** * Output whatever content is wanted within tpl_content(); + * + * @fixme we may want to return a Ui class here */ public function tplContent() { - echo 'No content for this action'; + throw new FatalException('No content for Action ' . $this->actionname); + } + + /** + * @return string + */ + public function getActionName() { + return $this->actionname; } } diff --git a/inc/Action/AbstractAliasAction.php b/inc/Action/AbstractAliasAction.php new file mode 100644 index 000000000..ebf0c2b6f --- /dev/null +++ b/inc/Action/AbstractAliasAction.php @@ -0,0 +1,15 @@ +<?php + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAclRequiredException; +use dokuwiki\Action\Exception\ActionException; + +abstract class AbstractAliasAction extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + +} diff --git a/inc/Action/AbstractUserAction.php b/inc/Action/AbstractUserAction.php index d94690fff..a46f094e5 100644 --- a/inc/Action/AbstractUserAction.php +++ b/inc/Action/AbstractUserAction.php @@ -4,7 +4,7 @@ namespace dokuwiki\Action; use dokuwiki\Action\Exception\ActionAclRequiredException; use dokuwiki\Action\Exception\ActionException; -use dokuwiki\Action\Exception\ActionNoUserException; +use dokuwiki\Action\Exception\ActionUserRequiredException; abstract class AbstractUserAction extends AbstractAclAction { @@ -13,7 +13,7 @@ abstract class AbstractUserAction extends AbstractAclAction { parent::checkPermissions(); global $INPUT; if(!$INPUT->server->str('REMOTE_USER')) { - throw new ActionNoUserException(); + throw new ActionUserRequiredException(); } } diff --git a/inc/Action/Admin.php b/inc/Action/Admin.php new file mode 100644 index 000000000..4929f45c2 --- /dev/null +++ b/inc/Action/Admin.php @@ -0,0 +1,55 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:33 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionException; + +class Admin extends AbstractUserAction { + + /** @inheritdoc */ + function minimumPermission() { + global $INFO; + + if($INFO['ismanager']) { + return AUTH_READ; // let in check later + } else { + return AUTH_ADMIN; + } + } + + public function checkPermissions() { + parent::checkPermissions(); + + global $INFO; + if(!$INFO['ismanager']) { + throw new ActionException('denied'); + } + } + + public function preProcess() { + global $INPUT; + global $INFO; + + // retrieve admin plugin name from $_REQUEST['page'] + if (($page = $INPUT->str('page', '', true)) != '') { + /** @var $plugin \DokuWiki_Admin_Plugin */ + if ($plugin = plugin_getRequestAdminPlugin()){ // FIXME this method does also permission checking + if($plugin->forAdminOnly() && !$INFO['isadmin'] ) { + throw new ActionException('denied'); + } + $plugin->handle(); + } + } + } + + public function tplContent() { + tpl_admin(); + } + +} diff --git a/inc/Action/Cancel.php b/inc/Action/Cancel.php new file mode 100644 index 000000000..095cf9791 --- /dev/null +++ b/inc/Action/Cancel.php @@ -0,0 +1,19 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:13 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Cancel extends AbstractAliasAction { + + public function preProcess() { + throw new ActionAbort(); + } + +} diff --git a/inc/Action/Conflict.php b/inc/Action/Conflict.php new file mode 100644 index 000000000..8b09b38fa --- /dev/null +++ b/inc/Action/Conflict.php @@ -0,0 +1,33 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:52 AM + */ + +namespace dokuwiki\Action; + +class Conflict extends AbstractAction { + + /** @inheritdoc */ + 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); + } + +} diff --git a/inc/Action/Draft.php b/inc/Action/Draft.php new file mode 100644 index 000000000..0b6d4b15b --- /dev/null +++ b/inc/Action/Draft.php @@ -0,0 +1,29 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:55 AM + */ + +namespace dokuwiki\Action; + +class Draft extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + global $INFO; + if($INFO['exists']) { + return AUTH_EDIT; + } else { + return AUTH_CREATE; + } + } + + // FIXME any permission checks needed? + + public function tplContent() { + html_draft(); + } + +} diff --git a/inc/Action/Draftdel.php b/inc/Action/Draftdel.php new file mode 100644 index 000000000..b89dadbd9 --- /dev/null +++ b/inc/Action/Draftdel.php @@ -0,0 +1,25 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:13 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Draftdel extends AbstractUserAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_EDIT; + } + + public function preProcess() { + act_draftdel('fixme'); // FIXME replace this utility function + throw new ActionAbort(); + } + +} diff --git a/inc/Action/Edit.php b/inc/Action/Edit.php new file mode 100644 index 000000000..40e81a14d --- /dev/null +++ b/inc/Action/Edit.php @@ -0,0 +1,85 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:42 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Edit extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + global $INFO; + if($INFO['exists']) { + return AUTH_READ; // we check again below + } else { + return AUTH_CREATE; + } + } + + public function checkPermissions() { + parent::checkPermissions(); + global $INFO; + + // no edit permission? view source + if($INFO['exists'] && !$INFO['writable']) { + throw new ActionAbort('source'); + } + } + + 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); + } + + public function tplContent() { + html_edit(); + } + +} diff --git a/inc/Action/Exception/ActionNoUserException.php b/inc/Action/Exception/ActionNoUserException.php deleted file mode 100644 index 385091547..000000000 --- a/inc/Action/Exception/ActionNoUserException.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php - -namespace dokuwiki\Action\Exception; - -class ActionNoUserException extends ActionException { - -} diff --git a/inc/Action/Exception/ActionUserRequiredException.php b/inc/Action/Exception/ActionUserRequiredException.php new file mode 100644 index 000000000..3485ed68e --- /dev/null +++ b/inc/Action/Exception/ActionUserRequiredException.php @@ -0,0 +1,7 @@ +<?php + +namespace dokuwiki\Action\Exception; + +class ActionUserRequiredException extends ActionException { + +} diff --git a/inc/Action/Export.php b/inc/Action/Export.php new file mode 100644 index 000000000..804b8c235 --- /dev/null +++ b/inc/Action/Export.php @@ -0,0 +1,112 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:10 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Export extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_READ; + } + + // FIXME proper mode should be checked + + /** + * 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> + */ + 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('FIXME', 7); // FIXME how to pass the proper mode? + 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; + + trigger_event('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(); + } + +} diff --git a/inc/Action/Index.php b/inc/Action/Index.php new file mode 100644 index 000000000..c3dbb28b6 --- /dev/null +++ b/inc/Action/Index.php @@ -0,0 +1,23 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:31 AM + */ + +namespace dokuwiki\Action; + +class Index extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + + public function tplContent() { + global $IDX; + html_index($IDX); + } + +} diff --git a/inc/Action/Locked.php b/inc/Action/Locked.php new file mode 100644 index 000000000..eb6451acd --- /dev/null +++ b/inc/Action/Locked.php @@ -0,0 +1,22 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:43 AM + */ + +namespace dokuwiki\Action; + +class Locked extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_READ; + } + + public function tplContent() { + html_locked(); + } + +} diff --git a/inc/Action/Logout.php b/inc/Action/Logout.php index e204ef9a0..3c5dc76a0 100644 --- a/inc/Action/Logout.php +++ b/inc/Action/Logout.php @@ -9,7 +9,7 @@ namespace dokuwiki\Action; use dokuwiki\Action\Exception\ActionException; -use dokuwiki\Action\Exception\ActionNoUserException; +use dokuwiki\Action\Exception\ActionUserRequiredException; class Logout extends AbstractAclAction { @@ -23,7 +23,7 @@ class Logout extends AbstractAclAction { global $INPUT; parent::checkPermissions(); if(!$INPUT->server->has('REMOTE_USER')) { - throw new ActionNoUserException('login'); + throw new ActionUserRequiredException('login'); } } diff --git a/inc/Action/Media.php b/inc/Action/Media.php new file mode 100644 index 000000000..4c31dba80 --- /dev/null +++ b/inc/Action/Media.php @@ -0,0 +1,22 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:49 AM + */ + +namespace dokuwiki\Action; + +class Media extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_READ; + } + + public function tplContent() { + tpl_media(); + } + +} diff --git a/inc/Action/Preview.php b/inc/Action/Preview.php new file mode 100644 index 000000000..279f65701 --- /dev/null +++ b/inc/Action/Preview.php @@ -0,0 +1,26 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:26 AM + */ + +namespace dokuwiki\Action; + +class Preview extends Edit { + + public function preProcess() { + header('X-XSS-Protection: 0'); // FIXME is it okay to send it right away here? + act_draftsave('fixme'); // reimplement thisutility function and take of duplicate code in ajax.php + + parent::preProcess(); + } + + public function tplContent() { + global $TEXT; + html_edit(); + html_show($TEXT); + } + +} diff --git a/inc/Action/Profile.php b/inc/Action/Profile.php new file mode 100644 index 000000000..a23328401 --- /dev/null +++ b/inc/Action/Profile.php @@ -0,0 +1,32 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 9:47 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Profile extends AbstractUserAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + + public function preProcess() { + global $lang; + if(updateprofile()) { + msg($lang['profchanged'], 1); + throw new ActionAbort('show'); + } + } + + public function tplContent() { + html_updateprofile(); + } + +} diff --git a/inc/Action/Profiledel.php b/inc/Action/Profiledel.php new file mode 100644 index 000000000..04d15a09d --- /dev/null +++ b/inc/Action/Profiledel.php @@ -0,0 +1,35 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 9:47 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +/** + * Class Profiledel + * @package dokuwiki\Action + * @fixme rename profile_delete action to profiledel + */ +class Profiledel extends AbstractUserAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + + public function preProcess() { + global $lang; + if(auth_deleteprofile()){ + msg($lang['profdeleted'],1); + throw new ActionAbort('show'); + } else { + throw new ActionAbort('profile'); + } + } + +} diff --git a/inc/Action/Recover.php b/inc/Action/Recover.php new file mode 100644 index 000000000..d911178e8 --- /dev/null +++ b/inc/Action/Recover.php @@ -0,0 +1,19 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:26 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Recover extends AbstractAliasAction { + + public function preProcess() { + throw new ActionAbort('edit'); + } + +} diff --git a/inc/Action/Register.php b/inc/Action/Register.php new file mode 100644 index 000000000..1b3453cd8 --- /dev/null +++ b/inc/Action/Register.php @@ -0,0 +1,30 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 9:18 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Register extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + + public function preProcess() { + if(register()) { // FIXME could be moved from auth to here + throw new ActionAbort('login'); + } + } + + public function tplContent() { + html_register(); + } + +} diff --git a/inc/Action/Resendpwd.php b/inc/Action/Resendpwd.php new file mode 100644 index 000000000..8afbc4a73 --- /dev/null +++ b/inc/Action/Resendpwd.php @@ -0,0 +1,159 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 9:33 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; + +class Resendpwd extends AbstractAclAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_NONE; + } + + /** @inheritdoc */ + public function preProcess() { + if($this->resendpwd()) { + throw new ActionAbort('login'); + } + } + + /** + * 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 + */ + function resendpwd() { + global $lang; + global $conf; + /* @var \DokuWiki_Auth_Plugin $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 + } + +} diff --git a/inc/Action/Revert.php b/inc/Action/Revert.php new file mode 100644 index 000000000..72c9230c6 --- /dev/null +++ b/inc/Action/Revert.php @@ -0,0 +1,66 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 9:56 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; +use dokuwiki\Action\Exception\ActionException; + +class Revert extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + global $INFO; + if($INFO['ismanager']) { + return AUTH_EDIT; + } else { + return AUTH_ADMIN; + } + } + + // fixme 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; + global $INPUT; + + // 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); + + //delete any draft + act_draftdel('fixme'); // FIXME replace this utility function + //session_write_close(); // FIXME sessions should be close somewhere higher up, maybe ActionRouter + + // when done, show current page + $INPUT->server->set('REQUEST_METHOD','post'); //should force a redirect // FIXME should we have a RedirectException? + $REV = ''; + + throw new ActionAbort(); + } + +} diff --git a/inc/Action/Save.php b/inc/Action/Save.php new file mode 100644 index 000000000..4f883bf37 --- /dev/null +++ b/inc/Action/Save.php @@ -0,0 +1,62 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 10:06 AM + */ + +namespace dokuwiki\Action; + +use dokuwiki\Action\Exception\ActionAbort; +use dokuwiki\Action\Exception\ActionException; + +class Save extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + global $INFO; + if($INFO['exists']) { + return AUTH_EDIT; + } else { + return AUTH_CREATE; + } + } + + 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); + + //delete draft + act_draftdel('fixme'); // FIXME replace this utility function + //session_write_close(); // FIXME close session higher up + + // when done, show page + throw new ActionAbort(); + } + +} diff --git a/inc/Action/Source.php b/inc/Action/Source.php new file mode 100644 index 000000000..e68537574 --- /dev/null +++ b/inc/Action/Source.php @@ -0,0 +1,22 @@ +<?php +/** + * Created by IntelliJ IDEA. + * User: andi + * Date: 2/11/17 + * Time: 11:44 AM + */ + +namespace dokuwiki\Action; + +class Source extends AbstractAction { + + /** @inheritdoc */ + function minimumPermission() { + return AUTH_READ; + } + + public function tplContent() { + html_edit(); // FIXME is this correct? Should we split it off completely? + } + +} diff --git a/inc/ActionRouter.php b/inc/ActionRouter.php index 54c39f3ca..9c8b21341 100644 --- a/inc/ActionRouter.php +++ b/inc/ActionRouter.php @@ -49,6 +49,8 @@ class ActionRouter { * * @param string $actionname * @fixme implement redirect on action change with post + * @fixme add event handling + * @fixme add the action name back to $ACT for plugins relying on it */ protected function setupAction($actionname) { try { -- GitLab