mirror of
https://github.com/php/web-wiki.git
synced 2026-03-23 23:12:22 +01:00
Upgrade to 2022-07-31a Igor
This commit is contained in:
16
dokuwiki/SECURITY.md
Normal file
16
dokuwiki/SECURITY.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Security Policy
|
||||
|
||||
Security vulnerabilities can be reported for the current stable release (branch `stable`) and the `master` branch.
|
||||
|
||||
We try to fix vulnerabilites as fast as possible, but please keep in mind that this is a project run by volunteers. Depending on the severity we may release hotfixes for the current stable release or may simply incorporate the fix in the next proper release.
|
||||
|
||||
**This policy only applies to DokuWiki and the bundled plugins. Do not report issues about 3rd party plugins here.**
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
You have multiple options on reporting vulnerabilities
|
||||
|
||||
* Use [huntr.dev](https://www.huntr.dev/bounties/disclose/?target=https%3A%2F%2Fgithub.com%2Fsplitbrain%2Fdokuwiki%2F)
|
||||
* Send an e-mail to [Andi](mailto:andi@splitbrain.org)
|
||||
* Open a [Github Issue](https://github.com/splitbrain/dokuwiki/issues)
|
||||
* Send a mail to the [Mailing List](https://www.dokuwiki.org/mailinglist)
|
||||
@@ -1 +1 @@
|
||||
2020-07-29a "Hogfather"
|
||||
2022-07-31a "Igor"
|
||||
|
||||
@@ -118,6 +118,27 @@ class PageCLI extends CLI {
|
||||
true,
|
||||
'unlock'
|
||||
);
|
||||
|
||||
/* gmeta command */
|
||||
$options->registerCommand(
|
||||
'getmeta',
|
||||
'Prints metadata value for a page to stdout.'
|
||||
);
|
||||
$options->registerArgument(
|
||||
'wikipage',
|
||||
'The wiki page to get the metadata for',
|
||||
true,
|
||||
'getmeta'
|
||||
);
|
||||
$options->registerArgument(
|
||||
'key',
|
||||
'The name of the metadata item to be retrieved.' . "\n" .
|
||||
'If empty, an array of all the metadata items is returned.' ."\n" .
|
||||
'For retrieving items that are stored in sub-arrays, separate the ' .
|
||||
'keys of the different levels by spaces, in quotes, eg "date modified".',
|
||||
false,
|
||||
'getmeta'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +181,13 @@ class PageCLI extends CLI {
|
||||
$this->clearLock($wiki_id);
|
||||
$this->success("$wiki_id unlocked");
|
||||
break;
|
||||
case 'getmeta':
|
||||
$wiki_id = array_shift($args);
|
||||
$key = trim(array_shift($args));
|
||||
$meta = p_get_metadata($wiki_id, $key, METADATA_RENDER_UNLIMITED);
|
||||
echo trim(json_encode($meta, JSON_PRETTY_PRINT));
|
||||
echo "\n";
|
||||
break;
|
||||
default:
|
||||
echo $options->help();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use dokuwiki\Utf8\Sort;
|
||||
use dokuwiki\File\PageResolver;
|
||||
use splitbrain\phpcli\CLI;
|
||||
use splitbrain\phpcli\Options;
|
||||
|
||||
@@ -77,13 +79,13 @@ class WantedPagesCLI extends CLI {
|
||||
foreach($this->getPages($startdir) as $page) {
|
||||
$this->internalLinks($page);
|
||||
}
|
||||
ksort($this->result);
|
||||
Sort::ksort($this->result);
|
||||
foreach($this->result as $main => $subs) {
|
||||
if($this->skip) {
|
||||
print "$main\n";
|
||||
} else {
|
||||
$subs = array_unique($subs);
|
||||
sort($subs);
|
||||
Sort::sort($subs);
|
||||
foreach($subs as $sub) {
|
||||
printf("%-40s %s\n", $main, $sub);
|
||||
}
|
||||
@@ -160,14 +162,12 @@ class WantedPagesCLI extends CLI {
|
||||
protected function internalLinks($page) {
|
||||
global $conf;
|
||||
$instructions = p_get_instructions(file_get_contents($page['file']));
|
||||
$cns = getNS($page['id']);
|
||||
$exists = false;
|
||||
$resolver = new PageResolver($page['id']);
|
||||
$pid = $page['id'];
|
||||
foreach($instructions as $ins) {
|
||||
if($ins[0] == 'internallink' || ($conf['camelcase'] && $ins[0] == 'camelcaselink')) {
|
||||
$mid = $ins[1][0];
|
||||
resolve_pageid($cns, $mid, $exists);
|
||||
if(!$exists) {
|
||||
$mid = $resolver->resolveId($ins[1][0]);
|
||||
if(!page_exists($mid)) {
|
||||
list($mid) = explode('#', $mid); //record pages without hashes
|
||||
|
||||
if($this->sort == 'origin') {
|
||||
|
||||
33
dokuwiki/composer.json
Normal file
33
dokuwiki/composer.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "splitbrain/dokuwiki",
|
||||
"description": "DokuWiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database",
|
||||
"homepage": "https://www.dokuwiki.org",
|
||||
"type": "project",
|
||||
"license": "GPL v2",
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"ext-json": "*",
|
||||
"splitbrain/php-archive": "~1.0",
|
||||
"phpseclib/phpseclib": "~2.0",
|
||||
"simplepie/simplepie": "^1.4",
|
||||
"geshi/geshi": "dev-master as 1.0.x-dev",
|
||||
"openpsa/universalfeedcreator": "^1.8",
|
||||
"aziraphale/email-address-validator": "^2",
|
||||
"marcusschwarz/lesserphp": "^0.5.1",
|
||||
"splitbrain/php-cli": "^1.1",
|
||||
"splitbrain/slika": "^1.0",
|
||||
"kissifrot/php-ixr": "^1.8"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.2"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "The mbstring extension is highly reccomended to speed up all UTF-8 handling",
|
||||
"ext-intl": "The intl extension allows for better locale specific sorting",
|
||||
"squizlabs/php_codesniffer": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
|
||||
"phpunit/phpunit": "Allows automated tests to be run without system-wide install (only version 4-7 are supported)."
|
||||
},
|
||||
"prefer-stable": true
|
||||
}
|
||||
621
dokuwiki/composer.lock
generated
Normal file
621
dokuwiki/composer.lock
generated
Normal file
@@ -0,0 +1,621 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "09f5f3726c3993d80980df99ab2a59dc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aziraphale/email-address-validator",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aziraphale/email-address-validator.git",
|
||||
"reference": "fa25bc22c1c0b6491657c91473fae3e40719a650"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aziraphale/email-address-validator/zipball/fa25bc22c1c0b6491657c91473fae3e40719a650",
|
||||
"reference": "fa25bc22c1c0b6491657c91473fae3e40719a650",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"EmailAddressValidator": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Dave Child",
|
||||
"email": "dave@addedbytes.com"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Gillard",
|
||||
"email": "andrew@lorddeath.net"
|
||||
}
|
||||
],
|
||||
"description": "Fork of AddedBytes' PHP EmailAddressValidator script, now with Composer support!",
|
||||
"homepage": "https://github.com/aziraphale/email-address-validator",
|
||||
"support": {
|
||||
"issues": "https://github.com/aziraphale/email-address-validator/issues",
|
||||
"source": "https://github.com/aziraphale/email-address-validator/tree/master"
|
||||
},
|
||||
"time": "2017-05-22T14:05:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "geshi/geshi",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GeSHi/geshi-1.0.git",
|
||||
"reference": "3c12a7931d509c5e3557c5ed44c9a32e9c917c7d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/GeSHi/geshi-1.0/zipball/3c12a7931d509c5e3557c5ed44c9a32e9c917c7d",
|
||||
"reference": "3c12a7931d509c5e3557c5ed44c9a32e9c917c7d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.2"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/geshi/",
|
||||
"src/geshi.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0+"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Benny Baumann",
|
||||
"email": "BenBE@geshi.org",
|
||||
"homepage": "http://blog.benny-baumann.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Generic Syntax Highlighter",
|
||||
"homepage": "http://qbnz.com/highlighter/",
|
||||
"support": {
|
||||
"forum": "https://lists.sourceforge.net/lists/listinfo/geshi-users",
|
||||
"irc": "irc://irc.freenode.org/geshi",
|
||||
"issues": "https://sourceforge.net/p/geshi/feature-requests/",
|
||||
"source": "https://github.com/GeSHi/geshi-1.0/tree/master"
|
||||
},
|
||||
"time": "2020-06-22T15:46:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kissifrot/php-ixr",
|
||||
"version": "1.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kissifrot/php-ixr.git",
|
||||
"reference": "4477cd1a67416ce5b6a2080f9a79d9eb50a965c1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kissifrot/php-ixr/zipball/4477cd1a67416ce5b6a2080f9a79d9eb50a965c1",
|
||||
"reference": "4477cd1a67416ce5b6a2080f9a79d9eb50a965c1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"IXR\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Incutio Ltd 2010 - Simon Willison",
|
||||
"homepage": "http://scripts.incutio.com/xmlrpc/"
|
||||
}
|
||||
],
|
||||
"description": "Incutio XML-RPC library (IXR)",
|
||||
"homepage": "http://scripts.incutio.com/xmlrpc/",
|
||||
"keywords": [
|
||||
"remote procedure call",
|
||||
"rpc",
|
||||
"xlm-rpc",
|
||||
"xmlrpc"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/kissifrot/php-ixr/issues",
|
||||
"source": "https://github.com/kissifrot/php-ixr/tree/master"
|
||||
},
|
||||
"time": "2016-11-17T12:00:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "marcusschwarz/lesserphp",
|
||||
"version": "v0.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarcusSchwarz/lesserphp.git",
|
||||
"reference": "77ba82b5218ff228267d3b0e5ec8697be75e86a7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarcusSchwarz/lesserphp/zipball/77ba82b5218ff228267d3b0e5ec8697be75e86a7",
|
||||
"reference": "77ba82b5218ff228267d3b0e5ec8697be75e86a7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=4.8.35 <8"
|
||||
},
|
||||
"bin": [
|
||||
"plessc"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.5.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"lessc.inc.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT",
|
||||
"GPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Leaf Corcoran",
|
||||
"email": "leafot@gmail.com",
|
||||
"homepage": "http://leafo.net"
|
||||
},
|
||||
{
|
||||
"name": "Marcus Schwarz",
|
||||
"email": "github@maswaba.de",
|
||||
"homepage": "https://www.maswaba.de"
|
||||
}
|
||||
],
|
||||
"description": "lesserphp is a compiler for LESS written in PHP based on leafo's lessphp.",
|
||||
"homepage": "http://leafo.net/lessphp/",
|
||||
"support": {
|
||||
"issues": "https://github.com/MarcusSchwarz/lesserphp/issues",
|
||||
"source": "https://github.com/MarcusSchwarz/lesserphp/tree/v0.5.5"
|
||||
},
|
||||
"time": "2021-03-10T17:56:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "openpsa/universalfeedcreator",
|
||||
"version": "v1.8.4.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/flack/UniversalFeedCreator.git",
|
||||
"reference": "e4736a68eef454a83acd100230a2e15b424f899f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/flack/UniversalFeedCreator/zipball/e4736a68eef454a83acd100230a2e15b424f899f",
|
||||
"reference": "e4736a68eef454a83acd100230a2e15b424f899f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"lib/constants.php"
|
||||
],
|
||||
"classmap": [
|
||||
"lib"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Flack",
|
||||
"email": "flack@contentcontrol-berlin.de",
|
||||
"homepage": "http://www.contentcontrol-berlin.de/"
|
||||
}
|
||||
],
|
||||
"description": "RSS and Atom feed generator by Kai Blankenhorn",
|
||||
"keywords": [
|
||||
"atom",
|
||||
"georss",
|
||||
"gpx",
|
||||
"opml",
|
||||
"pie",
|
||||
"rss"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/flack/UniversalFeedCreator/issues",
|
||||
"source": "https://github.com/flack/UniversalFeedCreator/tree/v1.8.4.1"
|
||||
},
|
||||
"time": "2022-04-04T10:02:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
"version": "2.0.31",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpseclib/phpseclib.git",
|
||||
"reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4",
|
||||
"reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phing/phing": "~2.7",
|
||||
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
|
||||
"squizlabs/php_codesniffer": "~2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
|
||||
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
|
||||
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
|
||||
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"phpseclib/bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"phpseclib\\": "phpseclib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jim Wigginton",
|
||||
"email": "terrafrost@php.net",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Patrick Monnerat",
|
||||
"email": "pm@datasphere.ch",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Andreas Fischer",
|
||||
"email": "bantu@phpbb.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Hans-Jürgen Petrich",
|
||||
"email": "petrich@tronic-media.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
|
||||
"homepage": "http://phpseclib.sourceforge.net",
|
||||
"keywords": [
|
||||
"BigInteger",
|
||||
"aes",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"blowfish",
|
||||
"crypto",
|
||||
"cryptography",
|
||||
"encryption",
|
||||
"rsa",
|
||||
"security",
|
||||
"sftp",
|
||||
"signature",
|
||||
"signing",
|
||||
"ssh",
|
||||
"twofish",
|
||||
"x.509",
|
||||
"x509"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpseclib/phpseclib/issues",
|
||||
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.31"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/terrafrost",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpseclib",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-04-06T13:56:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplepie/simplepie",
|
||||
"version": "1.5.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplepie/simplepie.git",
|
||||
"reference": "1c68e14ca3ac84346b6e6fe3c5eedf725d0f92c6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplepie/simplepie/zipball/1c68e14ca3ac84346b6e6fe3c5eedf725d0f92c6",
|
||||
"reference": "1c68e14ca3ac84346b6e6fe3c5eedf725d0f92c6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pcre": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5.4.3 || ~6.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "",
|
||||
"ext-iconv": "",
|
||||
"ext-intl": "",
|
||||
"ext-mbstring": "",
|
||||
"mf2/mf2": "Microformat module that allows for parsing HTML for microformats"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"SimplePie": "library"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ryan Parman",
|
||||
"homepage": "http://ryanparman.com/",
|
||||
"role": "Creator, alumnus developer"
|
||||
},
|
||||
{
|
||||
"name": "Sam Sneddon",
|
||||
"homepage": "https://gsnedders.com/",
|
||||
"role": "Alumnus developer"
|
||||
},
|
||||
{
|
||||
"name": "Ryan McCue",
|
||||
"email": "me@ryanmccue.info",
|
||||
"homepage": "http://ryanmccue.info/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple Atom/RSS parsing library for PHP",
|
||||
"homepage": "http://simplepie.org/",
|
||||
"keywords": [
|
||||
"atom",
|
||||
"feeds",
|
||||
"rss"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/simplepie/simplepie/issues",
|
||||
"source": "https://github.com/simplepie/simplepie/tree/1.5.6"
|
||||
},
|
||||
"time": "2020-10-14T07:17:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "splitbrain/php-archive",
|
||||
"version": "1.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/splitbrain/php-archive.git",
|
||||
"reference": "211a2198b73b233d7d2b6159462e11cd9a91348a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/splitbrain/php-archive/zipball/211a2198b73b233d7d2b6159462e11cd9a91348a",
|
||||
"reference": "211a2198b73b233d7d2b6159462e11cd9a91348a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-bz2": "*",
|
||||
"ext-zip": "*",
|
||||
"mikey179/vfsstream": "^1.6",
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "Used for proper filename encode handling",
|
||||
"ext-mbstring": "Can be used alternatively for handling filename encoding"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"splitbrain\\PHPArchive\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"description": "Pure-PHP implementation to read and write TAR and ZIP archives",
|
||||
"keywords": [
|
||||
"archive",
|
||||
"extract",
|
||||
"tar",
|
||||
"unpack",
|
||||
"unzip",
|
||||
"zip"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/splitbrain/php-archive/issues",
|
||||
"source": "https://github.com/splitbrain/php-archive/tree/1.2.1"
|
||||
},
|
||||
"time": "2021-02-22T17:59:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "splitbrain/php-cli",
|
||||
"version": "1.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/splitbrain/php-cli.git",
|
||||
"reference": "8c2c001b1b55d194402cf18aad2757049ac6d575"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/splitbrain/php-cli/zipball/8c2c001b1b55d194402cf18aad2757049ac6d575",
|
||||
"reference": "8c2c001b1b55d194402cf18aad2757049ac6d575",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows you to make the CLI available as PSR-3 logger"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"splitbrain\\phpcli\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"description": "Easy command line scripts for PHP with opt parsing and color output. No dependencies",
|
||||
"keywords": [
|
||||
"argparse",
|
||||
"cli",
|
||||
"command line",
|
||||
"console",
|
||||
"getopt",
|
||||
"optparse",
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/splitbrain/php-cli/issues",
|
||||
"source": "https://github.com/splitbrain/php-cli/tree/1.1.8"
|
||||
},
|
||||
"time": "2021-02-05T12:02:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "splitbrain/slika",
|
||||
"version": "1.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/splitbrain/slika.git",
|
||||
"reference": "be0785cb6b7def847df5d92e0e5fde57def7220f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/splitbrain/slika/zipball/be0785cb6b7def847df5d92e0e5fde57def7220f",
|
||||
"reference": "be0785cb6b7def847df5d92e0e5fde57def7220f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "PHP's builtin image manipulation library. Alternatively use an installation of ImageMagick"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"splitbrain\\slika\\": "src",
|
||||
"splitbrain\\slika\\tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andreas Gohr",
|
||||
"email": "andi@splitbrain.org"
|
||||
}
|
||||
],
|
||||
"description": "Simple image resizing",
|
||||
"support": {
|
||||
"issues": "https://github.com/splitbrain/slika/issues",
|
||||
"source": "https://github.com/splitbrain/slika/tree/1.0.5"
|
||||
},
|
||||
"time": "2022-02-04T22:41:26+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [
|
||||
{
|
||||
"package": "geshi/geshi",
|
||||
"version": "9999999-dev",
|
||||
"alias": "1.0.x-dev",
|
||||
"alias_normalized": "1.0.9999999.9999999-dev"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"geshi/geshi": 20
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=7.2",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.2"
|
||||
},
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
@@ -66,6 +66,7 @@ $conf['auth_security_timeout'] = 900; //time (seconds) auth data is considere
|
||||
$conf['securecookie'] = 1; //never send HTTPS cookies via HTTP
|
||||
$conf['remote'] = 0; //Enable/disable remote interfaces
|
||||
$conf['remoteuser'] = '!!not set!!'; //user/groups that have access to remote interface (comma separated). leave empty to allow all users
|
||||
$conf['remotecors'] = ''; //enable Cross-Origin Resource Sharing (CORS) for the remote interfaces. Asterisk (*) to allow all origins. leave empty to deny.
|
||||
|
||||
/* Antispam Features */
|
||||
$conf['usewordblock']= 1; //block spam based on words? 0|1
|
||||
@@ -111,6 +112,7 @@ $conf['mailfrom'] = ''; //use this email when sending mails
|
||||
$conf['mailreturnpath'] = ''; //use this email as returnpath for bounce mails
|
||||
$conf['mailprefix'] = ''; //use this as prefix of outgoing mails
|
||||
$conf['htmlmail'] = 1; //send HTML multipart mails
|
||||
$conf['dontlog'] = 'debug'; //logging facilites that should be disabled
|
||||
|
||||
/* Syndication Settings */
|
||||
$conf['sitemap'] = 0; //Create a google sitemap? How often? In days.
|
||||
|
||||
@@ -33,6 +33,7 @@ amazon.uk https://www.amazon.co.uk/dp/{URL}
|
||||
paypal https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=
|
||||
phpfn https://secure.php.net/{NAME}
|
||||
skype skype:{NAME}
|
||||
google https://www.google.com/search?q=
|
||||
google.de https://www.google.de/search?q=
|
||||
go https://www.google.com/search?q={URL}&btnI=lucky
|
||||
user :user:{NAME}
|
||||
|
||||
@@ -53,6 +53,8 @@ odp !application/vnd.oasis.opendocument.presentation
|
||||
ods !application/vnd.oasis.opendocument.spreadsheet
|
||||
odt !application/vnd.oasis.opendocument.text
|
||||
|
||||
svg image/svg+xml
|
||||
|
||||
# You should enable HTML and Text uploads only for restricted Wikis.
|
||||
# Spammers are known to upload spam pages through unprotected Wikis.
|
||||
# Note: Enabling HTML opens Cross Site Scripting vulnerabilities
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
# Smileys configured here will be replaced by the
|
||||
# configured images in the smiley directory
|
||||
|
||||
8-) icon_cool.gif
|
||||
8-O icon_eek.gif
|
||||
8-o icon_eek.gif
|
||||
:-( icon_sad.gif
|
||||
:-) icon_smile.gif
|
||||
=) icon_smile2.gif
|
||||
:-/ icon_doubt.gif
|
||||
:-\ icon_doubt2.gif
|
||||
:-? icon_confused.gif
|
||||
:-D icon_biggrin.gif
|
||||
:-P icon_razz.gif
|
||||
:-o icon_surprised.gif
|
||||
:-O icon_surprised.gif
|
||||
:-x icon_silenced.gif
|
||||
:-X icon_silenced.gif
|
||||
:-| icon_neutral.gif
|
||||
;-) icon_wink.gif
|
||||
m( facepalm.gif
|
||||
^_^ icon_fun.gif
|
||||
:?: icon_question.gif
|
||||
:!: icon_exclaim.gif
|
||||
LOL icon_lol.gif
|
||||
FIXME fixme.gif
|
||||
DELETEME delete.gif
|
||||
8-) cool.svg
|
||||
8-O eek.svg
|
||||
8-o eek.svg
|
||||
:-( sad.svg
|
||||
:-) smile.svg
|
||||
=) smile2.svg
|
||||
:-/ doubt.svg
|
||||
:-\ doubt2.svg
|
||||
:-? confused.svg
|
||||
:-D biggrin.svg
|
||||
:-P razz.svg
|
||||
:-o surprised.svg
|
||||
:-O surprised.svg
|
||||
:-x silenced.svg
|
||||
:-X silenced.svg
|
||||
:-| neutral.svg
|
||||
;-) wink.svg
|
||||
m( facepalm.svg
|
||||
^_^ fun.svg
|
||||
:?: question.svg
|
||||
:!: exclaim.svg
|
||||
LOL lol.svg
|
||||
FIXME fixme.svg
|
||||
DELETEME deleteme.svg
|
||||
|
||||
|
||||
@@ -2,6 +2,73 @@
|
||||
# but were removed later. An up to date DokuWiki should not have any of
|
||||
# the files installed
|
||||
|
||||
# removed in 2022-06-26
|
||||
.travis.yml
|
||||
appveyor.yml
|
||||
inc/IXR_Library.php
|
||||
inc/cli.php
|
||||
lib/images/interwiki/amazon.de.gif
|
||||
lib/images/interwiki/amazon.gif
|
||||
lib/images/interwiki/amazon.uk.gif
|
||||
lib/images/interwiki/callto.gif
|
||||
lib/images/interwiki/doku.gif
|
||||
lib/images/interwiki/google.gif
|
||||
lib/images/interwiki/paypal.gif
|
||||
lib/images/interwiki/phpfn.gif
|
||||
lib/images/interwiki/skype.gif
|
||||
lib/images/interwiki/tel.gif
|
||||
lib/images/interwiki/user.png
|
||||
lib/images/interwiki/wp.gif
|
||||
lib/images/interwiki/wpde.gif
|
||||
lib/images/interwiki/wpes.gif
|
||||
lib/images/interwiki/wpfr.gif
|
||||
lib/images/interwiki/wpjp.gif
|
||||
lib/images/interwiki/wpmeta.gif
|
||||
lib/images/interwiki/wppl.gif
|
||||
lib/images/smileys/delete.gif
|
||||
lib/images/smileys/facepalm.gif
|
||||
lib/images/smileys/fixme.gif
|
||||
lib/images/smileys/icon_arrow.gif
|
||||
lib/images/smileys/icon_biggrin.gif
|
||||
lib/images/smileys/icon_confused.gif
|
||||
lib/images/smileys/icon_cool.gif
|
||||
lib/images/smileys/icon_cry.gif
|
||||
lib/images/smileys/icon_doubt.gif
|
||||
lib/images/smileys/icon_doubt2.gif
|
||||
lib/images/smileys/icon_eek.gif
|
||||
lib/images/smileys/icon_evil.gif
|
||||
lib/images/smileys/icon_exclaim.gif
|
||||
lib/images/smileys/icon_frown.gif
|
||||
lib/images/smileys/icon_fun.gif
|
||||
lib/images/smileys/icon_idea.gif
|
||||
lib/images/smileys/icon_kaddi.gif
|
||||
lib/images/smileys/icon_lol.gif
|
||||
lib/images/smileys/icon_mrgreen.gif
|
||||
lib/images/smileys/icon_neutral.gif
|
||||
lib/images/smileys/icon_question.gif
|
||||
lib/images/smileys/icon_razz.gif
|
||||
lib/images/smileys/icon_redface.gif
|
||||
lib/images/smileys/icon_rolleyes.gif
|
||||
lib/images/smileys/icon_sad.gif
|
||||
lib/images/smileys/icon_silenced.gif
|
||||
lib/images/smileys/icon_smile.gif
|
||||
lib/images/smileys/icon_smile2.gif
|
||||
lib/images/smileys/icon_surprised.gif
|
||||
lib/images/smileys/icon_twisted.gif
|
||||
lib/images/smileys/icon_wink.gif
|
||||
vendor/paragonie/random_compat/LICENSE
|
||||
vendor/paragonie/random_compat/composer.json
|
||||
vendor/paragonie/random_compat/lib/byte_safe_strings.php
|
||||
vendor/paragonie/random_compat/lib/cast_to_int.php
|
||||
vendor/paragonie/random_compat/lib/error_polyfill.php
|
||||
vendor/paragonie/random_compat/lib/random.php
|
||||
vendor/paragonie/random_compat/lib/random_bytes_com_dotnet.php
|
||||
vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php
|
||||
vendor/paragonie/random_compat/lib/random_bytes_libsodium.php
|
||||
vendor/paragonie/random_compat/lib/random_bytes_libsodium_legacy.php
|
||||
vendor/paragonie/random_compat/lib/random_bytes_mcrypt.php
|
||||
vendor/paragonie/random_compat/lib/random_int.php
|
||||
|
||||
# removed in 2020-06-01
|
||||
inc/PluginInterface.php
|
||||
inc/PluginTrait.php
|
||||
|
||||
@@ -1,586 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128.17094"
|
||||
height="128.03864"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.1 "
|
||||
sodipodi:docname="dokuwiki-logo.svg"
|
||||
version="1.1">
|
||||
<title
|
||||
id="title3181">DokuWiki Logo</title>
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient2624">
|
||||
<stop
|
||||
style="stop-color:#3a9030;stop-opacity:0.83673471;"
|
||||
offset="0"
|
||||
id="stop2626" />
|
||||
<stop
|
||||
style="stop-color:#3d9c32;stop-opacity:0.79591835;"
|
||||
offset="1"
|
||||
id="stop2628" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128.17 128.04">
|
||||
<defs>
|
||||
<linearGradient id="a">
|
||||
<stop stop-color="#d69c00" offset="0"/>
|
||||
<stop stop-color="#ffe658" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2612">
|
||||
<stop
|
||||
style="stop-color:#25901b;stop-opacity:0.83673471;"
|
||||
offset="0"
|
||||
id="stop2614" />
|
||||
<stop
|
||||
style="stop-color:#25901b;stop-opacity:0.37755102;"
|
||||
offset="1"
|
||||
id="stop2616" />
|
||||
<linearGradient id="n" x1="192.04" x2="263.67" y1="262.26" y2="262.26" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00a423" offset="0"/>
|
||||
<stop stop-color="#00b427" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2600">
|
||||
<stop
|
||||
style="stop-color:#e32525;stop-opacity:0.81632656;"
|
||||
offset="0"
|
||||
id="stop2602" />
|
||||
<stop
|
||||
style="stop-color:#e32525;stop-opacity:0.5714286;"
|
||||
offset="1"
|
||||
id="stop2604" />
|
||||
<linearGradient id="p" x1="191.75" x2="255.66" y1="258.92" y2="258.92" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00b62b" offset="0"/>
|
||||
<stop stop-color="#a1d784" offset="1"/>
|
||||
</linearGradient>
|
||||
<marker
|
||||
inkscape:stockid="TriangleOutL"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="TriangleOutL"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path2488"
|
||||
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
|
||||
transform="scale(0.8,0.8)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow2Lstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow2Lstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path2571"
|
||||
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
|
||||
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
|
||||
transform="matrix(1.1,0,0,1.1,-5.5,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<linearGradient
|
||||
id="linearGradient2408">
|
||||
<stop
|
||||
id="stop2410"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:0.17346939;" />
|
||||
<stop
|
||||
id="stop2412"
|
||||
offset="1"
|
||||
style="stop-color:#c7cec2;stop-opacity:0;" />
|
||||
<linearGradient id="m" x1="184.07" x2="201.41" y1="246.36" y2="246.36" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<linearGradient id="d" x1="162.76" x2="240.85" y1="184.99" y2="289.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#ede1ae" offset="0"/>
|
||||
<stop stop-color="#fefdfa" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2389">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.17346939;"
|
||||
offset="0"
|
||||
id="stop2391" />
|
||||
<stop
|
||||
style="stop-color:#c7cec2;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2393" />
|
||||
<linearGradient id="b" x1="140.16" x2="136.14" y1="303.79" y2="195.87" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fbf6f0" offset="0"/>
|
||||
<stop stop-color="#e9dac7" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2370">
|
||||
<stop
|
||||
style="stop-color:#fbfaf9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2372" />
|
||||
<stop
|
||||
style="stop-color:#e9dac7;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2374" />
|
||||
<linearGradient id="c" x1="286.16" x2="185.81" y1="262.29" y2="172.32" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fbfaf9" offset="0"/>
|
||||
<stop stop-color="#e9dac7" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2364">
|
||||
<stop
|
||||
id="stop2366"
|
||||
offset="0"
|
||||
style="stop-color:#fbf6f0;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2368"
|
||||
offset="1"
|
||||
style="stop-color:#e9dac7;stop-opacity:1;" />
|
||||
<linearGradient id="h" x1="213.97" x2="244.79" y1="220.07" y2="265.4" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity=".173" offset="0"/>
|
||||
<stop stop-color="#c7cec2" stop-opacity="0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2348">
|
||||
<stop
|
||||
style="stop-color:#fbf6f0;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2350" />
|
||||
<stop
|
||||
style="stop-color:#e9dac7;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2352" />
|
||||
<linearGradient id="l" x1="184.31" x2="224.67" y1="241.53" y2="307.53" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity=".173" offset="0"/>
|
||||
<stop stop-color="#c7cec2" stop-opacity="0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2332">
|
||||
<stop
|
||||
style="stop-color:#ede1ae;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2334" />
|
||||
<stop
|
||||
style="stop-color:#fefdfa;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2336" />
|
||||
<linearGradient id="e" x1="202.42" x2="206.06" y1="222.05" y2="210.36" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#e32525" stop-opacity=".816" offset="0"/>
|
||||
<stop stop-color="#e32525" stop-opacity=".571" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2249">
|
||||
<stop
|
||||
style="stop-color:#00a423;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2251" />
|
||||
<stop
|
||||
style="stop-color:#00b427;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2253" />
|
||||
<linearGradient id="f" x1="248.62" x2="251.64" y1="234.52" y2="213.12" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#25901b" stop-opacity=".837" offset="0"/>
|
||||
<stop stop-color="#25901b" stop-opacity=".378" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2229">
|
||||
<stop
|
||||
id="stop2231"
|
||||
offset="0"
|
||||
style="stop-color:#00b62b;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2233"
|
||||
offset="1"
|
||||
style="stop-color:#a1d784;stop-opacity:1;" />
|
||||
<linearGradient id="g" x1="275.72" x2="255.68" y1="251.56" y2="217.94" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3a9030" stop-opacity=".837" offset="0"/>
|
||||
<stop stop-color="#3d9c32" stop-opacity=".796" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2213">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2215" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2217" />
|
||||
<linearGradient id="k" x1="219.66" x2="277.88" y1="192.73" y2="192.73" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#ce411e" offset="0"/>
|
||||
<stop stop-color="#ecad8d" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2360">
|
||||
<stop
|
||||
style="stop-color:#d69c00;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2362" />
|
||||
<stop
|
||||
style="stop-color:#ffe658;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2364" />
|
||||
<linearGradient id="j" x1="219.21" x2="286.23" y1="189.02" y2="189.02" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#8f2a15" offset="0"/>
|
||||
<stop stop-color="#c8381b" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2352">
|
||||
<stop
|
||||
id="stop2354"
|
||||
offset="0"
|
||||
style="stop-color:#ce411e;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2356"
|
||||
offset="1"
|
||||
style="stop-color:#ecad8d;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2336">
|
||||
<stop
|
||||
style="stop-color:#8f2a15;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2338" />
|
||||
<stop
|
||||
style="stop-color:#c8381b;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2340" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2336"
|
||||
id="linearGradient2342"
|
||||
x1="219.21262"
|
||||
y1="189.01556"
|
||||
x2="286.22665"
|
||||
y2="189.01556"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2352"
|
||||
id="linearGradient2350"
|
||||
x1="219.66267"
|
||||
y1="192.73286"
|
||||
x2="277.8761"
|
||||
y2="192.73286"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2360"
|
||||
id="radialGradient2366"
|
||||
cx="224.41418"
|
||||
cy="212.80016"
|
||||
fx="224.41418"
|
||||
fy="212.80016"
|
||||
r="8.6813803"
|
||||
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2249"
|
||||
id="linearGradient2227"
|
||||
x1="192.03938"
|
||||
y1="262.25757"
|
||||
x2="263.67093"
|
||||
y2="262.25757"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2229"
|
||||
id="linearGradient2247"
|
||||
x1="191.75092"
|
||||
y1="258.91571"
|
||||
x2="255.6561"
|
||||
y2="258.91571"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2360"
|
||||
id="radialGradient2317"
|
||||
cx="257.41144"
|
||||
cy="274.64203"
|
||||
fx="257.41144"
|
||||
fy="274.64203"
|
||||
r="7.1440549"
|
||||
gradientTransform="matrix(1,0,0,1.631384,0,-173.4045)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2360"
|
||||
id="linearGradient2325"
|
||||
x1="184.07063"
|
||||
y1="246.35907"
|
||||
x2="201.40646"
|
||||
y2="246.35907"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2332"
|
||||
id="linearGradient2346"
|
||||
x1="162.76369"
|
||||
y1="184.99277"
|
||||
x2="240.84924"
|
||||
y2="289.50323"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2348"
|
||||
id="linearGradient2354"
|
||||
x1="140.15784"
|
||||
y1="303.78967"
|
||||
x2="136.14151"
|
||||
y2="195.87151"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2370"
|
||||
id="linearGradient2362"
|
||||
x1="286.15598"
|
||||
y1="262.28729"
|
||||
x2="185.81258"
|
||||
y2="172.32423"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2389"
|
||||
id="linearGradient2395"
|
||||
x1="213.96568"
|
||||
y1="220.07191"
|
||||
x2="244.79126"
|
||||
y2="265.40363"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2408"
|
||||
id="linearGradient2406"
|
||||
x1="184.30582"
|
||||
y1="241.52789"
|
||||
x2="224.67441"
|
||||
y2="307.52844"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2600"
|
||||
id="linearGradient2606"
|
||||
x1="202.41772"
|
||||
y1="222.05145"
|
||||
x2="206.06017"
|
||||
y2="210.3558"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2612"
|
||||
id="linearGradient2618"
|
||||
x1="248.62152"
|
||||
y1="234.52202"
|
||||
x2="251.64362"
|
||||
y2="213.12164"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2624"
|
||||
id="linearGradient2630"
|
||||
x1="275.71765"
|
||||
y1="251.56442"
|
||||
x2="255.68353"
|
||||
y2="217.94008"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2352"
|
||||
id="linearGradient2640"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="219.66267"
|
||||
y1="192.73286"
|
||||
x2="277.8761"
|
||||
y2="192.73286" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2336"
|
||||
id="linearGradient2643"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="219.21262"
|
||||
y1="189.01556"
|
||||
x2="286.22665"
|
||||
y2="189.01556" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2360"
|
||||
id="radialGradient2647"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
|
||||
cx="224.41418"
|
||||
cy="212.80016"
|
||||
fx="224.41418"
|
||||
fy="212.80016"
|
||||
r="8.6813803" />
|
||||
<radialGradient id="o" cx="257.41" cy="274.64" r="7.144" gradientTransform="matrix(1 0 0 1.6314 0 -173.4)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
<radialGradient id="i" cx="224.41" cy="212.8" r="8.681" gradientTransform="matrix(1 0 0 .98418 0 3.367)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.03"
|
||||
inkscape:cx="35.144424"
|
||||
inkscape:cy="83.160427"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer3"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="716"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:showpageshadow="false"
|
||||
showborder="true"
|
||||
borderlayer="false" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>DokuWiki Logo</dc:title>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Esther Brunner</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<cc:license
|
||||
rdf:resource="http://www.gnu.org/licenses/gpl-2.0.html" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="paper"
|
||||
style="display:inline"
|
||||
transform="translate(-158.10602,-158.67323)">
|
||||
<g
|
||||
id="g1419"
|
||||
transform="matrix(0.99993322,0,0,0.9959778,0.01483419,0.8957919)">
|
||||
<g
|
||||
id="g2376">
|
||||
<path
|
||||
transform="matrix(0.989976,-0.141236,0.201069,0.979577,0,0)"
|
||||
style="fill:url(#linearGradient2354);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.7216621px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
|
||||
d="m 120.21543,196.43769 70.90655,-0.79226 -2.40261,109.05308 -71.71761,0.37344 3.21367,-108.63426 z"
|
||||
id="rect1422"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2362);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
|
||||
d="m 179.20033,182.08731 79.84173,-19.51687 26.61391,101.72428 -82.50312,21.58684 -23.95252,-103.79425 z"
|
||||
id="rect1425"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="matrix(0.995676,-0.09289891,0.08102261,0.996712,0,0)"
|
||||
style="fill:url(#linearGradient2346);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00418305px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
|
||||
d="m 159.01353,181.74387 85.58587,0.53396 0,110.47429 -84.53387,-2.5127 -1.052,-108.49555 z"
|
||||
id="rect1419"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
id="text2382"
|
||||
d="m 167.55116,214.00773 0,-20.1846 5.34962,0 0,2.37403 -2.48145,0 0,15.43654 2.48145,0 0,2.37403 -5.34962,0 m 7.34767,0 0,-20.1846 5.34961,0 0,2.37403 -2.48144,0 0,15.43654 2.48144,0 0,2.37403 -5.34961,0 m 7.36915,-20.1846 5.81153,0 c 1.31054,2e-5 2.30956,0.10028 2.99707,0.30078 0.92382,0.27216 1.71516,0.75555 2.37403,1.4502 0.65884,0.69468 1.16014,1.54689 1.50391,2.55664 0.34373,1.00262 0.51561,2.24155 0.51562,3.71681 -10e-6,1.29623 -0.16115,2.41342 -0.4834,3.35156 -0.39389,1.14584 -0.95607,2.07325 -1.68652,2.78223 -0.55145,0.53711 -1.29624,0.95606 -2.23438,1.25684 -0.70183,0.222 -1.63999,0.33301 -2.81446,0.33301 l -5.9834,0 0,-15.74807 m 3.17969,2.66407 0,10.43067 2.37402,0 c 0.88802,1e-5 1.52897,-0.0501 1.92286,-0.15039 0.51561,-0.1289 0.94172,-0.34732 1.27832,-0.65527 0.34374,-0.30794 0.62304,-0.81282 0.83789,-1.51465 0.21483,-0.70898 0.32226,-1.6722 0.32227,-2.88965 -1e-5,-1.21744 -0.10744,-2.15201 -0.32227,-2.80372 -0.21485,-0.65168 -0.51563,-1.16014 -0.90234,-1.52539 -0.38673,-0.36522 -0.87729,-0.61229 -1.47168,-0.74121 -0.44402,-0.10025 -1.31414,-0.15038 -2.61036,-0.15039 l -1.42871,0 m 14.96388,13.084 -3.75977,-15.74807 3.25489,0 2.37403,10.8174 2.87891,-10.8174 3.78125,0 2.76074,11.00002 2.417,-11.00002 3.20118,0 -3.82423,15.74807 -3.37305,0 -3.13672,-11.77345 -3.12598,11.77345 -3.44825,0 m 22.76272,-15.74807 0,20.1846 -5.34961,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34961,0 m 7.34767,0 0,20.1846 -5.34962,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34962,0"
|
||||
style="font-size:12.0000124px;font-style:normal;font-weight:normal;line-height:125%;fill:#6184a3;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
|
||||
transform="matrix(0.995433,-0.09546066,0.09546066,0.995433,0,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
id="g2632"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:url(#linearGradient2606);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker-end:none"
|
||||
d="m 174.75585,201.60224 c -6.04576,2.46667 -10.16789,4.4194 -12.88454,6.35064 -2.71665,1.93124 -3.19257,4.60007 -3.24631,6.26587 -0.0269,0.8329 0.0809,1.77774 0.63189,2.44014 0.55103,0.6624 1.80769,1.87421 2.75794,2.38558 1.90049,1.02274 7.5417,2.42901 10.51899,3.07308 11.90917,2.57627 26.80568,1.68117 26.80568,1.68117 1.69307,1.2452 2.83283,2.82434 3.269,4.26902 4.5766,-1.88674 11.81084,-6.58439 13.15657,-8.57706 -5.45142,-4.19955 -10.79692,-6.33346 -16.51317,-8.30847 -1.59867,-0.71918 -2.87956,-1.22649 -0.71773,2.55635 0.98506,2.47275 0.85786,5.05143 0.57176,7.41825 0,0 -16.52749,0.40678 -28.23838,-2.1266 -2.92772,-0.63334 -5.46627,-0.95523 -7.21875,-1.89832 -0.87624,-0.47154 -1.48296,-0.8208 -1.91578,-1.3411 -0.43282,-0.5203 -0.2196,-1.29055 -0.20128,-1.85858 0.0366,-1.13607 0.25336,-1.67063 2.86177,-3.52492 2.60841,-1.85429 5.65407,-3.36195 11.65936,-5.81211 -0.0877,-1.29125 -0.29025,-2.5059 -1.29702,-2.99294 z"
|
||||
id="path2414"
|
||||
sodipodi:nodetypes="csssssccccccssssscc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2618);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 269.62539,220.7482 c -1.43576,-0.13963 -2.58044,0.30288 -2.56084,1.50218 0.94391,0.85652 1.34942,2.43518 1.48562,3.14008 0.1362,0.7049 0.0359,1.21914 -0.48562,1.89004 -1.043,1.3418 -3.12498,1.56875 -6.5006,2.72063 -6.75124,2.30377 -16.89306,2.52561 -27.90689,3.84639 -22.02767,2.64157 -39.03164,3.76107 -39.03164,3.76107 1.98346,-4.64758 6.32828,-4.41197 6.34903,-8.20969 0.27376,-0.89755 -3.14597,-1.31638 -5.09943,-0.10731 -4.26694,3.70137 -7.59152,6.75353 -10.69418,10.51311 l 1.88795,3.08438 c 0,0 26.13006,-2.88973 48.19776,-5.5361 11.03385,-1.32318 20.95601,-1.99856 27.80968,-4.33728 3.42683,-1.16936 5.95975,-1.49022 7.6409,-3.51958 0.63172,-0.76256 1.35238,-3.04699 1.06804,-4.73369 -0.21951,-1.30213 -1.14979,-3.09774 -2.15978,-4.01423 z"
|
||||
id="path2608"
|
||||
sodipodi:nodetypes="ccsssscccccssssc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2630);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 254.36185,220.33948 c -6.84997,3.24198 -7.15311,8.60912 -5.95953,12.79884 1.19358,4.18972 5.26293,8.75677 9.32121,12.40608 8.11656,7.29861 12.06046,9.33163 12.06046,9.33163 -3.71515,-0.10342 -7.89887,-1.41174 -8.13315,0.49304 -0.9483,2.97582 11.49137,3.47486 17.43787,2.70205 -1.39456,-7.57836 -3.79323,-13.21546 -7.73151,-14.90312 -1.68464,-0.14804 0.31242,4.72441 0.76985,9.39604 0,0 -3.62454,-1.73122 -11.60519,-8.90762 -3.99032,-3.5882 -7.37386,-7.3421 -8.47319,-11.20099 -1.09933,-3.85889 0.0776,-6.1205 4.95082,-9.53176 0.92816,-0.99528 -1.28985,-2.45913 -2.63764,-2.58419 z"
|
||||
id="path2620"
|
||||
sodipodi:nodetypes="csscccccsscc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cccccc"
|
||||
id="rect2386"
|
||||
d="m 213.96569,234.57806 2.18756,-14.42897 15.21982,6.08793 21.49387,29.94828 -20.40591,9.21832 -18.49534,-30.82556 z"
|
||||
style="fill:url(#linearGradient2395);fill-opacity:1;stroke:none;display:inline"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
id="g2649"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:url(#radialGradient2647);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 232.55816,219.5295 -15.92827,0.32199 3.08809,-15.15716 12.84018,14.83517 z"
|
||||
id="path1443"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#812310;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 221.60041,219.29315 -4.41205,0.0782 0.85429,-3.98263 3.55776,3.90445 z"
|
||||
id="path1452"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2643);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 269.44172,159.27421 0.098,8.91471 8.0581,8.72344 7.75906,0.7992 -52.80669,41.84092 -6.66532,-3.30696 -5.08243,-5.618 -1.08987,-5.91194 49.72911,-45.44137 z"
|
||||
id="rect1437"
|
||||
sodipodi:nodetypes="ccccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2640);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 268.94766,168.32844 8.3426,8.82719 -51.1007,38.68262 -4.9197,-5.4436 47.6778,-42.06621 z"
|
||||
id="rect1446"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#ffe965;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;display:inline"
|
||||
d="m 285.33776,177.73216 -8.16219,-0.86619 -7.7518,-8.67862 0.0132,-9.14293 8.36213,0.75209 7.18862,9.57682 0.35007,8.35883 z"
|
||||
id="path1440"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#cb391c;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 280.72049,168.46367 0.1644,4.05654 -3.81335,-0.71676 -2.87504,-3.18901 -0.28089,-3.53393 3.85447,-0.16637 2.95041,3.54953 z"
|
||||
id="path1449"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
id="g2657"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="fill:url(#linearGradient2406);fill-opacity:1;stroke:none"
|
||||
d="m 183.88617,256.82796 0.99991,-16.30721 17.2878,8.44012 26.05488,38.00946 -29.28095,-1.13363 -15.06164,-29.00874 z"
|
||||
id="rect2397"
|
||||
sodipodi:nodetypes="cccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2325);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline"
|
||||
d="m 200.90647,238.44836 -8.04601,15.77386 -7.05577,-13.57337 15.10178,-2.20049 z"
|
||||
id="rect2207"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2227);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 201.05389,238.55401 62.11704,24.91912 -7.88689,3.21429 -4.35152,9.30976 1.1716,9.96396 -59.31453,-31.72759 -0.49402,-7.36382 3.09592,-5.82826 5.6624,-2.48746 z"
|
||||
id="rect1328"
|
||||
sodipodi:nodetypes="ccccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#radialGradient2317);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline"
|
||||
d="m 255.27801,266.53504 7.9241,-3.04772 0.85337,10.24037 -3.9011,8.28983 -8.04601,3.77919 -1.341,-9.63083 4.51064,-9.63084 z"
|
||||
id="rect2204"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:url(#linearGradient2247);fill-opacity:1;stroke:none;display:inline"
|
||||
d="m 195.7549,241.421 59.13059,24.7962 -4.5917,9.76614 -57.48995,-29.00967 2.95106,-5.55267 z"
|
||||
id="rect2210"
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#00b527;fill-opacity:1;stroke:none"
|
||||
d="m 255.02263,275.21029 2.08411,-4.1069 2.96459,-1.06995 0.69433,3.37197 -1.76759,3.85723 -3.15516,1.38315 -0.82028,-3.4355 z"
|
||||
id="rect2308"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:#258209;fill-opacity:1;stroke:none;display:inline"
|
||||
d="m 186.56849,241.00362 3.54963,-0.47312 -2.02297,3.53926 -1.52666,-3.06614 z"
|
||||
id="rect2327"
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
<g fill-rule="evenodd" stroke="#000">
|
||||
<path transform="matrix(.98991 -.14067 .20106 .97564 -158.095 -157.774)" d="m120.22 196.44 70.907-.792-2.403 109.05-71.718.373 3.214-108.63z" fill="url(#b)" stroke-width=".722"/>
|
||||
<path d="m179.2 182.09 79.842-19.517 26.614 101.72-82.503 21.587L179.2 182.09z" fill="url(#c)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path transform="matrix(.99561 -.09253 .08102 .9927 -158.095 -157.774)" d="m159.01 181.74 85.586.534v110.47l-84.534-2.513-1.052-108.5z" fill="url(#d)" stroke-width="1.004"/>
|
||||
</g>
|
||||
<path d="M29.106 38.471 27.179 18.46l5.325-.508.226 2.353-2.47.236 1.474 15.305 2.47-.236.226 2.354-5.324.508m7.313-.698L34.492 17.76l5.325-.509.227 2.354-2.47.236 1.474 15.304 2.47-.236.226 2.354-5.325.509m5.409-20.713 5.784-.553c1.304-.124 2.308-.12 3.012.014.945.182 1.78.586 2.501 1.212.722.626 1.303 1.423 1.741 2.391.438.962.728 2.174.868 3.636.124 1.285.07 2.408-.16 3.37-.284 1.173-.755 2.145-1.414 2.918-.498.585-1.199 1.071-2.104 1.458-.677.287-1.6.486-2.77.598l-5.955.569-1.504-15.613m3.42 2.339.995 10.341 2.363-.225c.884-.085 1.517-.195 1.9-.332.5-.177.904-.434 1.21-.771.313-.338.542-.865.69-1.582.145-.723.16-1.688.044-2.895s-.312-2.123-.588-2.75c-.276-.625-.624-1.1-1.044-1.426-.42-.325-.932-.523-1.536-.595-.451-.057-1.322-.024-2.612.1l-1.422.135M61.39 30.95l-5.245-15.256 3.24-.31 3.395 10.499 1.833-10.998 3.764-.36 3.797 10.644 1.356-11.136 3.187-.304-2.304 15.976-3.357.321-4.246-11.374-1.988 11.97-3.432.327m21.154-17.777 1.927 20.012-5.325.509-.226-2.354 2.47-.236-1.476-15.325-2.47.236-.224-2.333 5.324-.508m7.314-.699 1.927 20.012-5.325.509-.227-2.354 2.47-.236-1.475-15.325-2.47.236-.225-2.333 5.325-.508" fill="#6184a3"/>
|
||||
<g fill-rule="evenodd">
|
||||
<path d="M174.76 201.6c-6.046 2.467-10.168 4.42-12.885 6.35s-3.193 4.6-3.246 6.267c-.027.832.08 1.777.632 2.44.55.662 1.807 1.874 2.757 2.385 1.901 1.023 7.542 2.43 10.52 3.073 11.908 2.577 26.805 1.682 26.805 1.682 1.694 1.245 2.833 2.824 3.27 4.269 4.576-1.887 11.81-6.585 13.156-8.578-5.45-4.2-10.797-6.333-16.513-8.308-1.598-.72-2.88-1.227-.717 2.556.985 2.473.858 5.052.572 7.419 0 0-16.527.406-28.238-2.127-2.928-.633-5.467-.955-7.22-1.898-.876-.472-1.482-.821-1.915-1.341s-.22-1.291-.201-1.86c.036-1.135.253-1.67 2.861-3.524s5.655-3.362 11.66-5.812c-.088-1.291-.29-2.506-1.298-2.993z" fill="url(#e)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="M269.63 220.75c-1.436-.14-2.58.303-2.56 1.502.943.857 1.349 2.435 1.485 3.14s.036 1.22-.486 1.89c-1.043 1.342-3.125 1.57-6.5 2.72-6.752 2.305-16.893 2.526-27.907 3.847-22.028 2.642-39.032 3.761-39.032 3.761 1.983-4.647 6.328-4.412 6.349-8.21.273-.897-3.146-1.316-5.1-.107-4.267 3.702-7.591 6.754-10.694 10.513l1.888 3.085s26.13-2.89 48.198-5.536c11.034-1.324 20.956-1.999 27.81-4.338 3.427-1.169 5.96-1.49 7.641-3.52.632-.762 1.352-3.046 1.068-4.733-.22-1.302-1.15-3.098-2.16-4.014z" fill="url(#f)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="M254.36 220.34c-6.85 3.242-7.153 8.61-5.96 12.799s5.263 8.757 9.322 12.406c8.116 7.299 12.06 9.332 12.06 9.332-3.715-.104-7.9-1.412-8.133.493-.949 2.975 11.49 3.475 17.438 2.702-1.395-7.579-3.794-13.215-7.732-14.903-1.685-.148.312 4.724.77 9.396 0 0-3.625-1.732-11.605-8.908-3.99-3.588-7.374-7.342-8.473-11.201s.077-6.12 4.95-9.532c.929-.995-1.29-2.459-2.637-2.584z" fill="url(#g)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
</g>
|
||||
<path d="m213.97 234.58 2.188-14.429 15.22 6.088 21.494 29.948-20.406 9.218-18.495-30.826z" fill="url(#h)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m232.56 219.53-15.928.322 3.088-15.157 12.84 14.835z" fill="url(#i)" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m63.49 60.634-4.412.078.854-3.966 3.557 3.888z" fill="#812310" fill-rule="evenodd"/>
|
||||
<path d="m269.44 159.27.098 8.915 8.058 8.723 7.76.8-52.808 41.84-6.665-3.307-5.083-5.618-1.09-5.912 49.73-45.44z" fill="url(#j)" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m268.95 168.33 8.343 8.827-51.101 38.683-4.92-5.443 47.678-42.066z" fill="url(#k)" fill-rule="evenodd" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m285.34 177.73-8.162-.866-7.752-8.679.013-9.143 8.362.752 7.189 9.577.35 8.359z" fill="#ffe965" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m122.605 10.009.165 4.04-3.814-.714-2.874-3.176-.281-3.52 3.854-.166 2.95 3.536z" fill="#cb391c" fill-rule="evenodd"/>
|
||||
<path d="m183.89 256.83 1-16.307 17.288 8.44 26.055 38.01-29.281-1.135-15.062-29.009z" fill="url(#l)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m200.91 238.45-8.046 15.774-7.056-13.573 15.102-2.2z" fill="url(#m)" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m201.05 238.55 62.117 24.919-7.887 3.214-4.351 9.31 1.171 9.964-59.315-31.728-.494-7.364 3.096-5.828 5.662-2.488z" fill="url(#n)" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m255.28 266.54 7.924-3.048.853 10.24-3.9 8.29-8.047 3.78-1.34-9.631 4.51-9.631z" fill="url(#o)" stroke="#000" stroke-linejoin="round" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m195.75 241.42 59.131 24.796-4.592 9.766-57.49-29.01 2.951-5.553z" fill="url(#p)" transform="matrix(.99993 0 0 .99598 -158.095 -157.774)"/>
|
||||
<path d="m96.907 116.33 2.084-4.09 2.964-1.067.695 3.359-1.768 3.841-3.155 1.378-.82-3.422z" fill="#00b527"/>
|
||||
<path d="m28.462 82.257 3.55-.471-2.024 3.525-1.526-3.054z" fill="#258209"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 9.1 KiB |
@@ -246,6 +246,7 @@ DokuWiki converts commonly used [[wp>emoticon]]s to their graphical equivalents.
|
||||
* :-| %% :-| %%
|
||||
* ;-) %% ;-) %%
|
||||
* ^_^ %% ^_^ %%
|
||||
* m( %% m( %%
|
||||
* :?: %% :?: %%
|
||||
* :!: %% :!: %%
|
||||
* LOL %% LOL %%
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// update message version - always use a string to avoid localized floats!
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
$updateVersion = "51.4";
|
||||
$updateVersion = "53";
|
||||
|
||||
// xdebug_start_profiling();
|
||||
|
||||
@@ -79,10 +79,14 @@ if($DATE_AT) {
|
||||
$DATE_AT = null;
|
||||
} else if ($rev_t === false) { //page did not exist
|
||||
$rev_n = $pagelog->getRelativeRevision($DATE_AT,+1);
|
||||
msg(sprintf($lang['page_nonexist_rev'],
|
||||
strftime($conf['dformat'],$DATE_AT),
|
||||
wl($ID, array('rev' => $rev_n)),
|
||||
strftime($conf['dformat'],$rev_n)));
|
||||
msg(
|
||||
sprintf(
|
||||
$lang['page_nonexist_rev'],
|
||||
dformat($DATE_AT),
|
||||
wl($ID, array('rev' => $rev_n)),
|
||||
dformat($rev_n)
|
||||
)
|
||||
);
|
||||
$REV = $DATE_AT; //will result in a page not exists message
|
||||
} else {
|
||||
$REV = $rev_t;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* XML feed export
|
||||
*
|
||||
@@ -15,14 +16,14 @@ use dokuwiki\ChangeLog\PageChangeLog;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
if(!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__).'/');
|
||||
require_once(DOKU_INC.'inc/init.php');
|
||||
if (!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__) . '/');
|
||||
require_once(DOKU_INC . 'inc/init.php');
|
||||
|
||||
//close session
|
||||
session_write_close();
|
||||
|
||||
//feed disabled?
|
||||
if(!actionOK('rss')) {
|
||||
if (!actionOK('rss')) {
|
||||
http_status(404);
|
||||
echo '<error>RSS feed is disabled.</error>';
|
||||
exit;
|
||||
@@ -33,7 +34,8 @@ $opt = rss_parseOptions();
|
||||
|
||||
// the feed is dynamic - we need a cache for each combo
|
||||
// (but most people just use the default feed so it's still effective)
|
||||
$key = join('', array_values($opt)).'$'.$_SERVER['REMOTE_USER'].'$'.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'];
|
||||
$key = join('', array_values($opt)) . '$' . $_SERVER['REMOTE_USER']
|
||||
. '$' . $_SERVER['HTTP_HOST'] . $_SERVER['SERVER_PORT'];
|
||||
$cache = new Cache($key, '.feed');
|
||||
|
||||
// prepare cache depends
|
||||
@@ -47,9 +49,9 @@ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Pragma: public');
|
||||
header('Content-Type: application/xml; charset=utf-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
if($cache->useCache($depends)) {
|
||||
if ($cache->useCache($depends)) {
|
||||
http_conditionalRequest($cache->getTime());
|
||||
if($conf['allowdebug']) header("X-CacheUsed: $cache->cache");
|
||||
if ($conf['allowdebug']) header("X-CacheUsed: $cache->cache");
|
||||
print $cache->retrieveCache();
|
||||
exit;
|
||||
} else {
|
||||
@@ -58,32 +60,33 @@ if($cache->useCache($depends)) {
|
||||
|
||||
// create new feed
|
||||
$rss = new UniversalFeedCreator();
|
||||
$rss->title = $conf['title'].(($opt['namespace']) ? ' '.$opt['namespace'] : '');
|
||||
$rss->title = $conf['title'] . (($opt['namespace']) ? ' ' . $opt['namespace'] : '');
|
||||
$rss->link = DOKU_URL;
|
||||
$rss->syndicationURL = DOKU_URL.'feed.php';
|
||||
$rss->cssStyleSheet = DOKU_URL.'lib/exe/css.php?s=feed';
|
||||
$rss->syndicationURL = DOKU_URL . 'feed.php';
|
||||
$rss->cssStyleSheet = DOKU_URL . 'lib/exe/css.php?s=feed';
|
||||
|
||||
$image = new FeedImage();
|
||||
$image->title = $conf['title'];
|
||||
$image->url = tpl_getMediaFile(array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico'), true);
|
||||
$image->url = tpl_getMediaFile([':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico'], true);
|
||||
$image->link = DOKU_URL;
|
||||
$rss->image = $image;
|
||||
|
||||
$data = null;
|
||||
$modes = array(
|
||||
$modes = [
|
||||
'list' => 'rssListNamespace',
|
||||
'search' => 'rssSearch',
|
||||
'recent' => 'rssRecentChanges'
|
||||
);
|
||||
if(isset($modes[$opt['feed_mode']])) {
|
||||
];
|
||||
|
||||
if (isset($modes[$opt['feed_mode']])) {
|
||||
$data = $modes[$opt['feed_mode']]($opt);
|
||||
} else {
|
||||
$eventData = array(
|
||||
$eventData = [
|
||||
'opt' => &$opt,
|
||||
'data' => &$data,
|
||||
);
|
||||
];
|
||||
$event = new Event('FEED_MODE_UNKNOWN', $eventData);
|
||||
if($event->advise_before(true)) {
|
||||
if ($event->advise_before(true)) {
|
||||
echo sprintf('<error>Unknown feed mode %s</error>', hsc($opt['feed_mode']));
|
||||
exit;
|
||||
}
|
||||
@@ -106,59 +109,62 @@ print $feed;
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
function rss_parseOptions() {
|
||||
function rss_parseOptions()
|
||||
{
|
||||
global $conf;
|
||||
global $INPUT;
|
||||
|
||||
$opt = array();
|
||||
$opt = [];
|
||||
|
||||
foreach(array(
|
||||
// Basic feed properties
|
||||
// Plugins may probably want to add new values to these
|
||||
// properties for implementing own feeds
|
||||
foreach (
|
||||
[
|
||||
// Basic feed properties
|
||||
// Plugins may probably want to add new values to these
|
||||
// properties for implementing own feeds
|
||||
|
||||
// One of: list, search, recent
|
||||
'feed_mode' => array('str', 'mode', 'recent'),
|
||||
// One of: diff, page, rev, current
|
||||
'link_to' => array('str', 'linkto', $conf['rss_linkto']),
|
||||
// One of: abstract, diff, htmldiff, html
|
||||
'item_content' => array('str', 'content', $conf['rss_content']),
|
||||
// One of: list, search, recent
|
||||
'feed_mode' => ['str', 'mode', 'recent'],
|
||||
// One of: diff, page, rev, current
|
||||
'link_to' => ['str', 'linkto', $conf['rss_linkto']],
|
||||
// One of: abstract, diff, htmldiff, html
|
||||
'item_content' => ['str', 'content', $conf['rss_content']],
|
||||
|
||||
// Special feed properties
|
||||
// These are only used by certain feed_modes
|
||||
// Special feed properties
|
||||
// These are only used by certain feed_modes
|
||||
|
||||
// String, used for feed title, in list and rc mode
|
||||
'namespace' => array('str', 'ns', null),
|
||||
// Positive integer, only used in rc mode
|
||||
'items' => array('int', 'num', $conf['recent']),
|
||||
// Boolean, only used in rc mode
|
||||
'show_minor' => array('bool', 'minor', false),
|
||||
// Boolean, only used in rc mode
|
||||
'only_new' => array('bool', 'onlynewpages', false),
|
||||
// String, only used in list mode
|
||||
'sort' => array('str', 'sort', 'natural'),
|
||||
// String, only used in search mode
|
||||
'search_query' => array('str', 'q', null),
|
||||
// One of: pages, media, both
|
||||
'content_type' => array('str', 'view', $conf['rss_media'])
|
||||
// String, used for feed title, in list and rc mode
|
||||
'namespace' => ['str', 'ns', null],
|
||||
// Positive integer, only used in rc mode
|
||||
'items' => ['int', 'num', $conf['recent']],
|
||||
// Boolean, only used in rc mode
|
||||
'show_minor' => ['bool', 'minor', false],
|
||||
// Boolean, only used in rc mode
|
||||
'only_new' => ['bool', 'onlynewpages', false],
|
||||
// String, only used in list mode
|
||||
'sort' => ['str', 'sort', 'natural'],
|
||||
// String, only used in search mode
|
||||
'search_query' => ['str', 'q', null],
|
||||
// One of: pages, media, both
|
||||
'content_type' => ['str', 'view', $conf['rss_media']]
|
||||
|
||||
) as $name => $val) {
|
||||
] as $name => $val
|
||||
) {
|
||||
$opt[$name] = $INPUT->{$val[0]}($val[1], $val[2], true);
|
||||
}
|
||||
|
||||
$opt['items'] = max(0, (int) $opt['items']);
|
||||
$opt['show_minor'] = (bool) $opt['show_minor'];
|
||||
$opt['only_new'] = (bool) $opt['only_new'];
|
||||
$opt['sort'] = valid_input_set('sort', array('default' => 'natural', 'date'), $opt);
|
||||
$opt['sort'] = valid_input_set('sort', ['default' => 'natural', 'date'], $opt);
|
||||
|
||||
$opt['guardmail'] = ($conf['mailguard'] != '' && $conf['mailguard'] != 'none');
|
||||
|
||||
$type = $INPUT->valid(
|
||||
'type',
|
||||
array( 'rss', 'rss2', 'atom', 'atom1', 'rss1'),
|
||||
['rss', 'rss2', 'atom', 'atom1', 'rss1'],
|
||||
$conf['rss_type']
|
||||
);
|
||||
switch($type) {
|
||||
switch ($type) {
|
||||
case 'rss':
|
||||
$opt['feed_type'] = 'RSS0.91';
|
||||
$opt['mime_type'] = 'text/xml';
|
||||
@@ -180,9 +186,9 @@ function rss_parseOptions() {
|
||||
$opt['mime_type'] = 'application/xml';
|
||||
}
|
||||
|
||||
$eventData = array(
|
||||
$eventData = [
|
||||
'opt' => &$opt,
|
||||
);
|
||||
];
|
||||
Event::createAndTrigger('FEED_OPTS_POSTPROCESS', $eventData);
|
||||
return $opt;
|
||||
}
|
||||
@@ -190,98 +196,105 @@ function rss_parseOptions() {
|
||||
/**
|
||||
* Add recent changed pages to a feed object
|
||||
*
|
||||
* @param FeedCreator $rss the FeedCreator Object
|
||||
* @param array $data the items to add
|
||||
* @param array $opt the feed options
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @param FeedCreator $rss the FeedCreator Object
|
||||
* @param array $data the items to add
|
||||
* @param array $opt the feed options
|
||||
*/
|
||||
function rss_buildItems(&$rss, &$data, $opt) {
|
||||
function rss_buildItems(&$rss, &$data, $opt)
|
||||
{
|
||||
global $conf;
|
||||
global $lang;
|
||||
/* @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
|
||||
$eventData = array(
|
||||
$eventData = [
|
||||
'rss' => &$rss,
|
||||
'data' => &$data,
|
||||
'opt' => &$opt,
|
||||
);
|
||||
];
|
||||
$event = new Event('FEED_DATA_PROCESS', $eventData);
|
||||
if($event->advise_before(false)) {
|
||||
foreach($data as $ditem) {
|
||||
if(!is_array($ditem)) {
|
||||
if ($event->advise_before(false)) {
|
||||
foreach ($data as $ditem) {
|
||||
if (!is_array($ditem)) {
|
||||
// not an array? then only a list of IDs was given
|
||||
$ditem = array('id' => $ditem);
|
||||
$ditem = ['id' => $ditem];
|
||||
}
|
||||
|
||||
$item = new FeedItem();
|
||||
$id = $ditem['id'];
|
||||
if(!$ditem['media']) {
|
||||
if (!$ditem['media']) {
|
||||
$meta = p_get_metadata($id);
|
||||
} else {
|
||||
$meta = array();
|
||||
$meta = [];
|
||||
}
|
||||
|
||||
// add date
|
||||
if($ditem['date']) {
|
||||
if ($ditem['date']) {
|
||||
$date = $ditem['date'];
|
||||
} elseif ($ditem['media']) {
|
||||
$date = @filemtime(mediaFN($id));
|
||||
} elseif (file_exists(wikiFN($id))) {
|
||||
$date = @filemtime(wikiFN($id));
|
||||
} elseif($meta['date']['modified']) {
|
||||
} elseif ($meta['date']['modified']) {
|
||||
$date = $meta['date']['modified'];
|
||||
} else {
|
||||
$date = 0;
|
||||
}
|
||||
if($date) $item->date = date('r', $date);
|
||||
if ($date) $item->date = date('r', $date);
|
||||
|
||||
// add title
|
||||
if($conf['useheading'] && $meta['title']) {
|
||||
if ($conf['useheading'] && $meta['title']) {
|
||||
$item->title = $meta['title'];
|
||||
} else {
|
||||
$item->title = $ditem['id'];
|
||||
}
|
||||
if($conf['rss_show_summary'] && !empty($ditem['sum'])) {
|
||||
$item->title .= ' - '.strip_tags($ditem['sum']);
|
||||
if ($conf['rss_show_summary'] && !empty($ditem['sum'])) {
|
||||
$item->title .= ' - ' . strip_tags($ditem['sum']);
|
||||
}
|
||||
|
||||
// add item link
|
||||
switch($opt['link_to']) {
|
||||
switch ($opt['link_to']) {
|
||||
case 'page':
|
||||
if($ditem['media']) {
|
||||
if ($ditem['media']) {
|
||||
$item->link = media_managerURL(
|
||||
array(
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date
|
||||
), '&', true
|
||||
[
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date
|
||||
],
|
||||
'&',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$item->link = wl($id, 'rev='.$date, true, '&');
|
||||
$item->link = wl($id, 'rev=' . $date, true, '&');
|
||||
}
|
||||
break;
|
||||
case 'rev':
|
||||
if($ditem['media']) {
|
||||
if ($ditem['media']) {
|
||||
$item->link = media_managerURL(
|
||||
array(
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date,
|
||||
'tab_details' => 'history'
|
||||
), '&', true
|
||||
[
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date,
|
||||
'tab_details' => 'history'
|
||||
],
|
||||
'&',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$item->link = wl($id, 'do=revisions&rev='.$date, true, '&');
|
||||
$item->link = wl($id, 'do=revisions&rev=' . $date, true, '&');
|
||||
}
|
||||
break;
|
||||
case 'current':
|
||||
if($ditem['media']) {
|
||||
if ($ditem['media']) {
|
||||
$item->link = media_managerURL(
|
||||
array(
|
||||
'image' => $id,
|
||||
'ns' => getNS($id)
|
||||
), '&', true
|
||||
[
|
||||
'image' => $id,
|
||||
'ns' => getNS($id)
|
||||
],
|
||||
'&',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$item->link = wl($id, '', true, '&');
|
||||
@@ -289,85 +302,91 @@ function rss_buildItems(&$rss, &$data, $opt) {
|
||||
break;
|
||||
case 'diff':
|
||||
default:
|
||||
if($ditem['media']) {
|
||||
if ($ditem['media']) {
|
||||
$item->link = media_managerURL(
|
||||
array(
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date,
|
||||
'tab_details' => 'history',
|
||||
'mediado' => 'diff'
|
||||
), '&', true
|
||||
[
|
||||
'image' => $id,
|
||||
'ns' => getNS($id),
|
||||
'rev' => $date,
|
||||
'tab_details' => 'history',
|
||||
'mediado' => 'diff'
|
||||
],
|
||||
'&',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$item->link = wl($id, 'rev='.$date.'&do=diff', true, '&');
|
||||
$item->link = wl($id, 'rev=' . $date . '&do=diff', true, '&');
|
||||
}
|
||||
}
|
||||
|
||||
// add item content
|
||||
switch($opt['item_content']) {
|
||||
switch ($opt['item_content']) {
|
||||
case 'diff':
|
||||
case 'htmldiff':
|
||||
if($ditem['media']) {
|
||||
if ($ditem['media']) {
|
||||
$medialog = new MediaChangeLog($id);
|
||||
$revs = $medialog->getRevisions(0, 1);
|
||||
$rev = $revs[0];
|
||||
$src_r = '';
|
||||
$src_l = '';
|
||||
$revs = $medialog->getRevisions(0, 1);
|
||||
$rev = $revs[0];
|
||||
$src_r = '';
|
||||
$src_l = '';
|
||||
|
||||
if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)), 300)) {
|
||||
$more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
|
||||
if ($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)), 300)) {
|
||||
$more = 'w=' . $size[0] . '&h=' . $size[1] . '&t=' . @filemtime(mediaFN($id));
|
||||
$src_r = ml($id, $more, true, '&', true);
|
||||
}
|
||||
if($rev && $size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)), 300)) {
|
||||
$more = 'rev='.$rev.'&w='.$size[0].'&h='.$size[1];
|
||||
if ($rev && $size = media_image_preview_size($id, $rev, new JpegMeta(mediaFN($id, $rev)),
|
||||
300)) {
|
||||
$more = 'rev=' . $rev . '&w=' . $size[0] . '&h=' . $size[1];
|
||||
$src_l = ml($id, $more, true, '&', true);
|
||||
}
|
||||
$content = '';
|
||||
if($src_r) {
|
||||
if ($src_r) {
|
||||
$content = '<table>';
|
||||
$content .= '<tr><th width="50%">'.$rev.'</th>';
|
||||
$content .= '<th width="50%">'.$lang['current'].'</th></tr>';
|
||||
$content .= '<tr align="center"><td><img src="'.$src_l.'" alt="" /></td><td>';
|
||||
$content .= '<img src="'.$src_r.'" alt="'.$id.'" /></td></tr>';
|
||||
$content .= '<tr><th width="50%">' . $rev . '</th>';
|
||||
$content .= '<th width="50%">' . $lang['current'] . '</th></tr>';
|
||||
$content .= '<tr align="center"><td><img src="' . $src_l . '" alt="" /></td><td>';
|
||||
$content .= '<img src="' . $src_r . '" alt="' . $id . '" /></td></tr>';
|
||||
$content .= '</table>';
|
||||
}
|
||||
|
||||
} else {
|
||||
require_once(DOKU_INC.'inc/DifferenceEngine.php');
|
||||
require_once(DOKU_INC . 'inc/DifferenceEngine.php');
|
||||
$pagelog = new PageChangeLog($id);
|
||||
$revs = $pagelog->getRevisions(0, 1);
|
||||
$rev = $revs[0];
|
||||
$revs = $pagelog->getRevisions(0, 1);
|
||||
$rev = $revs[0];
|
||||
|
||||
if($rev) {
|
||||
$df = new Diff(explode("\n", rawWiki($id, $rev)),
|
||||
explode("\n", rawWiki($id, '')));
|
||||
if ($rev) {
|
||||
$df = new Diff(
|
||||
explode("\n", rawWiki($id, $rev)),
|
||||
explode("\n", rawWiki($id, ''))
|
||||
);
|
||||
} else {
|
||||
$df = new Diff(array(''),
|
||||
explode("\n", rawWiki($id, '')));
|
||||
$df = new Diff(
|
||||
[''],
|
||||
explode("\n", rawWiki($id, ''))
|
||||
);
|
||||
}
|
||||
|
||||
if($opt['item_content'] == 'htmldiff') {
|
||||
if ($opt['item_content'] == 'htmldiff') {
|
||||
// note: no need to escape diff output, TableDiffFormatter provides 'safe' html
|
||||
$tdf = new TableDiffFormatter();
|
||||
$content = '<table>';
|
||||
$content .= '<tr><th colspan="2" width="50%">'.$rev.'</th>';
|
||||
$content .= '<th colspan="2" width="50%">'.$lang['current'].'</th></tr>';
|
||||
$content .= '<tr><th colspan="2" width="50%">' . $rev . '</th>';
|
||||
$content .= '<th colspan="2" width="50%">' . $lang['current'] . '</th></tr>';
|
||||
$content .= $tdf->format($df);
|
||||
$content .= '</table>';
|
||||
} else {
|
||||
// note: diff output must be escaped, UnifiedDiffFormatter provides plain text
|
||||
$udf = new UnifiedDiffFormatter();
|
||||
$content = "<pre>\n".hsc($udf->format($df))."\n</pre>";
|
||||
$content = "<pre>\n" . hsc($udf->format($df)) . "\n</pre>";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'html':
|
||||
if($ditem['media']) {
|
||||
if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
|
||||
$more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
|
||||
$src = ml($id, $more, true, '&', true);
|
||||
$content = '<img src="'.$src.'" alt="'.$id.'" />';
|
||||
if ($ditem['media']) {
|
||||
if ($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
|
||||
$more = 'w=' . $size[0] . '&h=' . $size[1] . '&t=' . @filemtime(mediaFN($id));
|
||||
$src = ml($id, $more, true, '&', true);
|
||||
$content = '<img src="' . $src . '" alt="' . $id . '" />';
|
||||
} else {
|
||||
$content = '';
|
||||
}
|
||||
@@ -385,20 +404,23 @@ function rss_buildItems(&$rss, &$data, $opt) {
|
||||
$content = preg_replace('/(<img .*?class="mediaright")/s', '\\1 align="right"', $content);
|
||||
|
||||
// make URLs work when canonical is not set, regexp instead of rerendering!
|
||||
if(!$conf['canonical']) {
|
||||
if (!$conf['canonical']) {
|
||||
$base = preg_quote(DOKU_REL, '/');
|
||||
$content = preg_replace('/(<a href|<img src)="('.$base.')/s', '$1="'.DOKU_URL, $content);
|
||||
$content = preg_replace(
|
||||
'/(<a href|<img src)="(' . $base . ')/s', '$1="' . DOKU_URL,
|
||||
$content
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'abstract':
|
||||
default:
|
||||
if($ditem['media']) {
|
||||
if($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
|
||||
$more = 'w='.$size[0].'&h='.$size[1].'&t='.@filemtime(mediaFN($id));
|
||||
$src = ml($id, $more, true, '&', true);
|
||||
$content = '<img src="'.$src.'" alt="'.$id.'" />';
|
||||
if ($ditem['media']) {
|
||||
if ($size = media_image_preview_size($id, '', new JpegMeta(mediaFN($id)))) {
|
||||
$more = 'w=' . $size[0] . '&h=' . $size[1] . '&t=' . @filemtime(mediaFN($id));
|
||||
$src = ml($id, $more, true, '&', true);
|
||||
$content = '<img src="' . $src . '" alt="' . $id . '" />';
|
||||
} else {
|
||||
$content = '';
|
||||
}
|
||||
@@ -410,19 +432,19 @@ function rss_buildItems(&$rss, &$data, $opt) {
|
||||
|
||||
// add user
|
||||
# FIXME should the user be pulled from metadata as well?
|
||||
$user = @$ditem['user']; // the @ spares time repeating lookup
|
||||
if(blank($user)) {
|
||||
$item->author = 'Anonymous';
|
||||
$user = @$ditem['user']; // the @ spares time repeating lookup
|
||||
if (blank($user)) {
|
||||
$item->author = 'Anonymous';
|
||||
$item->authorEmail = 'anonymous@undisclosed.example.com';
|
||||
} else {
|
||||
$item->author = $user;
|
||||
$item->author = $user;
|
||||
$item->authorEmail = $user . '@undisclosed.example.com';
|
||||
|
||||
// get real user name if configured
|
||||
if($conf['useacl'] && $auth) {
|
||||
if ($conf['useacl'] && $auth) {
|
||||
$userInfo = $auth->getUserData($user);
|
||||
if($userInfo) {
|
||||
switch($conf['showuseras']) {
|
||||
if ($userInfo) {
|
||||
switch ($conf['showuseras']) {
|
||||
case 'username':
|
||||
case 'username_link':
|
||||
$item->author = $userInfo['name'];
|
||||
@@ -438,22 +460,22 @@ function rss_buildItems(&$rss, &$data, $opt) {
|
||||
}
|
||||
|
||||
// add category
|
||||
if(isset($meta['subject'])) {
|
||||
if (isset($meta['subject'])) {
|
||||
$item->category = $meta['subject'];
|
||||
} else {
|
||||
$cat = getNS($id);
|
||||
if($cat) $item->category = $cat;
|
||||
if ($cat) $item->category = $cat;
|
||||
}
|
||||
|
||||
// finally add the item to the feed object, after handing it to registered plugins
|
||||
$evdata = array(
|
||||
$evdata = [
|
||||
'item' => &$item,
|
||||
'opt' => &$opt,
|
||||
'ditem' => &$ditem,
|
||||
'rss' => &$rss
|
||||
);
|
||||
];
|
||||
$evt = new Event('FEED_ITEM_ADD', $evdata);
|
||||
if($evt->advise_before()) {
|
||||
if ($evt->advise_before()) {
|
||||
$rss->addItem($item);
|
||||
}
|
||||
$evt->advise_after(); // for completeness
|
||||
@@ -467,14 +489,15 @@ function rss_buildItems(&$rss, &$data, $opt) {
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
function rssRecentChanges($opt) {
|
||||
function rssRecentChanges($opt)
|
||||
{
|
||||
global $conf;
|
||||
$flags = 0;
|
||||
if(!$conf['rss_show_deleted']) $flags += RECENTS_SKIP_DELETED;
|
||||
if(!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS;
|
||||
if($opt['only_new']) $flags += RECENTS_ONLY_CREATION;
|
||||
if($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES;
|
||||
if($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED;
|
||||
if (!$conf['rss_show_deleted']) $flags += RECENTS_SKIP_DELETED;
|
||||
if (!$opt['show_minor']) $flags += RECENTS_SKIP_MINORS;
|
||||
if ($opt['only_new']) $flags += RECENTS_ONLY_CREATION;
|
||||
if ($opt['content_type'] == 'media' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_CHANGES;
|
||||
if ($opt['content_type'] == 'both' && $conf['mediarevisions']) $flags += RECENTS_MEDIA_PAGES_MIXED;
|
||||
|
||||
$recents = getRecents(0, $opt['items'], $opt['namespace'], $flags);
|
||||
return $recents;
|
||||
@@ -485,19 +508,20 @@ function rssRecentChanges($opt) {
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
function rssListNamespace($opt) {
|
||||
require_once(DOKU_INC.'inc/search.php');
|
||||
function rssListNamespace($opt)
|
||||
{
|
||||
require_once(DOKU_INC . 'inc/search.php');
|
||||
global $conf;
|
||||
|
||||
$ns = ':'.cleanID($opt['namespace']);
|
||||
$ns = ':' . cleanID($opt['namespace']);
|
||||
$ns = utf8_encodeFN(str_replace(':', '/', $ns));
|
||||
|
||||
$data = array();
|
||||
$search_opts = array(
|
||||
'depth' => 1,
|
||||
$data = [];
|
||||
$search_opts = [
|
||||
'depth' => 1,
|
||||
'pagesonly' => true,
|
||||
'listfiles' => true
|
||||
);
|
||||
];
|
||||
search($data, $conf['datadir'], 'search_universal', $search_opts, $ns, $lvl = 1, $opt['sort']);
|
||||
|
||||
return $data;
|
||||
@@ -508,10 +532,11 @@ function rssListNamespace($opt) {
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*/
|
||||
function rssSearch($opt) {
|
||||
if(!$opt['search_query']) return array();
|
||||
function rssSearch($opt)
|
||||
{
|
||||
if (!$opt['search_query'] || !actionOK('search')) return [];
|
||||
|
||||
require_once(DOKU_INC.'inc/fulltext.php');
|
||||
require_once(DOKU_INC . 'inc/fulltext.php');
|
||||
$data = ft_pageSearch($opt['search_query'], $poswords);
|
||||
$data = array_keys($data);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
|
||||
@@ -69,6 +68,7 @@ abstract class AbstractAction {
|
||||
* Output whatever content is wanted within tpl_content();
|
||||
*
|
||||
* @fixme we may want to return a Ui class here
|
||||
* @throws FatalException
|
||||
*/
|
||||
public function tplContent() {
|
||||
throw new FatalException('No content for Action ' . $this->actionname);
|
||||
|
||||
@@ -21,6 +21,9 @@ abstract class AbstractAliasAction extends AbstractAction {
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FatalException
|
||||
*/
|
||||
public function preProcess() {
|
||||
throw new FatalException('Alias Actions need to implement preProcess to load the aliased action');
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ abstract class AbstractUserAction extends AbstractAclAction {
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
global $INPUT;
|
||||
if(!$INPUT->server->str('REMOTE_USER')) {
|
||||
if($INPUT->server->str('REMOTE_USER') === '') {
|
||||
throw new ActionUserRequiredException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Extension\AdminPlugin;
|
||||
|
||||
/**
|
||||
* Class Admin
|
||||
@@ -18,17 +19,13 @@ class Admin extends AbstractUserAction {
|
||||
return AUTH_READ; // let in check later
|
||||
}
|
||||
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
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($INPUT->str('page', '', true) != '') {
|
||||
/** @var AdminPlugin $plugin */
|
||||
if($plugin = plugin_getRequestAdminPlugin()) { // FIXME this method does also permission checking
|
||||
if(!$plugin->isAccessibleByCurrentUser()) {
|
||||
throw new ActionException('denied');
|
||||
@@ -38,8 +35,8 @@ class Admin extends AbstractUserAction {
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function tplContent() {
|
||||
tpl_admin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Backlink
|
||||
*
|
||||
@@ -9,16 +11,18 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Backlink extends AbstractAction {
|
||||
|
||||
class Backlink extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_backlinks();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Backlinks)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ use dokuwiki\Action\Exception\ActionAbort;
|
||||
*/
|
||||
class Cancel extends AbstractAliasAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
global $ID;
|
||||
unlock($ID);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Conflict
|
||||
*
|
||||
@@ -9,26 +11,29 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Conflict extends AbstractAction {
|
||||
|
||||
class Conflict extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if($INFO['exists']) {
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_EDIT;
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
public function tplContent() {
|
||||
/** @inheritdoc */
|
||||
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);
|
||||
$text = con($PRE, $TEXT, $SUF);
|
||||
(new Ui\PageConflict($text, $SUM))->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Denied
|
||||
*
|
||||
@@ -9,15 +12,41 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Denied extends AbstractAclAction {
|
||||
|
||||
class Denied extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
public function tplContent() {
|
||||
html_denied();
|
||||
/** @inheritdoc */
|
||||
public function tplContent()
|
||||
{
|
||||
$this->showBanner();
|
||||
|
||||
$data = null;
|
||||
$event = new Event('ACTION_DENIED_TPLCONTENT', $data);
|
||||
if ($event->advise_before()) {
|
||||
global $INPUT;
|
||||
if (empty($INPUT->server->str('REMOTE_USER')) && actionOK('login')) {
|
||||
(new Ui\Login)->show();
|
||||
}
|
||||
}
|
||||
$event->advise_after();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error on denied pages
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showBanner()
|
||||
{
|
||||
// print intro
|
||||
print p_locale_xhtml('denied');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Diff
|
||||
*
|
||||
@@ -9,27 +11,31 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Diff extends AbstractAction {
|
||||
|
||||
class Diff extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
// store the selected diff type in cookie
|
||||
$difftype = $INPUT->str('difftype');
|
||||
if(!empty($difftype)) {
|
||||
if (!empty($difftype)) {
|
||||
set_doku_pref('difftype', $difftype);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_diff();
|
||||
public function tplContent()
|
||||
{
|
||||
global $INFO;
|
||||
(new Ui\PageDiff($INFO['id']))->preference('showIntro', true)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Draft
|
||||
@@ -12,12 +13,13 @@ use dokuwiki\Action\Exception\ActionException;
|
||||
* @package dokuwiki\Action
|
||||
* @fixme combine with Recover?
|
||||
*/
|
||||
class Draft extends AbstractAction {
|
||||
|
||||
class Draft extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if($INFO['exists']) {
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_EDIT;
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
@@ -25,15 +27,17 @@ class Draft extends AbstractAction {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
global $INFO;
|
||||
if(!file_exists($INFO['draft'])) throw new ActionException('edit');
|
||||
if (!file_exists($INFO['draft'])) throw new ActionException('edit');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_draft();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\PageDraft)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class Draftdel extends AbstractAction {
|
||||
public function preProcess() {
|
||||
global $INFO, $ID;
|
||||
$draft = new \dokuwiki\Draft($ID, $INFO['client']);
|
||||
if ($draft->isDraftAvailable()) {
|
||||
if ($draft->isDraftAvailable() && checkSecurityToken()) {
|
||||
$draft->deleteDraft();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Edit
|
||||
@@ -11,12 +12,13 @@ use dokuwiki\Action\Exception\ActionAbort;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Edit extends AbstractAction {
|
||||
|
||||
class Edit extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
global $INFO;
|
||||
if($INFO['exists']) {
|
||||
if ($INFO['exists']) {
|
||||
return AUTH_READ; // we check again below
|
||||
} else {
|
||||
return AUTH_CREATE;
|
||||
@@ -26,18 +28,20 @@ class Edit extends AbstractAction {
|
||||
/**
|
||||
* @inheritdoc falls back to 'source' if page not writable
|
||||
*/
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
global $INFO;
|
||||
|
||||
// no edit permission? view source
|
||||
if($INFO['exists'] && !$INFO['writable']) {
|
||||
if ($INFO['exists'] && !$INFO['writable']) {
|
||||
throw new ActionAbort('source');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $ID;
|
||||
global $INFO;
|
||||
|
||||
@@ -50,9 +54,9 @@ class Edit extends AbstractAction {
|
||||
global $lang;
|
||||
global $DATE;
|
||||
|
||||
if(!isset($TEXT)) {
|
||||
if($INFO['exists']) {
|
||||
if($RANGE) {
|
||||
if (!isset($TEXT)) {
|
||||
if ($INFO['exists']) {
|
||||
if ($RANGE) {
|
||||
list($PRE, $TEXT, $SUF) = rawWikiSlices($RANGE, $ID, $REV);
|
||||
} else {
|
||||
$TEXT = rawWiki($ID, $REV);
|
||||
@@ -63,29 +67,30 @@ class Edit extends AbstractAction {
|
||||
}
|
||||
|
||||
//set summary default
|
||||
if(!$SUM) {
|
||||
if($REV) {
|
||||
if (!$SUM) {
|
||||
if ($REV) {
|
||||
$SUM = sprintf($lang['restored'], dformat($REV));
|
||||
} elseif(!$INFO['exists']) {
|
||||
} 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));
|
||||
if (!$DATE) $DATE = @filemtime(wikiFN($ID));
|
||||
|
||||
//check if locked by anyone - if not lock for my self
|
||||
$lockedby = checklock($ID);
|
||||
if($lockedby) {
|
||||
if ($lockedby) {
|
||||
throw new ActionAbort('locked');
|
||||
};
|
||||
}
|
||||
lock($ID);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_edit();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Index
|
||||
*
|
||||
@@ -9,17 +11,19 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Index extends AbstractAction {
|
||||
|
||||
class Index extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
public function tplContent()
|
||||
{
|
||||
global $IDX;
|
||||
html_index($IDX);
|
||||
(new Ui\Index($IDX))->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Locked
|
||||
*
|
||||
@@ -9,17 +11,46 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Locked extends AbstractAction {
|
||||
|
||||
class Locked extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_locked();
|
||||
html_edit();
|
||||
public function tplContent()
|
||||
{
|
||||
$this->showBanner();
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error on locked pages
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function showBanner()
|
||||
{
|
||||
global $ID;
|
||||
global $conf;
|
||||
global $lang;
|
||||
global $INFO;
|
||||
|
||||
$locktime = filemtime(wikiLockFN($ID));
|
||||
$expire = dformat($locktime + $conf['locktime']);
|
||||
$min = round(($conf['locktime'] - (time() - $locktime) )/60);
|
||||
|
||||
// print intro
|
||||
print p_locale_xhtml('locked');
|
||||
|
||||
print '<ul>';
|
||||
print '<li><div class="li"><strong>'.$lang['lockedby'].'</strong> '.editorinfo($INFO['locked']).'</div></li>';
|
||||
print '<li><div class="li"><strong>'.$lang['lockexpire'].'</strong> '.$expire.' ('.$min.' min)</div></li>';
|
||||
print '</ul>'.DOKU_LF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Login
|
||||
@@ -11,26 +12,29 @@ use dokuwiki\Action\Exception\ActionException;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Login extends AbstractAclAction {
|
||||
|
||||
class Login extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
global $INPUT;
|
||||
parent::checkPreconditions();
|
||||
if($INPUT->server->has('REMOTE_USER')) {
|
||||
if ($INPUT->server->has('REMOTE_USER')) {
|
||||
// nothing to do
|
||||
throw new ActionException();
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_login();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Login)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Action\Exception\ActionException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
|
||||
/**
|
||||
* Class Logout
|
||||
@@ -23,7 +24,7 @@ class Logout extends AbstractUserAction {
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('logout')) throw new ActionDisabledException();
|
||||
}
|
||||
@@ -33,6 +34,8 @@ class Logout extends AbstractUserAction {
|
||||
global $ID;
|
||||
global $INPUT;
|
||||
|
||||
if (!checkSecurityToken()) throw new ActionException();
|
||||
|
||||
// when logging out during an edit session, unlock the page
|
||||
$lockedby = checklock($ID);
|
||||
if($lockedby == $INPUT->server->str('REMOTE_USER')) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Plugin
|
||||
*
|
||||
@@ -23,7 +25,7 @@ class Plugin extends AbstractAction {
|
||||
* @triggers TPL_ACT_UNKNOWN
|
||||
*/
|
||||
public function tplContent() {
|
||||
$evt = new \dokuwiki\Extension\Event('TPL_ACT_UNKNOWN', $this->actionname);
|
||||
$evt = new Event('TPL_ACT_UNKNOWN', $this->actionname);
|
||||
if($evt->advise_before()) {
|
||||
msg('Failed to handle action: ' . hsc($this->actionname), -1);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Preview
|
||||
*
|
||||
@@ -9,26 +11,29 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Preview extends Edit {
|
||||
|
||||
class Preview extends Edit
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
header('X-XSS-Protection: 0');
|
||||
$this->savedraft();
|
||||
parent::preProcess();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
public function tplContent()
|
||||
{
|
||||
global $TEXT;
|
||||
html_edit();
|
||||
html_show($TEXT);
|
||||
(new Ui\Editor)->show();
|
||||
(new Ui\PageView($TEXT))->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a draft on preview
|
||||
*/
|
||||
protected function savedraft() {
|
||||
protected function savedraft()
|
||||
{
|
||||
global $ID, $INFO;
|
||||
$draft = new \dokuwiki\Draft($ID, $INFO['client']);
|
||||
if (!$draft->saveDraft()) {
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Profile
|
||||
@@ -12,34 +14,38 @@ use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Profile extends AbstractUserAction {
|
||||
|
||||
class Profile extends AbstractUserAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('Profile')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $lang;
|
||||
if(updateprofile()) {
|
||||
if (updateprofile()) {
|
||||
msg($lang['profchanged'], 1);
|
||||
throw new ActionAbort('show');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_updateprofile();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserProfile)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
|
||||
/**
|
||||
* Class ProfileDelete
|
||||
@@ -23,7 +24,7 @@ class ProfileDelete extends AbstractUserAction {
|
||||
public function checkPreconditions() {
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
if(!$auth->canDo('delUser')) throw new ActionDisabledException();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Recent
|
||||
*
|
||||
@@ -9,21 +11,23 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Recent extends AbstractAction {
|
||||
|
||||
class Recent extends AbstractAction
|
||||
{
|
||||
/** @var string what type of changes to show */
|
||||
protected $showType = 'both';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $INPUT;
|
||||
$show_changes = $INPUT->str('show_changes');
|
||||
if(!empty($show_changes)) {
|
||||
if (!empty($show_changes)) {
|
||||
set_doku_pref('show_changes', $show_changes);
|
||||
$this->showType = $show_changes;
|
||||
} else {
|
||||
@@ -32,9 +36,10 @@ class Recent extends AbstractAction {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
public function tplContent()
|
||||
{
|
||||
global $INPUT;
|
||||
html_recent((int) $INPUT->extract('first')->int('first'), $this->showType);
|
||||
(new Ui\Recent($INPUT->extract('first')->int('first'), $this->showType))->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ use dokuwiki\Action\Exception\ActionAbort;
|
||||
*/
|
||||
class Recover extends AbstractAliasAction {
|
||||
|
||||
/** @inheritdoc */
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @throws ActionAbort
|
||||
*/
|
||||
public function preProcess() {
|
||||
throw new ActionAbort('edit');
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Register
|
||||
@@ -12,34 +14,38 @@ use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Register extends AbstractAclAction {
|
||||
|
||||
class Register extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $conf;
|
||||
if(isset($conf['openregister']) && !$conf['openregister']) throw new ActionDisabledException();
|
||||
if(!$auth->canDo('addUser')) throw new ActionDisabledException();
|
||||
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
|
||||
public function preProcess()
|
||||
{
|
||||
if (register()) { // FIXME could be moved from auth to here
|
||||
throw new ActionAbort('login');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_register();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserRegister)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Extension\AuthPlugin;
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Resendpwd
|
||||
@@ -12,34 +14,39 @@ use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Resendpwd extends AbstractAclAction {
|
||||
|
||||
class Resendpwd extends AbstractAclAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_NONE;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/** @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $conf;
|
||||
if(isset($conf['resendpasswd']) && !$conf['resendpasswd']) throw new ActionDisabledException(); //legacy option
|
||||
if(!$auth->canDo('modPass')) throw new ActionDisabledException();
|
||||
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()) {
|
||||
public function preProcess()
|
||||
{
|
||||
if ($this->resendpwd()) {
|
||||
throw new ActionAbort('login');
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_resendpwd();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\UserResendPwd)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,31 +63,32 @@ class Resendpwd extends AbstractAclAction {
|
||||
* @fixme this should be split up into multiple methods
|
||||
* @return bool true on success, false on any error
|
||||
*/
|
||||
protected function resendpwd() {
|
||||
protected function resendpwd()
|
||||
{
|
||||
global $lang;
|
||||
global $conf;
|
||||
/* @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
/* @var AuthPlugin $auth */
|
||||
global $auth;
|
||||
global $INPUT;
|
||||
|
||||
if(!actionOK('resendpwd')) {
|
||||
if (!actionOK('resendpwd')) {
|
||||
msg($lang['resendna'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
$token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
|
||||
|
||||
if($token) {
|
||||
if ($token) {
|
||||
// we're in token phase - get user info from token
|
||||
|
||||
$tfile = $conf['cachedir'] . '/' . $token[0] . '/' . $token . '.pwauth';
|
||||
if(!file_exists($tfile)) {
|
||||
$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)) {
|
||||
if ((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
|
||||
msg($lang['resendpwdbadauth'], -1);
|
||||
$INPUT->remove('pwauth');
|
||||
@unlink($tfile);
|
||||
@@ -89,23 +97,23 @@ class Resendpwd extends AbstractAclAction {
|
||||
|
||||
$user = io_readfile($tfile);
|
||||
$userinfo = $auth->getUserData($user, $requireGroups = false);
|
||||
if(!$userinfo['mail']) {
|
||||
if (!$userinfo['mail']) {
|
||||
msg($lang['resendpwdnouser'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$conf['autopasswd']) { // we let the user choose a password
|
||||
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')) {
|
||||
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)))) {
|
||||
if (!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
|
||||
msg($lang['proffail'], -1);
|
||||
return false;
|
||||
}
|
||||
@@ -113,12 +121,12 @@ class Resendpwd extends AbstractAclAction {
|
||||
} else { // autogenerate the password and send by mail
|
||||
|
||||
$pass = auth_pwgen($user);
|
||||
if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
|
||||
if (!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
|
||||
msg($lang['proffail'], -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(auth_sendPassword($user, $pass)) {
|
||||
if (auth_sendPassword($user, $pass)) {
|
||||
msg($lang['resendpwdsuccess'], 1);
|
||||
} else {
|
||||
msg($lang['regmailfail'], -1);
|
||||
@@ -131,9 +139,9 @@ class Resendpwd extends AbstractAclAction {
|
||||
} else {
|
||||
// we're in request phase
|
||||
|
||||
if(!$INPUT->post->bool('save')) return false;
|
||||
if (!$INPUT->post->bool('save')) return false;
|
||||
|
||||
if(!$INPUT->post->str('login')) {
|
||||
if (!$INPUT->post->str('login')) {
|
||||
msg($lang['resendpwdmissing'], -1);
|
||||
return false;
|
||||
} else {
|
||||
@@ -141,14 +149,14 @@ class Resendpwd extends AbstractAclAction {
|
||||
}
|
||||
|
||||
$userinfo = $auth->getUserData($user, $requireGroups = false);
|
||||
if(!$userinfo['mail']) {
|
||||
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';
|
||||
$tfile = $conf['cachedir'] .'/'. $token[0] .'/'. $token .'.pwauth';
|
||||
$url = wl('', array('do' => 'resendpwd', 'pwauth' => $token), true, '&');
|
||||
|
||||
io_saveFile($tfile, $user);
|
||||
@@ -161,10 +169,10 @@ class Resendpwd extends AbstractAclAction {
|
||||
);
|
||||
|
||||
$mail = new \Mailer();
|
||||
$mail->to($userinfo['name'] . ' <' . $userinfo['mail'] . '>');
|
||||
$mail->to($userinfo['name'] .' <'. $userinfo['mail'] .'>');
|
||||
$mail->subject($lang['regpwmail']);
|
||||
$mail->setBody($text, $trep);
|
||||
if($mail->send()) {
|
||||
if ($mail->send()) {
|
||||
msg($lang['resendpwdconfirm'], 1);
|
||||
} else {
|
||||
msg($lang['regmailfail'], -1);
|
||||
|
||||
@@ -12,10 +12,12 @@ use dokuwiki\Action\Exception\ActionException;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Revert extends AbstractAction {
|
||||
class Revert extends AbstractUserAction
|
||||
{
|
||||
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_EDIT;
|
||||
}
|
||||
|
||||
@@ -26,8 +28,9 @@ class Revert extends AbstractAction {
|
||||
* @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();
|
||||
public function preProcess()
|
||||
{
|
||||
if (!checkSecurityToken()) throw new ActionException();
|
||||
|
||||
global $ID;
|
||||
global $REV;
|
||||
@@ -37,14 +40,14 @@ class Revert extends AbstractAction {
|
||||
// FIXME this feature is not exposed in the GUI currently
|
||||
$text = '';
|
||||
$sum = $lang['deleted'];
|
||||
if($REV) {
|
||||
if ($REV) {
|
||||
$text = rawWiki($ID, $REV);
|
||||
if(!$text) throw new ActionException(); //something went wrong
|
||||
if (!$text) throw new ActionException(); //something went wrong
|
||||
$sum = sprintf($lang['restored'], dformat($REV));
|
||||
}
|
||||
|
||||
// spam check
|
||||
if(checkwordblock($text)) {
|
||||
if (checkwordblock($text)) {
|
||||
msg($lang['wordblock'], -1);
|
||||
throw new ActionException('edit');
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Revisions
|
||||
*
|
||||
@@ -9,16 +11,18 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Revisions extends AbstractAction {
|
||||
|
||||
class Revisions extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
global $INPUT;
|
||||
html_revisions($INPUT->int('first'));
|
||||
public function tplContent()
|
||||
{
|
||||
global $INFO, $INPUT;
|
||||
(new Ui\PageRevisions($INFO['id']))->show($INPUT->int('first'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ class Save extends AbstractAction {
|
||||
throw new ActionException('edit');
|
||||
}
|
||||
//conflict check
|
||||
if($DATE != 0 && $INFO['meta']['date']['modified'] > $DATE) {
|
||||
if($DATE != 0
|
||||
&& isset($INFO['meta']['date']['modified'])
|
||||
&& $INFO['meta']['date']['modified'] > $DATE
|
||||
) {
|
||||
throw new ActionException('conflict');
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Show
|
||||
*
|
||||
@@ -15,22 +17,24 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Show extends AbstractAction {
|
||||
|
||||
class Show extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $ID;
|
||||
unlock($ID);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_show();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\PageView())->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Action\Exception\FatalException;
|
||||
use dokuwiki\Sitemap\Mapper;
|
||||
use dokuwiki\Utf8\PhpString;
|
||||
|
||||
/**
|
||||
* Class Sitemap
|
||||
@@ -48,7 +49,7 @@ class Sitemap extends AbstractAction {
|
||||
if(is_readable($sitemap)) {
|
||||
// Send headers
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Content-Disposition: attachment; filename=' . \dokuwiki\Utf8\PhpString::basename($sitemap));
|
||||
header('Content-Disposition: attachment; filename=' . PhpString::basename($sitemap));
|
||||
|
||||
http_conditionalRequest(filemtime($sitemap));
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Action;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
|
||||
/**
|
||||
* Class Source
|
||||
*
|
||||
@@ -9,28 +11,31 @@ namespace dokuwiki\Action;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Source extends AbstractAction {
|
||||
|
||||
class Source extends AbstractAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
global $TEXT;
|
||||
global $INFO;
|
||||
global $ID;
|
||||
global $REV;
|
||||
|
||||
if($INFO['exists']) {
|
||||
if ($INFO['exists']) {
|
||||
$TEXT = rawWiki($ID, $REV);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
html_edit();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Editor)->show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use dokuwiki\Action\Exception\ActionAbort;
|
||||
use dokuwiki\Action\Exception\ActionDisabledException;
|
||||
use dokuwiki\Subscriptions\SubscriberManager;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Ui;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class Subscribe
|
||||
@@ -14,15 +16,17 @@ use dokuwiki\Extension\Event;
|
||||
*
|
||||
* @package dokuwiki\Action
|
||||
*/
|
||||
class Subscribe extends AbstractUserAction {
|
||||
|
||||
class Subscribe extends AbstractUserAction
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function minimumPermission() {
|
||||
public function minimumPermission()
|
||||
{
|
||||
return AUTH_READ;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function checkPreconditions() {
|
||||
public function checkPreconditions()
|
||||
{
|
||||
parent::checkPreconditions();
|
||||
|
||||
global $conf;
|
||||
@@ -30,43 +34,46 @@ class Subscribe extends AbstractUserAction {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function preProcess() {
|
||||
public function preProcess()
|
||||
{
|
||||
try {
|
||||
$this->handleSubscribeData();
|
||||
} catch(ActionAbort $e) {
|
||||
} catch (ActionAbort $e) {
|
||||
throw $e;
|
||||
} catch(\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
msg($e->getMessage(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function tplContent() {
|
||||
tpl_subscribe();
|
||||
public function tplContent()
|
||||
{
|
||||
(new Ui\Subscribe)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle page 'subscribe'
|
||||
*
|
||||
* @author Adrian Lang <lang@cosmocode.de>
|
||||
* @throws \Exception if (un)subscribing fails
|
||||
* @throws Exception if (un)subscribing fails
|
||||
* @throws ActionAbort when (un)subscribing worked
|
||||
*/
|
||||
protected function handleSubscribeData() {
|
||||
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")) {
|
||||
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;
|
||||
if (empty($params['action']) || !checkSecurityToken()) return;
|
||||
|
||||
// Handle POST data, may throw exception.
|
||||
Event::createAndTrigger('ACTION_HANDLE_SUBSCRIBE', $params, array($this, 'handlePostData'));
|
||||
@@ -77,13 +84,13 @@ class Subscribe extends AbstractUserAction {
|
||||
|
||||
// Perform action.
|
||||
$subManager = new SubscriberManager();
|
||||
if($action === 'unsubscribe') {
|
||||
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) {
|
||||
if ($ok) {
|
||||
msg(
|
||||
sprintf(
|
||||
$lang["subscr_{$action}_success"], hsc($INFO['userinfo']['name']),
|
||||
@@ -93,7 +100,7 @@ class Subscribe extends AbstractUserAction {
|
||||
throw new ActionAbort('redirect');
|
||||
}
|
||||
|
||||
throw new \Exception(
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
$lang["subscr_{$action}_error"],
|
||||
hsc($INFO['userinfo']['name']),
|
||||
@@ -111,20 +118,21 @@ class Subscribe extends AbstractUserAction {
|
||||
* @author Adrian Lang <lang@cosmocode.de>
|
||||
*
|
||||
* @param array &$params the parameters: target, style and action
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public function handlePostData(&$params) {
|
||||
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');
|
||||
if (!isset($params['target'])) {
|
||||
throw new Exception('no subscription target given');
|
||||
}
|
||||
$target = $params['target'];
|
||||
$valid_styles = array('every', 'digest');
|
||||
if(substr($target, -1, 1) === ':') {
|
||||
if (substr($target, -1, 1) === ':') {
|
||||
// Allow “list” subscribe style since the target is a namespace.
|
||||
$valid_styles[] = 'list';
|
||||
}
|
||||
@@ -138,19 +146,19 @@ class Subscribe extends AbstractUserAction {
|
||||
);
|
||||
|
||||
// Check other conditions.
|
||||
if($action === 'subscribe') {
|
||||
if($INFO['userinfo']['mail'] === '') {
|
||||
throw new \Exception($lang['subscr_subscribe_noaddress']);
|
||||
if ($action === 'subscribe') {
|
||||
if ($INFO['userinfo']['mail'] === '') {
|
||||
throw new Exception($lang['subscr_subscribe_noaddress']);
|
||||
}
|
||||
} elseif($action === 'unsubscribe') {
|
||||
} elseif ($action === 'unsubscribe') {
|
||||
$is = false;
|
||||
foreach($INFO['subscribed'] as $subscr) {
|
||||
if($subscr['target'] === $target) {
|
||||
foreach ($INFO['subscribed'] as $subscr) {
|
||||
if ($subscr['target'] === $target) {
|
||||
$is = true;
|
||||
}
|
||||
}
|
||||
if($is === false) {
|
||||
throw new \Exception(
|
||||
if ($is === false) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
$lang['subscr_not_subscribed'],
|
||||
$INPUT->server->str('REMOTE_USER'),
|
||||
|
||||
@@ -156,6 +156,7 @@ class ActionRouter {
|
||||
if(defined('DOKU_UNITTEST')) {
|
||||
throw $e;
|
||||
}
|
||||
ErrorHandler::logException($e);
|
||||
$msg = 'Something unforeseen has happened: ' . $e->getMessage();
|
||||
nice_die(hsc($msg));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Ui;
|
||||
use dokuwiki\Utf8\Sort;
|
||||
|
||||
/**
|
||||
* Manage all builtin AJAX calls
|
||||
*
|
||||
@@ -98,7 +101,7 @@ class Ajax {
|
||||
$data = array_map('trim', $data);
|
||||
$data = array_map('noNS', $data);
|
||||
$data = array_unique($data);
|
||||
sort($data);
|
||||
Sort::sort($data);
|
||||
|
||||
/* now construct a json */
|
||||
$suggestions = array(
|
||||
@@ -165,8 +168,10 @@ class Ajax {
|
||||
$client = $_SERVER['REMOTE_USER'];
|
||||
if(!$client) $client = clientIP(true);
|
||||
|
||||
$cname = getCacheName($client . $id, '.draft');
|
||||
@unlink($cname);
|
||||
$draft = new Draft($id, $client);
|
||||
if ($draft->isDraftAvailable() && checkSecurityToken()) {
|
||||
$draft->deleteDraft();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,14 +243,11 @@ class Ajax {
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
protected function callMediadiff() {
|
||||
global $NS;
|
||||
global $INPUT;
|
||||
|
||||
$image = '';
|
||||
if($INPUT->has('image')) $image = cleanID($INPUT->str('image'));
|
||||
$NS = getNS($image);
|
||||
$auth = auth_quickaclcheck("$NS:*");
|
||||
media_diff($image, $NS, $auth, true);
|
||||
(new Ui\MediaDiff($image))->preference('fromAjax', true)->show();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,10 +322,11 @@ class Ajax {
|
||||
|
||||
$data = array();
|
||||
search($data, $conf['datadir'], 'search_index', array('ns' => $ns), $dir);
|
||||
foreach(array_keys($data) as $item) {
|
||||
foreach (array_keys($data) as $item) {
|
||||
$data[$item]['level'] = $lvl + 1;
|
||||
}
|
||||
echo html_buildlist($data, 'idx', 'html_list_index', 'html_li_index');
|
||||
$idx = new Ui\Index;
|
||||
echo html_buildlist($data, 'idx', [$idx,'formatListItem'], [$idx,'tagListItem']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,13 +354,20 @@ class Ajax {
|
||||
// use index to lookup matching pages
|
||||
$pages = ft_pageLookup($id, true);
|
||||
|
||||
// If 'useheading' option is 'always' or 'content',
|
||||
// search page titles with original query as well.
|
||||
if ($conf['useheading'] == '1' || $conf['useheading'] == 'content') {
|
||||
$pages = array_merge($pages, ft_pageLookup($q, true, true));
|
||||
asort($pages, SORT_STRING);
|
||||
}
|
||||
|
||||
// result contains matches in pages and namespaces
|
||||
// we now extract the matching namespaces to show
|
||||
// them seperately
|
||||
$dirs = array();
|
||||
|
||||
foreach($pages as $pid => $title) {
|
||||
if(strpos(noNS($pid), $id) === false) {
|
||||
if(strpos(getNS($pid), $id) !== false) {
|
||||
// match was in the namespace
|
||||
$dirs[getNS($pid)] = 1; // assoc array avoids dupes
|
||||
} else {
|
||||
|
||||
56
dokuwiki/inc/Cache/CacheImageMod.php
Normal file
56
dokuwiki/inc/Cache/CacheImageMod.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Cache;
|
||||
|
||||
/**
|
||||
* Handle the caching of modified (resized/cropped) images
|
||||
*/
|
||||
class CacheImageMod extends Cache
|
||||
{
|
||||
|
||||
/** @var string source file */
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* @param string $file Original source file
|
||||
* @param int $w new width in pixel
|
||||
* @param int $h new height in pixel
|
||||
* @param string $ext Image extension - no leading dot
|
||||
* @param bool $crop Is this a crop?
|
||||
*/
|
||||
public function __construct($file, $w, $h, $ext, $crop)
|
||||
{
|
||||
$fullext = '.media.' . $w . 'x' . $h;
|
||||
$fullext .= $crop ? '.crop' : '';
|
||||
$fullext .= ".$ext";
|
||||
|
||||
$this->file = $file;
|
||||
|
||||
$this->setEvent('IMAGEMOD_CACHE_USE');
|
||||
parent::__construct($file, $fullext);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function makeDefaultCacheDecision()
|
||||
{
|
||||
if (!file_exists($this->file)) {
|
||||
return false;
|
||||
}
|
||||
return parent::makeDefaultCacheDecision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Caching depends on the source and the wiki config
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function addDependencies()
|
||||
{
|
||||
parent::addDependencies();
|
||||
|
||||
$this->depends['files'] = array_merge(
|
||||
[$this->file],
|
||||
getConfigFiles('main')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,16 +2,19 @@
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
use dokuwiki\Logger;
|
||||
|
||||
/**
|
||||
* methods for handling of changelog of pages or media files
|
||||
* ChangeLog Prototype; methods for handling changelog
|
||||
*/
|
||||
abstract class ChangeLog
|
||||
{
|
||||
use ChangeLogTrait;
|
||||
|
||||
/** @var string */
|
||||
protected $id;
|
||||
/** @var int */
|
||||
protected $chunk_size;
|
||||
/** @var false|int */
|
||||
protected $currentRevision;
|
||||
/** @var array */
|
||||
protected $cache;
|
||||
|
||||
@@ -32,29 +35,8 @@ abstract class ChangeLog
|
||||
|
||||
$this->id = $id;
|
||||
$this->setChunkSize($chunk_size);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set chunk size for file reading
|
||||
* Chunk size zero let read whole file at once
|
||||
*
|
||||
* @param int $chunk_size maximum block size read from file
|
||||
*/
|
||||
public function setChunkSize($chunk_size)
|
||||
{
|
||||
if (!is_numeric($chunk_size)) $chunk_size = 0;
|
||||
|
||||
$this->chunk_size = (int)max($chunk_size, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to changelog
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
abstract protected function getChangelogFilename();
|
||||
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
@@ -63,13 +45,82 @@ abstract class ChangeLog
|
||||
abstract protected function getFilename();
|
||||
|
||||
/**
|
||||
* Get the changelog information for a specific page id and revision (timestamp)
|
||||
* Check whether given revision is the current page
|
||||
*
|
||||
* @param int $rev timestamp of current page
|
||||
* @return bool true if $rev is current revision, otherwise false
|
||||
*/
|
||||
public function isCurrentRevision($rev)
|
||||
{
|
||||
return $rev == $this->currentRevision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the revision is last revision
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @return bool true if $rev is last revision, otherwise false
|
||||
*/
|
||||
public function isLastRevision($rev = null)
|
||||
{
|
||||
return $rev === $this->lastRevision();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current revision identifier
|
||||
*
|
||||
* The "current" revision means current version of the page or media file. It is either
|
||||
* identical with or newer than the "last" revision, that depends on whether the file
|
||||
* has modified, created or deleted outside of DokuWiki.
|
||||
* The value of identifier can be determined by timestamp as far as the file exists,
|
||||
* otherwise it must be assigned larger than any other revisions to keep them sortable.
|
||||
*
|
||||
* @return int|false revision timestamp
|
||||
*/
|
||||
public function currentRevision()
|
||||
{
|
||||
if (!isset($this->currentRevision)) {
|
||||
// set ChangeLog::currentRevision property
|
||||
$this->getCurrentRevisionInfo();
|
||||
}
|
||||
return $this->currentRevision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last revision identifier, date value of the last entry of the changelog
|
||||
*
|
||||
* @return int|false revision timestamp
|
||||
*/
|
||||
public function lastRevision()
|
||||
{
|
||||
$revs = $this->getRevisions(-1, 1);
|
||||
return empty($revs) ? false : $revs[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save revision info to the cache pool
|
||||
*
|
||||
* @param array $info Revision info structure
|
||||
* @return bool
|
||||
*/
|
||||
protected function cacheRevisionInfo($info)
|
||||
{
|
||||
if (!is_array($info)) return false;
|
||||
//$this->cache[$this->id][$info['date']] ??= $info; // since php 7.4
|
||||
$this->cache[$this->id][$info['date']] = $this->cache[$this->id][$info['date']] ?? $info;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the changelog information for a specific revision (timestamp)
|
||||
*
|
||||
* Adjacent changelog lines are optimistically parsed and cached to speed up
|
||||
* consecutive calls to getRevisionInfo. For large changelog files, only the chunk
|
||||
* containing the requested changelog line is read.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @param bool $retrieveCurrentRevInfo allows to skip for getting other revision info in the
|
||||
* getCurrentRevisionInfo() where $currentRevision is not yet determined
|
||||
* @return bool|array false or array with entries:
|
||||
* - date: unix timestamp
|
||||
* - ip: IPv4 address (127.0.0.1)
|
||||
@@ -78,13 +129,20 @@ abstract class ChangeLog
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
*
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
* @author Kate Arzamastseva <pshns@ukr.net>
|
||||
*/
|
||||
public function getRevisionInfo($rev)
|
||||
public function getRevisionInfo($rev, $retrieveCurrentRevInfo = true)
|
||||
{
|
||||
$rev = max($rev, 0);
|
||||
$rev = max(0, $rev);
|
||||
if (!$rev) return false;
|
||||
|
||||
//ensure the external edits are cached as well
|
||||
if (!isset($this->currentRevision) && $retrieveCurrentRevInfo) {
|
||||
$this->getCurrentRevisionInfo();
|
||||
}
|
||||
|
||||
// check if it's already in the memory cache
|
||||
if (isset($this->cache[$this->id]) && isset($this->cache[$this->id][$rev])) {
|
||||
@@ -100,10 +158,8 @@ abstract class ChangeLog
|
||||
|
||||
// parse and cache changelog lines
|
||||
foreach ($lines as $value) {
|
||||
$tmp = parseChangelogLine($value);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
}
|
||||
$info = $this->parseLogLine($value);
|
||||
$this->cacheRevisionInfo($info);
|
||||
}
|
||||
if (!isset($this->cache[$this->id][$rev])) {
|
||||
return false;
|
||||
@@ -125,7 +181,7 @@ abstract class ChangeLog
|
||||
* For efficiency, the log lines are parsed and cached for later
|
||||
* calls to getRevisionInfo. Large changelog files are read
|
||||
* backwards in chunks until the requested number of changelog
|
||||
* lines are recieved.
|
||||
* lines are received.
|
||||
*
|
||||
* @param int $first skip the first n changelog lines
|
||||
* @param int $num number of revisions to return
|
||||
@@ -140,6 +196,9 @@ abstract class ChangeLog
|
||||
$lines = array();
|
||||
$count = 0;
|
||||
|
||||
$logfile = $this->getChangelogFilename();
|
||||
if (!file_exists($logfile)) return $revs;
|
||||
|
||||
$num = max($num, 0);
|
||||
if ($num == 0) {
|
||||
return $revs;
|
||||
@@ -148,26 +207,22 @@ abstract class ChangeLog
|
||||
if ($first < 0) {
|
||||
$first = 0;
|
||||
} else {
|
||||
if (file_exists($this->getFilename())) {
|
||||
// skip current revision if the page exists
|
||||
$fileLastMod = $this->getFilename();
|
||||
if (file_exists($fileLastMod) && $this->isLastRevision(filemtime($fileLastMod))) {
|
||||
// skip last revision if the page exists
|
||||
$first = max($first + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->getChangelogFilename();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return $revs;
|
||||
}
|
||||
if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
if (filesize($logfile) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
// read whole file
|
||||
$lines = file($file);
|
||||
$lines = file($logfile);
|
||||
if ($lines === false) {
|
||||
return $revs;
|
||||
}
|
||||
} else {
|
||||
// read chunks backwards
|
||||
$fp = fopen($file, 'rb'); // "file pointer"
|
||||
$fp = fopen($logfile, 'rb'); // "file pointer"
|
||||
if ($fp === false) {
|
||||
return $revs;
|
||||
}
|
||||
@@ -221,20 +276,17 @@ abstract class ChangeLog
|
||||
$num = max(min(count($lines) - $first, $num), 0);
|
||||
if ($first > 0 && $num > 0) {
|
||||
$lines = array_slice($lines, max(count($lines) - $first - $num, 0), $num);
|
||||
} else {
|
||||
if ($first > 0 && $num == 0) {
|
||||
$lines = array_slice($lines, 0, max(count($lines) - $first, 0));
|
||||
} elseif ($first == 0 && $num > 0) {
|
||||
$lines = array_slice($lines, max(count($lines) - $num, 0));
|
||||
}
|
||||
} elseif ($first > 0 && $num == 0) {
|
||||
$lines = array_slice($lines, 0, max(count($lines) - $first, 0));
|
||||
} elseif ($first == 0 && $num > 0) {
|
||||
$lines = array_slice($lines, max(count($lines) - $num, 0));
|
||||
}
|
||||
|
||||
// handle lines in reverse order
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$tmp = parseChangelogLine($lines[$i]);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
$revs[] = $tmp['date'];
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,16 +294,18 @@ abstract class ChangeLog
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth revision left or right handside for a specific page id and revision (timestamp)
|
||||
* Get the nth revision left or right-hand side for a specific page id and revision (timestamp)
|
||||
*
|
||||
* For large changelog files, only the chunk containing the
|
||||
* reference revision $rev is read and sometimes a next chunck.
|
||||
* reference revision $rev is read and sometimes a next chunk.
|
||||
*
|
||||
* Adjacent changelog lines are optimistically parsed and cached to speed up
|
||||
* consecutive calls to getRevisionInfo.
|
||||
*
|
||||
* @param int $rev revision timestamp used as startdate (doesn't need to be revisionnumber)
|
||||
* @param int $direction give position of returned revision with respect to $rev; positive=next, negative=prev
|
||||
* @param int $rev revision timestamp used as start date
|
||||
* (doesn't need to be exact revision number)
|
||||
* @param int $direction give position of returned revision with respect to $rev;
|
||||
positive=next, negative=prev
|
||||
* @return bool|int
|
||||
* timestamp of the requested revision
|
||||
* otherwise false
|
||||
@@ -270,13 +324,13 @@ abstract class ChangeLog
|
||||
list($fp, $lines, $head, $tail, $eof) = $this->readloglines($rev);
|
||||
if (empty($lines)) return false;
|
||||
|
||||
// look for revisions later/earlier then $rev, when founded count till the wanted revision is reached
|
||||
// look for revisions later/earlier than $rev, when founded count till the wanted revision is reached
|
||||
// also parse and cache changelog lines for getRevisionInfo().
|
||||
$revcounter = 0;
|
||||
$relativerev = false;
|
||||
$checkotherchunck = true; //always runs once
|
||||
while (!$relativerev && $checkotherchunck) {
|
||||
$tmp = array();
|
||||
$revCounter = 0;
|
||||
$relativeRev = false;
|
||||
$checkOtherChunk = true; //always runs once
|
||||
while (!$relativeRev && $checkOtherChunk) {
|
||||
$info = array();
|
||||
//parse in normal or reverse order
|
||||
$count = count($lines);
|
||||
if ($direction > 0) {
|
||||
@@ -287,25 +341,24 @@ abstract class ChangeLog
|
||||
$step = -1;
|
||||
}
|
||||
for ($i = $start; $i >= 0 && $i < $count; $i = $i + $step) {
|
||||
$tmp = parseChangelogLine($lines[$i]);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
//look for revs older/earlier then reference $rev and select $direction-th one
|
||||
if (($direction > 0 && $tmp['date'] > $rev) || ($direction < 0 && $tmp['date'] < $rev)) {
|
||||
$revcounter++;
|
||||
if ($revcounter == abs($direction)) {
|
||||
$relativerev = $tmp['date'];
|
||||
if (($direction > 0 && $info['date'] > $rev) || ($direction < 0 && $info['date'] < $rev)) {
|
||||
$revCounter++;
|
||||
if ($revCounter == abs($direction)) {
|
||||
$relativeRev = $info['date'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//true when $rev is found, but not the wanted follow-up.
|
||||
$checkotherchunck = $fp
|
||||
&& ($tmp['date'] == $rev || ($revcounter > 0 && !$relativerev))
|
||||
$checkOtherChunk = $fp
|
||||
&& ($info['date'] == $rev || ($revCounter > 0 && !$relativeRev))
|
||||
&& !(($tail == $eof && $direction > 0) || ($head == 0 && $direction < 0));
|
||||
|
||||
if ($checkotherchunck) {
|
||||
if ($checkOtherChunk) {
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, $direction);
|
||||
|
||||
if (empty($lines)) break;
|
||||
@@ -315,7 +368,7 @@ abstract class ChangeLog
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
return $relativerev;
|
||||
return $relativeRev;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,7 +382,7 @@ abstract class ChangeLog
|
||||
*/
|
||||
public function getRevisionsAround($rev1, $rev2, $max = 50)
|
||||
{
|
||||
$max = floor(abs($max) / 2) * 2 + 1;
|
||||
$max = intval(abs($max) / 2) * 2 + 1;
|
||||
$rev1 = max($rev1, 0);
|
||||
$rev2 = max($rev2, 0);
|
||||
|
||||
@@ -341,187 +394,46 @@ abstract class ChangeLog
|
||||
}
|
||||
} else {
|
||||
//empty right side means a removed page. Look up last revision.
|
||||
$revs = $this->getRevisions(-1, 1);
|
||||
$rev2 = $revs[0];
|
||||
$rev2 = $this->currentRevision();
|
||||
}
|
||||
//collect revisions around rev2
|
||||
list($revs2, $allrevs, $fp, $lines, $head, $tail) = $this->retrieveRevisionsAround($rev2, $max);
|
||||
list($revs2, $allRevs, $fp, $lines, $head, $tail) = $this->retrieveRevisionsAround($rev2, $max);
|
||||
|
||||
if (empty($revs2)) return array(array(), array());
|
||||
|
||||
//collect revisions around rev1
|
||||
$index = array_search($rev1, $allrevs);
|
||||
$index = array_search($rev1, $allRevs);
|
||||
if ($index === false) {
|
||||
//no overlapping revisions
|
||||
list($revs1, , , , ,) = $this->retrieveRevisionsAround($rev1, $max);
|
||||
if (empty($revs1)) $revs1 = array();
|
||||
} else {
|
||||
//revisions overlaps, reuse revisions around rev2
|
||||
$revs1 = $allrevs;
|
||||
$lastRev = array_pop($allRevs); //keep last entry that could be external edit
|
||||
$revs1 = $allRevs;
|
||||
while ($head > 0) {
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$tmp = parseChangelogLine($lines[$i]);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
$revs1[] = $tmp['date'];
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs1[] = $info['date'];
|
||||
$index++;
|
||||
|
||||
if ($index > floor($max / 2)) break 2;
|
||||
if ($index > intval($max / 2)) break 2;
|
||||
}
|
||||
}
|
||||
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1);
|
||||
}
|
||||
sort($revs1);
|
||||
$revs1[] = $lastRev; //push back last entry
|
||||
|
||||
//return wanted selection
|
||||
$revs1 = array_slice($revs1, max($index - floor($max / 2), 0), $max);
|
||||
$revs1 = array_slice($revs1, max($index - intval($max / 2), 0), $max);
|
||||
}
|
||||
|
||||
return array(array_reverse($revs1), array_reverse($revs2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the ID has old revisons
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasRevisions() {
|
||||
$file = $this->getChangelogFilename();
|
||||
return file_exists($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines from changelog.
|
||||
* If file larger than $chuncksize, only chunck is read that could contain $rev.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @return array|false
|
||||
* if success returns array(fp, array(changeloglines), $head, $tail, $eof)
|
||||
* where fp only defined for chuck reading, needs closing.
|
||||
* otherwise false
|
||||
*/
|
||||
protected function readloglines($rev)
|
||||
{
|
||||
$file = $this->getChangelogFilename();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fp = null;
|
||||
$head = 0;
|
||||
$tail = 0;
|
||||
$eof = 0;
|
||||
|
||||
if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
// read whole file
|
||||
$lines = file($file);
|
||||
if ($lines === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// read by chunk
|
||||
$fp = fopen($file, 'rb'); // "file pointer"
|
||||
if ($fp === false) {
|
||||
return false;
|
||||
}
|
||||
$head = 0;
|
||||
fseek($fp, 0, SEEK_END);
|
||||
$eof = ftell($fp);
|
||||
$tail = $eof;
|
||||
|
||||
// find chunk
|
||||
while ($tail - $head > $this->chunk_size) {
|
||||
$finger = $head + floor(($tail - $head) / 2.0);
|
||||
$finger = $this->getNewlinepointer($fp, $finger);
|
||||
$tmp = fgets($fp);
|
||||
if ($finger == $head || $finger == $tail) {
|
||||
break;
|
||||
}
|
||||
$tmp = parseChangelogLine($tmp);
|
||||
$finger_rev = $tmp['date'];
|
||||
|
||||
if ($finger_rev > $rev) {
|
||||
$tail = $finger;
|
||||
} else {
|
||||
$head = $finger;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tail - $head < 1) {
|
||||
// cound not find chunk, assume requested rev is missing
|
||||
fclose($fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
}
|
||||
return array(
|
||||
$fp,
|
||||
$lines,
|
||||
$head,
|
||||
$tail,
|
||||
$eof,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read chunk and return array with lines of given chunck.
|
||||
* Has no check if $head and $tail are really at a new line
|
||||
*
|
||||
* @param resource $fp resource filepointer
|
||||
* @param int $head start point chunck
|
||||
* @param int $tail end point chunck
|
||||
* @return array lines read from chunck
|
||||
*/
|
||||
protected function readChunk($fp, $head, $tail)
|
||||
{
|
||||
$chunk = '';
|
||||
$chunk_size = max($tail - $head, 0); // found chunk size
|
||||
$got = 0;
|
||||
fseek($fp, $head);
|
||||
while ($got < $chunk_size && !feof($fp)) {
|
||||
$tmp = @fread($fp, max(min($this->chunk_size, $chunk_size - $got), 0));
|
||||
if ($tmp === false) { //error state
|
||||
break;
|
||||
}
|
||||
$got += strlen($tmp);
|
||||
$chunk .= $tmp;
|
||||
}
|
||||
$lines = explode("\n", $chunk);
|
||||
array_pop($lines); // remove trailing newline
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pointer to first new line after $finger and return its position
|
||||
*
|
||||
* @param resource $fp filepointer
|
||||
* @param int $finger a pointer
|
||||
* @return int pointer
|
||||
*/
|
||||
protected function getNewlinepointer($fp, $finger)
|
||||
{
|
||||
fseek($fp, $finger);
|
||||
$nl = $finger;
|
||||
if ($finger > 0) {
|
||||
fgets($fp); // slip the finger forward to a new line
|
||||
$nl = ftell($fp);
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether given revision is the current page
|
||||
*
|
||||
* @param int $rev timestamp of current page
|
||||
* @return bool true if $rev is current revision, otherwise false
|
||||
*/
|
||||
public function isCurrentRevision($rev)
|
||||
{
|
||||
return $rev == @filemtime($this->getFilename());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an existing revision for a specific date which is
|
||||
* the current one or younger or equal then the date
|
||||
@@ -531,8 +443,9 @@ abstract class ChangeLog
|
||||
*/
|
||||
public function getLastRevisionAt($date_at)
|
||||
{
|
||||
$fileLastMod = $this->getFilename();
|
||||
//requested date_at(timestamp) younger or equal then modified_time($this->id) => load current
|
||||
if (file_exists($this->getFilename()) && $date_at >= @filemtime($this->getFilename())) {
|
||||
if (file_exists($fileLastMod) && $date_at >= @filemtime($fileLastMod)) {
|
||||
return '';
|
||||
} else {
|
||||
if ($rev = $this->getRelativeRevision($date_at + 1, -1)) { //+1 to get also the requested date revision
|
||||
@@ -543,124 +456,223 @@ abstract class ChangeLog
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next lines of the changelog of the chunck before head or after tail
|
||||
*
|
||||
* @param resource $fp filepointer
|
||||
* @param int $head position head of last chunk
|
||||
* @param int $tail position tail of last chunk
|
||||
* @param int $direction positive forward, negative backward
|
||||
* @return array with entries:
|
||||
* - $lines: changelog lines of readed chunk
|
||||
* - $head: head of chunk
|
||||
* - $tail: tail of chunk
|
||||
*/
|
||||
protected function readAdjacentChunk($fp, $head, $tail, $direction)
|
||||
{
|
||||
if (!$fp) return array(array(), $head, $tail);
|
||||
|
||||
if ($direction > 0) {
|
||||
//read forward
|
||||
$head = $tail;
|
||||
$tail = $head + floor($this->chunk_size * (2 / 3));
|
||||
$tail = $this->getNewlinepointer($fp, $tail);
|
||||
} else {
|
||||
//read backward
|
||||
$tail = $head;
|
||||
$head = max($tail - $this->chunk_size, 0);
|
||||
while (true) {
|
||||
$nl = $this->getNewlinepointer($fp, $head);
|
||||
// was the chunk big enough? if not, take another bite
|
||||
if ($nl > 0 && $tail <= $nl) {
|
||||
$head = max($head - $this->chunk_size, 0);
|
||||
} else {
|
||||
$head = $nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load next chunck
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
return array($lines, $head, $tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the $max revisions near to the timestamp $rev
|
||||
*
|
||||
* Ideally, half of retrieved timestamps are older than $rev, another half are newer.
|
||||
* The returned array $requestedRevs may not contain the reference timestamp $rev
|
||||
* when it does not match any revision value recorded in changelog.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @param int $max maximum number of revisions to be returned
|
||||
* @return bool|array
|
||||
* return array with entries:
|
||||
* - $requestedrevs: array of with $max revision timestamps
|
||||
* - $requestedRevs: array of with $max revision timestamps
|
||||
* - $revs: all parsed revision timestamps
|
||||
* - $fp: filepointer only defined for chuck reading, needs closing.
|
||||
* - $fp: file pointer only defined for chuck reading, needs closing.
|
||||
* - $lines: non-parsed changelog lines before the parsed revisions
|
||||
* - $head: position of first readed changelogline
|
||||
* - $lasttail: position of end of last readed changelogline
|
||||
* - $head: position of first read changelog line
|
||||
* - $lastTail: position of end of last read changelog line
|
||||
* otherwise false
|
||||
*/
|
||||
protected function retrieveRevisionsAround($rev, $max)
|
||||
{
|
||||
$revs = array();
|
||||
$afterCount = $beforeCount = 0;
|
||||
|
||||
//get lines from changelog
|
||||
list($fp, $lines, $starthead, $starttail, /* $eof */) = $this->readloglines($rev);
|
||||
list($fp, $lines, $startHead, $startTail, $eof) = $this->readloglines($rev);
|
||||
if (empty($lines)) return false;
|
||||
|
||||
//parse chunk containing $rev, and read forward more chunks until $max/2 is reached
|
||||
$head = $starthead;
|
||||
$tail = $starttail;
|
||||
$revs = array();
|
||||
$aftercount = $beforecount = 0;
|
||||
//parse changelog lines in chunk, and read forward more chunks until $max/2 is reached
|
||||
$head = $startHead;
|
||||
$tail = $startTail;
|
||||
while (count($lines) > 0) {
|
||||
foreach ($lines as $line) {
|
||||
$tmp = parseChangelogLine($line);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
$revs[] = $tmp['date'];
|
||||
if ($tmp['date'] >= $rev) {
|
||||
$info = $this->parseLogLine($line);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
if ($info['date'] >= $rev) {
|
||||
//count revs after reference $rev
|
||||
$aftercount++;
|
||||
if ($aftercount == 1) $beforecount = count($revs);
|
||||
$afterCount++;
|
||||
if ($afterCount == 1) $beforeCount = count($revs);
|
||||
}
|
||||
//enough revs after reference $rev?
|
||||
if ($aftercount > floor($max / 2)) break 2;
|
||||
if ($afterCount > intval($max / 2)) break 2;
|
||||
}
|
||||
}
|
||||
//retrieve next chunk
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, 1);
|
||||
}
|
||||
if ($aftercount == 0) return false;
|
||||
$lastTail = $tail;
|
||||
|
||||
$lasttail = $tail;
|
||||
// add a possible revision of external edit, create or deletion
|
||||
if ($lastTail == $eof && $afterCount <= intval($max / 2) &&
|
||||
count($revs) && !$this->isCurrentRevision($revs[count($revs)-1])
|
||||
) {
|
||||
$revs[] = $this->currentRevision;
|
||||
$afterCount++;
|
||||
}
|
||||
|
||||
//read additional chuncks backward until $max/2 is reached and total number of revs is equal to $max
|
||||
if ($afterCount == 0) {
|
||||
//given timestamp $rev is newer than the most recent line in chunk
|
||||
return false; //FIXME: or proceed to collect older revisions?
|
||||
}
|
||||
|
||||
//read more chunks backward until $max/2 is reached and total number of revs is equal to $max
|
||||
$lines = array();
|
||||
$i = 0;
|
||||
if ($aftercount > 0) {
|
||||
$head = $starthead;
|
||||
$tail = $starttail;
|
||||
if ($afterCount > 0) {
|
||||
$head = $startHead;
|
||||
$tail = $startTail;
|
||||
while ($head > 0) {
|
||||
list($lines, $head, $tail) = $this->readAdjacentChunk($fp, $head, $tail, -1);
|
||||
|
||||
for ($i = count($lines) - 1; $i >= 0; $i--) {
|
||||
$tmp = parseChangelogLine($lines[$i]);
|
||||
if ($tmp !== false) {
|
||||
$this->cache[$this->id][$tmp['date']] = $tmp;
|
||||
$revs[] = $tmp['date'];
|
||||
$beforecount++;
|
||||
$info = $this->parseLogLine($lines[$i]);
|
||||
if ($this->cacheRevisionInfo($info)) {
|
||||
$revs[] = $info['date'];
|
||||
$beforeCount++;
|
||||
//enough revs before reference $rev?
|
||||
if ($beforecount > max(floor($max / 2), $max - $aftercount)) break 2;
|
||||
if ($beforeCount > max(intval($max / 2), $max - $afterCount)) break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($revs);
|
||||
|
||||
//keep only non-parsed lines
|
||||
$lines = array_slice($lines, 0, $i);
|
||||
//trunk desired selection
|
||||
$requestedrevs = array_slice($revs, -$max, $max);
|
||||
|
||||
return array($requestedrevs, $revs, $fp, $lines, $head, $lasttail);
|
||||
sort($revs);
|
||||
|
||||
//trunk desired selection
|
||||
$requestedRevs = array_slice($revs, -$max, $max);
|
||||
|
||||
return array($requestedRevs, $revs, $fp, $lines, $head, $lastTail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current revision information, considering external edit, create or deletion
|
||||
*
|
||||
* When the file has not modified since its last revision, the information of the last
|
||||
* change that had already recorded in the changelog is returned as current change info.
|
||||
* Otherwise, the change information since the last revision caused outside DokuWiki
|
||||
* should be returned, which is referred as "external revision".
|
||||
*
|
||||
* The change date of the file can be determined by timestamp as far as the file exists,
|
||||
* however this is not possible when the file has already deleted outside of DokuWiki.
|
||||
* In such case we assign 1 sec before current time() for the external deletion.
|
||||
* As a result, the value of current revision identifier may change each time because:
|
||||
* 1) the file has again modified outside of DokuWiki, or
|
||||
* 2) the value is essentially volatile for deleted but once existed files.
|
||||
*
|
||||
* @return bool|array false when page had never existed or array with entries:
|
||||
* - date: revision identifier (timestamp or last revision +1)
|
||||
* - ip: IPv4 address (127.0.0.1)
|
||||
* - type: log line type
|
||||
* - id: id of page or media
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
* - timestamp: unix timestamp or false (key set only for external edit occurred)
|
||||
*
|
||||
* @author Satoshi Sahara <sahara.satoshi@gmail.com>
|
||||
*/
|
||||
public function getCurrentRevisionInfo()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (isset($this->currentRevision)) return $this->getRevisionInfo($this->currentRevision);
|
||||
|
||||
// get revision id from the item file timestamp and changelog
|
||||
$fileLastMod = $this->getFilename();
|
||||
$fileRev = @filemtime($fileLastMod); // false when the file not exist
|
||||
$lastRev = $this->lastRevision(); // false when no changelog
|
||||
|
||||
if (!$fileRev && !$lastRev) { // has never existed
|
||||
$this->currentRevision = false;
|
||||
return false;
|
||||
} elseif ($fileRev === $lastRev) { // not external edit
|
||||
$this->currentRevision = $lastRev;
|
||||
return $this->getRevisionInfo($lastRev);
|
||||
}
|
||||
|
||||
if (!$fileRev && $lastRev) { // item file does not exist
|
||||
// check consistency against changelog
|
||||
$revInfo = $this->getRevisionInfo($lastRev, false);
|
||||
if ($revInfo['type'] == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$this->currentRevision = $lastRev;
|
||||
return $revInfo;
|
||||
}
|
||||
|
||||
// externally deleted, set revision date as late as possible
|
||||
$revInfo = [
|
||||
'date' => max($lastRev +1, time() -1), // 1 sec before now or new page save
|
||||
'ip' => '127.0.0.1',
|
||||
'type' => DOKU_CHANGE_TYPE_DELETE,
|
||||
'id' => $this->id,
|
||||
'user' => '',
|
||||
'sum' => $lang['deleted'].' - '.$lang['external_edit'].' ('.$lang['unknowndate'].')',
|
||||
'extra' => '',
|
||||
'sizechange' => -io_getSizeFile($this->getFilename($lastRev)),
|
||||
'timestamp' => false,
|
||||
];
|
||||
|
||||
} else { // item file exists, with timestamp $fileRev
|
||||
// here, file timestamp $fileRev is different with last revision timestamp $lastRev in changelog
|
||||
$isJustCreated = $lastRev === false || (
|
||||
$fileRev > $lastRev &&
|
||||
$this->getRevisionInfo($lastRev, false)['type'] == DOKU_CHANGE_TYPE_DELETE
|
||||
);
|
||||
$filesize_new = filesize($this->getFilename());
|
||||
$filesize_old = $isJustCreated ? 0 : io_getSizeFile($this->getFilename($lastRev));
|
||||
$sizechange = $filesize_new - $filesize_old;
|
||||
|
||||
if ($isJustCreated) {
|
||||
$timestamp = $fileRev;
|
||||
$sum = $lang['created'].' - '.$lang['external_edit'];
|
||||
} elseif ($fileRev > $lastRev) {
|
||||
$timestamp = $fileRev;
|
||||
$sum = $lang['external_edit'];
|
||||
} else {
|
||||
// $fileRev is older than $lastRev, that is erroneous/incorrect occurrence.
|
||||
$msg = "Warning: current file modification time is older than last revision date";
|
||||
$details = 'File revision: '.$fileRev.' '.dformat($fileRev, "%Y-%m-%d %H:%M:%S")."\n"
|
||||
.'Last revision: '.$lastRev.' '.dformat($lastRev, "%Y-%m-%d %H:%M:%S");
|
||||
Logger::error($msg, $details, $this->getFilename());
|
||||
$timestamp = false;
|
||||
$sum = $lang['external_edit'].' ('.$lang['unknowndate'].')';
|
||||
}
|
||||
|
||||
// externally created or edited
|
||||
$revInfo = [
|
||||
'date' => $timestamp ?: $lastRev +1,
|
||||
'ip' => '127.0.0.1',
|
||||
'type' => $isJustCreated ? DOKU_CHANGE_TYPE_CREATE : DOKU_CHANGE_TYPE_EDIT,
|
||||
'id' => $this->id,
|
||||
'user' => '',
|
||||
'sum' => $sum,
|
||||
'extra' => '',
|
||||
'sizechange' => $sizechange,
|
||||
'timestamp' => $timestamp,
|
||||
];
|
||||
}
|
||||
|
||||
// cache current revision information of external edition
|
||||
$this->currentRevision = $revInfo['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $revInfo;
|
||||
return $this->getRevisionInfo($this->currentRevision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mechanism to trace no-actual external current revision
|
||||
* @param int $rev
|
||||
*/
|
||||
public function traceCurrentRevision($rev)
|
||||
{
|
||||
if ($rev > $this->lastRevision()) {
|
||||
$rev = $this->currentRevision();
|
||||
}
|
||||
return $rev;
|
||||
}
|
||||
}
|
||||
|
||||
269
dokuwiki/inc/ChangeLog/ChangeLogTrait.php
Normal file
269
dokuwiki/inc/ChangeLog/ChangeLogTrait.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
use dokuwiki\Utf8\PhpString;
|
||||
|
||||
/**
|
||||
* Provides methods for handling of changelog
|
||||
*/
|
||||
trait ChangeLogTrait
|
||||
{
|
||||
/**
|
||||
* Adds an entry to the changelog file
|
||||
*
|
||||
* @return array added log line as revision info
|
||||
*/
|
||||
abstract public function addLogEntry(array $info, $timestamp = null);
|
||||
|
||||
/**
|
||||
* Parses a changelog line into it's components
|
||||
*
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
*
|
||||
* @param string $line changelog line
|
||||
* @return array|bool parsed line or false
|
||||
*/
|
||||
public static function parseLogLine($line)
|
||||
{
|
||||
$info = explode("\t", rtrim($line, "\n"));
|
||||
if ($info !== false && count($info) > 1) {
|
||||
return [
|
||||
'date' => (int)$info[0], // unix timestamp
|
||||
'ip' => $info[1], // IPv4 address (127.0.0.1)
|
||||
'type' => $info[2], // log line type
|
||||
'id' => $info[3], // page id
|
||||
'user' => $info[4], // user name
|
||||
'sum' => $info[5], // edit summary (or action reason)
|
||||
'extra' => $info[6], // extra data (varies by line type)
|
||||
'sizechange' => (isset($info[7]) && $info[7] !== '') ? (int)$info[7] : null, //
|
||||
];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a changelog line from it's components
|
||||
*
|
||||
* @param array $info Revision info structure
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return string changelog line
|
||||
*/
|
||||
public static function buildLogLine(array &$info, $timestamp = null)
|
||||
{
|
||||
$strip = ["\t", "\n"];
|
||||
$entry = array(
|
||||
'date' => $timestamp ?? $info['date'],
|
||||
'ip' => $info['ip'],
|
||||
'type' => str_replace($strip, '', $info['type']),
|
||||
'id' => $info['id'],
|
||||
'user' => $info['user'],
|
||||
'sum' => PhpString::substr(str_replace($strip, '', $info['sum']), 0, 255),
|
||||
'extra' => str_replace($strip, '', $info['extra']),
|
||||
'sizechange' => $info['sizechange'],
|
||||
);
|
||||
$info = $entry;
|
||||
return implode("\t", $entry) ."\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to changelog
|
||||
*
|
||||
* @return string path to file
|
||||
*/
|
||||
abstract protected function getChangelogFilename();
|
||||
|
||||
/**
|
||||
* Checks if the ID has old revisions
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasRevisions()
|
||||
{
|
||||
$logfile = $this->getChangelogFilename();
|
||||
return file_exists($logfile);
|
||||
}
|
||||
|
||||
|
||||
/** @var int */
|
||||
protected $chunk_size;
|
||||
|
||||
/**
|
||||
* Set chunk size for file reading
|
||||
* Chunk size zero let read whole file at once
|
||||
*
|
||||
* @param int $chunk_size maximum block size read from file
|
||||
*/
|
||||
public function setChunkSize($chunk_size)
|
||||
{
|
||||
if (!is_numeric($chunk_size)) $chunk_size = 0;
|
||||
|
||||
$this->chunk_size = (int)max($chunk_size, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns lines from changelog.
|
||||
* If file larger than $chunk_size, only chunk is read that could contain $rev.
|
||||
*
|
||||
* When reference timestamp $rev is outside time range of changelog, readloglines() will return
|
||||
* lines in first or last chunk, but they obviously does not contain $rev.
|
||||
*
|
||||
* @param int $rev revision timestamp
|
||||
* @return array|false
|
||||
* if success returns array(fp, array(changeloglines), $head, $tail, $eof)
|
||||
* where fp only defined for chuck reading, needs closing.
|
||||
* otherwise false
|
||||
*/
|
||||
protected function readloglines($rev)
|
||||
{
|
||||
$file = $this->getChangelogFilename();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fp = null;
|
||||
$head = 0;
|
||||
$tail = 0;
|
||||
$eof = 0;
|
||||
|
||||
if (filesize($file) < $this->chunk_size || $this->chunk_size == 0) {
|
||||
// read whole file
|
||||
$lines = file($file);
|
||||
if ($lines === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// read by chunk
|
||||
$fp = fopen($file, 'rb'); // "file pointer"
|
||||
if ($fp === false) {
|
||||
return false;
|
||||
}
|
||||
fseek($fp, 0, SEEK_END);
|
||||
$eof = ftell($fp);
|
||||
$tail = $eof;
|
||||
|
||||
// find chunk
|
||||
while ($tail - $head > $this->chunk_size) {
|
||||
$finger = $head + intval(($tail - $head) / 2);
|
||||
$finger = $this->getNewlinepointer($fp, $finger);
|
||||
$tmp = fgets($fp);
|
||||
if ($finger == $head || $finger == $tail) {
|
||||
break;
|
||||
}
|
||||
$info = $this->parseLogLine($tmp);
|
||||
$finger_rev = $info['date'];
|
||||
|
||||
if ($finger_rev > $rev) {
|
||||
$tail = $finger;
|
||||
} else {
|
||||
$head = $finger;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tail - $head < 1) {
|
||||
// could not find chunk, assume requested rev is missing
|
||||
fclose($fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
}
|
||||
return array(
|
||||
$fp,
|
||||
$lines,
|
||||
$head,
|
||||
$tail,
|
||||
$eof,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read chunk and return array with lines of given chunk.
|
||||
* Has no check if $head and $tail are really at a new line
|
||||
*
|
||||
* @param resource $fp resource file pointer
|
||||
* @param int $head start point chunk
|
||||
* @param int $tail end point chunk
|
||||
* @return array lines read from chunk
|
||||
*/
|
||||
protected function readChunk($fp, $head, $tail)
|
||||
{
|
||||
$chunk = '';
|
||||
$chunk_size = max($tail - $head, 0); // found chunk size
|
||||
$got = 0;
|
||||
fseek($fp, $head);
|
||||
while ($got < $chunk_size && !feof($fp)) {
|
||||
$tmp = @fread($fp, max(min($this->chunk_size, $chunk_size - $got), 0));
|
||||
if ($tmp === false) { //error state
|
||||
break;
|
||||
}
|
||||
$got += strlen($tmp);
|
||||
$chunk .= $tmp;
|
||||
}
|
||||
$lines = explode("\n", $chunk);
|
||||
array_pop($lines); // remove trailing newline
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pointer to first new line after $finger and return its position
|
||||
*
|
||||
* @param resource $fp file pointer
|
||||
* @param int $finger a pointer
|
||||
* @return int pointer
|
||||
*/
|
||||
protected function getNewlinepointer($fp, $finger)
|
||||
{
|
||||
fseek($fp, $finger);
|
||||
$nl = $finger;
|
||||
if ($finger > 0) {
|
||||
fgets($fp); // slip the finger forward to a new line
|
||||
$nl = ftell($fp);
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next lines of the changelog of the chunk before head or after tail
|
||||
*
|
||||
* @param resource $fp file pointer
|
||||
* @param int $head position head of last chunk
|
||||
* @param int $tail position tail of last chunk
|
||||
* @param int $direction positive forward, negative backward
|
||||
* @return array with entries:
|
||||
* - $lines: changelog lines of read chunk
|
||||
* - $head: head of chunk
|
||||
* - $tail: tail of chunk
|
||||
*/
|
||||
protected function readAdjacentChunk($fp, $head, $tail, $direction)
|
||||
{
|
||||
if (!$fp) return array(array(), $head, $tail);
|
||||
|
||||
if ($direction > 0) {
|
||||
//read forward
|
||||
$head = $tail;
|
||||
$tail = $head + intval($this->chunk_size * (2 / 3));
|
||||
$tail = $this->getNewlinepointer($fp, $tail);
|
||||
} else {
|
||||
//read backward
|
||||
$tail = $head;
|
||||
$head = max($tail - $this->chunk_size, 0);
|
||||
while (true) {
|
||||
$nl = $this->getNewlinepointer($fp, $head);
|
||||
// was the chunk big enough? if not, take another bite
|
||||
if ($nl > 0 && $tail <= $nl) {
|
||||
$head = max($head - $this->chunk_size, 0);
|
||||
} else {
|
||||
$head = $nl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load next chunk
|
||||
$lines = $this->readChunk($fp, $head, $tail);
|
||||
return array($lines, $head, $tail);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* handles changelog of a media file
|
||||
* Class MediaChangeLog; handles changelog of a media file
|
||||
*/
|
||||
class MediaChangeLog extends ChangeLog
|
||||
{
|
||||
@@ -21,10 +21,40 @@ class MediaChangeLog extends ChangeLog
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
* @param string|int $rev empty string or revision timestamp
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getFilename()
|
||||
protected function getFilename($rev = '')
|
||||
{
|
||||
return mediaFN($this->id);
|
||||
return mediaFN($this->id, $rev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds an entry to the changelog
|
||||
*
|
||||
* @param array $info Revision info structure of a media file
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return array revision info of added log line
|
||||
*
|
||||
* @see also addMediaLogEntry() in inc/changelog.php file
|
||||
*/
|
||||
public function addLogEntry(array $info, $timestamp = null)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (isset($timestamp)) unset($this->cache[$this->id][$info['date']]);
|
||||
|
||||
// add changelog lines
|
||||
$logline = $this->buildLogLine($info, $timestamp);
|
||||
io_saveFile(mediaMetaFN($this->id,'.changes'), $logline, $append = true);
|
||||
io_saveFile($conf['media_changelog'], $logline, $append = true); //global changelog cache
|
||||
|
||||
// update cache
|
||||
$this->currentRevision = $info['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $info;
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* handles changelog of a wiki page
|
||||
* Class PageChangeLog; handles changelog of a wiki page
|
||||
*/
|
||||
class PageChangeLog extends ChangeLog
|
||||
{
|
||||
@@ -21,10 +21,40 @@ class PageChangeLog extends ChangeLog
|
||||
/**
|
||||
* Returns path to current page/media
|
||||
*
|
||||
* @param string|int $rev empty string or revision timestamp
|
||||
* @return string path to file
|
||||
*/
|
||||
protected function getFilename()
|
||||
protected function getFilename($rev = '')
|
||||
{
|
||||
return wikiFN($this->id);
|
||||
return wikiFN($this->id, $rev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds an entry to the changelog
|
||||
*
|
||||
* @param array $info Revision info structure of a page
|
||||
* @param int $timestamp log line date (optional)
|
||||
* @return array revision info of added log line
|
||||
*
|
||||
* @see also addLogEntry() in inc/changelog.php file
|
||||
*/
|
||||
public function addLogEntry(array $info, $timestamp = null)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if (isset($timestamp)) unset($this->cache[$this->id][$info['date']]);
|
||||
|
||||
// add changelog lines
|
||||
$logline = $this->buildLogLine($info, $timestamp);
|
||||
io_saveFile(metaFN($this->id,'.changes'), $logline, true);
|
||||
io_saveFile($conf['changelog'], $logline, true); //global changelog cache
|
||||
|
||||
// update cache
|
||||
$this->currentRevision = $info['date'];
|
||||
$this->cache[$this->id][$this->currentRevision] = $info;
|
||||
return $info;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
396
dokuwiki/inc/ChangeLog/RevisionInfo.php
Normal file
396
dokuwiki/inc/ChangeLog/RevisionInfo.php
Normal file
@@ -0,0 +1,396 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\ChangeLog;
|
||||
|
||||
/**
|
||||
* Class RevisionInfo
|
||||
*
|
||||
* Provides methods to show Revision Information in DokuWiki Ui components:
|
||||
* - Ui\Recent
|
||||
* - Ui\PageRevisions
|
||||
* - Ui\MediaRevisions
|
||||
*/
|
||||
class RevisionInfo
|
||||
{
|
||||
/* @var array */
|
||||
protected $info;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $info Revision Information structure with entries:
|
||||
* - date: unix timestamp
|
||||
* - ip: IPv4 or IPv6 address
|
||||
* - type: change type (log line type)
|
||||
* - id: page id
|
||||
* - user: user name
|
||||
* - sum: edit summary (or action reason)
|
||||
* - extra: extra data (varies by line type)
|
||||
* - sizechange: change of filesize
|
||||
* additionally,
|
||||
* - current: (optional) whether current revision or not
|
||||
* - timestamp: (optional) set only when external edits occurred
|
||||
* - mode: (internal use) ether "media" or "page"
|
||||
*/
|
||||
public function __construct($info = null)
|
||||
{
|
||||
if (is_array($info) && isset($info['id'])) {
|
||||
// define strategy context
|
||||
$info['mode'] = strrpos($info['id'], '.') ? 'media' : 'page';
|
||||
} else {
|
||||
$info = [
|
||||
'mode' => 'page',
|
||||
'date' => false,
|
||||
];
|
||||
}
|
||||
$this->info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or return whether this revision is current page or media file
|
||||
*
|
||||
* This method does not check exactly whether the revision is current or not. Instead,
|
||||
* set value of associated "current" key for internal use. Some UI element like diff
|
||||
* link button depend on relation to current page or media file. A changelog line does
|
||||
* not indicate whether it corresponds to current page or media file.
|
||||
*
|
||||
* @param bool $value true if the revision is current, otherwise false
|
||||
* @return bool
|
||||
*/
|
||||
public function isCurrent($value = null)
|
||||
{
|
||||
return (bool) $this->val('current', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return or set a value of associated key of revision information
|
||||
* but does not allow to change values of existing keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public function val($key, $value = null)
|
||||
{
|
||||
if (isset($value) && !array_key_exists($key, $this->info)) {
|
||||
// setter, only for new keys
|
||||
$this->info[$key] = $value;
|
||||
}
|
||||
if (array_key_exists($key, $this->info)) {
|
||||
// getter
|
||||
return $this->info[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra key-value to the revision information
|
||||
* but does not allow to change values of existing keys
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
public function append(array $info)
|
||||
{
|
||||
foreach ($info as $key => $value) {
|
||||
$this->val($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* file icon of the page or media file
|
||||
* used in [Ui\recent]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showFileIcon()
|
||||
{
|
||||
$id = $this->val('id');
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
return media_printicon($id);
|
||||
case 'page': // page revision
|
||||
return '<img class="icon" src="'.DOKU_BASE.'lib/images/fileicons/file.png" alt="'.$id.'" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* edit date and time of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @param bool $checkTimestamp enable timestamp check, alter formatted string when timestamp is false
|
||||
* @return string
|
||||
*/
|
||||
public function showEditDate($checkTimestamp = false)
|
||||
{
|
||||
$formatted = dformat($this->val('date'));
|
||||
if ($checkTimestamp && $this->val('timestamp') === false) {
|
||||
// exact date is unknown for externally deleted file
|
||||
// when unknown, alter formatted string "YYYY-mm-DD HH:MM" to "____-__-__ __:__"
|
||||
$formatted = preg_replace('/[0-9a-zA-Z]/','_', $formatted);
|
||||
}
|
||||
return '<span class="date">'. $formatted .'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* edit summary
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showEditSummary()
|
||||
{
|
||||
return '<span class="sum">'.' – '. hsc($this->val('sum')).'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* editor of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showEditor()
|
||||
{
|
||||
if ($this->val('user')) {
|
||||
$html = '<bdi>'. editorinfo($this->val('user')) .'</bdi>';
|
||||
if (auth_ismanager()) $html .= ' <bdo dir="ltr">('. $this->val('ip') .')</bdo>';
|
||||
} else {
|
||||
$html = '<bdo dir="ltr">'. $this->val('ip') .'</bdo>';
|
||||
}
|
||||
return '<span class="user">'. $html. '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* name of the page or media file
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showFileName()
|
||||
{
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$params = ['tab_details'=> 'view', 'ns'=> getNS($id), 'image'=> $id];
|
||||
if ($rev) $params += ['rev'=> $rev];
|
||||
$href = media_managerURL($params, '&');
|
||||
$display_name = $id;
|
||||
$exists = file_exists(mediaFN($id, $rev));
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$params = $rev ? ['rev'=> $rev] : [];
|
||||
$href = wl($id, $params, false, '&');
|
||||
$display_name = useHeading('navigation') ? hsc(p_get_first_heading($id)) : $id;
|
||||
if (!$display_name) $display_name = $id;
|
||||
$exists = page_exists($id, $rev);
|
||||
}
|
||||
|
||||
if($exists) {
|
||||
$class = 'wikilink1';
|
||||
} else {
|
||||
if($this->isCurrent()) {
|
||||
//show only not-existing link for current page, which allows for directly create a new page/upload
|
||||
$class = 'wikilink2';
|
||||
} else {
|
||||
//revision is not in attic
|
||||
return $display_name;
|
||||
}
|
||||
}
|
||||
if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$class = 'wikilink2';
|
||||
}
|
||||
return '<a href="'.$href.'" class="'.$class.'">'.$display_name.'</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Revision Title for PageDiff table headline
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showRevisionTitle()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (!$this->val('date')) return '—';
|
||||
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
$params = ($rev) ? ['rev'=> $rev] : [];
|
||||
|
||||
// revision info may have timestamp key when external edits occurred
|
||||
$date = ($this->val('timestamp') === false)
|
||||
? $lang['unknowndate']
|
||||
: dformat($this->val('date'));
|
||||
|
||||
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$href = ml($id, $params, false, '&');
|
||||
$exists = file_exists(mediaFN($id, $rev));
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$href = wl($id, $params, false, '&');
|
||||
$exists = page_exists($id, $rev);
|
||||
}
|
||||
if($exists) {
|
||||
$class = 'wikilink1';
|
||||
} else {
|
||||
if($this->isCurrent()) {
|
||||
//show only not-existing link for current page, which allows for directly create a new page/upload
|
||||
$class = 'wikilink2';
|
||||
} else {
|
||||
//revision is not in attic
|
||||
return $id.' ['.$date.']';
|
||||
}
|
||||
}
|
||||
if ($this->val('type') == DOKU_CHANGE_TYPE_DELETE) {
|
||||
$class = 'wikilink2';
|
||||
}
|
||||
return '<bdi><a class="'.$class.'" href="'.$href.'">'.$id.' ['.$date.']'.'</a></bdi>';
|
||||
}
|
||||
|
||||
/**
|
||||
* diff link icon in recent changes list, to compare (this) current revision with previous one
|
||||
* all items in "recent changes" are current revision of the page or media
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconCompareWithPrevious()
|
||||
{
|
||||
global $lang;
|
||||
$id = $this->val('id');
|
||||
|
||||
$href = '';
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
// unlike page, media file does not copied to media_attic when uploaded.
|
||||
// diff icon will not be shown when external edit occurred
|
||||
// because no attic file to be compared with current.
|
||||
$revs = (new MediaChangeLog($id))->getRevisions(0, 1);
|
||||
$showLink = (count($revs) && file_exists(mediaFN($id,$revs[0])) && file_exists(mediaFN($id)));
|
||||
if ($showLink) {
|
||||
$param = ['tab_details'=>'history', 'mediado'=>'diff', 'ns'=> getNS($id), 'image'=> $id];
|
||||
$href = media_managerURL($param, '&');
|
||||
}
|
||||
break;
|
||||
case 'page': // page revision
|
||||
// when a page just created anyway, it is natural to expect no older revisions
|
||||
// even if it had once existed but deleted before. Simply ignore to check changelog.
|
||||
if ($this->val('type') !== DOKU_CHANGE_TYPE_CREATE) {
|
||||
$href = wl($id, ['do'=>'diff'], false, '&');
|
||||
}
|
||||
}
|
||||
|
||||
if ($href) {
|
||||
return '<a href="'.$href.'" class="diff_link">'
|
||||
.'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
|
||||
.' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
|
||||
.'</a>';
|
||||
} else {
|
||||
return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* diff link icon in revisions list, compare this revision with current one
|
||||
* the icon does not displayed for the current revision
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconCompareWithCurrent()
|
||||
{
|
||||
global $lang;
|
||||
$id = $this->val('id');
|
||||
$rev = $this->isCurrent() ? '' : $this->val('date');
|
||||
|
||||
$href = '';
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
if (!$this->isCurrent() && file_exists(mediaFN($id, $rev))) {
|
||||
$param = ['mediado'=>'diff', 'image'=> $id, 'rev'=> $rev];
|
||||
$href = media_managerURL($param, '&');
|
||||
}
|
||||
break;
|
||||
case 'page': // page revision
|
||||
if (!$this->isCurrent()) {
|
||||
$href = wl($id, ['rev'=> $rev, 'do'=>'diff'], false, '&');
|
||||
}
|
||||
}
|
||||
|
||||
if ($href) {
|
||||
return '<a href="'.$href.'" class="diff_link">'
|
||||
.'<img src="'.DOKU_BASE.'lib/images/diff.png" width="15" height="11"'
|
||||
.' title="'. $lang['diff'] .'" alt="'.$lang['diff'] .'" />'
|
||||
.'</a>';
|
||||
} else {
|
||||
return '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="15" height="11" alt="" />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* icon for revision action
|
||||
* used in [Ui\recent]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showIconRevisions()
|
||||
{
|
||||
global $lang;
|
||||
|
||||
if (!actionOK('revisions')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$id = $this->val('id');
|
||||
switch ($this->val('mode')) {
|
||||
case 'media': // media file revision
|
||||
$param = ['tab_details'=>'history', 'ns'=> getNS($id), 'image'=> $id];
|
||||
$href = media_managerURL($param, '&');
|
||||
break;
|
||||
case 'page': // page revision
|
||||
$href = wl($id, ['do'=>'revisions'], false, '&');
|
||||
}
|
||||
return '<a href="'.$href.'" class="revisions_link">'
|
||||
. '<img src="'.DOKU_BASE.'lib/images/history.png" width="12" height="14"'
|
||||
. ' title="'.$lang['btn_revs'].'" alt="'.$lang['btn_revs'].'" />'
|
||||
. '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* size change
|
||||
* used in [Ui\recent, Ui\Revisions]
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showSizeChange()
|
||||
{
|
||||
$class = 'sizechange';
|
||||
$value = filesize_h(abs($this->val('sizechange')));
|
||||
if ($this->val('sizechange') > 0) {
|
||||
$class .= ' positive';
|
||||
$value = '+' . $value;
|
||||
} elseif ($this->val('sizechange') < 0) {
|
||||
$class .= ' negative';
|
||||
$value = '-' . $value;
|
||||
} else {
|
||||
$value = '±' . $value;
|
||||
}
|
||||
return '<span class="'.$class.'">'.$value.'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* current indicator, used in revision list
|
||||
* not used in Ui\Recent because recent files are always current one
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function showCurrentIndicator()
|
||||
{
|
||||
global $lang;
|
||||
return $this->isCurrent() ? '('.$lang['current'].')' : '';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace dokuwiki\Debug;
|
||||
|
||||
use Doku_Event;
|
||||
use dokuwiki\Extension\EventHandler;
|
||||
use dokuwiki\Logger;
|
||||
|
||||
class DebugHelper
|
||||
{
|
||||
@@ -13,12 +14,12 @@ class DebugHelper
|
||||
/**
|
||||
* Log accesses to deprecated fucntions to the debug log
|
||||
*
|
||||
* @param string $alternative (optional) The function or method that should be used instead
|
||||
* @param int $callerOffset (optional) How far the deprecated method is removed from this one
|
||||
*
|
||||
* @param string $alternative (optional) The function or method that should be used instead
|
||||
* @param int $callerOffset (optional) How far the deprecated method is removed from this one
|
||||
* @param string $thing (optional) The deprecated thing, defaults to the calling method
|
||||
* @triggers \dokuwiki\Debug::INFO_DEPRECATION_LOG_EVENT
|
||||
*/
|
||||
public static function dbgDeprecatedFunction($alternative = '', $callerOffset = 1)
|
||||
public static function dbgDeprecatedFunction($alternative = '', $callerOffset = 1, $thing = '')
|
||||
{
|
||||
global $conf;
|
||||
/** @var EventHandler $EVENT_HANDLER */
|
||||
@@ -38,17 +39,21 @@ class DebugHelper
|
||||
|
||||
list($self, $call) = $backtrace;
|
||||
|
||||
if (!$thing) {
|
||||
$thing = trim(
|
||||
(!empty($self['class']) ? ($self['class'] . '::') : '') .
|
||||
$self['function'] . '()', ':');
|
||||
}
|
||||
|
||||
self::triggerDeprecationEvent(
|
||||
$backtrace,
|
||||
$alternative,
|
||||
trim(
|
||||
(!empty($self['class']) ? ($self['class'] . '::') : '') .
|
||||
$self['function'] . '()', ':'),
|
||||
$thing,
|
||||
trim(
|
||||
(!empty($call['class']) ? ($call['class'] . '::') : '') .
|
||||
$call['function'] . '()', ':'),
|
||||
$call['file'],
|
||||
$call['line']
|
||||
$self['file'],
|
||||
$self['line']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -160,7 +165,7 @@ class DebugHelper
|
||||
if ($event->data['alternative']) {
|
||||
$msg .= ' ' . $event->data['alternative'] . ' should be used instead!';
|
||||
}
|
||||
dbglog($msg);
|
||||
Logger::getInstance(Logger::LOG_DEPRECATED)->log($msg);
|
||||
}
|
||||
$event->advise_after();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class Draft
|
||||
{
|
||||
$this->id = $ID;
|
||||
$this->client = $client;
|
||||
$this->cname = getCacheName($client.$ID, '.draft');
|
||||
$this->cname = getCacheName("$client\n$ID", '.draft');
|
||||
if(file_exists($this->cname) && file_exists(wikiFN($ID))) {
|
||||
if (filemtime($this->cname) < filemtime(wikiFN($ID))) {
|
||||
// remove stale draft
|
||||
|
||||
141
dokuwiki/inc/ErrorHandler.php
Normal file
141
dokuwiki/inc/ErrorHandler.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Exception\FatalException;
|
||||
|
||||
/**
|
||||
* Manage the global handling of errors and exceptions
|
||||
*
|
||||
* Developer may use this to log and display exceptions themselves
|
||||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
/**
|
||||
* Register the default error handling
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
if (!defined('DOKU_UNITTEST')) {
|
||||
set_exception_handler([ErrorHandler::class, 'fatalException']);
|
||||
register_shutdown_function([ErrorHandler::class, 'fatalShutdown']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Exception handler to show a nice user message before dieing
|
||||
*
|
||||
* The exception is logged to the error log
|
||||
*
|
||||
* @param \Throwable $e
|
||||
*/
|
||||
public static function fatalException($e)
|
||||
{
|
||||
$plugin = self::guessPlugin($e);
|
||||
$title = hsc(get_class($e) . ': ' . $e->getMessage());
|
||||
$msg = 'An unforeseen error has occured. This is most likely a bug somewhere.';
|
||||
if ($plugin) $msg .= ' It might be a problem in the ' . $plugin . ' plugin.';
|
||||
$logged = self::logException($e)
|
||||
? 'More info has been written to the DokuWiki error log.'
|
||||
: $e->getFile() . ':' . $e->getLine();
|
||||
|
||||
echo <<<EOT
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>$title</title></head>
|
||||
<body style="font-family: Arial, sans-serif">
|
||||
<div style="width:60%; margin: auto; background-color: #fcc;
|
||||
border: 1px solid #faa; padding: 0.5em 1em;">
|
||||
<h1 style="font-size: 120%">$title</h1>
|
||||
<p>$msg</p>
|
||||
<p>$logged</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to display an error message for the given Exception
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @param string $intro
|
||||
*/
|
||||
public static function showExceptionMsg($e, $intro = 'Error!')
|
||||
{
|
||||
$msg = hsc($intro) . '<br />' . hsc(get_class($e) . ': ' . $e->getMessage());
|
||||
if (self::logException($e)) $msg .= '<br />More info is available in the error log.';
|
||||
msg($msg, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Last resort to handle fatal errors that still can't be caught
|
||||
*/
|
||||
public static function fatalShutdown()
|
||||
{
|
||||
$error = error_get_last();
|
||||
// Check if it's a core/fatal error, otherwise it's a normal shutdown
|
||||
if (
|
||||
$error !== null &&
|
||||
in_array(
|
||||
$error['type'],
|
||||
[
|
||||
E_ERROR,
|
||||
E_CORE_ERROR,
|
||||
E_COMPILE_ERROR,
|
||||
]
|
||||
)
|
||||
) {
|
||||
self::fatalException(
|
||||
new FatalException($error['message'], 0, $error['type'], $error['file'], $error['line'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the given exception to the error log
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return bool false if the logging failed
|
||||
*/
|
||||
public static function logException($e)
|
||||
{
|
||||
return Logger::getInstance()->log(
|
||||
get_class($e) . ': ' . $e->getMessage(),
|
||||
$e->getTraceAsString(),
|
||||
$e->getFile(),
|
||||
$e->getLine()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the the stacktrace for plugin files
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return false|string
|
||||
*/
|
||||
protected static function guessPlugin($e)
|
||||
{
|
||||
if (preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $e->getFile()), $match)) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
foreach ($e->getTrace() as $line) {
|
||||
if (
|
||||
isset($line['class']) &&
|
||||
preg_match('/\w+?_plugin_(\w+)/', $line['class'], $match)
|
||||
) {
|
||||
return $match[1];
|
||||
}
|
||||
|
||||
if (
|
||||
isset($line['file']) &&
|
||||
preg_match('/lib\/plugins\/(\w+)\//', str_replace('\\', '/', $line['file']), $match)
|
||||
) {
|
||||
return $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
11
dokuwiki/inc/Exception/FatalException.php
Normal file
11
dokuwiki/inc/Exception/FatalException.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Exception;
|
||||
|
||||
/**
|
||||
* Fatal Errors are converted into this Exception in out Shutdown handler
|
||||
*/
|
||||
class FatalException extends \ErrorException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use dokuwiki\Logger;
|
||||
|
||||
/**
|
||||
* The Action plugin event
|
||||
*/
|
||||
@@ -71,7 +73,8 @@ class Event
|
||||
if ($EVENT_HANDLER !== null) {
|
||||
$EVENT_HANDLER->process_event($this, 'BEFORE');
|
||||
} else {
|
||||
dbglog($this->name . ':BEFORE event triggered before event system was initialized');
|
||||
Logger::getInstance(Logger::LOG_DEBUG)
|
||||
->log($this->name . ':BEFORE event triggered before event system was initialized');
|
||||
}
|
||||
|
||||
return (!$enablePreventDefault || $this->runDefault);
|
||||
@@ -92,7 +95,8 @@ class Event
|
||||
if ($EVENT_HANDLER !== null) {
|
||||
$EVENT_HANDLER->process_event($this, 'AFTER');
|
||||
} else {
|
||||
dbglog($this->name . ':AFTER event triggered before event system was initialized');
|
||||
Logger::getInstance(Logger::LOG_DEBUG)->
|
||||
log($this->name . ':AFTER event triggered before event system was initialized');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace dokuwiki\Extension;
|
||||
|
||||
use dokuwiki\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Class to encapsulate access to dokuwiki plugins
|
||||
*
|
||||
@@ -90,42 +92,49 @@ class PluginController
|
||||
|
||||
$class = $type . '_plugin_' . $name;
|
||||
|
||||
//plugin already loaded?
|
||||
if (!empty($DOKU_PLUGINS[$type][$name])) {
|
||||
if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
|
||||
return class_exists($class, true) ? new $class : null;
|
||||
try {
|
||||
//plugin already loaded?
|
||||
if (!empty($DOKU_PLUGINS[$type][$name])) {
|
||||
if ($new || !$DOKU_PLUGINS[$type][$name]->isSingleton()) {
|
||||
|
||||
return class_exists($class, true) ? new $class : null;
|
||||
}
|
||||
|
||||
return $DOKU_PLUGINS[$type][$name];
|
||||
}
|
||||
|
||||
return $DOKU_PLUGINS[$type][$name];
|
||||
}
|
||||
|
||||
//construct class and instantiate
|
||||
if (!class_exists($class, true)) {
|
||||
|
||||
# the plugin might be in the wrong directory
|
||||
$inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
|
||||
if ($inf['base'] && $inf['base'] != $plugin) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
|
||||
hsc($plugin),
|
||||
hsc(
|
||||
$inf['base']
|
||||
)
|
||||
), -1
|
||||
);
|
||||
} elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin name '%s' is not a valid plugin name, only the characters a-z and 0-9 are allowed. " .
|
||||
'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
|
||||
), -1
|
||||
);
|
||||
//construct class and instantiate
|
||||
if (!class_exists($class, true)) {
|
||||
# the plugin might be in the wrong directory
|
||||
$inf = confToHash(DOKU_PLUGIN . "$plugin/plugin.info.txt");
|
||||
if ($inf['base'] && $inf['base'] != $plugin) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin installed incorrectly. Rename plugin directory '%s' to '%s'.",
|
||||
hsc($plugin),
|
||||
hsc(
|
||||
$inf['base']
|
||||
)
|
||||
), -1
|
||||
);
|
||||
} elseif (preg_match('/^' . DOKU_PLUGIN_NAME_REGEX . '$/', $plugin) !== 1) {
|
||||
msg(
|
||||
sprintf(
|
||||
"Plugin name '%s' is not a valid plugin name, only the characters a-z ".
|
||||
"and 0-9 are allowed. " .
|
||||
'Maybe the plugin has been installed in the wrong directory?', hsc($plugin)
|
||||
), -1
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
$DOKU_PLUGINS[$type][$name] = new $class;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
ErrorHandler::showExceptionMsg($e, sprintf('Failed to load plugin %s', $plugin));
|
||||
return null;
|
||||
}
|
||||
|
||||
$DOKU_PLUGINS[$type][$name] = new $class;
|
||||
return $DOKU_PLUGINS[$type][$name];
|
||||
}
|
||||
|
||||
@@ -265,7 +274,7 @@ class PluginController
|
||||
$backup = $file . '.bak';
|
||||
if (file_exists($backup)) @unlink($backup);
|
||||
if (!@copy($file, $backup)) return false;
|
||||
if (!empty($conf['fperm'])) chmod($backup, $conf['fperm']);
|
||||
if ($conf['fperm']) chmod($backup, $conf['fperm']);
|
||||
}
|
||||
//check if can open for writing, else restore
|
||||
return io_saveFile($file, $out);
|
||||
|
||||
166
dokuwiki/inc/File/MediaFile.php
Normal file
166
dokuwiki/inc/File/MediaFile.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
use JpegMeta;
|
||||
|
||||
class MediaFile
|
||||
{
|
||||
protected $id;
|
||||
protected $path;
|
||||
|
||||
protected $mime;
|
||||
protected $ext;
|
||||
protected $downloadable;
|
||||
|
||||
protected $width;
|
||||
protected $height;
|
||||
protected $meta;
|
||||
|
||||
/**
|
||||
* MediaFile constructor.
|
||||
* @param string $id
|
||||
* @param string|int $rev optional revision
|
||||
*/
|
||||
public function __construct($id, $rev = '')
|
||||
{
|
||||
$this->id = $id; //FIXME should it be cleaned?
|
||||
$this->path = mediaFN($id, $rev);
|
||||
|
||||
list($this->ext, $this->mime, $this->downloadable) = mimetype($this->path, false);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID without namespace, used for display purposes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName()
|
||||
{
|
||||
return noNS($this->id);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getMime()
|
||||
{
|
||||
if (!$this->mime) return 'application/octet-stream';
|
||||
return $this->mime;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getExtension()
|
||||
{
|
||||
return (string)$this->ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the extesion but does some clean up
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIcoClass()
|
||||
{
|
||||
$ext = $this->getExtension();
|
||||
if ($ext === '') $ext = 'file';
|
||||
return preg_replace('/[^_\-a-z0-9]+/i', '_', $ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this file be downloaded instead being displayed inline?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDownloadable()
|
||||
{
|
||||
return $this->downloadable;
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getFileSize()
|
||||
{
|
||||
return filesize($this->path);
|
||||
}
|
||||
|
||||
/** @return int */
|
||||
public function getLastModified()
|
||||
{
|
||||
return filemtime($this->path);
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function isWritable()
|
||||
{
|
||||
return is_writable($this->path);
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function isImage()
|
||||
{
|
||||
return (substr($this->mime, 0, 6) === 'image/');
|
||||
}
|
||||
|
||||
/**
|
||||
* initializes width and height for images when requested
|
||||
*/
|
||||
protected function initSizes()
|
||||
{
|
||||
$this->width = 0;
|
||||
$this->height = 0;
|
||||
if (!$this->isImage()) return;
|
||||
$info = getimagesize($this->path);
|
||||
if ($info === false) return;
|
||||
list($this->width, $this->height) = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width if this is a supported image, 0 otherwise
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
if ($this->width === null) $this->initSizes();
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height if this is a supported image, 0 otherwise
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
if ($this->height === null) $this->initSizes();
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permissions the current user has on the file
|
||||
*
|
||||
* @todo doing this for each file within a namespace is a waste, we need to cache this somehow
|
||||
* @return int
|
||||
*/
|
||||
public function userPermission()
|
||||
{
|
||||
return auth_quickaclcheck(getNS($this->id).':*');
|
||||
}
|
||||
|
||||
/** @return JpegMeta */
|
||||
public function getMeta()
|
||||
{
|
||||
if($this->meta === null) $this->meta = new JpegMeta($this->path);
|
||||
return $this->meta;
|
||||
}
|
||||
}
|
||||
15
dokuwiki/inc/File/MediaResolver.php
Normal file
15
dokuwiki/inc/File/MediaResolver.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Creates an absolute media ID from a relative one
|
||||
*/
|
||||
class MediaResolver extends Resolver {
|
||||
|
||||
/** @inheritDoc */
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
return cleanID(parent::resolveId($id, $rev, $isDateAt));
|
||||
}
|
||||
}
|
||||
334
dokuwiki/inc/File/PageFile.php
Normal file
334
dokuwiki/inc/File/PageFile.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
use dokuwiki\Cache\CacheInstructions;
|
||||
use dokuwiki\ChangeLog\PageChangeLog;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Input\Input;
|
||||
use dokuwiki\Logger;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class PageFile : handles wiki text file and its change management for specific page
|
||||
*/
|
||||
class PageFile
|
||||
{
|
||||
protected $id;
|
||||
|
||||
/* @var PageChangeLog $changelog */
|
||||
public $changelog;
|
||||
|
||||
/* @var array $data initial data when event COMMON_WIKIPAGE_SAVE triggered */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* PageFile constructor.
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function __construct($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->changelog = new PageChangeLog($this->id);
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/** @return string */
|
||||
public function getPath($rev = '')
|
||||
{
|
||||
return wikiFN($this->id, $rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw WikiText of the page, considering change type at revision date
|
||||
* similar to function rawWiki($id, $rev = '')
|
||||
*
|
||||
* @param int|false $rev timestamp when a revision of wikitext is desired
|
||||
* @return string
|
||||
*/
|
||||
public function rawWikiText($rev = null)
|
||||
{
|
||||
if ($rev !== null) {
|
||||
$revInfo = $rev ? $this->changelog->getRevisionInfo($rev) : false;
|
||||
return (!$revInfo || $revInfo['type'] == DOKU_CHANGE_TYPE_DELETE)
|
||||
? '' // attic stores complete last page version for a deleted page
|
||||
: io_readWikiPage($this->getPath($rev), $this->id, $rev); // retrieve from attic
|
||||
} else {
|
||||
return io_readWikiPage($this->getPath(), $this->id, '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a wikitext by calling io_writeWikiPage.
|
||||
* Also directs changelog and attic updates.
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
* @author Ben Coburn <btcoburn@silicodon.net>
|
||||
*
|
||||
* @param string $text wikitext being saved
|
||||
* @param string $summary summary of text update
|
||||
* @param bool $minor mark this saved version as minor update
|
||||
* @return array|void data of event COMMON_WIKIPAGE_SAVE
|
||||
*/
|
||||
public function saveWikiText($text, $summary, $minor = false)
|
||||
{
|
||||
/* Note to developers:
|
||||
This code is subtle and delicate. Test the behavior of
|
||||
the attic and changelog with dokuwiki and external edits
|
||||
after any changes. External edits change the wiki page
|
||||
directly without using php or dokuwiki.
|
||||
*/
|
||||
global $conf;
|
||||
global $lang;
|
||||
global $REV;
|
||||
/* @var Input $INPUT */
|
||||
global $INPUT;
|
||||
|
||||
// prevent recursive call
|
||||
if (isset($this->data)) return;
|
||||
|
||||
$pagefile = $this->getPath();
|
||||
$currentRevision = @filemtime($pagefile); // int or false
|
||||
$currentContent = $this->rawWikiText();
|
||||
$currentSize = file_exists($pagefile) ? filesize($pagefile) : 0;
|
||||
|
||||
// prepare data for event COMMON_WIKIPAGE_SAVE
|
||||
$data = array(
|
||||
'id' => $this->id, // should not be altered by any handlers
|
||||
'file' => $pagefile, // same above
|
||||
'changeType' => null, // set prior to event, and confirm later
|
||||
'revertFrom' => $REV,
|
||||
'oldRevision' => $currentRevision,
|
||||
'oldContent' => $currentContent,
|
||||
'newRevision' => 0, // only available in the after hook
|
||||
'newContent' => $text,
|
||||
'summary' => $summary,
|
||||
'contentChanged' => ($text != $currentContent), // confirm later
|
||||
'changeInfo' => '', // automatically determined by revertFrom
|
||||
'sizechange' => strlen($text) - strlen($currentContent), // TBD
|
||||
);
|
||||
|
||||
// determine tentatively change type and relevant elements of event data
|
||||
if ($data['revertFrom']) {
|
||||
// new text may differ from exact revert revision
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_REVERT;
|
||||
$data['changeInfo'] = $REV;
|
||||
} elseif (trim($data['newContent']) == '') {
|
||||
// empty or whitespace only content deletes
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_DELETE;
|
||||
} elseif (!file_exists($pagefile)) {
|
||||
$data['changeType'] = DOKU_CHANGE_TYPE_CREATE;
|
||||
} else {
|
||||
// minor edits allowable only for logged in users
|
||||
$is_minor_change = ($minor && $conf['useacl'] && $INPUT->server->str('REMOTE_USER'));
|
||||
$data['changeType'] = $is_minor_change
|
||||
? DOKU_CHANGE_TYPE_MINOR_EDIT
|
||||
: DOKU_CHANGE_TYPE_EDIT;
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$data['page'] = $this; // allow event handlers to use this class methods
|
||||
|
||||
$event = new Event('COMMON_WIKIPAGE_SAVE', $data);
|
||||
if (!$event->advise_before()) return;
|
||||
|
||||
// if the content has not been changed, no save happens (plugins may override this)
|
||||
if (!$data['contentChanged']) return;
|
||||
|
||||
// Check whether the pagefile has modified during $event->advise_before()
|
||||
clearstatcache();
|
||||
$fileRev = @filemtime($pagefile);
|
||||
if ($fileRev === $currentRevision) {
|
||||
// pagefile has not touched by plugin's event handler
|
||||
// add a potential external edit entry to changelog and store it into attic
|
||||
$this->detectExternalEdit();
|
||||
$filesize_old = $currentSize;
|
||||
} else {
|
||||
// pagefile has modified by plugin's event handler, confirm sizechange
|
||||
$filesize_old = (
|
||||
$data['changeType'] == DOKU_CHANGE_TYPE_CREATE || (
|
||||
$data['changeType'] == DOKU_CHANGE_TYPE_REVERT && !file_exists($pagefile))
|
||||
) ? 0 : filesize($pagefile);
|
||||
}
|
||||
|
||||
// make change to the current file
|
||||
if ($data['changeType'] == DOKU_CHANGE_TYPE_DELETE) {
|
||||
// nothing to do when the file has already deleted
|
||||
if (!file_exists($pagefile)) return;
|
||||
// autoset summary on deletion
|
||||
if (blank($data['summary'])) {
|
||||
$data['summary'] = $lang['deleted'];
|
||||
}
|
||||
// send "update" event with empty data, so plugins can react to page deletion
|
||||
$ioData = array([$pagefile, '', false], getNS($this->id), noNS($this->id), false);
|
||||
Event::createAndTrigger('IO_WIKIPAGE_WRITE', $ioData);
|
||||
// pre-save deleted revision
|
||||
@touch($pagefile);
|
||||
clearstatcache();
|
||||
$data['newRevision'] = $this->saveOldRevision();
|
||||
// remove empty file
|
||||
@unlink($pagefile);
|
||||
$filesize_new = 0;
|
||||
// don't remove old meta info as it should be saved, plugins can use
|
||||
// IO_WIKIPAGE_WRITE for removing their metadata...
|
||||
// purge non-persistant meta data
|
||||
p_purge_metadata($this->id);
|
||||
// remove empty namespaces
|
||||
io_sweepNS($this->id, 'datadir');
|
||||
io_sweepNS($this->id, 'mediadir');
|
||||
} else {
|
||||
// save file (namespace dir is created in io_writeWikiPage)
|
||||
io_writeWikiPage($pagefile, $data['newContent'], $this->id);
|
||||
// pre-save the revision, to keep the attic in sync
|
||||
$data['newRevision'] = $this->saveOldRevision();
|
||||
$filesize_new = filesize($pagefile);
|
||||
}
|
||||
$data['sizechange'] = $filesize_new - $filesize_old;
|
||||
|
||||
$event->advise_after();
|
||||
|
||||
unset($data['page']);
|
||||
|
||||
// adds an entry to the changelog and saves the metadata for the page
|
||||
$logEntry = $this->changelog->addLogEntry([
|
||||
'date' => $data['newRevision'],
|
||||
'ip' => clientIP(true),
|
||||
'type' => $data['changeType'],
|
||||
'id' => $this->id,
|
||||
'user' => $INPUT->server->str('REMOTE_USER'),
|
||||
'sum' => $data['summary'],
|
||||
'extra' => $data['changeInfo'],
|
||||
'sizechange' => $data['sizechange'],
|
||||
]);
|
||||
// update metadata
|
||||
$this->updateMetadata($logEntry);
|
||||
|
||||
// update the purgefile (timestamp of the last time anything within the wiki was changed)
|
||||
io_saveFile($conf['cachedir'].'/purgefile', time());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page version is newer than the last entry in the page's changelog.
|
||||
* If so, we assume it has been an external edit and we create an attic copy and add a proper
|
||||
* changelog line.
|
||||
*
|
||||
* This check is only executed when the page is about to be saved again from the wiki,
|
||||
* triggered in @see saveWikiText()
|
||||
*/
|
||||
public function detectExternalEdit()
|
||||
{
|
||||
$revInfo = $this->changelog->getCurrentRevisionInfo();
|
||||
|
||||
// only interested in external revision
|
||||
if (empty($revInfo) || !array_key_exists('timestamp', $revInfo)) return;
|
||||
|
||||
if ($revInfo['type'] != DOKU_CHANGE_TYPE_DELETE && !$revInfo['timestamp']) {
|
||||
// file is older than last revision, that is erroneous/incorrect occurence.
|
||||
// try to change file modification time
|
||||
$fileLastMod = $this->getPath();
|
||||
$wrong_timestamp = filemtime($fileLastMod);
|
||||
if (touch($fileLastMod, $revInfo['date'])) {
|
||||
clearstatcache();
|
||||
$msg = "PageFile($this->id)::detectExternalEdit(): timestamp successfully modified";
|
||||
$details = '('.$wrong_timestamp.' -> '.$revInfo['date'].')';
|
||||
Logger::error($msg, $details, $fileLastMod);
|
||||
} else {
|
||||
// runtime error
|
||||
$msg = "PageFile($this->id)::detectExternalEdit(): page file should be newer than last revision "
|
||||
.'('.filemtime($fileLastMod).' < '. $this->changelog->lastRevision() .')';
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
}
|
||||
|
||||
// keep at least 1 sec before new page save
|
||||
if ($revInfo['date'] == time()) sleep(1); // wait a tick
|
||||
|
||||
// store externally edited file to the attic folder
|
||||
$this->saveOldRevision();
|
||||
// add a changelog entry for externally edited file
|
||||
$this->changelog->addLogEntry($revInfo);
|
||||
// remove soon to be stale instructions
|
||||
$cache = new CacheInstructions($this->id, $this->getPath());
|
||||
$cache->removeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the current version to the attic and returns its revision date
|
||||
*
|
||||
* @author Andreas Gohr <andi@splitbrain.org>
|
||||
*
|
||||
* @return int|string revision timestamp
|
||||
*/
|
||||
public function saveOldRevision()
|
||||
{
|
||||
$oldfile = $this->getPath();
|
||||
if (!file_exists($oldfile)) return '';
|
||||
$date = filemtime($oldfile);
|
||||
$newfile = $this->getPath($date);
|
||||
io_writeWikiPage($newfile, $this->rawWikiText(), $this->id, $date);
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update metadata of changed page
|
||||
*
|
||||
* @param array $logEntry changelog entry
|
||||
*/
|
||||
public function updateMetadata(array $logEntry)
|
||||
{
|
||||
global $INFO;
|
||||
|
||||
list(
|
||||
'date' => $date,
|
||||
'type' => $changeType,
|
||||
'user' => $user,
|
||||
) = $logEntry;
|
||||
|
||||
$wasRemoved = ($changeType === DOKU_CHANGE_TYPE_DELETE);
|
||||
$wasCreated = ($changeType === DOKU_CHANGE_TYPE_CREATE);
|
||||
$wasReverted = ($changeType === DOKU_CHANGE_TYPE_REVERT);
|
||||
$wasMinorEdit = ($changeType === DOKU_CHANGE_TYPE_MINOR_EDIT);
|
||||
|
||||
$createdDate = @filectime($this->getPath());
|
||||
|
||||
if ($wasRemoved) return;
|
||||
|
||||
$oldmeta = p_read_metadata($this->id)['persistent'];
|
||||
$meta = array();
|
||||
|
||||
if ($wasCreated &&
|
||||
(empty($oldmeta['date']['created']) || $oldmeta['date']['created'] === $createdDate)
|
||||
) {
|
||||
// newly created
|
||||
$meta['date']['created'] = $createdDate;
|
||||
if ($user) {
|
||||
$meta['creator'] = $INFO['userinfo']['name'] ?? null;
|
||||
$meta['user'] = $user;
|
||||
}
|
||||
} elseif (($wasCreated || $wasReverted) && !empty($oldmeta['date']['created'])) {
|
||||
// re-created / restored
|
||||
$meta['date']['created'] = $oldmeta['date']['created'];
|
||||
$meta['date']['modified'] = $createdDate; // use the files ctime here
|
||||
$meta['creator'] = $oldmeta['creator'] ?? null;
|
||||
if ($user) {
|
||||
$meta['contributor'][$user] = $INFO['userinfo']['name'] ?? null;
|
||||
}
|
||||
} elseif (!$wasMinorEdit) { // non-minor modification
|
||||
$meta['date']['modified'] = $date;
|
||||
if ($user) {
|
||||
$meta['contributor'][$user] = $INFO['userinfo']['name'] ?? null;
|
||||
}
|
||||
}
|
||||
$meta['last_change'] = $logEntry;
|
||||
p_set_metadata($this->id, $meta);
|
||||
}
|
||||
|
||||
}
|
||||
98
dokuwiki/inc/File/PageResolver.php
Normal file
98
dokuwiki/inc/File/PageResolver.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Creates an absolute page ID from a relative one
|
||||
*/
|
||||
class PageResolver extends Resolver
|
||||
{
|
||||
|
||||
/**
|
||||
* Resolves a given ID to be absolute
|
||||
*
|
||||
* This handles all kinds of relative shortcuts, startpages and autoplurals
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// pages may have a hash attached, we separate it on resolving
|
||||
if (strpos($id, '#') !== false) {
|
||||
list($id, $hash) = explode('#', $id, 2);
|
||||
$hash = cleanID($hash);
|
||||
} else {
|
||||
$hash = '';
|
||||
}
|
||||
|
||||
if ($id !== '') {
|
||||
$id = parent::resolveId($id, $rev, $isDateAt);
|
||||
$id = $this->resolveStartPage($id, $rev, $isDateAt);
|
||||
if ($conf['autoplural']) {
|
||||
$id = $this->resolveAutoPlural($id, $rev, $isDateAt);
|
||||
}
|
||||
} else {
|
||||
$id = $this->contextID;
|
||||
}
|
||||
|
||||
$id = cleanID($id); // FIXME always? or support parameter
|
||||
// readd hash if any
|
||||
if ($hash !== '') $id .= "#$hash";
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs ending in :
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|int|false $rev
|
||||
* @param bool $isDateAt
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStartPage($id, $rev, $isDateAt)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if ($id[-1] !== ':') return $id;
|
||||
|
||||
if (page_exists($id . $conf['start'], $rev, true, $isDateAt)) {
|
||||
// start page inside namespace
|
||||
return $id . $conf['start'];
|
||||
} elseif (page_exists($id . noNS(cleanID($id)), $rev, true, $isDateAt)) {
|
||||
// page named like the NS inside the NS
|
||||
return $id . noNS(cleanID($id));
|
||||
} elseif (page_exists(substr($id, 0, -1), $rev, true, $isDateAt)) {
|
||||
// page named like the NS outside the NS
|
||||
return substr($id, 0, -1);
|
||||
}
|
||||
|
||||
// fall back to default start page
|
||||
return $id . $conf['start'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try alternative plural/singular form
|
||||
*
|
||||
* @param string $id
|
||||
* @param int $rev
|
||||
* @param bool $isDateAt
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveAutoPlural($id, $rev, $isDateAt)
|
||||
{
|
||||
if (page_exists($id, $rev, $isDateAt)) return $id;
|
||||
|
||||
if ($id[-1] === 's') {
|
||||
$try = substr($id, 0, -1);
|
||||
} else {
|
||||
$try = $id . 's';
|
||||
}
|
||||
|
||||
if (page_exists($try, $rev, true, $isDateAt)) {
|
||||
return $try;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
||||
104
dokuwiki/inc/File/Resolver.php
Normal file
104
dokuwiki/inc/File/Resolver.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\File;
|
||||
|
||||
/**
|
||||
* Resolving relative IDs to absolute ones
|
||||
*/
|
||||
abstract class Resolver
|
||||
{
|
||||
|
||||
/** @var string context page ID */
|
||||
protected $contextID;
|
||||
/** @var string namespace of context page ID */
|
||||
protected $contextNS;
|
||||
|
||||
/**
|
||||
* @param string $contextID the current pageID that's the context to resolve relative IDs to
|
||||
*/
|
||||
public function __construct($contextID)
|
||||
{
|
||||
$this->contextID = $contextID;
|
||||
$this->contextNS = (string)getNS($contextID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a given ID to be absolute
|
||||
*
|
||||
* @param string $id The ID to resolve
|
||||
* @param string|int|false $rev The revision time to use when resolving
|
||||
* @param bool $isDateAt Is the given revision only a datetime hint not an exact revision?
|
||||
* @return string
|
||||
*/
|
||||
public function resolveId($id, $rev = '', $isDateAt = false)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
// some pre cleaning for useslash:
|
||||
if ($conf['useslash']) $id = str_replace('/', ':', $id);
|
||||
// on some systems, semicolons might be used instead of colons:
|
||||
$id = str_replace(';', ':', $id);
|
||||
|
||||
$id = $this->resolvePrefix($id);
|
||||
return $this->resolveRelatives($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle IDs starting with . or ~ and prepend the proper prefix
|
||||
*
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
protected function resolvePrefix($id)
|
||||
{
|
||||
// relative to current page (makes the current page a start page)
|
||||
if ($id[0] === '~') {
|
||||
$id = $this->contextID . ':' . substr($id, 1);
|
||||
}
|
||||
|
||||
// relative to current namespace
|
||||
if ($id[0] === '.') {
|
||||
// normalize initial dots without a colon
|
||||
$id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id);
|
||||
$id = $this->contextNS . ':' . $id;
|
||||
}
|
||||
|
||||
// auto-relative, because there is a context namespace but no namespace in the ID
|
||||
if ($this->contextID !== '' && strpos($id, ':') === false) {
|
||||
$id = $this->contextNS . ':' . $id;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle . and .. within IDs
|
||||
*
|
||||
* @param string $id
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveRelatives($id)
|
||||
{
|
||||
if ($id === '') return '';
|
||||
$trail = ($id[-1] === ':') ? ':' : ''; // keep trailing colon
|
||||
|
||||
$result = [];
|
||||
$parts = explode(':', $id);
|
||||
|
||||
foreach ($parts as $dir) {
|
||||
if ($dir === '.') continue;
|
||||
if ($dir === '') continue;
|
||||
if ($dir === '..') {
|
||||
array_pop($result);
|
||||
continue;
|
||||
}
|
||||
array_push($result, $dir);
|
||||
}
|
||||
|
||||
$id = implode(':', $result);
|
||||
$id .= $trail;
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,8 +9,8 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class ButtonElement extends Element {
|
||||
|
||||
class ButtonElement extends Element
|
||||
{
|
||||
/** @var string HTML content */
|
||||
protected $content = '';
|
||||
|
||||
@@ -17,7 +18,8 @@ class ButtonElement extends Element {
|
||||
* @param string $name
|
||||
* @param string $content HTML content of the button. You have to escape it yourself.
|
||||
*/
|
||||
public function __construct($name, $content = '') {
|
||||
public function __construct($name, $content = '')
|
||||
{
|
||||
parent::__construct('button', array('name' => $name, 'value' => 1));
|
||||
$this->content = $content;
|
||||
}
|
||||
@@ -27,7 +29,8 @@ class ButtonElement extends Element {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '<button ' . buildAttributes($this->attrs(), true) . '>'.$this->content.'</button>';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,14 +9,15 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class CheckableElement extends InputElement {
|
||||
|
||||
class CheckableElement extends InputElement
|
||||
{
|
||||
/**
|
||||
* @param string $type The type of this element
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element
|
||||
*/
|
||||
public function __construct($type, $name, $label) {
|
||||
public function __construct($type, $name, $label)
|
||||
{
|
||||
parent::__construct($type, $name, $label);
|
||||
// default value is 1
|
||||
$this->attr('value', 1);
|
||||
@@ -24,17 +26,18 @@ class CheckableElement extends InputElement {
|
||||
/**
|
||||
* Handles the useInput flag and sets the checked attribute accordingly
|
||||
*/
|
||||
protected function prefillInput() {
|
||||
protected function prefillInput()
|
||||
{
|
||||
global $INPUT;
|
||||
list($name, $key) = $this->getInputName();
|
||||
$myvalue = $this->val();
|
||||
|
||||
if(!$INPUT->has($name)) return;
|
||||
if (!$INPUT->has($name)) return;
|
||||
|
||||
if($key === null) {
|
||||
if ($key === null) {
|
||||
// no key - single value
|
||||
$value = $INPUT->str($name);
|
||||
if($value == $myvalue) {
|
||||
if ($value == $myvalue) {
|
||||
$this->attr('checked', 'checked');
|
||||
} else {
|
||||
$this->rmattr('checked');
|
||||
@@ -42,16 +45,16 @@ class CheckableElement extends InputElement {
|
||||
} else {
|
||||
// we have an array, there might be several values in it
|
||||
$input = $INPUT->arr($name);
|
||||
if(isset($input[$key])) {
|
||||
if (isset($input[$key])) {
|
||||
$this->rmattr('checked');
|
||||
|
||||
// values seem to be in another sub array
|
||||
if(is_array($input[$key])) {
|
||||
if (is_array($input[$key])) {
|
||||
$input = $input[$key];
|
||||
}
|
||||
|
||||
foreach($input as $value) {
|
||||
if($value == $myvalue) {
|
||||
foreach ($input as $value) {
|
||||
if ($value == $myvalue) {
|
||||
$this->attr('checked', 'checked');
|
||||
}
|
||||
}
|
||||
@@ -59,4 +62,22 @@ class CheckableElement extends InputElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML representation of this element wrapped in a label
|
||||
* Note: allow HTML tags in label text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->label) {
|
||||
return '<label '. buildAttributes($this->label->attrs()) .'>'.DOKU_LF
|
||||
. $this->mainElementHTML() .DOKU_LF
|
||||
.'<span>'. $this->label->val() .'</span>'.DOKU_LF
|
||||
.'</label>';
|
||||
} else {
|
||||
return $this->mainElementHTML();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,8 +9,8 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class DropdownElement extends InputElement {
|
||||
|
||||
class DropdownElement extends InputElement
|
||||
{
|
||||
/** @var array OptGroup[] */
|
||||
protected $optGroups = array();
|
||||
|
||||
@@ -18,7 +19,8 @@ class DropdownElement extends InputElement {
|
||||
* @param array $options The available options
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
*/
|
||||
public function __construct($name, $options, $label = '') {
|
||||
public function __construct($name, $options, $label = '')
|
||||
{
|
||||
parent::__construct('dropdown', $name, $label);
|
||||
$this->rmattr('type');
|
||||
$this->optGroups[''] = new OptGroup(null, $options);
|
||||
@@ -33,7 +35,8 @@ class DropdownElement extends InputElement {
|
||||
* @return OptGroup a reference to the added optgroup
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function addOptGroup($label, $options) {
|
||||
public function addOptGroup($label, $options)
|
||||
{
|
||||
if (empty($label)) {
|
||||
throw new \InvalidArgumentException(hsc('<optgroup> must have a label!'));
|
||||
}
|
||||
@@ -51,8 +54,9 @@ class DropdownElement extends InputElement {
|
||||
* @param null|array $optGroups
|
||||
* @return OptGroup[]|DropdownElement
|
||||
*/
|
||||
public function optGroups($optGroups = null) {
|
||||
if($optGroups === null) {
|
||||
public function optGroups($optGroups = null)
|
||||
{
|
||||
if ($optGroups === null) {
|
||||
return $this->optGroups;
|
||||
}
|
||||
if (!is_array($optGroups)) {
|
||||
@@ -81,7 +85,8 @@ class DropdownElement extends InputElement {
|
||||
* @param null|array $options
|
||||
* @return $this|array
|
||||
*/
|
||||
public function options($options = null) {
|
||||
public function options($options = null)
|
||||
{
|
||||
if ($options === null) {
|
||||
return $this->optGroups['']->options();
|
||||
}
|
||||
@@ -102,8 +107,9 @@ class DropdownElement extends InputElement {
|
||||
* @param null|string $value New value to set
|
||||
* @return string|$this
|
||||
*/
|
||||
public function attr($name, $value = null) {
|
||||
if(strtolower($name) == 'multiple') {
|
||||
public function attr($name, $value = null)
|
||||
{
|
||||
if (strtolower($name) == 'multiple') {
|
||||
throw new \InvalidArgumentException(
|
||||
'Sorry, the dropdown element does not support the "multiple" attribute'
|
||||
);
|
||||
@@ -120,12 +126,13 @@ class DropdownElement extends InputElement {
|
||||
* @param null|string $value The value to set
|
||||
* @return $this|string
|
||||
*/
|
||||
public function val($value = null) {
|
||||
if($value === null) return $this->value;
|
||||
public function val($value = null)
|
||||
{
|
||||
if ($value === null) return $this->value;
|
||||
|
||||
$value_exists = $this->setValueInOptGroups($value);
|
||||
|
||||
if($value_exists) {
|
||||
if ($value_exists) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
// unknown value set, select first option instead
|
||||
@@ -141,7 +148,8 @@ class DropdownElement extends InputElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFirstOption() {
|
||||
protected function getFirstOption()
|
||||
{
|
||||
$options = $this->options();
|
||||
if (!empty($options)) {
|
||||
$keys = array_keys($options);
|
||||
@@ -162,7 +170,8 @@ class DropdownElement extends InputElement {
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function setValueInOptGroups($value) {
|
||||
protected function setValueInOptGroups($value)
|
||||
{
|
||||
$value_exists = false;
|
||||
/** @var OptGroup $optGroup */
|
||||
foreach ($this->optGroups as $optGroup) {
|
||||
@@ -179,8 +188,9 @@ class DropdownElement extends InputElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML() {
|
||||
if($this->useInput) $this->prefillInput();
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
if ($this->useInput) $this->prefillInput();
|
||||
|
||||
$html = '<select ' . buildAttributes($this->attrs()) . '>';
|
||||
$html = array_reduce(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,8 +9,8 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
abstract class Element {
|
||||
|
||||
abstract class Element
|
||||
{
|
||||
/**
|
||||
* @var array the attributes of this element
|
||||
*/
|
||||
@@ -24,7 +25,8 @@ abstract class Element {
|
||||
* @param string $type The type of this element
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($type, $attributes = array()) {
|
||||
public function __construct($type, $attributes = array())
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->attributes = $attributes;
|
||||
}
|
||||
@@ -34,7 +36,8 @@ abstract class Element {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType() {
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
@@ -51,15 +54,16 @@ abstract class Element {
|
||||
* @param null|string $value New value to set
|
||||
* @return string|$this
|
||||
*/
|
||||
public function attr($name, $value = null) {
|
||||
public function attr($name, $value = null)
|
||||
{
|
||||
// set
|
||||
if($value !== null) {
|
||||
if ($value !== null) {
|
||||
$this->attributes[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
// get
|
||||
if(isset($this->attributes[$name])) {
|
||||
if (isset($this->attributes[$name])) {
|
||||
return $this->attributes[$name];
|
||||
} else {
|
||||
return '';
|
||||
@@ -72,8 +76,9 @@ abstract class Element {
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function rmattr($name) {
|
||||
if(isset($this->attributes[$name])) {
|
||||
public function rmattr($name)
|
||||
{
|
||||
if (isset($this->attributes[$name])) {
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
return $this;
|
||||
@@ -85,10 +90,11 @@ abstract class Element {
|
||||
* @param array|null $attributes
|
||||
* @return array|$this
|
||||
*/
|
||||
public function attrs($attributes = null) {
|
||||
public function attrs($attributes = null)
|
||||
{
|
||||
// set
|
||||
if($attributes) {
|
||||
foreach((array) $attributes as $key => $val) {
|
||||
if ($attributes) {
|
||||
foreach ((array) $attributes as $key => $val) {
|
||||
$this->attr($key, $val);
|
||||
}
|
||||
return $this;
|
||||
@@ -105,7 +111,8 @@ abstract class Element {
|
||||
* @param string $class the new class to add
|
||||
* @return $this
|
||||
*/
|
||||
public function addClass($class) {
|
||||
public function addClass($class)
|
||||
{
|
||||
$classes = explode(' ', $this->attr('class'));
|
||||
$classes[] = $class;
|
||||
$classes = array_unique($classes);
|
||||
@@ -122,8 +129,9 @@ abstract class Element {
|
||||
* @param null|string $id
|
||||
* @return string|$this
|
||||
*/
|
||||
public function id($id = null) {
|
||||
if(strpos($id, '__') === false) {
|
||||
public function id($id = null)
|
||||
{
|
||||
if (strpos($id, '__') === false) {
|
||||
throw new \InvalidArgumentException('IDs in DokuWiki have to contain two subsequent underscores');
|
||||
}
|
||||
|
||||
@@ -138,7 +146,8 @@ abstract class Element {
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null) {
|
||||
public function val($value = null)
|
||||
{
|
||||
return $this->attr('value', $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,12 +9,13 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class FieldsetCloseElement extends TagCloseElement {
|
||||
|
||||
class FieldsetCloseElement extends TagCloseElement
|
||||
{
|
||||
/**
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($attributes = array()) {
|
||||
public function __construct($attributes = array())
|
||||
{
|
||||
parent::__construct('', $attributes);
|
||||
$this->type = 'fieldsetclose';
|
||||
}
|
||||
@@ -24,7 +26,8 @@ class FieldsetCloseElement extends TagCloseElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '</fieldset>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,13 +9,15 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class FieldsetOpenElement extends TagOpenElement {
|
||||
class FieldsetOpenElement extends TagOpenElement
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $legend
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($legend='', $attributes = array()) {
|
||||
public function __construct($legend='', $attributes = array())
|
||||
{
|
||||
// this is a bit messy and we just do it for the nicer class hierarchy
|
||||
// the parent would expect the tag in $value but we're storing the
|
||||
// legend there, so we have to set the type manually
|
||||
@@ -27,10 +30,11 @@ class FieldsetOpenElement extends TagOpenElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
$html = '<fieldset '.buildAttributes($this->attrs()).'>';
|
||||
$legend = $this->val();
|
||||
if($legend) $html .= DOKU_LF.'<legend>'.hsc($legend).'</legend>';
|
||||
if ($legend) $html .= DOKU_LF.'<legend>'.hsc($legend).'</legend>';
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Class Form
|
||||
*
|
||||
@@ -8,8 +11,8 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class Form extends Element {
|
||||
|
||||
class Form extends Element
|
||||
{
|
||||
/**
|
||||
* @var array name value pairs for hidden values
|
||||
*/
|
||||
@@ -26,26 +29,27 @@ class Form extends Element {
|
||||
* @param array $attributes
|
||||
* @param bool $unsafe if true, then the security token is ommited
|
||||
*/
|
||||
public function __construct($attributes = array(), $unsafe = false) {
|
||||
public function __construct($attributes = array(), $unsafe = false)
|
||||
{
|
||||
global $ID;
|
||||
|
||||
parent::__construct('form', $attributes);
|
||||
|
||||
// use the current URL as default action
|
||||
if(!$this->attr('action')) {
|
||||
if (!$this->attr('action')) {
|
||||
$get = $_GET;
|
||||
if(isset($get['id'])) unset($get['id']);
|
||||
if (isset($get['id'])) unset($get['id']);
|
||||
$self = wl($ID, $get, false, '&'); //attributes are escaped later
|
||||
$this->attr('action', $self);
|
||||
}
|
||||
|
||||
// post is default
|
||||
if(!$this->attr('method')) {
|
||||
if (!$this->attr('method')) {
|
||||
$this->attr('method', 'post');
|
||||
}
|
||||
|
||||
// we like UTF-8
|
||||
if(!$this->attr('accept-charset')) {
|
||||
if (!$this->attr('accept-charset')) {
|
||||
$this->attr('accept-charset', 'utf-8');
|
||||
}
|
||||
|
||||
@@ -65,7 +69,8 @@ class Form extends Element {
|
||||
* @param string $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setHiddenField($name, $value) {
|
||||
public function setHiddenField($name, $value)
|
||||
{
|
||||
$this->hidden[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
@@ -77,7 +82,8 @@ class Form extends Element {
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function elementCount() {
|
||||
public function elementCount()
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
@@ -105,10 +111,11 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function getElementAt($pos) {
|
||||
if($pos < 0) $pos = count($this->elements) + $pos;
|
||||
if($pos < 0) $pos = 0;
|
||||
if($pos >= count($this->elements)) $pos = count($this->elements) - 1;
|
||||
public function getElementAt($pos)
|
||||
{
|
||||
if ($pos < 0) $pos = count($this->elements) + $pos;
|
||||
if ($pos < 0) $pos = 0;
|
||||
if ($pos >= count($this->elements)) $pos = count($this->elements) - 1;
|
||||
return $this->elements[$pos];
|
||||
}
|
||||
|
||||
@@ -119,10 +126,11 @@ class Form extends Element {
|
||||
* @param int $offset search from this position onward
|
||||
* @return false|int position of element if found, otherwise false
|
||||
*/
|
||||
public function findPositionByType($type, $offset = 0) {
|
||||
public function findPositionByType($type, $offset = 0)
|
||||
{
|
||||
$len = $this->elementCount();
|
||||
for($pos = $offset; $pos < $len; $pos++) {
|
||||
if($this->elements[$pos]->getType() == $type) {
|
||||
for ($pos = $offset; $pos < $len; $pos++) {
|
||||
if ($this->elements[$pos]->getType() == $type) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
@@ -137,10 +145,11 @@ class Form extends Element {
|
||||
* @param int $offset search from this position onward
|
||||
* @return false|int position of element if found, otherwise false
|
||||
*/
|
||||
public function findPositionByAttribute($name, $value, $offset = 0) {
|
||||
public function findPositionByAttribute($name, $value, $offset = 0)
|
||||
{
|
||||
$len = $this->elementCount();
|
||||
for($pos = $offset; $pos < $len; $pos++) {
|
||||
if($this->elements[$pos]->attr($name) == $value) {
|
||||
for ($pos = $offset; $pos < $len; $pos++) {
|
||||
if ($this->elements[$pos]->attr($name) == $value) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
@@ -158,11 +167,12 @@ class Form extends Element {
|
||||
* @param int $pos 0-based position in the form, -1 for at the end
|
||||
* @return Element
|
||||
*/
|
||||
public function addElement(Element $element, $pos = -1) {
|
||||
if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
public function addElement(Element $element, $pos = -1)
|
||||
{
|
||||
if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
'You can\'t add a form to a form'
|
||||
);
|
||||
if($pos < 0) {
|
||||
if ($pos < 0) {
|
||||
$this->elements[] = $element;
|
||||
} else {
|
||||
array_splice($this->elements, $pos, 0, array($element));
|
||||
@@ -176,8 +186,9 @@ class Form extends Element {
|
||||
* @param Element $element the new element
|
||||
* @param int $pos 0-based position of the element to replace
|
||||
*/
|
||||
public function replaceElement(Element $element, $pos) {
|
||||
if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
public function replaceElement(Element $element, $pos)
|
||||
{
|
||||
if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
|
||||
'You can\'t add a form to a form'
|
||||
);
|
||||
array_splice($this->elements, $pos, 1, array($element));
|
||||
@@ -188,7 +199,8 @@ class Form extends Element {
|
||||
*
|
||||
* @param int $pos 0-based position of the element to remove
|
||||
*/
|
||||
public function removeElement($pos) {
|
||||
public function removeElement($pos)
|
||||
{
|
||||
array_splice($this->elements, $pos, 1);
|
||||
}
|
||||
|
||||
@@ -204,7 +216,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return InputElement
|
||||
*/
|
||||
public function addTextInput($name, $label = '', $pos = -1) {
|
||||
public function addTextInput($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new InputElement('text', $name, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -216,7 +229,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return InputElement
|
||||
*/
|
||||
public function addPasswordInput($name, $label = '', $pos = -1) {
|
||||
public function addPasswordInput($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new InputElement('password', $name, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -228,7 +242,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return CheckableElement
|
||||
*/
|
||||
public function addRadioButton($name, $label = '', $pos = -1) {
|
||||
public function addRadioButton($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -240,7 +255,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return CheckableElement
|
||||
*/
|
||||
public function addCheckbox($name, $label = '', $pos = -1) {
|
||||
public function addCheckbox($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -253,7 +269,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return DropdownElement
|
||||
*/
|
||||
public function addDropdown($name, $options, $label = '', $pos = -1) {
|
||||
public function addDropdown($name, $options, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new DropdownElement($name, $options, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -265,7 +282,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return TextareaElement
|
||||
*/
|
||||
public function addTextarea($name, $label = '', $pos = -1) {
|
||||
public function addTextarea($name, $label = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TextareaElement($name, $label), $pos);
|
||||
}
|
||||
|
||||
@@ -277,7 +295,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addButton($name, $content, $pos = -1) {
|
||||
public function addButton($name, $content, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
|
||||
}
|
||||
|
||||
@@ -289,7 +308,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addButtonHTML($name, $html, $pos = -1) {
|
||||
public function addButtonHTML($name, $html, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new ButtonElement($name, $html), $pos);
|
||||
}
|
||||
|
||||
@@ -301,7 +321,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addLabel($label, $for='', $pos = -1) {
|
||||
public function addLabel($label, $for='', $pos = -1)
|
||||
{
|
||||
return $this->addLabelHTML(hsc($label), $for, $pos);
|
||||
}
|
||||
|
||||
@@ -313,15 +334,16 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return Element
|
||||
*/
|
||||
public function addLabelHTML($content, $for='', $pos = -1) {
|
||||
public function addLabelHTML($content, $for='', $pos = -1)
|
||||
{
|
||||
$element = new LabelElement(hsc($content));
|
||||
|
||||
if(is_a($for, '\dokuwiki\Form\Element')) {
|
||||
if (is_a($for, '\dokuwiki\Form\Element')) {
|
||||
/** @var Element $for */
|
||||
$for = $for->id();
|
||||
}
|
||||
$for = (string) $for;
|
||||
if($for !== '') {
|
||||
if ($for !== '') {
|
||||
$element->attr('for', $for);
|
||||
}
|
||||
|
||||
@@ -335,7 +357,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return HTMLElement
|
||||
*/
|
||||
public function addHTML($html, $pos = -1) {
|
||||
public function addHTML($html, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new HTMLElement($html), $pos);
|
||||
}
|
||||
|
||||
@@ -346,7 +369,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return TagElement
|
||||
*/
|
||||
public function addTag($tag, $pos = -1) {
|
||||
public function addTag($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagElement($tag), $pos);
|
||||
}
|
||||
|
||||
@@ -359,7 +383,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return TagOpenElement
|
||||
*/
|
||||
public function addTagOpen($tag, $pos = -1) {
|
||||
public function addTagOpen($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagOpenElement($tag), $pos);
|
||||
}
|
||||
|
||||
@@ -372,7 +397,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return TagCloseElement
|
||||
*/
|
||||
public function addTagClose($tag, $pos = -1) {
|
||||
public function addTagClose($tag, $pos = -1)
|
||||
{
|
||||
return $this->addElement(new TagCloseElement($tag), $pos);
|
||||
}
|
||||
|
||||
@@ -383,7 +409,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return FieldsetOpenElement
|
||||
*/
|
||||
public function addFieldsetOpen($legend = '', $pos = -1) {
|
||||
public function addFieldsetOpen($legend = '', $pos = -1)
|
||||
{
|
||||
return $this->addElement(new FieldsetOpenElement($legend), $pos);
|
||||
}
|
||||
|
||||
@@ -393,7 +420,8 @@ class Form extends Element {
|
||||
* @param int $pos
|
||||
* @return TagCloseElement
|
||||
*/
|
||||
public function addFieldsetClose($pos = -1) {
|
||||
public function addFieldsetClose($pos = -1)
|
||||
{
|
||||
return $this->addElement(new FieldsetCloseElement(), $pos);
|
||||
}
|
||||
|
||||
@@ -402,15 +430,16 @@ class Form extends Element {
|
||||
/**
|
||||
* Adjust the elements so that fieldset open and closes are matching
|
||||
*/
|
||||
protected function balanceFieldsets() {
|
||||
protected function balanceFieldsets()
|
||||
{
|
||||
$lastclose = 0;
|
||||
$isopen = false;
|
||||
$len = count($this->elements);
|
||||
|
||||
for($pos = 0; $pos < $len; $pos++) {
|
||||
for ($pos = 0; $pos < $len; $pos++) {
|
||||
$type = $this->elements[$pos]->getType();
|
||||
if($type == 'fieldsetopen') {
|
||||
if($isopen) {
|
||||
if ($type == 'fieldsetopen') {
|
||||
if ($isopen) {
|
||||
//close previous fieldset
|
||||
$this->addFieldsetClose($pos);
|
||||
$lastclose = $pos + 1;
|
||||
@@ -418,8 +447,8 @@ class Form extends Element {
|
||||
$len++;
|
||||
}
|
||||
$isopen = true;
|
||||
} else if($type == 'fieldsetclose') {
|
||||
if(!$isopen) {
|
||||
} elseif ($type == 'fieldsetclose') {
|
||||
if (!$isopen) {
|
||||
// make sure there was a fieldsetopen
|
||||
// either right after the last close or at the begining
|
||||
$this->addFieldsetOpen('', $lastclose);
|
||||
@@ -432,7 +461,7 @@ class Form extends Element {
|
||||
}
|
||||
|
||||
// close open fieldset at the end
|
||||
if($isopen) {
|
||||
if ($isopen) {
|
||||
$this->addFieldsetClose();
|
||||
}
|
||||
}
|
||||
@@ -440,18 +469,26 @@ class Form extends Element {
|
||||
/**
|
||||
* The HTML representation of the whole form
|
||||
*
|
||||
* @param string $eventName (optional) name of the event: FORM_{$name}_OUTPUT
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML($eventName = null)
|
||||
{
|
||||
$this->balanceFieldsets();
|
||||
|
||||
$html = '<form ' . buildAttributes($this->attrs()) . '>';
|
||||
|
||||
foreach($this->hidden as $name => $value) {
|
||||
$html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />';
|
||||
// trigger event to provide an opportunity to modify this form
|
||||
if (isset($eventName)) {
|
||||
$eventName = 'FORM_'.strtoupper($eventName).'_OUTPUT';
|
||||
Event::createAndTrigger($eventName, $this, null, false);
|
||||
}
|
||||
|
||||
foreach($this->elements as $element) {
|
||||
$html = '<form '. buildAttributes($this->attrs()) .'>';
|
||||
|
||||
foreach ($this->hidden as $name => $value) {
|
||||
$html .= '<input type="hidden" name="'. $name .'" value="'. formText($value) .'" />';
|
||||
}
|
||||
|
||||
foreach ($this->elements as $element) {
|
||||
$html .= $element->toHTML();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,13 +9,13 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class HTMLElement extends ValueElement {
|
||||
|
||||
|
||||
class HTMLElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $html
|
||||
*/
|
||||
public function __construct($html) {
|
||||
public function __construct($html)
|
||||
{
|
||||
parent::__construct('html', $html);
|
||||
}
|
||||
|
||||
@@ -23,7 +24,8 @@ class HTMLElement extends ValueElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return $this->val();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -10,7 +11,8 @@ namespace dokuwiki\Form;
|
||||
* @todo figure out how to make wrapping or related label configurable
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class InputElement extends Element {
|
||||
class InputElement extends Element
|
||||
{
|
||||
/**
|
||||
* @var LabelElement
|
||||
*/
|
||||
@@ -26,11 +28,12 @@ class InputElement extends Element {
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
*/
|
||||
public function __construct($type, $name, $label = '') {
|
||||
public function __construct($type, $name, $label = '')
|
||||
{
|
||||
parent::__construct($type, array('name' => $name));
|
||||
$this->attr('name', $name);
|
||||
$this->attr('type', $type);
|
||||
if($label) $this->label = new LabelElement($label);
|
||||
if ($label) $this->label = new LabelElement($label);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +41,8 @@ class InputElement extends Element {
|
||||
*
|
||||
* @return LabelElement|null
|
||||
*/
|
||||
public function getLabel() {
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
@@ -51,7 +55,8 @@ class InputElement extends Element {
|
||||
* @param bool $useinput
|
||||
* @return $this
|
||||
*/
|
||||
public function useInput($useinput) {
|
||||
public function useInput($useinput)
|
||||
{
|
||||
$this->useInput = (bool) $useinput;
|
||||
return $this;
|
||||
}
|
||||
@@ -62,8 +67,9 @@ class InputElement extends Element {
|
||||
* @param null|string $id
|
||||
* @return string|$this
|
||||
*/
|
||||
public function id($id = null) {
|
||||
if($this->label) $this->label->attr('for', $id);
|
||||
public function id($id = null)
|
||||
{
|
||||
if ($this->label) $this->label->attr('for', $id);
|
||||
return parent::id($id);
|
||||
}
|
||||
|
||||
@@ -75,8 +81,9 @@ class InputElement extends Element {
|
||||
* @param string $class the new class to add
|
||||
* @return $this
|
||||
*/
|
||||
public function addClass($class) {
|
||||
if($this->label) $this->label->addClass($class);
|
||||
public function addClass($class)
|
||||
{
|
||||
if ($this->label) $this->label->addClass($class);
|
||||
return parent::addClass($class);
|
||||
}
|
||||
|
||||
@@ -92,14 +99,15 @@ class InputElement extends Element {
|
||||
*
|
||||
* @return array name and array key (null if not an array)
|
||||
*/
|
||||
protected function getInputName() {
|
||||
protected function getInputName()
|
||||
{
|
||||
$name = $this->attr('name');
|
||||
parse_str("$name=1", $parsed);
|
||||
|
||||
$name = array_keys($parsed);
|
||||
$name = array_shift($name);
|
||||
|
||||
if(is_array($parsed[$name])) {
|
||||
if (isset($parsed[$name]) && is_array($parsed[$name])) {
|
||||
$key = array_keys($parsed[$name]);
|
||||
$key = array_shift($key);
|
||||
} else {
|
||||
@@ -112,17 +120,18 @@ class InputElement extends Element {
|
||||
/**
|
||||
* Handles the useInput flag and set the value attribute accordingly
|
||||
*/
|
||||
protected function prefillInput() {
|
||||
protected function prefillInput()
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
list($name, $key) = $this->getInputName();
|
||||
if(!$INPUT->has($name)) return;
|
||||
if (!$INPUT->has($name)) return;
|
||||
|
||||
if($key === null) {
|
||||
if ($key === null) {
|
||||
$value = $INPUT->str($name);
|
||||
} else {
|
||||
$value = $INPUT->arr($name);
|
||||
if(isset($value[$key])) {
|
||||
if (isset($value[$key])) {
|
||||
$value = $value[$key];
|
||||
} else {
|
||||
$value = '';
|
||||
@@ -136,9 +145,10 @@ class InputElement extends Element {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML() {
|
||||
if($this->useInput) $this->prefillInput();
|
||||
return '<input ' . buildAttributes($this->attrs()) . ' />';
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
if ($this->useInput) $this->prefillInput();
|
||||
return '<input '. buildAttributes($this->attrs()) .' />';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,12 +156,13 @@ class InputElement extends Element {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
if($this->label) {
|
||||
return '<label ' . buildAttributes($this->label->attrs()) . '>' . DOKU_LF .
|
||||
'<span>' . hsc($this->label->val()) . '</span>' . DOKU_LF .
|
||||
$this->mainElementHTML() . DOKU_LF .
|
||||
'</label>';
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->label) {
|
||||
return '<label '. buildAttributes($this->label->attrs()) .'>'.DOKU_LF
|
||||
.'<span>'. hsc($this->label->val()) .'</span>'.DOKU_LF
|
||||
. $this->mainElementHTML() .DOKU_LF
|
||||
.'</label>';
|
||||
} else {
|
||||
return $this->mainElementHTML();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class Label
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class LabelElement extends ValueElement {
|
||||
|
||||
class LabelElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* Creates a new Label
|
||||
*
|
||||
* @param string $label This is is raw HTML and will not be escaped
|
||||
*/
|
||||
public function __construct($label) {
|
||||
public function __construct($label)
|
||||
{
|
||||
parent::__construct('label', $label);
|
||||
}
|
||||
|
||||
@@ -21,7 +23,8 @@ class LabelElement extends ValueElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '<label ' . buildAttributes($this->attrs()) . '>' . $this->val() . '</label>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -11,23 +12,24 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class LegacyForm extends Form {
|
||||
|
||||
class LegacyForm extends Form
|
||||
{
|
||||
/**
|
||||
* Creates a new modern form from an old legacy Doku_Form
|
||||
*
|
||||
* @param \Doku_Form $oldform
|
||||
*/
|
||||
public function __construct(\Doku_Form $oldform) {
|
||||
public function __construct(\Doku_Form $oldform)
|
||||
{
|
||||
parent::__construct($oldform->params);
|
||||
|
||||
$this->hidden = $oldform->_hidden;
|
||||
|
||||
foreach($oldform->_content as $element) {
|
||||
foreach ($oldform->_content as $element) {
|
||||
list($ctl, $attr) = $this->parseLegacyAttr($element);
|
||||
|
||||
if(is_array($element)) {
|
||||
switch($ctl['elem']) {
|
||||
if (is_array($element)) {
|
||||
switch ($ctl['elem']) {
|
||||
case 'wikitext':
|
||||
$this->addTextarea('wikitext')
|
||||
->attrs($attr)
|
||||
@@ -111,12 +113,13 @@ class LegacyForm extends Form {
|
||||
* @param array $legacy
|
||||
* @return array
|
||||
*/
|
||||
protected function parseLegacyAttr($legacy) {
|
||||
protected function parseLegacyAttr($legacy)
|
||||
{
|
||||
$attributes = array();
|
||||
$control = array();
|
||||
|
||||
foreach($legacy as $key => $val) {
|
||||
if($key[0] == '_') {
|
||||
foreach ($legacy as $key => $val) {
|
||||
if ($key[0] == '_') {
|
||||
$control[substr($key, 1)] = $val;
|
||||
} elseif($key == 'name') {
|
||||
$control[$key] = $val;
|
||||
@@ -136,7 +139,8 @@ class LegacyForm extends Form {
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function legacyType($type) {
|
||||
protected function legacyType($type)
|
||||
{
|
||||
static $types = array(
|
||||
'text' => 'textfield',
|
||||
'password' => 'passwordfield',
|
||||
@@ -147,7 +151,7 @@ class LegacyForm extends Form {
|
||||
'fieldsetopen' => 'openfieldset',
|
||||
'fieldsetclose' => 'closefieldset',
|
||||
);
|
||||
if(isset($types[$type])) return $types[$type];
|
||||
if (isset($types[$type])) return $types[$type];
|
||||
return $type;
|
||||
}
|
||||
|
||||
@@ -156,20 +160,21 @@ class LegacyForm extends Form {
|
||||
*
|
||||
* @return \Doku_Form
|
||||
*/
|
||||
public function toLegacy() {
|
||||
public function toLegacy()
|
||||
{
|
||||
$this->balanceFieldsets();
|
||||
|
||||
$legacy = new \Doku_Form($this->attrs());
|
||||
$legacy->_hidden = $this->hidden;
|
||||
foreach($this->elements as $element) {
|
||||
if(is_a($element, 'dokuwiki\Form\HTMLElement')) {
|
||||
foreach ($this->elements as $element) {
|
||||
if (is_a($element, 'dokuwiki\Form\HTMLElement')) {
|
||||
$legacy->_content[] = $element->toHTML();
|
||||
} elseif(is_a($element, 'dokuwiki\Form\InputElement')) {
|
||||
} elseif (is_a($element, 'dokuwiki\Form\InputElement')) {
|
||||
/** @var InputElement $element */
|
||||
$data = $element->attrs();
|
||||
$data['_elem'] = $this->legacyType($element->getType());
|
||||
$label = $element->getLabel();
|
||||
if($label) {
|
||||
if ($label) {
|
||||
$data['_class'] = $label->attr('class');
|
||||
}
|
||||
$legacy->_content[] = $data;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
|
||||
class OptGroup extends Element {
|
||||
class OptGroup extends Element
|
||||
{
|
||||
protected $options = array();
|
||||
protected $value;
|
||||
|
||||
@@ -11,7 +12,8 @@ class OptGroup extends Element {
|
||||
* @param string $label The label text for this element (will be autoescaped)
|
||||
* @param array $options The available options
|
||||
*/
|
||||
public function __construct($label, $options) {
|
||||
public function __construct($label, $options)
|
||||
{
|
||||
parent::__construct('optGroup', array('label' => $label));
|
||||
$this->options($options);
|
||||
}
|
||||
@@ -24,7 +26,8 @@ class OptGroup extends Element {
|
||||
* @param string $value
|
||||
* @return bool true if an option with the given value exists, false otherwise
|
||||
*/
|
||||
public function storeValue($value) {
|
||||
public function storeValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
return isset($this->options[$value]);
|
||||
}
|
||||
@@ -45,11 +48,12 @@ class OptGroup extends Element {
|
||||
* @param null|array $options
|
||||
* @return $this|array
|
||||
*/
|
||||
public function options($options = null) {
|
||||
if($options === null) return $this->options;
|
||||
if(!is_array($options)) throw new \InvalidArgumentException('Options have to be an array');
|
||||
public function options($options = null)
|
||||
{
|
||||
if ($options === null) return $this->options;
|
||||
if (!is_array($options)) throw new \InvalidArgumentException('Options have to be an array');
|
||||
$this->options = array();
|
||||
foreach($options as $key => $val) {
|
||||
foreach ($options as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
if (!key_exists('label', $val)) throw new \InvalidArgumentException(
|
||||
'If option is given as array, it has to have a "label"-key!'
|
||||
@@ -60,7 +64,7 @@ class OptGroup extends Element {
|
||||
);
|
||||
}
|
||||
$this->options[$key] = $val;
|
||||
} elseif(is_int($key)) {
|
||||
} elseif (is_int($key)) {
|
||||
$this->options[$val] = array('label' => (string) $val);
|
||||
} else {
|
||||
$this->options[$key] = array('label' => (string) $val);
|
||||
@@ -69,13 +73,13 @@ class OptGroup extends Element {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The HTML representation of this element
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
if ($this->attributes['label'] === null) {
|
||||
return $this->renderOptions();
|
||||
}
|
||||
@@ -89,9 +93,10 @@ class OptGroup extends Element {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function renderOptions() {
|
||||
protected function renderOptions()
|
||||
{
|
||||
$html = '';
|
||||
foreach($this->options as $key => $val) {
|
||||
foreach ($this->options as $key => $val) {
|
||||
$selected = ((string)$key === (string)$this->value) ? ' selected="selected"' : '';
|
||||
$attrs = '';
|
||||
if (!empty($val['attrs']) && is_array($val['attrs'])) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -9,13 +10,14 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagCloseElement extends ValueElement {
|
||||
|
||||
class TagCloseElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array()) {
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tagclose', $tag, $attributes);
|
||||
}
|
||||
|
||||
@@ -26,7 +28,8 @@ class TagCloseElement extends ValueElement {
|
||||
* @return void
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function addClass($class) {
|
||||
public function addClass($class)
|
||||
{
|
||||
throw new \BadMethodCallException('You can\t add classes to closing tag');
|
||||
}
|
||||
|
||||
@@ -37,7 +40,8 @@ class TagCloseElement extends ValueElement {
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function id($id = null) {
|
||||
public function id($id = null)
|
||||
{
|
||||
if ($id === null) {
|
||||
return '';
|
||||
} else {
|
||||
@@ -53,7 +57,8 @@ class TagCloseElement extends ValueElement {
|
||||
* @return string
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function attr($name, $value = null) {
|
||||
public function attr($name, $value = null)
|
||||
{
|
||||
if ($value === null) {
|
||||
return '';
|
||||
} else {
|
||||
@@ -68,7 +73,8 @@ class TagCloseElement extends ValueElement {
|
||||
* @return array
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function attrs($attributes = null) {
|
||||
public function attrs($attributes = null)
|
||||
{
|
||||
if ($attributes === null) {
|
||||
return array();
|
||||
} else {
|
||||
@@ -81,7 +87,8 @@ class TagCloseElement extends ValueElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '</'.$this->val().'>';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -8,13 +9,14 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagElement extends ValueElement {
|
||||
|
||||
class TagElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array()) {
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tag', $tag, $attributes);
|
||||
}
|
||||
|
||||
@@ -23,7 +25,8 @@ class TagElement extends ValueElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '<'.$this->val().' '.buildAttributes($this->attrs()).' />';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
@@ -9,13 +10,14 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TagOpenElement extends ValueElement {
|
||||
|
||||
class TagOpenElement extends ValueElement
|
||||
{
|
||||
/**
|
||||
* @param string $tag
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($tag, $attributes = array()) {
|
||||
public function __construct($tag, $attributes = array())
|
||||
{
|
||||
parent::__construct('tagopen', $tag, $attributes);
|
||||
}
|
||||
|
||||
@@ -24,7 +26,8 @@ class TagOpenElement extends ValueElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toHTML() {
|
||||
public function toHTML()
|
||||
{
|
||||
return '<'.$this->val().' '.buildAttributes($this->attrs()).'>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Form;
|
||||
|
||||
/**
|
||||
* Class TextareaElement
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
class TextareaElement extends InputElement {
|
||||
|
||||
class TextareaElement extends InputElement
|
||||
{
|
||||
/**
|
||||
* @var string the actual text within the area
|
||||
*/
|
||||
@@ -16,7 +17,8 @@ class TextareaElement extends InputElement {
|
||||
* @param string $name The name of this form element
|
||||
* @param string $label The label text for this element
|
||||
*/
|
||||
public function __construct($name, $label) {
|
||||
public function __construct($name, $label)
|
||||
{
|
||||
parent::__construct('textarea', $name, $label);
|
||||
$this->attr('dir', 'auto');
|
||||
}
|
||||
@@ -29,8 +31,9 @@ class TextareaElement extends InputElement {
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null) {
|
||||
if($value !== null) {
|
||||
public function val($value = null)
|
||||
{
|
||||
if ($value !== null) {
|
||||
$this->text = cleanText($value);
|
||||
return $this;
|
||||
}
|
||||
@@ -42,10 +45,11 @@ class TextareaElement extends InputElement {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function mainElementHTML() {
|
||||
if($this->useInput) $this->prefillInput();
|
||||
protected function mainElementHTML()
|
||||
{
|
||||
if ($this->useInput) $this->prefillInput();
|
||||
return '<textarea ' . buildAttributes($this->attrs()) . '>' .
|
||||
formText($this->val()) . '</textarea>';
|
||||
formText($this->val()) . '</textarea>';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace dokuwiki\Form;
|
||||
*
|
||||
* @package dokuwiki\Form
|
||||
*/
|
||||
abstract class ValueElement extends Element {
|
||||
|
||||
abstract class ValueElement extends Element
|
||||
{
|
||||
/**
|
||||
* @var string holds the element's value
|
||||
*/
|
||||
@@ -23,7 +23,8 @@ abstract class ValueElement extends Element {
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($type, $value, $attributes = array()) {
|
||||
public function __construct($type, $value, $attributes = array())
|
||||
{
|
||||
parent::__construct($type, $attributes);
|
||||
$this->val($value);
|
||||
}
|
||||
@@ -34,8 +35,9 @@ abstract class ValueElement extends Element {
|
||||
* @param null|string $value
|
||||
* @return string|$this
|
||||
*/
|
||||
public function val($value = null) {
|
||||
if($value !== null) {
|
||||
public function val($value = null)
|
||||
{
|
||||
if ($value !== null) {
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -168,6 +168,9 @@ class HTTPClient {
|
||||
$this->resp_body = '';
|
||||
$this->resp_headers = array();
|
||||
|
||||
// save unencoded data for recursive call
|
||||
$unencodedData = $data;
|
||||
|
||||
// don't accept gzip if truncated bodies might occur
|
||||
if($this->max_bodysize &&
|
||||
!$this->max_bodysize_abort &&
|
||||
@@ -178,10 +181,9 @@ class HTTPClient {
|
||||
// parse URL into bits
|
||||
$uri = parse_url($url);
|
||||
$server = $uri['host'];
|
||||
$path = $uri['path'];
|
||||
if(empty($path)) $path = '/';
|
||||
$path = !empty($uri['path']) ? $uri['path'] : '/';
|
||||
$uriPort = !empty($uri['port']) ? $uri['port'] : null;
|
||||
if(!empty($uri['query'])) $path .= '?'.$uri['query'];
|
||||
if(!empty($uri['port'])) $port = $uri['port'];
|
||||
if(isset($uri['user'])) $this->user = $uri['user'];
|
||||
if(isset($uri['pass'])) $this->pass = $uri['pass'];
|
||||
|
||||
@@ -194,7 +196,7 @@ class HTTPClient {
|
||||
$use_tls = $this->proxy_ssl;
|
||||
}else{
|
||||
$request_url = $path;
|
||||
if (!isset($port)) $port = ($uri['scheme'] == 'https') ? 443 : 80;
|
||||
$port = $uriPort ?: ($uri['scheme'] == 'https' ? 443 : 80);
|
||||
$use_tls = ($uri['scheme'] == 'https');
|
||||
}
|
||||
|
||||
@@ -209,8 +211,8 @@ class HTTPClient {
|
||||
|
||||
// prepare headers
|
||||
$headers = $this->headers;
|
||||
$headers['Host'] = $uri['host'];
|
||||
if(!empty($uri['port'])) $headers['Host'].= ':'.$uri['port'];
|
||||
$headers['Host'] = $uri['host']
|
||||
. ($uriPort ? ':' . $uriPort : '');
|
||||
$headers['User-Agent'] = $this->agent;
|
||||
$headers['Referer'] = $this->referer;
|
||||
|
||||
@@ -354,7 +356,7 @@ class HTTPClient {
|
||||
$this->debug('Object headers',$this->resp_headers);
|
||||
|
||||
// check server status code to follow redirect
|
||||
if($this->status == 301 || $this->status == 302 ){
|
||||
if(in_array($this->status, [301, 302, 303, 307, 308])){
|
||||
if (empty($this->resp_headers['location'])){
|
||||
throw new HTTPClientException('Redirect but no Location Header found');
|
||||
}elseif($this->redirect_count == $this->max_redirect){
|
||||
@@ -370,15 +372,20 @@ class HTTPClient {
|
||||
// handle non-RFC-compliant relative redirects
|
||||
if (!preg_match('/^http/i', $this->resp_headers['location'])){
|
||||
if($this->resp_headers['location'][0] != '/'){
|
||||
$this->resp_headers['location'] = $uri['scheme'].'://'.$uri['host'].':'.$uri['port'].
|
||||
dirname($uri['path']).'/'.$this->resp_headers['location'];
|
||||
$this->resp_headers['location'] = $uri['scheme'].'://'.$uri['host'].':'.$uriPort.
|
||||
dirname($path).'/'.$this->resp_headers['location'];
|
||||
}else{
|
||||
$this->resp_headers['location'] = $uri['scheme'].'://'.$uri['host'].':'.$uri['port'].
|
||||
$this->resp_headers['location'] = $uri['scheme'].'://'.$uri['host'].':'.$uriPort.
|
||||
$this->resp_headers['location'];
|
||||
}
|
||||
}
|
||||
// perform redirected request, always via GET (required by RFC)
|
||||
return $this->sendRequest($this->resp_headers['location'],array(),'GET');
|
||||
if($this->status == 307 || $this->status == 308) {
|
||||
// perform redirected request, same method as before (required by RFC)
|
||||
return $this->sendRequest($this->resp_headers['location'],$unencodedData,$method);
|
||||
}else{
|
||||
// perform redirected request, always via GET (required by RFC)
|
||||
return $this->sendRequest($this->resp_headers['location'],array(),'GET');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,7 +518,7 @@ class HTTPClient {
|
||||
if(!$this->useProxyForUrl($requesturl)) return false;
|
||||
$requestinfo = parse_url($requesturl);
|
||||
if($requestinfo['scheme'] != 'https') return false;
|
||||
if(!$requestinfo['port']) $requestinfo['port'] = 443;
|
||||
if(empty($requestinfo['port'])) $requestinfo['port'] = 443;
|
||||
|
||||
// build request
|
||||
$request = "CONNECT {$requestinfo['host']}:{$requestinfo['port']} HTTP/1.0".HTTP_NL;
|
||||
@@ -765,7 +772,7 @@ class HTTPClient {
|
||||
foreach($lines as $line){
|
||||
@list($key, $val) = explode(':',$line,2);
|
||||
$key = trim($key);
|
||||
$val = trim($val);
|
||||
$val = trim($val ?? '');
|
||||
$key = strtolower($key);
|
||||
if(!$key) continue;
|
||||
if(isset($headers[$key])){
|
||||
|
||||
39
dokuwiki/inc/HTTP/Headers.php
Normal file
39
dokuwiki/inc/HTTP/Headers.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\HTTP;
|
||||
|
||||
/**
|
||||
* Utilities to send HTTP Headers
|
||||
*/
|
||||
class Headers
|
||||
{
|
||||
/**
|
||||
* Send a Content-Security-Polica Header
|
||||
*
|
||||
* Expects an associative array with individual policies and their values
|
||||
*
|
||||
* @param array $policy
|
||||
*/
|
||||
static public function contentSecurityPolicy($policy)
|
||||
{
|
||||
foreach ($policy as $key => $values) {
|
||||
// if the value is not an array, we also accept newline terminated strings
|
||||
if (!is_array($values)) $values = explode("\n", $values);
|
||||
$values = array_map('trim', $values);
|
||||
$values = array_unique($values);
|
||||
$values = array_filter($values);
|
||||
$policy[$key] = $values;
|
||||
}
|
||||
|
||||
$cspheader = 'Content-Security-Policy:';
|
||||
foreach ($policy as $key => $values) {
|
||||
if ($values) {
|
||||
$cspheader .= " $key " . join(' ', $values) . ';';
|
||||
} else {
|
||||
$cspheader .= " $key;";
|
||||
}
|
||||
}
|
||||
|
||||
header($cspheader);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1317,79 +1317,81 @@ class JpegMeta {
|
||||
$this->_info['file']['UnixTime'] = filemtime($this->_fileName);
|
||||
|
||||
// get image size directly from file
|
||||
$size = getimagesize($this->_fileName);
|
||||
$this->_info['file']['Width'] = $size[0];
|
||||
$this->_info['file']['Height'] = $size[1];
|
||||
// set mime types and formats
|
||||
// http://php.net/manual/en/function.getimagesize.php
|
||||
// http://php.net/manual/en/function.image-type-to-mime-type.php
|
||||
switch ($size[2]){
|
||||
case 1:
|
||||
$this->_info['file']['Mime'] = 'image/gif';
|
||||
$this->_info['file']['Format'] = 'GIF';
|
||||
break;
|
||||
case 2:
|
||||
$this->_info['file']['Mime'] = 'image/jpeg';
|
||||
$this->_info['file']['Format'] = 'JPEG';
|
||||
break;
|
||||
case 3:
|
||||
$this->_info['file']['Mime'] = 'image/png';
|
||||
$this->_info['file']['Format'] = 'PNG';
|
||||
break;
|
||||
case 4:
|
||||
$this->_info['file']['Mime'] = 'application/x-shockwave-flash';
|
||||
$this->_info['file']['Format'] = 'SWF';
|
||||
break;
|
||||
case 5:
|
||||
$this->_info['file']['Mime'] = 'image/psd';
|
||||
$this->_info['file']['Format'] = 'PSD';
|
||||
break;
|
||||
case 6:
|
||||
$this->_info['file']['Mime'] = 'image/bmp';
|
||||
$this->_info['file']['Format'] = 'BMP';
|
||||
break;
|
||||
case 7:
|
||||
$this->_info['file']['Mime'] = 'image/tiff';
|
||||
$this->_info['file']['Format'] = 'TIFF (Intel)';
|
||||
break;
|
||||
case 8:
|
||||
$this->_info['file']['Mime'] = 'image/tiff';
|
||||
$this->_info['file']['Format'] = 'TIFF (Motorola)';
|
||||
break;
|
||||
case 9:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JPC';
|
||||
break;
|
||||
case 10:
|
||||
$this->_info['file']['Mime'] = 'image/jp2';
|
||||
$this->_info['file']['Format'] = 'JP2';
|
||||
break;
|
||||
case 11:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JPX';
|
||||
break;
|
||||
case 12:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JB2';
|
||||
break;
|
||||
case 13:
|
||||
$this->_info['file']['Mime'] = 'application/x-shockwave-flash';
|
||||
$this->_info['file']['Format'] = 'SWC';
|
||||
break;
|
||||
case 14:
|
||||
$this->_info['file']['Mime'] = 'image/iff';
|
||||
$this->_info['file']['Format'] = 'IFF';
|
||||
break;
|
||||
case 15:
|
||||
$this->_info['file']['Mime'] = 'image/vnd.wap.wbmp';
|
||||
$this->_info['file']['Format'] = 'WBMP';
|
||||
break;
|
||||
case 16:
|
||||
$this->_info['file']['Mime'] = 'image/xbm';
|
||||
$this->_info['file']['Format'] = 'XBM';
|
||||
break;
|
||||
default:
|
||||
$this->_info['file']['Mime'] = 'image/unknown';
|
||||
if ($size = getimagesize($this->_fileName)) {
|
||||
$this->_info['file']['Width'] = $size[0];
|
||||
$this->_info['file']['Height'] = $size[1];
|
||||
|
||||
// set mime types and formats
|
||||
// http://php.net/manual/en/function.getimagesize.php
|
||||
// http://php.net/manual/en/function.image-type-to-mime-type.php
|
||||
switch ($size[2]) {
|
||||
case 1:
|
||||
$this->_info['file']['Mime'] = 'image/gif';
|
||||
$this->_info['file']['Format'] = 'GIF';
|
||||
break;
|
||||
case 2:
|
||||
$this->_info['file']['Mime'] = 'image/jpeg';
|
||||
$this->_info['file']['Format'] = 'JPEG';
|
||||
break;
|
||||
case 3:
|
||||
$this->_info['file']['Mime'] = 'image/png';
|
||||
$this->_info['file']['Format'] = 'PNG';
|
||||
break;
|
||||
case 4:
|
||||
$this->_info['file']['Mime'] = 'application/x-shockwave-flash';
|
||||
$this->_info['file']['Format'] = 'SWF';
|
||||
break;
|
||||
case 5:
|
||||
$this->_info['file']['Mime'] = 'image/psd';
|
||||
$this->_info['file']['Format'] = 'PSD';
|
||||
break;
|
||||
case 6:
|
||||
$this->_info['file']['Mime'] = 'image/bmp';
|
||||
$this->_info['file']['Format'] = 'BMP';
|
||||
break;
|
||||
case 7:
|
||||
$this->_info['file']['Mime'] = 'image/tiff';
|
||||
$this->_info['file']['Format'] = 'TIFF (Intel)';
|
||||
break;
|
||||
case 8:
|
||||
$this->_info['file']['Mime'] = 'image/tiff';
|
||||
$this->_info['file']['Format'] = 'TIFF (Motorola)';
|
||||
break;
|
||||
case 9:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JPC';
|
||||
break;
|
||||
case 10:
|
||||
$this->_info['file']['Mime'] = 'image/jp2';
|
||||
$this->_info['file']['Format'] = 'JP2';
|
||||
break;
|
||||
case 11:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JPX';
|
||||
break;
|
||||
case 12:
|
||||
$this->_info['file']['Mime'] = 'application/octet-stream';
|
||||
$this->_info['file']['Format'] = 'JB2';
|
||||
break;
|
||||
case 13:
|
||||
$this->_info['file']['Mime'] = 'application/x-shockwave-flash';
|
||||
$this->_info['file']['Format'] = 'SWC';
|
||||
break;
|
||||
case 14:
|
||||
$this->_info['file']['Mime'] = 'image/iff';
|
||||
$this->_info['file']['Format'] = 'IFF';
|
||||
break;
|
||||
case 15:
|
||||
$this->_info['file']['Mime'] = 'image/vnd.wap.wbmp';
|
||||
$this->_info['file']['Format'] = 'WBMP';
|
||||
break;
|
||||
case 16:
|
||||
$this->_info['file']['Mime'] = 'image/xbm';
|
||||
$this->_info['file']['Format'] = 'XBM';
|
||||
break;
|
||||
default:
|
||||
$this->_info['file']['Mime'] = 'image/unknown';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->_info['file'] = array();
|
||||
|
||||
215
dokuwiki/inc/Logger.php
Normal file
215
dokuwiki/inc/Logger.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki;
|
||||
|
||||
use dokuwiki\Extension\Event;
|
||||
|
||||
/**
|
||||
* Log messages to a daily log file
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
const LOG_ERROR = 'error';
|
||||
const LOG_DEPRECATED = 'deprecated';
|
||||
const LOG_DEBUG = 'debug';
|
||||
|
||||
/** @var Logger[] */
|
||||
static protected $instances;
|
||||
|
||||
/** @var string what kind of log is this */
|
||||
protected $facility;
|
||||
|
||||
protected $isLogging = true;
|
||||
|
||||
/**
|
||||
* Logger constructor.
|
||||
*
|
||||
* @param string $facility The type of log
|
||||
*/
|
||||
protected function __construct($facility)
|
||||
{
|
||||
global $conf;
|
||||
$this->facility = $facility;
|
||||
|
||||
// Should logging be disabled for this facility?
|
||||
$dontlog = explode(',', $conf['dontlog']);
|
||||
$dontlog = array_map('trim', $dontlog);
|
||||
if (in_array($facility, $dontlog)) $this->isLogging = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Logger instance for the given facility
|
||||
*
|
||||
* @param string $facility The type of log
|
||||
* @return Logger
|
||||
*/
|
||||
static public function getInstance($facility = self::LOG_ERROR)
|
||||
{
|
||||
if (empty(self::$instances[$facility])) {
|
||||
self::$instances[$facility] = new Logger($facility);
|
||||
}
|
||||
return self::$instances[$facility];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to directly log to the error log
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param mixed $details Any details that should be added to the log entry
|
||||
* @param string $file A source filename if this is related to a source position
|
||||
* @param int $line A line number for the above file
|
||||
* @return bool has a log been written?
|
||||
*/
|
||||
static public function error($message, $details = null, $file = '', $line = 0)
|
||||
{
|
||||
return self::getInstance(self::LOG_ERROR)->log(
|
||||
$message, $details, $file, $line
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to directly log to the debug log
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param mixed $details Any details that should be added to the log entry
|
||||
* @param string $file A source filename if this is related to a source position
|
||||
* @param int $line A line number for the above file
|
||||
* @return bool has a log been written?
|
||||
*/
|
||||
static public function debug($message, $details = null, $file = '', $line = 0)
|
||||
{
|
||||
return self::getInstance(self::LOG_DEBUG)->log(
|
||||
$message, $details, $file, $line
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to directly log to the deprecation log
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param mixed $details Any details that should be added to the log entry
|
||||
* @param string $file A source filename if this is related to a source position
|
||||
* @param int $line A line number for the above file
|
||||
* @return bool has a log been written?
|
||||
*/
|
||||
static public function deprecated($message, $details = null, $file = '', $line = 0)
|
||||
{
|
||||
return self::getInstance(self::LOG_DEPRECATED)->log(
|
||||
$message, $details, $file, $line
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message to the facility log
|
||||
*
|
||||
* @param string $message The log message
|
||||
* @param mixed $details Any details that should be added to the log entry
|
||||
* @param string $file A source filename if this is related to a source position
|
||||
* @param int $line A line number for the above file
|
||||
* @triggers LOGGER_DATA_FORMAT can be used to change the logged data or intercept it
|
||||
* @return bool has a log been written?
|
||||
*/
|
||||
public function log($message, $details = null, $file = '', $line = 0)
|
||||
{
|
||||
global $EVENT_HANDLER;
|
||||
if (!$this->isLogging) return false;
|
||||
|
||||
$datetime = time();
|
||||
$data = [
|
||||
'facility' => $this->facility,
|
||||
'datetime' => $datetime,
|
||||
'message' => $message,
|
||||
'details' => $details,
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'loglines' => [],
|
||||
'logfile' => $this->getLogfile($datetime),
|
||||
];
|
||||
|
||||
if ($EVENT_HANDLER !== null) {
|
||||
$event = new Event('LOGGER_DATA_FORMAT', $data);
|
||||
if ($event->advise_before()) {
|
||||
$data['loglines'] = $this->formatLogLines($data);
|
||||
}
|
||||
$event->advise_after();
|
||||
} else {
|
||||
// The event system is not yet available, to ensure the log isn't lost even on
|
||||
// fatal errors, the default action is executed
|
||||
$data['loglines'] = $this->formatLogLines($data);
|
||||
}
|
||||
|
||||
// only log when any data available
|
||||
if (count($data['loglines'])) {
|
||||
return $this->writeLogLines($data['loglines'], $data['logfile']);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given data as loglines
|
||||
*
|
||||
* @param array $data Event data from LOGGER_DATA_FORMAT
|
||||
* @return string[] the lines to log
|
||||
*/
|
||||
protected function formatLogLines($data)
|
||||
{
|
||||
extract($data);
|
||||
|
||||
// details are logged indented
|
||||
if ($details) {
|
||||
if (!is_string($details)) {
|
||||
$details = json_encode($details, JSON_PRETTY_PRINT);
|
||||
}
|
||||
$details = explode("\n", $details);
|
||||
$loglines = array_map(function ($line) {
|
||||
return ' ' . $line;
|
||||
}, $details);
|
||||
} elseif ($details) {
|
||||
$loglines = [$details];
|
||||
} else {
|
||||
$loglines = [];
|
||||
}
|
||||
|
||||
// datetime, fileline, message
|
||||
$logline = gmdate('Y-m-d H:i:s', $datetime) . "\t";
|
||||
if ($file) {
|
||||
$logline .= $file;
|
||||
if ($line) $logline .= "($line)";
|
||||
}
|
||||
$logline .= "\t" . $message;
|
||||
array_unshift($loglines, $logline);
|
||||
|
||||
return $loglines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the log file for the given day
|
||||
*
|
||||
* @param false|string|int $date Date to access, false for today
|
||||
* @return string
|
||||
*/
|
||||
public function getLogfile($date = false)
|
||||
{
|
||||
global $conf;
|
||||
|
||||
if($date !== null && !is_numeric($date)) {
|
||||
$date = strtotime($date);
|
||||
}
|
||||
if (!$date) $date = time();
|
||||
|
||||
return $conf['logdir'] . '/' . $this->facility . '/' . date('Y-m-d', $date) . '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given lines to today's facility log
|
||||
*
|
||||
* @param string[] $lines the raw lines to append to the log
|
||||
* @param string $logfile where to write to
|
||||
* @return bool true if the log was written
|
||||
*/
|
||||
protected function writeLogLines($lines, $logfile)
|
||||
{
|
||||
return io_saveFile($logfile, join("\n", $lines) . "\n", true);
|
||||
}
|
||||
}
|
||||
@@ -7,15 +7,18 @@ namespace dokuwiki\Menu\Item;
|
||||
*
|
||||
* Quick revert to the currently shown page revision
|
||||
*/
|
||||
class Revert extends AbstractItem {
|
||||
class Revert extends AbstractItem
|
||||
{
|
||||
|
||||
/** @inheritdoc */
|
||||
public function __construct() {
|
||||
public function __construct()
|
||||
{
|
||||
global $REV;
|
||||
global $INFO;
|
||||
global $INPUT;
|
||||
parent::__construct();
|
||||
|
||||
if(!$REV || !$INFO['writable']) {
|
||||
if (!$REV || !$INFO['writable'] || $INPUT->server->str('REMOTE_USER') === '') {
|
||||
throw new \RuntimeException('revert not available');
|
||||
}
|
||||
$this->params['rev'] = $REV;
|
||||
|
||||
@@ -66,7 +66,7 @@ class ParallelRegex
|
||||
* subject.
|
||||
* @return bool|string False if no match found, label if label exists, true if not
|
||||
*/
|
||||
public function match($subject, &$match)
|
||||
public function apply($subject, &$match)
|
||||
{
|
||||
if (count($this->patterns) == 0) {
|
||||
return false;
|
||||
|
||||
@@ -23,8 +23,10 @@ class Externallink extends AbstractMode
|
||||
$this->patterns[] = '\b(?i)'.$scheme.'(?-i)://['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
|
||||
}
|
||||
|
||||
$this->patterns[] = '(?<=\s)(?i)www?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
|
||||
$this->patterns[] = '(?<=\s)(?i)ftp?(?-i)\.['.$host.']+?\.['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
|
||||
$this->patterns[] = '(?<![/\\\\])\b(?i)www?(?-i)\.['.$host.']+?\.'.
|
||||
'['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
|
||||
$this->patterns[] = '(?<![/\\\\])\b(?i)ftp?(?-i)\.['.$host.']+?\.'.
|
||||
'['.$host.']+?['.$any.']+?(?=['.$punc.']*[^'.$any.'])';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@@ -41,4 +43,12 @@ class Externallink extends AbstractMode
|
||||
{
|
||||
return 330;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPatterns()
|
||||
{
|
||||
return $this->patterns;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +92,13 @@ class PassHash {
|
||||
} elseif(preg_match('/^:B:(.+?):.{32}$/', $hash, $m)) {
|
||||
$method = 'mediawiki';
|
||||
$salt = $m[1];
|
||||
} elseif(preg_match('/^\$6\$(rounds=\d+)?\$?(.+?)\$/', $hash, $m)) {
|
||||
$method = 'sha512';
|
||||
$salt = $m[2];
|
||||
$magic = $m[1];
|
||||
} elseif(preg_match('/^\$(5|6)\$(rounds=\d+)?\$?(.+?)\$/', $hash, $m)) {
|
||||
$method = 'sha2';
|
||||
$salt = $m[3];
|
||||
$magic = array(
|
||||
'prefix' => $m[1],
|
||||
'rounds' => $m[2],
|
||||
);
|
||||
} elseif(preg_match('/^\$(argon2id?)/', $hash, $m)) {
|
||||
if(!defined('PASSWORD_'.strtoupper($m[1]))) {
|
||||
throw new \Exception('This PHP installation has no '.strtoupper($m[1]).' support');
|
||||
@@ -666,29 +669,58 @@ class PassHash {
|
||||
}
|
||||
|
||||
/**
|
||||
* Password hashing method SHA512
|
||||
* Password hashing method SHA-2
|
||||
*
|
||||
* This is only supported on PHP 5.3.2 or higher and will throw an exception if
|
||||
* the needed crypt support is not available
|
||||
*
|
||||
* Uses:
|
||||
* - SHA-2 with 256-bit output for prefix $5$
|
||||
* - SHA-2 with 512-bit output for prefix $6$ (default)
|
||||
*
|
||||
* @param string $clear The clear text to hash
|
||||
* @param string $salt The salt to use, null for random
|
||||
* @param string $magic The rounds for sha512 (for example "rounds=3000"), null for default value
|
||||
* @param array $opts ('rounds' => rounds for sha256/sha512, 'prefix' => selected method from SHA-2 family)
|
||||
* @return string Hashed password
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function hash_sha512($clear, $salt = null, $magic = null) {
|
||||
if(!defined('CRYPT_SHA512') || CRYPT_SHA512 != 1) {
|
||||
public function hash_sha2($clear, $salt = null, $opts = array()) {
|
||||
if(empty($opts['prefix'])) {
|
||||
$prefix = '6';
|
||||
} else {
|
||||
$prefix = $opts['prefix'];
|
||||
}
|
||||
if(empty($opts['rounds'])) {
|
||||
$rounds = null;
|
||||
} else {
|
||||
$rounds = $opts['rounds'];
|
||||
}
|
||||
if($prefix == '5' && (!defined('CRYPT_SHA256') || CRYPT_SHA256 != 1)) {
|
||||
throw new \Exception('This PHP installation has no SHA256 support');
|
||||
}
|
||||
if($prefix == '6' && (!defined('CRYPT_SHA512') || CRYPT_SHA512 != 1)) {
|
||||
throw new \Exception('This PHP installation has no SHA512 support');
|
||||
}
|
||||
$this->init_salt($salt, 8, false);
|
||||
if(empty($magic)) {
|
||||
return crypt($clear, '$6$'.$salt.'$');
|
||||
if(empty($rounds)) {
|
||||
return crypt($clear, '$'.$prefix.'$'.$salt.'$');
|
||||
}else{
|
||||
return crypt($clear, '$6$'.$magic.'$'.$salt.'$');
|
||||
return crypt($clear, '$'.$prefix.'$'.$rounds.'$'.$salt.'$');
|
||||
}
|
||||
}
|
||||
|
||||
/** @see sha2 */
|
||||
public function hash_sha512($clear, $salt = null, $opts=[]) {
|
||||
$opts['prefix'] = 6;
|
||||
return $this->hash_sha2($clear, $salt, $opts);
|
||||
}
|
||||
|
||||
/** @see sha2 */
|
||||
public function hash_sha256($clear, $salt = null, $opts=[]) {
|
||||
$opts['prefix'] = 5;
|
||||
return $this->hash_sha2($clear, $salt, $opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Password hashing method 'mediawiki'
|
||||
*
|
||||
@@ -722,7 +754,7 @@ class PassHash {
|
||||
if(!defined('PASSWORD_ARGON2I')) {
|
||||
throw new \Exception('This PHP installation has no ARGON2I support');
|
||||
}
|
||||
return password_hash($clear,PASSWORD_ARGON2I);
|
||||
return password_hash($clear,PASSWORD_ARGON2I);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -740,7 +772,7 @@ class PassHash {
|
||||
if(!defined('PASSWORD_ARGON2ID')) {
|
||||
throw new \Exception('This PHP installation has no ARGON2ID support');
|
||||
}
|
||||
return password_hash($clear,PASSWORD_ARGON2ID);
|
||||
return password_hash($clear,PASSWORD_ARGON2ID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,7 +92,8 @@ class Api
|
||||
if ($args === null) {
|
||||
$args = array();
|
||||
}
|
||||
list($type, $pluginName, /* $call */) = explode('.', $method, 3);
|
||||
// Ensure we have at least one '.' in $method
|
||||
list($type, $pluginName, /* $call */) = explode('.', $method . '.', 3);
|
||||
if ($type === 'plugin') {
|
||||
return $this->callPlugin($pluginName, $method, $args);
|
||||
}
|
||||
@@ -128,8 +129,9 @@ class Api
|
||||
if (!array_key_exists($method, $customCalls)) {
|
||||
throw new RemoteException('Method does not exist', -32603);
|
||||
}
|
||||
$customCall = $customCalls[$method];
|
||||
return $this->callPlugin($customCall[0], $customCall[1], $args);
|
||||
list($plugin, $method) = $customCalls[$method];
|
||||
$fullMethod = "plugin.$plugin.$method";
|
||||
return $this->callPlugin($plugin, $fullMethod, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,7 +344,7 @@ class Api
|
||||
$this->coreMethods = $apiCore;
|
||||
}
|
||||
}
|
||||
return $this->coreMethods->__getRemoteInfo();
|
||||
return $this->coreMethods->getRemoteInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,8 +6,9 @@ use Doku_Renderer_xhtml;
|
||||
use dokuwiki\ChangeLog\MediaChangeLog;
|
||||
use dokuwiki\ChangeLog\PageChangeLog;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Utf8\Sort;
|
||||
|
||||
define('DOKU_API_VERSION', 10);
|
||||
define('DOKU_API_VERSION', 11);
|
||||
|
||||
/**
|
||||
* Provides the core methods for the remote API.
|
||||
@@ -16,7 +17,7 @@ define('DOKU_API_VERSION', 10);
|
||||
class ApiCore
|
||||
{
|
||||
/** @var int Increased whenever the API is changed */
|
||||
const API_VERSION = 10;
|
||||
const API_VERSION = 11;
|
||||
|
||||
|
||||
/** @var Api */
|
||||
@@ -35,7 +36,7 @@ class ApiCore
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __getRemoteInfo()
|
||||
public function getRemoteInfo()
|
||||
{
|
||||
return array(
|
||||
'dokuwiki.getVersion' => array(
|
||||
@@ -77,7 +78,11 @@ class ApiCore
|
||||
'args' => array('string', 'string', 'array'),
|
||||
'return' => 'bool',
|
||||
'doc' => 'Append text to a wiki page.'
|
||||
), 'dokuwiki.deleteUsers' => array(
|
||||
), 'dokuwiki.createUser' => array(
|
||||
'args' => array('struct'),
|
||||
'return' => 'bool',
|
||||
'doc' => 'Create a user. The result is boolean'
|
||||
),'dokuwiki.deleteUsers' => array(
|
||||
'args' => array('array'),
|
||||
'return' => 'bool',
|
||||
'doc' => 'Remove one or more users from the list of registered users.'
|
||||
@@ -142,11 +147,11 @@ class ApiCore
|
||||
), 'wiki.getRecentChanges' => array(
|
||||
'args' => array('int'),
|
||||
'return' => 'array',
|
||||
'Returns a struct about all recent changes since given timestamp.'
|
||||
'doc' => 'Returns a struct about all recent changes since given timestamp.'
|
||||
), 'wiki.getRecentMediaChanges' => array(
|
||||
'args' => array('int'),
|
||||
'return' => 'array',
|
||||
'Returns a struct about all recent media changes since given timestamp.'
|
||||
'doc' => 'Returns a struct about all recent media changes since given timestamp.'
|
||||
), 'wiki.aclCheck' => array(
|
||||
'args' => array('string', 'string', 'array'),
|
||||
'return' => 'int',
|
||||
@@ -310,6 +315,7 @@ class ApiCore
|
||||
$list = array();
|
||||
$pages = idx_get_indexer()->getPages();
|
||||
$pages = array_filter(array_filter($pages, 'isVisiblePage'), 'page_exists');
|
||||
Sort::ksort($pages);
|
||||
|
||||
foreach (array_keys($pages) as $idx) {
|
||||
$perm = auth_quickaclcheck($pages[$idx]);
|
||||
@@ -580,6 +586,68 @@ class ApiCore
|
||||
return $this->putPage($id, $currentpage . $text, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create one or more users
|
||||
*
|
||||
* @param array[] $userStruct User struct
|
||||
*
|
||||
* @return boolean Create state
|
||||
*
|
||||
* @throws AccessDeniedException
|
||||
* @throws RemoteException
|
||||
*/
|
||||
public function createUser($userStruct)
|
||||
{
|
||||
if (!auth_isadmin()) {
|
||||
throw new AccessDeniedException('Only admins are allowed to create users', 114);
|
||||
}
|
||||
|
||||
/** @var \dokuwiki\Extension\AuthPlugin $auth */
|
||||
global $auth;
|
||||
|
||||
if(!$auth->canDo('addUser')) {
|
||||
throw new AccessDeniedException(
|
||||
sprintf('Authentication backend %s can\'t do addUser', $auth->getPluginName()),
|
||||
114
|
||||
);
|
||||
}
|
||||
|
||||
$user = trim($auth->cleanUser($userStruct['user'] ?? ''));
|
||||
$password = $userStruct['password'] ?? '';
|
||||
$name = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $userStruct['name'] ?? ''));
|
||||
$mail = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $userStruct['mail'] ?? ''));
|
||||
$groups = $userStruct['groups'] ?? [];
|
||||
|
||||
$notify = (bool)$userStruct['notify'] ?? false;
|
||||
|
||||
if ($user === '') throw new RemoteException('empty or invalid user', 401);
|
||||
if ($name === '') throw new RemoteException('empty or invalid user name', 402);
|
||||
if (!mail_isvalid($mail)) throw new RemoteException('empty or invalid mail address', 403);
|
||||
|
||||
if(strlen($password) === 0) {
|
||||
$password = auth_pwgen($user);
|
||||
}
|
||||
|
||||
if (!is_array($groups) || count($groups) === 0) {
|
||||
$groups = null;
|
||||
}
|
||||
|
||||
$ok = $auth->triggerUserMod('create', array($user, $password, $name, $mail, $groups));
|
||||
|
||||
if ($ok !== false && $ok !== null) {
|
||||
$ok = true;
|
||||
}
|
||||
|
||||
if($ok) {
|
||||
if($notify) {
|
||||
auth_sendPassword($user, $password);
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove one or more users from the list of registered users
|
||||
*
|
||||
|
||||
88
dokuwiki/inc/Remote/IXR/Client.php
Normal file
88
dokuwiki/inc/Remote/IXR/Client.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\Remote\IXR;
|
||||
|
||||
use dokuwiki\HTTP\HTTPClient;
|
||||
use IXR\Message\Message;
|
||||
use IXR\Request\Request;
|
||||
|
||||
/**
|
||||
* This implements a XML-RPC client using our own HTTPClient
|
||||
*
|
||||
* Note: this now inherits from the IXR library's client and no longer from HTTPClient. Instead composition
|
||||
* is used to add the HTTP client.
|
||||
*/
|
||||
class Client extends \IXR\Client\Client
|
||||
{
|
||||
|
||||
/** @var HTTPClient */
|
||||
protected $httpClient;
|
||||
|
||||
/** @var string */
|
||||
protected $posturl = '';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function __construct($server, $path = false, $port = 80, $timeout = 15, $timeout_io = null)
|
||||
{
|
||||
parent::__construct($server, $path, $port, $timeout, $timeout_io);
|
||||
if (!$path) {
|
||||
// Assume we have been given an URL instead
|
||||
$this->posturl = $server;
|
||||
} else {
|
||||
$this->posturl = 'http://' . $server . ':' . $port . $path;
|
||||
}
|
||||
|
||||
$this->httpClient = new HTTPClient();
|
||||
$this->httpClient->timeout = $timeout;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function query()
|
||||
{
|
||||
$args = func_get_args();
|
||||
$method = array_shift($args);
|
||||
$request = new Request($method, $args);
|
||||
$length = $request->getLength();
|
||||
$xml = $request->getXml();
|
||||
|
||||
$this->headers['Content-Type'] = 'text/xml';
|
||||
$this->headers['Content-Length'] = $length;
|
||||
$this->httpClient->headers = array_merge($this->httpClient->headers, $this->headers);
|
||||
|
||||
if (!$this->httpClient->sendRequest($this->posturl, $xml, 'POST')) {
|
||||
$this->handleError(-32300, 'transport error - ' . $this->httpClient->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check HTTP Response code
|
||||
if ($this->httpClient->status < 200 || $this->httpClient->status > 206) {
|
||||
$this->handleError(-32300, 'transport error - HTTP status ' . $this->httpClient->status);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now parse what we've got back
|
||||
$this->message = new Message($this->httpClient->resp_body);
|
||||
if (!$this->message->parse()) {
|
||||
// XML error
|
||||
return $this->handleError(-32700, 'Parse error. Message not well formed');
|
||||
}
|
||||
|
||||
// Is the message a fault?
|
||||
if ($this->message->messageType == 'fault') {
|
||||
return $this->handleError($this->message->faultCode, $this->message->faultString);
|
||||
}
|
||||
|
||||
// Message must be OK
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct access to the underlying HTTP client if needed
|
||||
*
|
||||
* @return HTTPClient
|
||||
*/
|
||||
public function getHTTPClient()
|
||||
{
|
||||
return $this->httpClient;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,15 @@
|
||||
|
||||
namespace dokuwiki\Remote;
|
||||
|
||||
use IXR\DataType\Base64;
|
||||
use IXR\DataType\Date;
|
||||
use IXR\Exception\ServerException;
|
||||
use IXR\Server\Server;
|
||||
|
||||
/**
|
||||
* Contains needed wrapper functions and registers all available XMLRPC functions.
|
||||
*/
|
||||
class XmlRpcServer extends \IXR_Server
|
||||
class XmlRpcServer extends Server
|
||||
{
|
||||
protected $remote;
|
||||
|
||||
@@ -20,6 +25,20 @@ class XmlRpcServer extends \IXR_Server
|
||||
parent::__construct(false, false, $wait);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function serve($data = false)
|
||||
{
|
||||
global $conf;
|
||||
if (!$conf['remote']) {
|
||||
throw new ServerException("XML-RPC server not enabled.", -32605);
|
||||
}
|
||||
if (!empty($conf['remotecors'])) {
|
||||
header('Access-Control-Allow-Origin: ' . $conf['remotecors']);
|
||||
}
|
||||
|
||||
parent::serve($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -28,34 +47,34 @@ class XmlRpcServer extends \IXR_Server
|
||||
try {
|
||||
$result = $this->remote->call($methodname, $args);
|
||||
return $result;
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (AccessDeniedException $e) {
|
||||
} catch (AccessDeniedException $e) {
|
||||
if (!isset($_SERVER['REMOTE_USER'])) {
|
||||
http_status(401);
|
||||
return new \IXR_Error(-32603, "server error. not authorized to call method $methodname");
|
||||
return new ServerException("server error. not authorized to call method $methodname", -32603);
|
||||
} else {
|
||||
http_status(403);
|
||||
return new \IXR_Error(-32604, "server error. forbidden to call the method $methodname");
|
||||
return new ServerException("server error. forbidden to call the method $methodname", -32604);
|
||||
}
|
||||
} catch (RemoteException $e) {
|
||||
return new \IXR_Error($e->getCode(), $e->getMessage());
|
||||
return new ServerException($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $data iso date(yyyy[-]mm[-]dd[ hh:mm[:ss]]) or timestamp
|
||||
* @return \IXR_Date
|
||||
* @return Date
|
||||
*/
|
||||
public function toDate($data)
|
||||
{
|
||||
return new \IXR_Date($data);
|
||||
return new Date($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @return \IXR_Base64
|
||||
* @return Base64
|
||||
*/
|
||||
public function toFile($data)
|
||||
{
|
||||
return new \IXR_Base64($data);
|
||||
return new Base64($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,7 +927,7 @@ class Indexer {
|
||||
$status = true;
|
||||
$run = 0;
|
||||
$lock = $conf['lockdir'].'/_indexer.lock';
|
||||
while (!@mkdir($lock, $conf['dmode'])) {
|
||||
while (!@mkdir($lock)) {
|
||||
usleep(50);
|
||||
if(is_dir($lock) && time()-@filemtime($lock) > 60*5){
|
||||
// looks like a stale lock - remove it
|
||||
@@ -942,7 +942,7 @@ class Indexer {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!empty($conf['dperm'])) {
|
||||
if ($conf['dperm']) {
|
||||
chmod($lock, $conf['dperm']);
|
||||
}
|
||||
return $status;
|
||||
@@ -1186,8 +1186,10 @@ class Indexer {
|
||||
if ($tuple === '') continue;
|
||||
list($key, $cnt) = explode('*', $tuple);
|
||||
if (!$cnt) continue;
|
||||
$key = $keys[$key];
|
||||
if ($key === false || is_null($key)) continue;
|
||||
if (isset($keys[$key])) {
|
||||
$key = $keys[$key];
|
||||
if ($key === false || is_null($key)) continue;
|
||||
}
|
||||
$result[$key] = $cnt;
|
||||
}
|
||||
return $result;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace dokuwiki\Sitemap;
|
||||
|
||||
use dokuwiki\HTTP\DokuHTTPClient;
|
||||
use dokuwiki\Logger;
|
||||
|
||||
/**
|
||||
* A class for building sitemaps and pinging search engines with the sitemap URL.
|
||||
@@ -43,14 +44,14 @@ class Mapper {
|
||||
|
||||
if(@filesize($sitemap) &&
|
||||
@filemtime($sitemap) > (time()-($conf['sitemap']*86400))){ // 60*60*24=86400
|
||||
dbglog('Sitemapper::generate(): Sitemap up to date');
|
||||
Logger::debug('Sitemapper::generate(): Sitemap up to date');
|
||||
return false;
|
||||
}
|
||||
|
||||
dbglog("Sitemapper::generate(): using $sitemap");
|
||||
Logger::debug("Sitemapper::generate(): using $sitemap");
|
||||
|
||||
$pages = idx_get_indexer()->getPages();
|
||||
dbglog('Sitemapper::generate(): creating sitemap using '.count($pages).' pages');
|
||||
Logger::debug('Sitemapper::generate(): creating sitemap using '.count($pages).' pages');
|
||||
$items = array();
|
||||
|
||||
// build the sitemap items
|
||||
@@ -139,9 +140,9 @@ class Mapper {
|
||||
|
||||
$encoded_sitemap_url = urlencode(wl('', array('do' => 'sitemap'), true, '&'));
|
||||
$ping_urls = array(
|
||||
'google' => 'http://www.google.com/webmasters/sitemaps/ping?sitemap='.$encoded_sitemap_url,
|
||||
'google' => 'https://www.google.com/ping?sitemap='.$encoded_sitemap_url,
|
||||
'microsoft' => 'http://www.bing.com/webmaster/ping.aspx?siteMap='.$encoded_sitemap_url,
|
||||
'yandex' => 'http://blogs.yandex.ru/pings/?status=success&url='.$encoded_sitemap_url
|
||||
'yandex' => 'https://webmaster.yandex.com/ping?sitemap='.$encoded_sitemap_url
|
||||
);
|
||||
|
||||
$data = array('ping_urls' => $ping_urls,
|
||||
@@ -150,10 +151,11 @@ class Mapper {
|
||||
$event = new \dokuwiki\Extension\Event('SITEMAP_PING', $data);
|
||||
if ($event->advise_before(true)) {
|
||||
foreach ($data['ping_urls'] as $name => $url) {
|
||||
dbglog("Sitemapper::PingSearchEngines(): pinging $name");
|
||||
Logger::debug("Sitemapper::PingSearchEngines(): pinging $name");
|
||||
$resp = $http->get($url);
|
||||
if($http->error) dbglog("Sitemapper:pingSearchengines(): $http->error");
|
||||
dbglog('Sitemapper:pingSearchengines(): '.preg_replace('/[\n\r]/',' ',strip_tags($resp)));
|
||||
if($http->error) {
|
||||
Logger::debug("Sitemapper:pingSearchengines(): $http->error", $resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
$event->advise_after();
|
||||
|
||||
@@ -99,7 +99,7 @@ class StyleUtils
|
||||
if (file_exists($inifile)) {
|
||||
$config = parse_ini_file($inifile, true);
|
||||
|
||||
if (is_array($config['stylesheets'])) {
|
||||
if (isset($config['stylesheets']) && is_array($config['stylesheets'])) {
|
||||
foreach ($config['stylesheets'] as $inifile => $mode) {
|
||||
// validate and include style files
|
||||
$stylesheets = array_merge(
|
||||
@@ -110,7 +110,7 @@ class StyleUtils
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($config['replacements'])) {
|
||||
if (isset($config['replacements']) && is_array($config['replacements'])) {
|
||||
$replacements = array_replace(
|
||||
$replacements,
|
||||
$this->cssFixreplacementurls($config['replacements'], $webbase)
|
||||
|
||||
@@ -87,7 +87,8 @@ class BulkSubscriptionSender extends SubscriptionSender
|
||||
$n = 0;
|
||||
while (!is_null($rev) && $rev['date'] >= $lastupdate &&
|
||||
($INPUT->server->str('REMOTE_USER') === $rev['user'] ||
|
||||
$rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)) {
|
||||
$rev['type'] === DOKU_CHANGE_TYPE_MINOR_EDIT)
|
||||
) {
|
||||
$pagelog = new PageChangeLog($rev['id']);
|
||||
$rev = $pagelog->getRevisions($n++, 1);
|
||||
$rev = (count($rev) > 0) ? $rev[0] : null;
|
||||
@@ -152,11 +153,11 @@ class BulkSubscriptionSender extends SubscriptionSender
|
||||
}
|
||||
|
||||
// try creating the lock directory
|
||||
if (!@mkdir($lock, $conf['dmode'])) {
|
||||
if (!@mkdir($lock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($conf['dperm'])) {
|
||||
if ($conf['dperm']) {
|
||||
chmod($lock, $conf['dperm']);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace dokuwiki;
|
||||
use dokuwiki\Extension\Event;
|
||||
use dokuwiki\Sitemap\Mapper;
|
||||
use dokuwiki\Subscriptions\BulkSubscriptionSender;
|
||||
use dokuwiki\ChangeLog\ChangeLog;
|
||||
|
||||
/**
|
||||
* Class TaskRunner
|
||||
@@ -120,7 +121,7 @@ class TaskRunner
|
||||
$out_lines = [];
|
||||
$old_lines = [];
|
||||
for ($i = 0; $i < count($lines); $i++) {
|
||||
$log = parseChangelogLine($lines[$i]);
|
||||
$log = ChangeLog::parseLogLine($lines[$i]);
|
||||
if ($log === false) {
|
||||
continue; // discard junk
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user