summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_test/tests/inc/io_deletefromfile.test.php15
-rw-r--r--_test/tests/inc/io_readfile.test.php7
-rw-r--r--_test/tests/inc/io_readfile/large.txt.bz2bin0 -> 47 bytes
-rw-r--r--_test/tests/inc/io_readfile/long.txt.bz2bin0 -> 53 bytes
-rw-r--r--_test/tests/inc/io_replaceinfile.test.php108
-rw-r--r--_test/tests/inc/io_savefile.test.php49
-rw-r--r--_test/tests/inc/remote.test.php8
-rw-r--r--inc/auth.php23
-rw-r--r--inc/cliopts.php4
-rw-r--r--inc/feedcreator.class.php56
-rw-r--r--inc/fetch.functions.php5
-rw-r--r--inc/io.php186
-rw-r--r--inc/remote.php2
-rw-r--r--lib/plugins/acl/admin.php11
-rw-r--r--lib/plugins/authplain/auth.php12
-rw-r--r--lib/plugins/config/settings/extra.class.php2
-rw-r--r--lib/scripts/behaviour.js26
-rw-r--r--lib/scripts/page.js13
-rw-r--r--lib/tpl/dokuwiki/main.php4
19 files changed, 397 insertions, 134 deletions
diff --git a/_test/tests/inc/io_deletefromfile.test.php b/_test/tests/inc/io_deletefromfile.test.php
new file mode 100644
index 000000000..e86150aac
--- /dev/null
+++ b/_test/tests/inc/io_deletefromfile.test.php
@@ -0,0 +1,15 @@
+<?php
+
+class io_deletefromfile_test extends DokuWikiTest {
+
+ function test_delete(){
+ $file = TMP_DIR.'/test.txt';
+ $contents = "The\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012";
+ io_saveFile($file, $contents);
+ $this->assertTrue(io_deleteFromFile($file, "Delete\012"));
+ $this->assertEquals("The\012Delete01\012Delete02\012DeleteX\012Test\012", io_readFile($file));
+ $this->assertTrue(io_deleteFromFile($file, "#Delete\\d+\012#", true));
+ $this->assertEquals("The\012DeleteX\012Test\012", io_readFile($file));
+ }
+
+}
diff --git a/_test/tests/inc/io_readfile.test.php b/_test/tests/inc/io_readfile.test.php
index e3e90cd8d..700c1902b 100644
--- a/_test/tests/inc/io_readfile.test.php
+++ b/_test/tests/inc/io_readfile.test.php
@@ -48,6 +48,11 @@ class io_readfile_test extends DokuWikiTest {
$this->assertEquals("The\015\012Test\015\012", io_readFile(__DIR__.'/io_readfile/test.txt.bz2', false));
$this->assertEquals(false, io_readFile(__DIR__.'/io_readfile/nope.txt.bz2'));
$this->assertEquals(false, io_readFile(__DIR__.'/io_readfile/corrupt.txt.bz2'));
+ // internal bzfile function
+ $this->assertEquals(array("The\015\012","Test\015\012"), bzfile(__DIR__.'/io_readfile/test.txt.bz2', true));
+ $this->assertEquals(array_fill(0, 120, str_repeat('a', 80)."\012"), bzfile(__DIR__.'/io_readfile/large.txt.bz2', true));
+ $line = str_repeat('a', 8888)."\012";
+ $this->assertEquals(array($line,"\012",$line,"!"), bzfile(__DIR__.'/io_readfile/long.txt.bz2', true));
}
-} \ No newline at end of file
+}
diff --git a/_test/tests/inc/io_readfile/large.txt.bz2 b/_test/tests/inc/io_readfile/large.txt.bz2
new file mode 100644
index 000000000..3135435f8
--- /dev/null
+++ b/_test/tests/inc/io_readfile/large.txt.bz2
Binary files differ
diff --git a/_test/tests/inc/io_readfile/long.txt.bz2 b/_test/tests/inc/io_readfile/long.txt.bz2
new file mode 100644
index 000000000..fb40759e6
--- /dev/null
+++ b/_test/tests/inc/io_readfile/long.txt.bz2
Binary files differ
diff --git a/_test/tests/inc/io_replaceinfile.test.php b/_test/tests/inc/io_replaceinfile.test.php
new file mode 100644
index 000000000..452ed7401
--- /dev/null
+++ b/_test/tests/inc/io_replaceinfile.test.php
@@ -0,0 +1,108 @@
+<?php
+
+class io_replaceinfile_test extends DokuWikiTest {
+
+ protected $contents = "The\012Delete\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012";
+
+ /*
+ * dependency for tests needing zlib extension to pass
+ */
+ public function test_ext_zlib() {
+ if (!extension_loaded('zlib')) {
+ $this->markTestSkipped('skipping all zlib tests. Need zlib extension');
+ }
+ }
+
+ /*
+ * dependency for tests needing zlib extension to pass
+ */
+ public function test_ext_bz2() {
+ if (!extension_loaded('bz2')) {
+ $this->markTestSkipped('skipping all bzip2 tests. Need bz2 extension');
+ }
+ }
+
+ function _write($file){
+
+ io_saveFile($file, $this->contents);
+ // Replace one, no regex
+ $this->assertTrue(io_replaceInFile($file, "Delete\012", "Delete00\012", false, 1));
+ $this->assertEquals("The\012Delete00\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012", io_readFile($file));
+ // Replace all, no regex
+ $this->assertTrue(io_replaceInFile($file, "Delete\012", "DeleteX\012", false, -1));
+ $this->assertEquals("The\012Delete00\012DeleteX\012Delete01\012Delete02\012DeleteX\012DeleteX\012Test\012", io_readFile($file));
+ // Replace two, regex and backreference
+ $this->assertTrue(io_replaceInFile($file, "#Delete(\\d+)\012#", "\\1\012", true, 2));
+ $this->assertEquals("The\01200\012DeleteX\01201\012Delete02\012DeleteX\012DeleteX\012Test\012", io_readFile($file));
+ // Delete and insert, no regex
+ $this->assertTrue(io_replaceInFile($file, "DeleteX\012", "Replace\012", false, 0));
+ $this->assertEquals("The\01200\01201\012Delete02\012Test\012Replace\012", io_readFile($file));
+ }
+
+ function test_replace(){
+ $this->_write(TMP_DIR.'/test.txt');
+ }
+
+
+ /**
+ * @depends test_ext_zlib
+ */
+ function test_gzwrite(){
+ $this->_write(TMP_DIR.'/test.txt.gz');
+ }
+
+ /**
+ * @depends test_ext_bz2
+ */
+ function test_bzwrite(){
+ $this->_write(TMP_DIR.'/test.txt.bz2');
+ }
+
+ /**
+ * Test for a non-regex replacement where $newline contains a backreference like construct - it shouldn't affect the replacement
+ */
+ function test_edgecase1()
+ {
+ $file = TMP_DIR . '/test.txt';
+
+ io_saveFile($file, $this->contents);
+ $this->assertTrue(io_replaceInFile($file, "Delete\012", "Delete\\00\012", false, -1));
+ $this->assertEquals("The\012Delete\\00\012Delete\\00\012Delete01\012Delete02\012Delete\\00\012DeleteX\012Test\012", io_readFile($file), "Edge case: backreference like construct in replacement line");
+ }
+ /**
+ * Test with replace all where replacement line == search line - must not timeout
+ *
+ * @small
+ */
+ function test_edgecase2() {
+ $file = TMP_DIR.'/test.txt';
+
+ io_saveFile($file, $this->contents);
+ $this->assertTrue(io_replaceInFile($file, "Delete\012", "Delete\012", false, -1));
+ $this->assertEquals("The\012Delete\012Delete\012Delete01\012Delete02\012Delete\012DeleteX\012Test\012", io_readFile($file), "Edge case: new line the same as old line");
+ }
+
+ /**
+ * Test where $oldline exactly matches one line and also matches part of other lines - only the exact match should be replaced
+ */
+ function test_edgecase3()
+ {
+ $file = TMP_DIR . '/test.txt';
+ $contents = "The\012Delete\01201Delete\01202Delete\012Test\012";
+
+ io_saveFile($file, $contents);
+ $this->assertTrue(io_replaceInFile($file, "Delete\012", "Replace\012", false, -1));
+ $this->assertEquals("The\012Replace\01201Delete\01202Delete\012Test\012", io_readFile($file), "Edge case: old line is a match for parts of other lines");
+ }
+
+ /**
+ * Test passing an invalid parameter.
+ *
+ * @expectedException PHPUnit_Framework_Error_Warning
+ */
+ function test_badparam()
+ {
+ /* The empty $oldline parameter should be caught before the file doesn't exist test. */
+ $this->assertFalse(io_replaceInFile(TMP_DIR.'/not_existing_file.txt', '', '', false, 0));
+ }
+}
diff --git a/_test/tests/inc/io_savefile.test.php b/_test/tests/inc/io_savefile.test.php
new file mode 100644
index 000000000..4a4d4671d
--- /dev/null
+++ b/_test/tests/inc/io_savefile.test.php
@@ -0,0 +1,49 @@
+<?php
+
+class io_savefile_test extends DokuWikiTest {
+
+ /*
+ * dependency for tests needing zlib extension to pass
+ */
+ public function test_ext_zlib() {
+ if (!extension_loaded('zlib')) {
+ $this->markTestSkipped('skipping all zlib tests. Need zlib extension');
+ }
+ }
+
+ /*
+ * dependency for tests needing zlib extension to pass
+ */
+ public function test_ext_bz2() {
+ if (!extension_loaded('bz2')) {
+ $this->markTestSkipped('skipping all bzip2 tests. Need bz2 extension');
+ }
+ }
+
+ function _write($file){
+ $contents = "The\012Write\012Test\012";
+ $this->assertTrue(io_saveFile($file, $contents));
+ $this->assertEquals($contents, io_readFile($file));
+ $this->assertTrue(io_saveFile($file, $contents, true));
+ $this->assertEquals($contents.$contents, io_readFile($file));
+ }
+
+ function test_write(){
+ $this->_write(TMP_DIR.'/test.txt');
+ }
+
+ /**
+ * @depends test_ext_zlib
+ */
+ function test_gzwrite(){
+ $this->_write(TMP_DIR.'/test.txt.gz');
+ }
+
+ /**
+ * @depends test_ext_bz2
+ */
+ function test_bzwrite(){
+ $this->_write(TMP_DIR.'/test.txt.bz2');
+ }
+
+}
diff --git a/_test/tests/inc/remote.test.php b/_test/tests/inc/remote.test.php
index d0d4eb7ce..037b1dc0b 100644
--- a/_test/tests/inc/remote.test.php
+++ b/_test/tests/inc/remote.test.php
@@ -160,10 +160,16 @@ class remote_test extends DokuWikiTest {
$this->assertTrue($this->remote->hasAccess());
}
+ /**
+ * @expectedException RemoteAccessDeniedException
+ */
function test_hasAccessFail() {
global $conf;
$conf['remote'] = 0;
- $this->assertFalse($this->remote->hasAccess());
+ // the hasAccess() should throw a Exception to keep the same semantics with xmlrpc.php.
+ // because the user(xmlrpc) check remote before .--> (!$conf['remote']) die('XML-RPC server not enabled.');
+ // so it must be a Exception when get here.
+ $this->remote->hasAccess();
}
function test_hasAccessFailAcl() {
diff --git a/inc/auth.php b/inc/auth.php
index 60b8c7c78..e04a6ca1a 100644
--- a/inc/auth.php
+++ b/inc/auth.php
@@ -739,28 +739,23 @@ function auth_aclcheck_cb($data) {
$user = utf8_strtolower($user);
$groups = array_map('utf8_strtolower', $groups);
}
- $user = $auth->cleanUser($user);
+ $user = auth_nameencode($auth->cleanUser($user));
$groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
- $user = auth_nameencode($user);
//prepend groups with @ and nameencode
- $cnt = count($groups);
- for($i = 0; $i < $cnt; $i++) {
- $groups[$i] = '@'.auth_nameencode($groups[$i]);
+ foreach($groups as &$group) {
+ $group = '@'.auth_nameencode($group);
}
$ns = getNS($id);
$perm = -1;
- if($user || count($groups)) {
- //add ALL group
- $groups[] = '@ALL';
- //add User
- if($user) $groups[] = $user;
- } else {
- $groups[] = '@ALL';
- }
-
+ //add ALL group
+ $groups[] = '@ALL';
+
+ //add User
+ if($user) $groups[] = $user;
+
//check exact match first
$matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
if(count($matches)) {
diff --git a/inc/cliopts.php b/inc/cliopts.php
index 7d71c7dc9..d7d06119a 100644
--- a/inc/cliopts.php
+++ b/inc/cliopts.php
@@ -447,7 +447,7 @@ class Doku_Cli_Opts_Error {
var $code;
var $msg;
- function Doku_Cli_Opts_Error($code, $msg) {
+ function __construct($code, $msg) {
$this->code = $code;
$this->msg = $msg;
}
@@ -468,7 +468,7 @@ class Doku_Cli_Opts_Container {
var $options = array();
var $args = array();
- function Doku_Cli_Opts_Container($options) {
+ function __construct($options) {
foreach ( $options[0] as $option ) {
if ( false !== ( strpos($option[0], '--') ) ) {
$opt_name = substr($option[0], 2);
diff --git a/inc/feedcreator.class.php b/inc/feedcreator.class.php
index b90da5724..fe444b39b 100644
--- a/inc/feedcreator.class.php
+++ b/inc/feedcreator.class.php
@@ -129,6 +129,9 @@ class FeedItem extends HtmlDescribable {
// var $source;
}
+/**
+ * Class EnclosureItem
+ */
class EnclosureItem extends HtmlDescribable {
/*
*
@@ -226,7 +229,7 @@ class FeedHtmlField {
* Creates a new instance of FeedHtmlField.
* @param string $parFieldContent: if given, sets the rawFieldContent property
*/
- function FeedHtmlField($parFieldContent) {
+ function __construct($parFieldContent) {
if ($parFieldContent) {
$this->rawFieldContent = $parFieldContent;
}
@@ -482,6 +485,8 @@ class FeedCreator extends HtmlDescribable {
var $additionalElements = Array();
+ var $_timeout;
+
/**
* Adds an FeedItem to the feed.
*
@@ -505,7 +510,7 @@ class FeedCreator extends HtmlDescribable {
* @param int $length the maximum length the string should be truncated to
* @return string the truncated string
*/
- function iTrunc($string, $length) {
+ static function iTrunc($string, $length) {
if (strlen($string)<=$length) {
return $string;
}
@@ -604,6 +609,8 @@ class FeedCreator extends HtmlDescribable {
/**
* @since 1.4
* @access private
+ *
+ * @param string $filename
*/
function _redirect($filename) {
// attention, heavily-commented-out-area
@@ -697,7 +704,7 @@ class FeedDate {
* Accepts RFC 822, ISO 8601 date formats as well as unix time stamps.
* @param mixed $dateString optional the date this FeedDate will represent. If not specified, the current date and time is used.
*/
- function FeedDate($dateString="") {
+ function __construct($dateString="") {
if ($dateString=="") $dateString = date("r");
if (is_numeric($dateString)) {
@@ -878,7 +885,10 @@ class RSSCreator091 extends FeedCreator {
*/
var $RSSVersion;
- function RSSCreator091() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->_setRSSVersion("0.91");
$this->contentType = "application/rss+xml";
}
@@ -886,6 +896,8 @@ class RSSCreator091 extends FeedCreator {
/**
* Sets this RSS feed's version number.
* @access private
+ *
+ * @param $version
*/
function _setRSSVersion($version) {
$this->RSSVersion = $version;
@@ -1034,7 +1046,10 @@ class RSSCreator091 extends FeedCreator {
*/
class RSSCreator20 extends RSSCreator091 {
- function RSSCreator20() {
+ /**
+ * Constructor
+ */
+ function __construct() {
parent::_setRSSVersion("2.0");
}
@@ -1051,7 +1066,10 @@ class RSSCreator20 extends RSSCreator091 {
*/
class PIECreator01 extends FeedCreator {
- function PIECreator01() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->encoding = "utf-8";
}
@@ -1113,7 +1131,10 @@ class PIECreator01 extends FeedCreator {
*/
class AtomCreator10 extends FeedCreator {
- function AtomCreator10() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->contentType = "application/atom+xml";
$this->encoding = "utf-8";
}
@@ -1200,7 +1221,10 @@ class AtomCreator10 extends FeedCreator {
*/
class AtomCreator03 extends FeedCreator {
- function AtomCreator03() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->contentType = "application/atom+xml";
$this->encoding = "utf-8";
}
@@ -1272,12 +1296,19 @@ class AtomCreator03 extends FeedCreator {
* @author Kai Blankenhorn <kaib@bitfolge.de>
*/
class MBOXCreator extends FeedCreator {
-
- function MBOXCreator() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->contentType = "text/plain";
$this->encoding = "utf-8";
}
+ /**
+ * @param string $input
+ * @param int $line_max
+ * @return string
+ */
function qp_enc($input = "", $line_max = 76) {
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$lines = preg_split("/(?:\r\n|\r|\n)/", $input);
@@ -1363,7 +1394,10 @@ class MBOXCreator extends FeedCreator {
*/
class OPMLCreator extends FeedCreator {
- function OPMLCreator() {
+ /**
+ * Constructor
+ */
+ function __construct() {
$this->encoding = "utf-8";
}
diff --git a/inc/fetch.functions.php b/inc/fetch.functions.php
index c99fbf20a..b8e75eaec 100644
--- a/inc/fetch.functions.php
+++ b/inc/fetch.functions.php
@@ -1,4 +1,4 @@
-<?php
+<?php
/**
* Functions used by lib/exe/fetch.php
* (not included by other parts of dokuwiki)
@@ -47,18 +47,15 @@ function sendFile($file, $mime, $dl, $cache, $public = false, $orig = null) {
// cache publically
header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT');
header('Cache-Control: public, proxy-revalidate, no-transform, max-age='.$maxage);
- header('Pragma: public');
} else {
// cache in browser
header('Expires: '.gmdate("D, d M Y H:i:s", $expires).' GMT');
header('Cache-Control: private, no-transform, max-age='.$maxage);
- header('Pragma: no-cache');
}
} else {
// no cache at all
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
header('Cache-Control: no-cache, no-transform');
- header('Pragma: no-cache');
}
//send important headers first, script stops here if '304 Not Modified' response
diff --git a/inc/io.php b/inc/io.php
index 0636a4b62..9be648824 100644
--- a/inc/io.php
+++ b/inc/io.php
@@ -127,22 +127,36 @@ function io_readFile($file,$clean=true){
* @author Andreas Gohr <andi@splitbrain.org>
*
* @param string $file filename
- * @return string|bool content or false on error
+ * @param bool $array return array of lines
+ * @return string|array|bool content or false on error
*/
-function bzfile($file){
+function bzfile($file, $array=false) {
$bz = bzopen($file,"r");
if($bz === false) return false;
+ if($array) $lines = array();
$str = '';
- while (!feof($bz)){
+ while (!feof($bz)) {
//8192 seems to be the maximum buffersize?
$buffer = bzread($bz,8192);
if(($buffer === false) || (bzerrno($bz) !== 0)) {
return false;
}
$str = $str . $buffer;
+ if($array) {
+ $pos = strpos($str, "\n");
+ while($pos !== false) {
+ $lines[] = substr($str, 0, $pos+1);
+ $str = substr($str, $pos+1);
+ $pos = strpos($str, "\n");
+ }
+ }
}
bzclose($bz);
+ if($array) {
+ if($str !== '') $lines[] = $str;
+ return $lines;
+ }
return $str;
}
@@ -191,13 +205,7 @@ function _io_writeWikiPage_action($data) {
}
/**
- * Saves $content to $file.
- *
- * If the third parameter is set to true the given content
- * will be appended.
- *
- * Uses gzip if extension is .gz
- * and bz2 if extension is .bz2
+ * Internal function to save contents to a file.
*
* @author Andreas Gohr <andi@splitbrain.org>
*
@@ -206,64 +214,97 @@ function _io_writeWikiPage_action($data) {
* @param bool $append
* @return bool true on success, otherwise false
*/
-function io_saveFile($file,$content,$append=false){
+function _io_saveFile($file, $content, $append) {
global $conf;
$mode = ($append) ? 'ab' : 'wb';
-
$fileexists = file_exists($file);
- io_makeFileDir($file);
- io_lock($file);
+
if(substr($file,-3) == '.gz'){
$fh = @gzopen($file,$mode.'9');
- if(!$fh){
- msg("Writing $file failed",-1);
- io_unlock($file);
- return false;
- }
+ if(!$fh) return false;
gzwrite($fh, $content);
gzclose($fh);
}else if(substr($file,-4) == '.bz2'){
- $fh = @bzopen($file,$mode{0});
- if(!$fh){
- msg("Writing $file failed", -1);
- io_unlock($file);
- return false;
+ if($append) {
+ $bzcontent = bzfile($file);
+ if($bzcontent === false) return false;
+ $content = $bzcontent.$content;
}
+ $fh = @bzopen($file,'w');
+ if(!$fh) return false;
bzwrite($fh, $content);
bzclose($fh);
}else{
$fh = @fopen($file,$mode);
- if(!$fh){
- msg("Writing $file failed",-1);
- io_unlock($file);
- return false;
- }
+ if(!$fh) return false;
fwrite($fh, $content);
fclose($fh);
}
if(!$fileexists and !empty($conf['fperm'])) chmod($file, $conf['fperm']);
- io_unlock($file);
return true;
}
/**
- * Delete exact linematch for $badline from $file.
+ * Saves $content to $file.
*
- * Be sure to include the trailing newline in $badline
+ * If the third parameter is set to true the given content
+ * will be appended.
*
* Uses gzip if extension is .gz
+ * and bz2 if extension is .bz2
*
- * 2005-10-14 : added regex option -- Christopher Smith <chris@jalakai.co.uk>
+ * @author Andreas Gohr <andi@splitbrain.org>
*
- * @author Steven Danz <steven-danz@kc.rr.com>
+ * @param string $file filename path to file
+ * @param string $content
+ * @param bool $append
+ * @return bool true on success, otherwise false
+ */
+function io_saveFile($file, $content, $append=false) {
+ io_makeFileDir($file);
+ io_lock($file);
+ if(!_io_saveFile($file, $content, $append)) {
+ msg("Writing $file failed",-1);
+ io_unlock($file);
+ return false;
+ }
+ io_unlock($file);
+ return true;
+}
+
+/**
+ * Replace one or more occurrences of a line in a file.
*
- * @param string $file filename
- * @param string $badline exact linematch to remove
- * @param bool $regex use regexp?
+ * The default, when $maxlines is 0 is to delete all matching lines then append a single line.
+ * A regex that matches any part of the line will remove the entire line in this mode.
+ * Captures in $newline are not available.
+ *
+ * Otherwise each line is matched and replaced individually, up to the first $maxlines lines
+ * or all lines if $maxlines is -1. If $regex is true then captures can be used in $newline.
+ *
+ * Be sure to include the trailing newline in $oldline when replacing entire lines.
+ *
+ * Uses gzip if extension is .gz
+ * and bz2 if extension is .bz2
+ *
+ * @author Steven Danz <steven-danz@kc.rr.com>
+ * @author Christopher Smith <chris@jalakai.co.uk>
+ * @author Patrick Brown <ptbrown@whoopdedo.org>
+ *
+ * @param string $file filename
+ * @param string $oldline exact linematch to remove
+ * @param string $newline new line to insert
+ * @param bool $regex use regexp?
+ * @param int $maxlines number of occurrences of the line to replace
* @return bool true on success
*/
-function io_deleteFromFile($file,$badline,$regex=false){
+function io_replaceInFile($file, $oldline, $newline, $regex=false, $maxlines=0) {
+ if ((string)$oldline === '') {
+ trigger_error('$oldline parameter cannot be empty in io_replaceInFile()', E_USER_WARNING);
+ return false;
+ }
+
if (!file_exists($file)) return true;
io_lock($file);
@@ -271,41 +312,40 @@ function io_deleteFromFile($file,$badline,$regex=false){
// load into array
if(substr($file,-3) == '.gz'){
$lines = gzfile($file);
+ }else if(substr($file,-4) == '.bz2'){
+ $lines = bzfile($file, true);
}else{
$lines = file($file);
}
- // remove all matching lines
- if ($regex) {
- $lines = preg_grep($badline,$lines,PREG_GREP_INVERT);
- } else {
- $pos = array_search($badline,$lines); //return null or false if not found
- while(is_int($pos)){
- unset($lines[$pos]);
- $pos = array_search($badline,$lines);
+ // make non-regexes into regexes
+ $pattern = $regex ? $oldline : '/^'.preg_quote($oldline,'/').'$/';
+ $replace = $regex ? $newline : addcslashes($newline, '\$');
+
+ // remove matching lines
+ if ($maxlines > 0) {
+ $count = 0;
+ $matched = 0;
+ while (($count < $maxlines) && (list($i,$line) = each($lines))) {
+ // $matched will be set to 0|1 depending on whether pattern is matched and line replaced
+ $lines[$i] = preg_replace($pattern, $replace, $line, -1, $matched);
+ if ($matched) $count++;
+ }
+ } else if ($maxlines == 0) {
+ $lines = preg_grep($pattern, $lines, PREG_GREP_INVERT);
+
+ if ((string)$newline !== ''){
+ $lines[] = $newline;
}
+ } else {
+ $lines = preg_replace($pattern, $replace, $lines);
}
if(count($lines)){
- $content = join('',$lines);
- if(substr($file,-3) == '.gz'){
- $fh = @gzopen($file,'wb9');
- if(!$fh){
- msg("Removing content from $file failed",-1);
- io_unlock($file);
- return false;
- }
- gzwrite($fh, $content);
- gzclose($fh);
- }else{
- $fh = @fopen($file,'wb');
- if(!$fh){
- msg("Removing content from $file failed",-1);
- io_unlock($file);
- return false;
- }
- fwrite($fh, $content);
- fclose($fh);
+ if(!_io_saveFile($file, join('',$lines), false)) {
+ msg("Removing content from $file failed",-1);
+ io_unlock($file);
+ return false;
}
}else{
@unlink($file);
@@ -316,6 +356,22 @@ function io_deleteFromFile($file,$badline,$regex=false){
}
/**
+ * Delete lines that match $badline from $file.
+ *
+ * Be sure to include the trailing newline in $badline
+ *
+ * @author Patrick Brown <ptbrown@whoopdedo.org>
+ *
+ * @param string $file filename
+ * @param string $badline exact linematch to remove
+ * @param bool $regex use regexp?
+ * @return bool true on success
+ */
+function io_deleteFromFile($file,$badline,$regex=false){
+ return io_replaceInFile($file,$badline,null,$regex,0);
+}
+
+/**
* Tries to lock a file
*
* Locking is only done for io_savefile and uses directories
diff --git a/inc/remote.php b/inc/remote.php
index 861353a19..3e032049d 100644
--- a/inc/remote.php
+++ b/inc/remote.php
@@ -234,7 +234,7 @@ class RemoteAPI {
global $INPUT;
if (!$conf['remote']) {
- return false;
+ throw new RemoteAccessDeniedException('server error. RPC server not enabled.',-32604); //should not be here,just throw
}
if(!$conf['useacl']) {
return true;
diff --git a/lib/plugins/acl/admin.php b/lib/plugins/acl/admin.php
index 374205769..f4baec994 100644
--- a/lib/plugins/acl/admin.php
+++ b/lib/plugins/acl/admin.php
@@ -682,7 +682,6 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin {
*/
function _acl_add($acl_scope, $acl_user, $acl_level){
global $config_cascade;
- $acl_config = file_get_contents($config_cascade['acl']['default']);
$acl_user = auth_nameencode($acl_user,true);
// max level for pagenames is edit
@@ -692,9 +691,7 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin {
$new_acl = "$acl_scope\t$acl_user\t$acl_level\n";
- $new_config = $acl_config.$new_acl;
-
- return io_saveFile($config_cascade['acl']['default'], $new_config);
+ return io_saveFile($config_cascade['acl']['default'], $new_acl, true);
}
/**
@@ -704,15 +701,11 @@ class admin_plugin_acl extends DokuWiki_Admin_Plugin {
*/
function _acl_del($acl_scope, $acl_user){
global $config_cascade;
- $acl_config = file($config_cascade['acl']['default']);
$acl_user = auth_nameencode($acl_user,true);
$acl_pattern = '^'.preg_quote($acl_scope,'/').'[ \t]+'.$acl_user.'[ \t]+[0-8].*$';
- // save all non!-matching
- $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT);
-
- return io_saveFile($config_cascade['acl']['default'], join('',$new_config));
+ return io_deleteFromFile($config_cascade['acl']['default'], "/$acl_pattern/", true);
}
/**
diff --git a/lib/plugins/authplain/auth.php b/lib/plugins/authplain/auth.php
index bd46c61a7..8ec632dad 100644
--- a/lib/plugins/authplain/auth.php
+++ b/lib/plugins/authplain/auth.php
@@ -188,15 +188,9 @@ class auth_plugin_authplain extends DokuWiki_Auth_Plugin {
$userline = $this->_createUserLine($newuser, $userinfo['pass'], $userinfo['name'], $userinfo['mail'], $userinfo['grps']);
- if(!$this->deleteUsers(array($user))) {
- msg($this->getLang('writefail'), -1);
- return false;
- }
-
- if(!io_saveFile($config_cascade['plainauth.users']['default'], $userline, true)) {
- msg('There was an error modifying your user data. You should register again.', -1);
- // FIXME, user has been deleted but not recreated, should force a logout and redirect to login page
- // Should replace the delete/save hybrid modify with an atomic io_replaceInFile
+ if(!io_replaceInFile($config_cascade['plainauth.users']['default'], '/^'.$user.':/', $userline, true)) {
+ msg('There was an error modifying your user data. You may need to register again.', -1);
+ // FIXME, io functions should be fail-safe so existing data isn't lost
$ACT = 'register';
return false;
}
diff --git a/lib/plugins/config/settings/extra.class.php b/lib/plugins/config/settings/extra.class.php
index c6a3f9dae..2445577d1 100644
--- a/lib/plugins/config/settings/extra.class.php
+++ b/lib/plugins/config/settings/extra.class.php
@@ -15,7 +15,7 @@ if (!class_exists('setting_sepchar')) {
* @param string $key
* @param array|null $param array with metadata of setting
*/
- function setting_sepchar($key,$param=null) {
+ function __construct($key,$param=null) {
$str = '_-.';
for ($i=0;$i<strlen($str);$i++) $this->_choices[] = $str{$i};
diff --git a/lib/scripts/behaviour.js b/lib/scripts/behaviour.js
index 97955dad9..b05949a90 100644
--- a/lib/scripts/behaviour.js
+++ b/lib/scripts/behaviour.js
@@ -1,37 +1,41 @@
/**
* Hides elements with a slide animation
*
- * @param fn optional callback to run after hiding
+ * @param {function} fn optional callback to run after hiding
+ * @param {bool} noaria supress aria-expanded state setting
* @author Adrian Lang <mail@adrianlang.de>
*/
-jQuery.fn.dw_hide = function(fn) {
- this.attr('aria-expanded', 'false');
+jQuery.fn.dw_hide = function(fn, noaria) {
+ if(!noaria) this.attr('aria-expanded', 'false');
return this.slideUp('fast', fn);
};
/**
* Unhides elements with a slide animation
*
- * @param fn optional callback to run after hiding
+ * @param {function} fn optional callback to run after hiding
+ * @param {bool} noaria supress aria-expanded state setting
* @author Adrian Lang <mail@adrianlang.de>
*/
-jQuery.fn.dw_show = function(fn) {
- this.attr('aria-expanded', 'true');
+jQuery.fn.dw_show = function(fn, noaria) {
+ if(!noaria) this.attr('aria-expanded', 'true');
return this.slideDown('fast', fn);
};
/**
* Toggles visibility of an element using a slide element
*
- * @param bool the current state of the element (optional)
+ * @param {bool} state the current state of the element (optional)
+ * @param {function} fn callback after the state has been toggled
+ * @param {bool} noaria supress aria-expanded state setting
*/
-jQuery.fn.dw_toggle = function(bool, fn) {
+jQuery.fn.dw_toggle = function(state, fn, noaria) {
return this.each(function() {
var $this = jQuery(this);
- if (typeof bool === 'undefined') {
- bool = $this.is(':hidden');
+ if (typeof state === 'undefined') {
+ state = $this.is(':hidden');
}
- $this[bool ? "dw_show" : "dw_hide" ](fn);
+ $this[state ? "dw_show" : "dw_hide" ](fn, noaria);
});
};
diff --git a/lib/scripts/page.js b/lib/scripts/page.js
index 7b4958d82..a179ae2a8 100644
--- a/lib/scripts/page.js
+++ b/lib/scripts/page.js
@@ -109,8 +109,14 @@ dw_page = {
* as well. A state indicator is inserted into the handle and can be styled
* by CSS.
*
- * @param selector handle What should be clicked to toggle
- * @param selector content This element will be toggled
+ * To properly reserve space for the expanded element, the sliding animation is
+ * done on the children of the content. To make that look good and to make sure aria
+ * attributes are assigned correctly, it's recommended to make sure that the content
+ * element contains a single child element only.
+ *
+ * @param {selector} handle What should be clicked to toggle
+ * @param {selector} content This element will be toggled
+ * @param {int} state initial state (-1 = open, 1 = closed)
*/
makeToggle: function(handle, content, state){
var $handle, $content, $clicky, $child, setClicky;
@@ -160,8 +166,9 @@ dw_page = {
// Start animation and assure that $toc is hidden/visible
$child.dw_toggle(hidden, function () {
$content.toggle(hidden);
+ $content.attr('aria-expanded', hidden);
$content.css('min-height',''); // remove min-height again
- });
+ }, true);
};
// the state indicator
diff --git a/lib/tpl/dokuwiki/main.php b/lib/tpl/dokuwiki/main.php
index 165230e8a..eea1df71a 100644
--- a/lib/tpl/dokuwiki/main.php
+++ b/lib/tpl/dokuwiki/main.php
@@ -38,12 +38,12 @@ $showSidebar = $hasSidebar && ($ACT=='show');
<!-- ********** ASIDE ********** -->
<div id="dokuwiki__aside"><div class="pad aside include group">
<h3 class="toggle"><?php echo $lang['sidebar'] ?></h3>
- <div class="content">
+ <div class="content"><div class="group">
<?php tpl_flush() ?>
<?php tpl_includeFile('sidebarheader.html') ?>
<?php tpl_include_page($conf['sidebar'], true, true) ?>
<?php tpl_includeFile('sidebarfooter.html') ?>
- </div>
+ </div></div>
</div></div><!-- /aside -->
<?php endif; ?>