contactedit-actions-button: move repair back to tabbar
This commit is contained in:
commit
0db83b9586
66
boot.php
66
boot.php
|
@ -17,6 +17,8 @@
|
|||
* easily as email does today.
|
||||
*/
|
||||
|
||||
require_once('include/autoloader.php');
|
||||
|
||||
require_once('include/config.php');
|
||||
require_once('include/network.php');
|
||||
require_once('include/plugin.php');
|
||||
|
@ -588,15 +590,6 @@ class App {
|
|||
if(x($_SERVER,'SERVER_NAME')) {
|
||||
$this->hostname = $_SERVER['SERVER_NAME'];
|
||||
|
||||
// See bug 437 - this didn't work so disabling it
|
||||
//if(stristr($this->hostname,'xn--')) {
|
||||
// PHP or webserver may have converted idn to punycode, so
|
||||
// convert punycode back to utf-8
|
||||
// require_once('library/simplepie/idn/idna_convert.class.php');
|
||||
// $x = new idna_convert();
|
||||
// $this->hostname = $x->decode($_SERVER['SERVER_NAME']);
|
||||
//}
|
||||
|
||||
if(x($_SERVER,'SERVER_PORT') && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443)
|
||||
$this->hostname .= ':' . $_SERVER['SERVER_PORT'];
|
||||
/*
|
||||
|
@ -862,11 +855,11 @@ class App {
|
|||
|
||||
$shortcut_icon = get_config("system", "shortcut_icon");
|
||||
if ($shortcut_icon == "")
|
||||
$shortcut_icon = $this->get_baseurl()."/images/friendica-32.png";
|
||||
$shortcut_icon = "images/friendica-32.png";
|
||||
|
||||
$touch_icon = get_config("system", "touch_icon");
|
||||
if ($touch_icon == "")
|
||||
$touch_icon = $this->get_baseurl()."/images/friendica-128.png";
|
||||
$touch_icon = "images/friendica-128.png";
|
||||
|
||||
$tpl = get_markup_template('head.tpl');
|
||||
$this->page['htmlhead'] = replace_macros($tpl,array(
|
||||
|
@ -945,6 +938,25 @@ class App {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Removes the baseurl from an url. This avoids some mixed content problems.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string The cleaned url
|
||||
*/
|
||||
function remove_baseurl($url){
|
||||
|
||||
// Is the function called statically?
|
||||
if (!is_object($this))
|
||||
return(self::$a->remove_baseurl($url));
|
||||
|
||||
$url = normalise_link($url);
|
||||
$base = normalise_link($this->get_baseurl());
|
||||
$url = str_replace($base."/", "", $url);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register template engine class
|
||||
*
|
||||
|
@ -1037,19 +1049,29 @@ class App {
|
|||
$this->performance[$value] += (float)$duration;
|
||||
$this->performance["marktime"] += (float)$duration;
|
||||
|
||||
// Trace the different functions with their timestamps
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
||||
$callstack = $this->callstack();
|
||||
|
||||
$this->callstack[$value][$callstack] += (float)$duration;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a string with a callstack. Can be used for logging.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function callstack() {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 6);
|
||||
|
||||
// We remove the first two items from the list since they contain data that we don't need.
|
||||
array_shift($trace);
|
||||
array_shift($trace);
|
||||
|
||||
$function = array();
|
||||
$callstack = array();
|
||||
foreach ($trace AS $func)
|
||||
$function[] = $func["function"];
|
||||
|
||||
$function = implode(", ", $function);
|
||||
|
||||
$this->callstack[$value][$function] += (float)$duration;
|
||||
$callstack[] = $func["function"];
|
||||
|
||||
return implode(", ", $callstack);
|
||||
}
|
||||
|
||||
function mark_timestamp($mark) {
|
||||
|
@ -1416,7 +1438,7 @@ function login($register = false, $hiddens=false) {
|
|||
|
||||
$noid = get_config('system','no_openid');
|
||||
|
||||
$dest_url = $a->get_baseurl(true) . '/' . $a->query_string;
|
||||
$dest_url = $a->query_string;
|
||||
|
||||
if(local_user()) {
|
||||
$tpl = get_markup_template("logout.tpl");
|
||||
|
@ -1735,9 +1757,9 @@ function current_theme_url() {
|
|||
|
||||
$opts = (($a->profile_uid) ? '?f=&puid=' . $a->profile_uid : '');
|
||||
if (file_exists('view/theme/' . $t . '/style.php'))
|
||||
return($a->get_baseurl() . '/view/theme/' . $t . '/style.pcss' . $opts);
|
||||
return('view/theme/'.$t.'/style.pcss'.$opts);
|
||||
|
||||
return($a->get_baseurl() . '/view/theme/' . $t . '/style.css');
|
||||
return('view/theme/'.$t.'/style.css');
|
||||
}
|
||||
|
||||
function feed_birthday($uid,$tz) {
|
||||
|
|
18
database.sql
18
database.sql
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 3.5-dev (Asparagus)
|
||||
-- DB_UPDATE_VERSION 1193
|
||||
-- DB_UPDATE_VERSION 1194
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
@ -119,6 +119,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`keywords` text NOT NULL,
|
||||
`gender` varchar(32) NOT NULL DEFAULT '',
|
||||
`attag` varchar(255) NOT NULL DEFAULT '',
|
||||
`avatar` varchar(255) NOT NULL DEFAULT '',
|
||||
`photo` text NOT NULL,
|
||||
`thumb` text NOT NULL,
|
||||
`micro` text NOT NULL,
|
||||
|
@ -411,21 +412,6 @@ CREATE TABLE IF NOT EXISTS `gserver` (
|
|||
INDEX `nurl` (`nurl`)
|
||||
) DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
-- TABLE guid
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `guid` (
|
||||
`id` int(10) unsigned NOT NULL auto_increment,
|
||||
`guid` varchar(255) NOT NULL DEFAULT '',
|
||||
`plink` varchar(255) NOT NULL DEFAULT '',
|
||||
`uri` varchar(255) NOT NULL DEFAULT '',
|
||||
`network` varchar(32) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `guid` (`guid`),
|
||||
INDEX `plink` (`plink`),
|
||||
INDEX `uri` (`uri`)
|
||||
) DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
-- TABLE hook
|
||||
--
|
||||
|
|
|
@ -37,6 +37,7 @@ General
|
|||
* o: Profile
|
||||
* t: Contacts
|
||||
* d: Common friends
|
||||
* r: Advanced
|
||||
|
||||
/message
|
||||
--------
|
||||
|
|
|
@ -47,8 +47,10 @@ Friendica Documentation and Resources
|
|||
* [Theme Development](help/themes)
|
||||
* [Smarty 3 Templates](help/smarty3-templates)
|
||||
* [Database schema documantation](help/database)
|
||||
* [Class Autoloading](help/autoloader)
|
||||
* [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
|
||||
|
||||
|
||||
**External Resources**
|
||||
|
||||
* [Main Website](http://friendica.com)
|
||||
|
|
436
doc/api.md
436
doc/api.md
|
@ -7,6 +7,21 @@ Please refer to the linked documentation for further information.
|
|||
## Implemented API calls
|
||||
|
||||
### General
|
||||
#### HTTP Method
|
||||
|
||||
API endpoints can restrict the method used to request them.
|
||||
Using an invalid method results in HTTP error 405 "Method Not Allowed".
|
||||
|
||||
In this document, the required method is listed after the endpoint name. "*" means every method can be used.
|
||||
|
||||
#### Auth
|
||||
|
||||
Friendica supports basic http auth and OAuth 1 to authenticate the user to the api.
|
||||
|
||||
OAuth settings can be added by the user in web UI under /settings/oauth/
|
||||
|
||||
In this document, endpoints which requires auth are marked with "AUTH" after endpoint name
|
||||
|
||||
#### Unsupported parameters
|
||||
* cursor: Not implemented in GNU Social
|
||||
* trim_user: Not implemented in GNU Social
|
||||
|
@ -38,9 +53,9 @@ Error body is
|
|||
json:
|
||||
```
|
||||
{
|
||||
"error": "Specific error message",
|
||||
"request": "API path requested",
|
||||
"code": "HTTP error code"
|
||||
"error": "Specific error message",
|
||||
"request": "API path requested",
|
||||
"code": "HTTP error code"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -54,19 +69,20 @@ xml:
|
|||
```
|
||||
|
||||
---
|
||||
### account/rate_limit_status
|
||||
### account/rate_limit_status (*; AUTH)
|
||||
|
||||
---
|
||||
### account/verify_credentials
|
||||
### account/verify_credentials (*; AUTH)
|
||||
#### Parameters
|
||||
|
||||
* skip_status: Don't show the "status" field. (Default: false)
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### conversation/show
|
||||
### conversation/show (*; AUTH)
|
||||
Unofficial Twitter command. It shows all direct answers (excluding the original post) to a given id.
|
||||
|
||||
#### Parameters
|
||||
#### Parameter
|
||||
* id: id of the post
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -80,7 +96,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
|
|||
* contributor_details
|
||||
|
||||
---
|
||||
### direct_messages
|
||||
### direct_messages (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -93,7 +109,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
|
|||
* skip_status
|
||||
|
||||
---
|
||||
### direct_messages/all
|
||||
### direct_messages/all (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -102,7 +118,7 @@ Unofficial Twitter command. It shows all direct answers (excluding the original
|
|||
* getText: Defines the format of the status field. Can be "html" or "plain"
|
||||
|
||||
---
|
||||
### direct_messages/conversation
|
||||
### direct_messages/conversation (*; AUTH)
|
||||
Shows all direct messages of a conversation
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
|
@ -113,7 +129,7 @@ Shows all direct messages of a conversation
|
|||
* uri: URI of the conversation
|
||||
|
||||
---
|
||||
### direct_messages/new
|
||||
### direct_messages/new (POST,PUT; AUTH)
|
||||
#### Parameters
|
||||
* user_id: id of the user
|
||||
* screen_name: screen name (for technical reasons, this value is not unique!)
|
||||
|
@ -122,7 +138,7 @@ Shows all direct messages of a conversation
|
|||
* title: Title of the direct message
|
||||
|
||||
---
|
||||
### direct_messages/sent
|
||||
### direct_messages/sent (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -132,7 +148,7 @@ Shows all direct messages of a conversation
|
|||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### favorites
|
||||
### favorites (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -144,22 +160,23 @@ Shows all direct messages of a conversation
|
|||
* user_id
|
||||
* screen_name
|
||||
|
||||
Favorites aren't displayed to other users, so "user_id" and "screen_name". So setting this value will result in an empty array.
|
||||
Favorites aren't displayed to other users, so "user_id" and "screen_name" are unsupported.
|
||||
Set this values will result in an empty array.
|
||||
|
||||
---
|
||||
### favorites/create
|
||||
### favorites/create (POST,PUT; AUTH)
|
||||
#### Parameters
|
||||
* id
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### favorites/destroy
|
||||
### favorites/destroy (POST,DELETE; AUTH)
|
||||
#### Parameters
|
||||
* id
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### followers/ids
|
||||
### followers/ids (*; AUTH)
|
||||
#### Parameters
|
||||
* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false)
|
||||
|
||||
|
@ -171,139 +188,7 @@ Favorites aren't displayed to other users, so "user_id" and "screen_name". So se
|
|||
Friendica doesn't allow showing followers of other users.
|
||||
|
||||
---
|
||||
### friendica/activity/<verb>
|
||||
#### parameters
|
||||
* id: item id
|
||||
|
||||
Add or remove an activity from an item.
|
||||
'verb' can be one of:
|
||||
- like
|
||||
- dislike
|
||||
- attendyes
|
||||
- attendno
|
||||
- attendmaybe
|
||||
|
||||
To remove an activity, prepend the verb with "un", eg. "unlike" or "undislike"
|
||||
Attend verbs disable eachother: that means that if "attendyes" was added to an item, adding "attendno" remove previous "attendyes".
|
||||
Attend verbs should be used only with event-related items (there is no check at the moment)
|
||||
|
||||
#### Return values
|
||||
|
||||
On success:
|
||||
json
|
||||
```"ok"```
|
||||
|
||||
xml
|
||||
```<ok>true</ok>```
|
||||
|
||||
On error:
|
||||
HTTP 400 BadRequest
|
||||
|
||||
---
|
||||
### friendica/photo
|
||||
#### Parameters
|
||||
* photo_id: Resource id of a photo.
|
||||
* scale: (optional) scale value of the photo
|
||||
|
||||
Returns data of a picture with the given resource.
|
||||
If 'scale' isn't provided, returned data include full url to each scale of the photo.
|
||||
If 'scale' is set, returned data include image data base64 encoded.
|
||||
|
||||
possibile scale value are:
|
||||
0: original or max size by server settings
|
||||
1: image with or height at <= 640
|
||||
2: image with or height at <= 320
|
||||
3: thumbnail 160x160
|
||||
|
||||
4: Profile image at 175x175
|
||||
5: Profile image at 80x80
|
||||
6: Profile image at 48x48
|
||||
|
||||
An image used as profile image has only scale 4-6, other images only 0-3
|
||||
|
||||
#### Return values
|
||||
|
||||
json
|
||||
```
|
||||
{
|
||||
"id": "photo id"
|
||||
"created": "date(YYYY-MM-GG HH:MM:SS)",
|
||||
"edited": "date(YYYY-MM-GG HH:MM:SS)",
|
||||
"title": "photo title",
|
||||
"desc": "photo description",
|
||||
"album": "album name",
|
||||
"filename": "original file name",
|
||||
"type": "mime type",
|
||||
"height": "number",
|
||||
"width": "number",
|
||||
"profile": "1 if is profile photo",
|
||||
"link": {
|
||||
"<scale>": "url to image"
|
||||
...
|
||||
},
|
||||
// if 'scale' is set
|
||||
"datasize": "size in byte",
|
||||
"data": "base64 encoded image data"
|
||||
}
|
||||
```
|
||||
|
||||
xml
|
||||
```
|
||||
<photo>
|
||||
<id>photo id</id>
|
||||
<created>date(YYYY-MM-GG HH:MM:SS)</created>
|
||||
<edited>date(YYYY-MM-GG HH:MM:SS)</edited>
|
||||
<title>photo title</title>
|
||||
<desc>photo description</desc>
|
||||
<album>album name</album>
|
||||
<filename>original file name</filename>
|
||||
<type>mime type</type>
|
||||
<height>number</height>
|
||||
<width>number</width>
|
||||
<profile>1 if is profile photo</profile>
|
||||
<links type="array">
|
||||
<link type="mime type" scale="scale number" href="image url"/>
|
||||
...
|
||||
</links>
|
||||
</photo>
|
||||
```
|
||||
|
||||
---
|
||||
### friendica/photos/list
|
||||
|
||||
Returns a list of all photo resources of the logged in user.
|
||||
|
||||
#### Return values
|
||||
|
||||
json
|
||||
```
|
||||
[
|
||||
{
|
||||
id: "resource_id",
|
||||
album: "album name",
|
||||
filename: "original file name",
|
||||
type: "image mime type",
|
||||
thumb: "url to thumb sized image"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
xml
|
||||
```
|
||||
<photos type="array">
|
||||
<photo id="resource_id"
|
||||
album="album name"
|
||||
filename="original file name"
|
||||
type="image mime type">
|
||||
"url to thumb sized image"
|
||||
</photo>
|
||||
...
|
||||
</photos>
|
||||
```
|
||||
|
||||
---
|
||||
### friends/ids
|
||||
### friends/ids (*; AUTH)
|
||||
#### Parameters
|
||||
* stringify_ids: Should the id numbers be sent as text (true) or number (false)? (default: false)
|
||||
|
||||
|
@ -315,15 +200,15 @@ xml
|
|||
Friendica doesn't allow showing friends of other users.
|
||||
|
||||
---
|
||||
### help/test
|
||||
### help/test (*)
|
||||
|
||||
---
|
||||
### media/upload
|
||||
### media/upload (POST,PUT; AUTH)
|
||||
#### Parameters
|
||||
* media: image data
|
||||
|
||||
---
|
||||
### oauth/request_token
|
||||
### oauth/request_token (*)
|
||||
#### Parameters
|
||||
* oauth_callback
|
||||
|
||||
|
@ -331,7 +216,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* x_auth_access_type
|
||||
|
||||
---
|
||||
### oauth/access_token
|
||||
### oauth/access_token (*)
|
||||
#### Parameters
|
||||
* oauth_verifier
|
||||
|
||||
|
@ -341,7 +226,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* x_auth_mode
|
||||
|
||||
---
|
||||
### statuses/destroy
|
||||
### statuses/destroy (POST,DELETE; AUTH)
|
||||
#### Parameters
|
||||
* id: message number
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
@ -350,15 +235,21 @@ Friendica doesn't allow showing friends of other users.
|
|||
* trim_user
|
||||
|
||||
---
|
||||
### statuses/followers
|
||||
### statuses/followers (*; AUTH)
|
||||
|
||||
#### Parameters
|
||||
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### statuses/friends
|
||||
### statuses/friends (*; AUTH)
|
||||
|
||||
#### Parameters
|
||||
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
---
|
||||
### statuses/friends_timeline
|
||||
### statuses/friends_timeline (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -374,7 +265,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* contributor_details
|
||||
|
||||
---
|
||||
### statuses/home_timeline
|
||||
### statuses/home_timeline (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -390,7 +281,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* contributor_details
|
||||
|
||||
---
|
||||
### statuses/mentions
|
||||
### statuses/mentions (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -404,7 +295,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* contributor_details
|
||||
|
||||
---
|
||||
### statuses/public_timeline
|
||||
### statuses/public_timeline (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -418,7 +309,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* trim_user
|
||||
|
||||
---
|
||||
### statuses/replies
|
||||
### statuses/replies (*; AUTH)
|
||||
#### Parameters
|
||||
* count: Items per page (default: 20)
|
||||
* page: page number
|
||||
|
@ -432,7 +323,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* contributor_details
|
||||
|
||||
---
|
||||
### statuses/retweet
|
||||
### statuses/retweet (POST,PUT; AUTH)
|
||||
#### Parameters
|
||||
* id: message number
|
||||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
@ -441,7 +332,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* trim_user
|
||||
|
||||
---
|
||||
### statuses/show
|
||||
### statuses/show (*; AUTH)
|
||||
#### Parameters
|
||||
* id: message number
|
||||
* conversation: if set to "1" show all messages of the conversation with the given id
|
||||
|
@ -476,7 +367,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
* display_coordinates
|
||||
|
||||
---
|
||||
### statuses/user_timeline
|
||||
### statuses/user_timeline (*; AUTH)
|
||||
#### Parameters
|
||||
* user_id: id of the user
|
||||
* screen_name: screen name (for technical reasons, this value is not unique!)
|
||||
|
@ -489,15 +380,16 @@ Friendica doesn't allow showing friends of other users.
|
|||
* include_entities: "true" shows entities for pictures and links (Default: false)
|
||||
|
||||
#### Unsupported parameters
|
||||
|
||||
* include_rts
|
||||
* trim_user
|
||||
* contributor_details
|
||||
|
||||
---
|
||||
### statusnet/config
|
||||
### statusnet/config (*)
|
||||
|
||||
---
|
||||
### statusnet/version
|
||||
### statusnet/version (*)
|
||||
|
||||
#### Unsupported parameters
|
||||
* user_id
|
||||
|
@ -507,7 +399,7 @@ Friendica doesn't allow showing friends of other users.
|
|||
Friendica doesn't allow showing followers of other users.
|
||||
|
||||
---
|
||||
### users/search
|
||||
### users/search (*)
|
||||
#### Parameters
|
||||
* q: name of the user
|
||||
|
||||
|
@ -517,7 +409,7 @@ Friendica doesn't allow showing followers of other users.
|
|||
* include_entities
|
||||
|
||||
---
|
||||
### users/show
|
||||
### users/show (*)
|
||||
#### Parameters
|
||||
* user_id: id of the user
|
||||
* screen_name: screen name (for technical reasons, this value is not unique!)
|
||||
|
@ -533,8 +425,39 @@ Friendica doesn't allow showing friends of other users.
|
|||
|
||||
## Implemented API calls (not compatible with other APIs)
|
||||
|
||||
|
||||
---
|
||||
### friendica/group_show
|
||||
### friendica/activity/<verb>
|
||||
#### parameters
|
||||
* id: item id
|
||||
|
||||
Add or remove an activity from an item.
|
||||
'verb' can be one of:
|
||||
|
||||
- like
|
||||
- dislike
|
||||
- attendyes
|
||||
- attendno
|
||||
- attendmaybe
|
||||
|
||||
To remove an activity, prepend the verb with "un", eg. "unlike" or "undislike"
|
||||
Attend verbs disable eachother: that means that if "attendyes" was added to an item, adding "attendno" remove previous "attendyes".
|
||||
Attend verbs should be used only with event-related items (there is no check at the moment)
|
||||
|
||||
#### Return values
|
||||
|
||||
On success:
|
||||
json
|
||||
```"ok"```
|
||||
|
||||
xml
|
||||
```<ok>true</ok>```
|
||||
|
||||
On error:
|
||||
HTTP 400 BadRequest
|
||||
|
||||
---
|
||||
### friendica/group_show (*; AUTH)
|
||||
Return all or a specified group of the user with the containing contacts as array.
|
||||
|
||||
#### Parameters
|
||||
|
@ -542,22 +465,23 @@ Return all or a specified group of the user with the containing contacts as arra
|
|||
|
||||
#### Return values
|
||||
Array of:
|
||||
|
||||
* name: name of the group
|
||||
* gid: id of the group
|
||||
* user: array of group members (return from api_get_user() function for each member)
|
||||
|
||||
|
||||
---
|
||||
### friendica/group_delete
|
||||
### friendica/group_delete (POST,DELETE; AUTH)
|
||||
delete the specified group of contacts; API call need to include the correct gid AND name of the group to be deleted.
|
||||
|
||||
---
|
||||
### Parameters
|
||||
#### Parameters
|
||||
* gid: id of the group to be deleted
|
||||
* name: name of the group to be deleted
|
||||
|
||||
#### Return values
|
||||
Array of:
|
||||
|
||||
* success: true if successfully deleted
|
||||
* gid: gid of the deleted group
|
||||
* name: name of the deleted group
|
||||
|
@ -566,19 +490,22 @@ Array of:
|
|||
|
||||
|
||||
---
|
||||
### friendica/group_create
|
||||
### friendica/group_create (POST,PUT; AUTH)
|
||||
Create the group with the posted array of contacts as members.
|
||||
|
||||
#### Parameters
|
||||
* name: name of the group to be created
|
||||
|
||||
#### POST data
|
||||
JSON data as Array like the result of „users/group_show“:
|
||||
JSON data as Array like the result of "users/group_show":
|
||||
|
||||
* gid
|
||||
* name
|
||||
* array of users
|
||||
|
||||
#### Return values
|
||||
Array of:
|
||||
|
||||
* success: true if successfully created or reactivated
|
||||
* gid: gid of the created group
|
||||
* name: name of the created group
|
||||
|
@ -587,26 +514,175 @@ Array of:
|
|||
|
||||
|
||||
---
|
||||
### friendica/group_update
|
||||
### friendica/group_update (POST)
|
||||
Update the group with the posted array of contacts as members (post all members of the group to the call; function will remove members not posted).
|
||||
|
||||
#### Parameters
|
||||
* gid: id of the group to be changed
|
||||
* name: name of the group to be changed
|
||||
|
||||
#### POST data
|
||||
JSON data as array like the result of „users/group_show“:
|
||||
|
||||
* gid
|
||||
* name
|
||||
* array of users
|
||||
|
||||
#### Return values
|
||||
Array of:
|
||||
|
||||
* success: true if successfully updated
|
||||
* gid: gid of the changed group
|
||||
* name: name of the changed group
|
||||
* status: „missing user“ | „ok“
|
||||
* wrong users: array of users, which were not available in the contact table
|
||||
|
||||
|
||||
|
||||
---
|
||||
### friendica/notifications (GET)
|
||||
Return last 50 notification for current user, ordered by date with unseen item on top
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Return values
|
||||
Array of:
|
||||
|
||||
* id: id of the note
|
||||
* type: type of notification as int (see NOTIFY_* constants in boot.php)
|
||||
* name: full name of the contact subject of the note
|
||||
* url: contact's profile url
|
||||
* photo: contact's profile photo
|
||||
* date: datetime string of the note
|
||||
* timestamp: timestamp of the node
|
||||
* date_rel: relative date of the note (eg. "1 hour ago")
|
||||
* msg: note message in bbcode
|
||||
* msg_html: note message in html
|
||||
* msg_plain: note message in plain text
|
||||
* link: link to note
|
||||
* seen: seen state: 0 or 1
|
||||
|
||||
|
||||
---
|
||||
### friendica/notifications/seen (POST)
|
||||
Set note as seen, returns item object if possible
|
||||
|
||||
#### Parameters
|
||||
id: id of the note to set seen
|
||||
|
||||
#### Return values
|
||||
If the note is linked to an item, the item is returned, just like one of the "statuses/*_timeline" api.
|
||||
|
||||
If the note is not linked to an item, a success status is returned:
|
||||
|
||||
* "success" (json) | "<status>success</status>" (xml)
|
||||
|
||||
|
||||
---
|
||||
### friendica/photo (*; AUTH)
|
||||
#### Parameters
|
||||
* photo_id: Resource id of a photo.
|
||||
* scale: (optional) scale value of the photo
|
||||
|
||||
Returns data of a picture with the given resource.
|
||||
If 'scale' isn't provided, returned data include full url to each scale of the photo.
|
||||
If 'scale' is set, returned data include image data base64 encoded.
|
||||
|
||||
possibile scale value are:
|
||||
|
||||
* 0: original or max size by server settings
|
||||
* 1: image with or height at <= 640
|
||||
* 2: image with or height at <= 320
|
||||
* 3: thumbnail 160x160
|
||||
* 4: Profile image at 175x175
|
||||
* 5: Profile image at 80x80
|
||||
* 6: Profile image at 48x48
|
||||
|
||||
An image used as profile image has only scale 4-6, other images only 0-3
|
||||
|
||||
#### Return values
|
||||
|
||||
json
|
||||
```
|
||||
{
|
||||
"id": "photo id"
|
||||
"created": "date(YYYY-MM-GG HH:MM:SS)",
|
||||
"edited": "date(YYYY-MM-GG HH:MM:SS)",
|
||||
"title": "photo title",
|
||||
"desc": "photo description",
|
||||
"album": "album name",
|
||||
"filename": "original file name",
|
||||
"type": "mime type",
|
||||
"height": "number",
|
||||
"width": "number",
|
||||
"profile": "1 if is profile photo",
|
||||
"link": {
|
||||
"<scale>": "url to image"
|
||||
...
|
||||
},
|
||||
// if 'scale' is set
|
||||
"datasize": "size in byte",
|
||||
"data": "base64 encoded image data"
|
||||
}
|
||||
```
|
||||
|
||||
xml
|
||||
```
|
||||
<photo>
|
||||
<id>photo id</id>
|
||||
<created>date(YYYY-MM-GG HH:MM:SS)</created>
|
||||
<edited>date(YYYY-MM-GG HH:MM:SS)</edited>
|
||||
<title>photo title</title>
|
||||
<desc>photo description</desc>
|
||||
<album>album name</album>
|
||||
<filename>original file name</filename>
|
||||
<type>mime type</type>
|
||||
<height>number</height>
|
||||
<width>number</width>
|
||||
<profile>1 if is profile photo</profile>
|
||||
<links type="array">
|
||||
<link type="mime type" scale="scale number" href="image url"/>
|
||||
...
|
||||
</links>
|
||||
</photo>
|
||||
```
|
||||
|
||||
---
|
||||
### friendica/photos/list (*; AUTH)
|
||||
|
||||
Returns a list of all photo resources of the logged in user.
|
||||
|
||||
#### Return values
|
||||
|
||||
json
|
||||
```
|
||||
[
|
||||
{
|
||||
id: "resource_id",
|
||||
album: "album name",
|
||||
filename: "original file name",
|
||||
type: "image mime type",
|
||||
thumb: "url to thumb sized image"
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
xml
|
||||
```
|
||||
<photos type="array">
|
||||
<photo id="resource_id"
|
||||
album="album name"
|
||||
filename="original file name"
|
||||
type="image mime type">
|
||||
"url to thumb sized image"
|
||||
</photo>
|
||||
...
|
||||
</photos>
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
## Not Implemented API calls
|
||||
The following API calls are implemented in GNU Social but not in Friendica: (incomplete)
|
||||
|
@ -702,13 +778,13 @@ The following API calls from the Twitter API aren't implemented neither in Frien
|
|||
### BASH / cURL
|
||||
Betamax has documentated some example API usage from a [bash script](https://en.wikipedia.org/wiki/Bash_(Unix_shell) employing [curl](https://en.wikipedia.org/wiki/CURL) (see [his posting](https://betamax65.de/display/betamax65/43539)).
|
||||
|
||||
/usr/bin/curl -u USER:PASS https://YOUR.FRIENDICA.TLD/api/statuses/update.xml -d source="some source id" -d status="the status you want to post"
|
||||
/usr/bin/curl -u USER:PASS https://YOUR.FRIENDICA.TLD/api/statuses/update.xml -d source="some source id" -d status="the status you want to post"
|
||||
|
||||
### Python
|
||||
The [RSStoFriedika](https://github.com/pafcu/RSStoFriendika) code can be used as an example of how to use the API with python. The lines for posting are located at [line 21](https://github.com/pafcu/RSStoFriendika/blob/master/RSStoFriendika.py#L21) and following.
|
||||
|
||||
def tweet(server, message, group_allow=None):
|
||||
url = server + '/api/statuses/update'
|
||||
urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True))
|
||||
def tweet(server, message, group_allow=None):
|
||||
url = server + '/api/statuses/update'
|
||||
urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True))
|
||||
|
||||
There is also a [module for python 3](https://bitbucket.org/tobiasd/python-friendica) for using the API.
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
Autoloader
|
||||
==========
|
||||
|
||||
* [Home](help)
|
||||
|
||||
There is some initial support to class autoloading in Friendica core.
|
||||
|
||||
The autoloader code is in `include/autoloader.php`.
|
||||
It's derived from composer autoloader code.
|
||||
|
||||
Namespaces and Classes are mapped to folders and files in `library/`,
|
||||
and the map must be updated by hand, because we don't use composer yet.
|
||||
The mapping is defined by files in `include/autoloader/` folder.
|
||||
|
||||
Currently, only HTMLPurifier library is loaded using autoloader.
|
||||
|
||||
|
||||
## A quick introdution to class autoloading
|
||||
|
||||
The autoloader it's a way for php to automagically include the file that define a class when the class is first used, without the need to use "require_once" every time.
|
||||
|
||||
Once is setup you don't have to use it in any way. You need a class? you use the class.
|
||||
|
||||
At his basic is a function passed to the "spl_autoload_register()" function, which receive as argument the class name the script want and is it job to include the correct php file where that class is defined.
|
||||
The best source for documentation is [php site](http://php.net/manual/en/language.oop5.autoload.php).
|
||||
|
||||
One example, based on fictional friendica code.
|
||||
|
||||
Let's say you have a php file in "include/" that define a very useful class:
|
||||
|
||||
```
|
||||
file: include/ItemsManager.php
|
||||
<?php
|
||||
namespace \Friendica;
|
||||
|
||||
class ItemsManager {
|
||||
public function getAll() { ... }
|
||||
public function getByID($id) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
The class "ItemsManager" has been declared in "Friendica" namespace.
|
||||
Namespaces are useful to keep things separated and avoid names clash (could be that a library you want to use defines a class named "ItemsManager", but as long as is in another namespace, you don't have any problem)
|
||||
|
||||
If we were using composer, we had configured it with path where to find the classes of "Friendica" namespace, and then the composer script will generate the autoloader machinery for us.
|
||||
As we don't use composer, we need check that the autoloader knows the Friendica namespace.
|
||||
So in "include/autoloader/autoload_psr4.php" there should be something like
|
||||
|
||||
```
|
||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
||||
$baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
"Friendica" => array($baseDir."/include");
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
That tells the autoloader code to look for files that defines classes in "Friendica" namespace under "include/" folder. (And btw, that's why the file has the same name as the class it defines.)
|
||||
|
||||
*note*: The structure of files in "include/autoloader/" has been copied from the code generated by composer, to ease the work of enable autoloader for external libraries under "library/"
|
||||
|
||||
Let's say now that you need to load some items in a view, maybe in a fictional "mod/network.php".
|
||||
Somewere at the start of the scripts, the autoloader was initialized. In Friendica is done at the top of "boot.php", with "require_once('include/autoloader.php');".
|
||||
|
||||
The code will be something like:
|
||||
|
||||
```
|
||||
file: mod/network.php
|
||||
<?php
|
||||
|
||||
function network_content(&$a) {
|
||||
$itemsmanager = new \Friendica\ItemsManager();
|
||||
$items = $itemsmanager->getAll();
|
||||
|
||||
// pass $items to template
|
||||
// return result
|
||||
}
|
||||
```
|
||||
|
||||
That's a quite simple example, but look: no "require()"!
|
||||
You need to use a class, you use the class and you don't need to do anything more.
|
||||
|
||||
Going further: now we have a bunch of "*Manager" classes that cause some code duplication, let's define a BaseManager class, where to move all code in common between all managers:
|
||||
|
||||
```
|
||||
file: include/BaseManager.php
|
||||
<?php
|
||||
namespace \Friendica;
|
||||
|
||||
class BaseManager {
|
||||
public function thatFunctionEveryManagerUses() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
and then let's change the ItemsManager class to use this code
|
||||
|
||||
```
|
||||
file: include/ItemsManager.php
|
||||
<?php
|
||||
namespace \Friendica;
|
||||
|
||||
class ItemsManager extends BaseManager {
|
||||
public function getAll() { ... }
|
||||
public function getByID($id) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
The autoloader don't mind what you need the class for. You need a class, you get the class.
|
||||
It works with the "BaseManager" example here, it works when we need to call static methods on a class:
|
||||
|
||||
```
|
||||
file: include/dfrn.php
|
||||
<?php
|
||||
namespace \Friendica;
|
||||
|
||||
class dfrn {
|
||||
public static function mail($item, $owner) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
file: mod/mail.php
|
||||
<?php
|
||||
|
||||
mail_post($a){
|
||||
...
|
||||
\Friendica\dfrn::mail($item, $owner);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
If your code is in same namespace as the class you need, you don't need to prepend it:
|
||||
|
||||
```
|
||||
file: include/delivery.php
|
||||
<?php
|
||||
|
||||
namespace \Friendica;
|
||||
|
||||
// this is the same content of current include/delivery.php,
|
||||
// but has been declared to be in "Friendica" namespace
|
||||
|
||||
[...]
|
||||
switch($contact['network']) {
|
||||
|
||||
case NETWORK_DFRN:
|
||||
if ($mail) {
|
||||
$item['body'] = ...
|
||||
$atom = dfrn::mail($item, $owner);
|
||||
} elseif ($fsuggest) {
|
||||
$atom = dfrn::fsuggest($item, $owner);
|
||||
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
|
||||
} elseif ($relocate)
|
||||
$atom = dfrn::relocate($owner, $uid);
|
||||
[...]
|
||||
```
|
||||
|
||||
This is real "include/delivery.php" unchanged, but as the code is declared to be in "Friendica" namespace, you don't need to write it when you need to use the "dfrn" class.
|
||||
But if you want to use classes from another library, you need to use the full namespace, e.g.
|
||||
|
||||
```
|
||||
<?php
|
||||
namespace \Frienidca;
|
||||
|
||||
class Diaspora {
|
||||
public function md2bbcode() {
|
||||
$html = \Michelf\MarkdownExtra::defaultTransform($text);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
if you use that class in many places of the code and you don't want to write the full path to the class everytime, you can use the "use" php keyword
|
||||
|
||||
```
|
||||
<?php
|
||||
namespace \Frienidca;
|
||||
|
||||
use \Michelf\MarkdownExtra;
|
||||
|
||||
class Diaspora {
|
||||
public function md2bbcode() {
|
||||
$html = MarkdownExtra::defaultTransform($text);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
|
||||
You can go more deep if you want to, like:
|
||||
|
||||
```
|
||||
<?php
|
||||
namespace \Friendica\Network;
|
||||
|
||||
class DFRN {
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
<?php
|
||||
namespace \Friendica\DBA;
|
||||
|
||||
class MySQL {
|
||||
}
|
||||
```
|
||||
|
||||
So you can think of namespaces as folders in a unix filesystem, with global scope as the root ("\").
|
||||
|
|
@ -27,7 +27,6 @@ Database Tables
|
|||
| [group](help/database/db_group) | privacy groups, group info |
|
||||
| [group_member](help/database/db_group_member) | privacy groups, member info |
|
||||
| [gserver](help/database/db_gserver) | |
|
||||
| [guid](help/database/db_guid) | |
|
||||
| [hook](help/database/db_hook) | plugin hook registry |
|
||||
| [intro](help/database/db_intro) | |
|
||||
| [item](help/database/db_item) | all posts |
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
Table guid
|
||||
==========
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
|---------|------------------|------------------|------|-----|---------|----------------|
|
||||
| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
||||
| guid | | varchar(255) | NO | MUL | | |
|
||||
| plink | | varchar(255) | NO | MUL | | |
|
||||
| uri | | varchar(255) | NO | MUL | | |
|
||||
| network | | varchar(32) | NO | | | |
|
||||
|
||||
Return to [database documentation](help/database)
|
|
@ -34,6 +34,7 @@ line to your .htconfig.php:
|
|||
* like_no_comment (Boolean) - Don't update the "commented" value of an item when it is liked.
|
||||
* local_block (Boolean) - Used in conjunction with "block_public".
|
||||
* local_search (Boolean) - Blocks the search for not logged in users to prevent crawlers from blocking your system.
|
||||
* max_connections - The poller process isn't started when 3/4 of the possible database connections are used. When the system can't detect the maximum numbers of connection then this value can be used.
|
||||
* max_contact_queue - Default value is 500.
|
||||
* max_batch_queue - Default value is 1000.
|
||||
* no_oembed (Boolean) - Don't use OEmbed to fetch more information about a link.
|
||||
|
|
|
@ -95,12 +95,12 @@ class ForumManager {
|
|||
$selected = (($cid == $contact['id']) ? ' forum-selected' : '');
|
||||
|
||||
$entry = array(
|
||||
'url' => z_root() . '/network?f=&cid=' . $contact['id'],
|
||||
'external_url' => z_root() . '/redir/' . $contact['id'],
|
||||
'url' => 'network?f=&cid=' . $contact['id'],
|
||||
'external_url' => 'redir/' . $contact['id'],
|
||||
'name' => $contact['name'],
|
||||
'cid' => $contact['id'],
|
||||
'selected' => $selected,
|
||||
'micro' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO),
|
||||
'micro' => App::remove_baseurl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)),
|
||||
'id' => ++$id,
|
||||
);
|
||||
$entries[] = $entry;
|
||||
|
@ -187,4 +187,4 @@ class ForumManager {
|
|||
return $r;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/NotificationsManager.php
|
||||
*/
|
||||
require_once('include/html2plain.php');
|
||||
require_once("include/datetime.php");
|
||||
require_once("include/bbcode.php");
|
||||
|
||||
/**
|
||||
* @brief Read and write notifications from/to database
|
||||
*/
|
||||
class NotificationsManager {
|
||||
private $a;
|
||||
|
||||
public function __construct() {
|
||||
$this->a = get_app();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set some extra note properties
|
||||
*
|
||||
* @param array $notes array of note arrays from db
|
||||
* @return array Copy of input array with added properties
|
||||
*
|
||||
* Set some extra properties to note array from db:
|
||||
* - timestamp as int in default TZ
|
||||
* - date_rel : relative date string
|
||||
* - msg_html: message as html string
|
||||
* - msg_plain: message as plain text string
|
||||
*/
|
||||
private function _set_extra($notes) {
|
||||
$rets = array();
|
||||
foreach($notes as $n) {
|
||||
$local_time = datetime_convert('UTC',date_default_timezone_get(),$n['date']);
|
||||
$n['timestamp'] = strtotime($local_time);
|
||||
$n['date_rel'] = relative_date($n['date']);
|
||||
$n['msg_html'] = bbcode($n['msg'], false, false, false, false);
|
||||
$n['msg_plain'] = explode("\n",trim(html2plain($n['msg_html'], 0)))[0];
|
||||
|
||||
$rets[] = $n;
|
||||
}
|
||||
return $rets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief get all notifications for local_user()
|
||||
*
|
||||
* @param array $filter optional Array "column name"=>value: filter query by columns values
|
||||
* @param string $order optional Space separated list of column to sort by. prepend name with "+" to sort ASC, "-" to sort DESC. Default to "-date"
|
||||
* @param string $limit optional Query limits
|
||||
*
|
||||
* @return array of results or false on errors
|
||||
*/
|
||||
public function getAll($filter = array(), $order="-date", $limit="") {
|
||||
$filter_str = array();
|
||||
$filter_sql = "";
|
||||
foreach($filter as $column => $value) {
|
||||
$filter_str[] = sprintf("`%s` = '%s'", $column, dbesc($value));
|
||||
}
|
||||
if (count($filter_str)>0) {
|
||||
$filter_sql = "AND ".implode(" AND ", $filter_str);
|
||||
}
|
||||
|
||||
$aOrder = explode(" ", $order);
|
||||
$asOrder = array();
|
||||
foreach($aOrder as $o) {
|
||||
$dir = "asc";
|
||||
if ($o[0]==="-") {
|
||||
$dir = "desc";
|
||||
$o = substr($o,1);
|
||||
}
|
||||
if ($o[0]==="+") {
|
||||
$dir = "asc";
|
||||
$o = substr($o,1);
|
||||
}
|
||||
$asOrder[] = "$o $dir";
|
||||
}
|
||||
$order_sql = implode(", ", $asOrder);
|
||||
|
||||
if ($limit!="") $limit = " LIMIT ".$limit;
|
||||
|
||||
$r = q("SELECT * FROM `notify` WHERE `uid` = %d $filter_sql ORDER BY $order_sql $limit",
|
||||
intval(local_user())
|
||||
);
|
||||
if ($r!==false && count($r)>0) return $this->_set_extra($r);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get one note for local_user() by $id value
|
||||
*
|
||||
* @param int $id
|
||||
* @return array note values or null if not found
|
||||
*/
|
||||
public function getByID($id) {
|
||||
$r = q("SELECT * FROM `notify` WHERE `id` = %d AND `uid` = %d LIMIT 1",
|
||||
intval($id),
|
||||
intval(local_user())
|
||||
);
|
||||
if($r!==false && count($r)>0) {
|
||||
return $this->_set_extra($r)[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set seen state of $note of local_user()
|
||||
*
|
||||
* @param array $note
|
||||
* @param bool $seen optional true or false, default true
|
||||
* @return bool true on success, false on errors
|
||||
*/
|
||||
public function setSeen($note, $seen = true) {
|
||||
return q("UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
|
||||
intval($seen),
|
||||
dbesc($note['link']),
|
||||
intval($note['parent']),
|
||||
dbesc($note['otype']),
|
||||
intval(local_user())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set seen state of all notifications of local_user()
|
||||
*
|
||||
* @param bool $seen optional true or false. default true
|
||||
* @return bool true on success, false on error
|
||||
*/
|
||||
public function setAllSeen($seen = true) {
|
||||
return q("UPDATE `notify` SET `seen` = %d WHERE `uid` = %d",
|
||||
intval($seen),
|
||||
intval(local_user())
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
require_once('library/HTML5/Parser.php');
|
||||
require_once('include/crypto.php');
|
||||
require_once('include/feed.php');
|
||||
|
||||
if(! function_exists('scrape_dfrn')) {
|
||||
function scrape_dfrn($url, $dont_probe = false) {
|
||||
|
@ -12,6 +13,20 @@ function scrape_dfrn($url, $dont_probe = false) {
|
|||
|
||||
logger('scrape_dfrn: url=' . $url);
|
||||
|
||||
// Try to fetch the data from noscrape. This is faster than parsing the HTML
|
||||
$noscrape = str_replace("/hcard/", "/noscrape/", $url);
|
||||
$noscrapejson = fetch_url($noscrape);
|
||||
$noscrapedata = array();
|
||||
if ($noscrapejson) {
|
||||
$noscrapedata = json_decode($noscrapejson, true);
|
||||
|
||||
if (is_array($noscrapedata)) {
|
||||
if ($noscrapedata["nick"] != "")
|
||||
return($noscrapedata);
|
||||
} else
|
||||
$noscrapedata = array();
|
||||
}
|
||||
|
||||
$s = fetch_url($url);
|
||||
|
||||
if(! $s)
|
||||
|
@ -91,8 +106,7 @@ function scrape_dfrn($url, $dont_probe = false) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
return array_merge($ret, $noscrapedata);
|
||||
}}
|
||||
|
||||
|
||||
|
@ -366,8 +380,6 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
$network = NETWORK_TWITTER;
|
||||
}
|
||||
|
||||
// Twitter is deactivated since twitter closed its old API
|
||||
//$twitter = ((strpos($url,'twitter.com') !== false) ? true : false);
|
||||
$lastfm = ((strpos($url,'last.fm/user') !== false) ? true : false);
|
||||
|
||||
$at_addr = ((strpos($url,'@') !== false) ? true : false);
|
||||
|
@ -604,21 +616,6 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
$vcard['nick'] = $addr_parts[0];
|
||||
}
|
||||
|
||||
/* if($twitter) {
|
||||
logger('twitter: setup');
|
||||
$tid = basename($url);
|
||||
$tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss';
|
||||
if(intval($tid))
|
||||
$poll = $tapi . '?user_id=' . $tid;
|
||||
else
|
||||
$poll = $tapi . '?screen_name=' . $tid;
|
||||
$profile = 'http://twitter.com/#!/' . $tid;
|
||||
//$vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid;
|
||||
$vcard['photo'] = 'https://api.twitter.com/1/users/profile_image?screen_name=' . $tid . '&size=bigger';
|
||||
$vcard['nick'] = $tid;
|
||||
$vcard['fn'] = $tid;
|
||||
} */
|
||||
|
||||
if($lastfm) {
|
||||
$profile = $url;
|
||||
$poll = str_replace(array('www.','last.fm/'),array('','ws.audioscrobbler.com/1.0/'),$url) . '/recenttracks.rss';
|
||||
|
@ -662,85 +659,34 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
|
||||
if(x($feedret,'photo') && (! x($vcard,'photo')))
|
||||
$vcard['photo'] = $feedret['photo'];
|
||||
require_once('library/simplepie/simplepie.inc');
|
||||
$feed = new SimplePie();
|
||||
|
||||
$cookiejar = tempnam(get_temppath(), 'cookiejar-scrape-feed-');
|
||||
$xml = fetch_url($poll, false, $redirects, 0, Null, $cookiejar);
|
||||
unlink($cookiejar);
|
||||
|
||||
logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA);
|
||||
$a = get_app();
|
||||
|
||||
logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA);
|
||||
|
||||
// Don't try and parse an empty string
|
||||
$feed->set_raw_data(($xml) ? $xml : '<?xml version="1.0" encoding="utf-8" ?><xml></xml>');
|
||||
|
||||
$feed->init();
|
||||
if($feed->error()) {
|
||||
logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error());
|
||||
if ($xml == "") {
|
||||
logger("scrape_feed: XML is empty for feed ".$poll);
|
||||
$network = NETWORK_PHANTOM;
|
||||
}
|
||||
} else {
|
||||
$data = feed_import($xml,$dummy1,$dummy2, $dummy3, true);
|
||||
|
||||
if(! x($vcard,'photo'))
|
||||
$vcard['photo'] = $feed->get_image_url();
|
||||
$author = $feed->get_author();
|
||||
if (!is_array($data)) {
|
||||
logger("scrape_feed: This doesn't seem to be a feed: ".$poll);
|
||||
$network = NETWORK_PHANTOM;
|
||||
} else {
|
||||
if (($vcard["photo"] == "") AND ($data["header"]["author-avatar"] != ""))
|
||||
$vcard["photo"] = $data["header"]["author-avatar"];
|
||||
|
||||
if($author) {
|
||||
$vcard['fn'] = unxmlify(trim($author->get_name()));
|
||||
if(! $vcard['fn'])
|
||||
$vcard['fn'] = trim(unxmlify($author->get_email()));
|
||||
if(strpos($vcard['fn'],'@') !== false)
|
||||
$vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
|
||||
if (($vcard["fn"] == "") AND ($data["header"]["author-name"] != ""))
|
||||
$vcard["fn"] = $data["header"]["author-name"];
|
||||
|
||||
$email = unxmlify($author->get_email());
|
||||
if(! $profile && $author->get_link())
|
||||
$profile = trim(unxmlify($author->get_link()));
|
||||
if(! $vcard['photo']) {
|
||||
$rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
|
||||
if($rawtags) {
|
||||
$elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
|
||||
if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
|
||||
$vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
|
||||
}
|
||||
}
|
||||
// Fetch fullname via poco:displayName
|
||||
$pocotags = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
|
||||
if ($pocotags) {
|
||||
$elems = $pocotags[0]['child']['http://portablecontacts.net/spec/1.0'];
|
||||
if (isset($elems["displayName"]))
|
||||
$vcard['fn'] = $elems["displayName"][0]["data"];
|
||||
if (isset($elems["preferredUsername"]))
|
||||
$vcard['nick'] = $elems["preferredUsername"][0]["data"];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$item = $feed->get_item(0);
|
||||
if($item) {
|
||||
$author = $item->get_author();
|
||||
if($author) {
|
||||
$vcard['fn'] = trim(unxmlify($author->get_name()));
|
||||
if(! $vcard['fn'])
|
||||
$vcard['fn'] = trim(unxmlify($author->get_email()));
|
||||
if(strpos($vcard['fn'],'@') !== false)
|
||||
$vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@'));
|
||||
$email = unxmlify($author->get_email());
|
||||
if(! $profile && $author->get_link())
|
||||
$profile = trim(unxmlify($author->get_link()));
|
||||
}
|
||||
if(! $vcard['photo']) {
|
||||
$rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail');
|
||||
if($rawmedia && $rawmedia[0]['attribs']['']['url'])
|
||||
$vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']);
|
||||
}
|
||||
if(! $vcard['photo']) {
|
||||
$rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
|
||||
if($rawtags) {
|
||||
$elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
|
||||
if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo'))
|
||||
$vcard['photo'] = $elems['link'][0]['attribs']['']['href'];
|
||||
}
|
||||
}
|
||||
if (($vcard["nick"] == "") AND ($data["header"]["author-nick"] != ""))
|
||||
$vcard["nick"] = $data["header"]["author-nick"];
|
||||
|
||||
if(!$profile AND ($data["header"]["author-link"] != "") AND !in_array($network, array("", NETWORK_FEED)))
|
||||
$profile = $data["header"]["author-link"];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,27 +729,9 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
}
|
||||
}
|
||||
|
||||
if((! $vcard['photo']) && strlen($email))
|
||||
$vcard['photo'] = avatar_img($email);
|
||||
if($poll === $profile)
|
||||
$lnk = $feed->get_permalink();
|
||||
if(isset($lnk) && strlen($lnk))
|
||||
$profile = $lnk;
|
||||
|
||||
if(! $network) {
|
||||
if(! $network)
|
||||
$network = NETWORK_FEED;
|
||||
// If it is a feed, don't take the author name as feed name
|
||||
unset($vcard['fn']);
|
||||
}
|
||||
if(! (x($vcard,'fn')))
|
||||
$vcard['fn'] = notags($feed->get_title());
|
||||
if(! (x($vcard,'fn')))
|
||||
$vcard['fn'] = notags($feed->get_description());
|
||||
|
||||
if(strpos($vcard['fn'],'Twitter / ') !== false) {
|
||||
$vcard['fn'] = substr($vcard['fn'],strpos($vcard['fn'],'/')+1);
|
||||
$vcard['fn'] = trim($vcard['fn']);
|
||||
}
|
||||
if(! x($vcard,'nick')) {
|
||||
$vcard['nick'] = strtolower(notags(unxmlify($vcard['fn'])));
|
||||
if(strpos($vcard['nick'],' '))
|
||||
|
@ -816,7 +744,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
|
||||
if(! x($vcard,'photo')) {
|
||||
$a = get_app();
|
||||
$vcard['photo'] = $a->get_baseurl() . '/images/person-175.jpg' ;
|
||||
$vcard['photo'] = App::get_baseurl() . '/images/person-175.jpg' ;
|
||||
}
|
||||
|
||||
if(! $profile)
|
||||
|
@ -828,18 +756,18 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
$vcard['fn'] = $url;
|
||||
|
||||
if (($notify != "") AND ($poll != "")) {
|
||||
$baseurl = matching(normalise_link($notify), normalise_link($poll));
|
||||
$baseurl = matching_url(normalise_link($notify), normalise_link($poll));
|
||||
|
||||
$baseurl2 = matching($baseurl, normalise_link($profile));
|
||||
$baseurl2 = matching_url($baseurl, normalise_link($profile));
|
||||
if ($baseurl2 != "")
|
||||
$baseurl = $baseurl2;
|
||||
}
|
||||
|
||||
if (($baseurl == "") AND ($notify != ""))
|
||||
$baseurl = matching(normalise_link($profile), normalise_link($notify));
|
||||
$baseurl = matching_url(normalise_link($profile), normalise_link($notify));
|
||||
|
||||
if (($baseurl == "") AND ($poll != ""))
|
||||
$baseurl = matching(normalise_link($profile), normalise_link($poll));
|
||||
$baseurl = matching_url(normalise_link($profile), normalise_link($poll));
|
||||
|
||||
$baseurl = rtrim($baseurl, "/");
|
||||
|
||||
|
@ -894,19 +822,56 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) {
|
|||
return $result;
|
||||
}
|
||||
|
||||
function matching($part1, $part2) {
|
||||
$len = min(strlen($part1), strlen($part2));
|
||||
/**
|
||||
* @brief Find the matching part between two url
|
||||
*
|
||||
* @param string $url1
|
||||
* @param string $url2
|
||||
* @return string The matching part
|
||||
*/
|
||||
function matching_url($url1, $url2) {
|
||||
|
||||
if (($url1 == "") OR ($url2 == ""))
|
||||
return "";
|
||||
|
||||
$url1 = normalise_link($url1);
|
||||
$url2 = normalise_link($url2);
|
||||
|
||||
$parts1 = parse_url($url1);
|
||||
$parts2 = parse_url($url2);
|
||||
|
||||
if (!isset($parts1["host"]) OR !isset($parts2["host"]))
|
||||
return "";
|
||||
|
||||
if ($parts1["scheme"] != $parts2["scheme"])
|
||||
return "";
|
||||
|
||||
if ($parts1["host"] != $parts2["host"])
|
||||
return "";
|
||||
|
||||
if ($parts1["port"] != $parts2["port"])
|
||||
return "";
|
||||
|
||||
$match = $parts1["scheme"]."://".$parts1["host"];
|
||||
|
||||
if ($parts1["port"])
|
||||
$match .= ":".$parts1["port"];
|
||||
|
||||
$pathparts1 = explode("/", $parts1["path"]);
|
||||
$pathparts2 = explode("/", $parts2["path"]);
|
||||
|
||||
$match = "";
|
||||
$matching = true;
|
||||
$i = 0;
|
||||
while (($i <= $len) AND $matching) {
|
||||
if (substr($part1, $i, 1) == substr($part2, $i, 1))
|
||||
$match .= substr($part1, $i, 1);
|
||||
else
|
||||
$matching = false;
|
||||
$path = "";
|
||||
do {
|
||||
$path1 = $pathparts1[$i];
|
||||
$path2 = $pathparts2[$i];
|
||||
|
||||
$i++;
|
||||
}
|
||||
return($match);
|
||||
if ($path1 == $path2)
|
||||
$path .= $path1."/";
|
||||
|
||||
} while (($path1 == $path2) AND ($i++ <= count($pathparts1)));
|
||||
|
||||
$match .= $path;
|
||||
|
||||
return normalise_link($match);
|
||||
}
|
||||
|
|
135
include/api.php
135
include/api.php
|
@ -23,6 +23,7 @@
|
|||
require_once('include/message.php');
|
||||
require_once('include/group.php');
|
||||
require_once('include/like.php');
|
||||
require_once('include/NotificationsManager.php');
|
||||
|
||||
|
||||
define('API_METHOD_ANY','*');
|
||||
|
@ -160,10 +161,7 @@
|
|||
if (!isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
|
||||
header('WWW-Authenticate: Basic realm="Friendica"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
die((api_error($a, 'json', "This api requires login")));
|
||||
|
||||
//die('This api requires login');
|
||||
throw new UnauthorizedException("This API requires login");
|
||||
}
|
||||
|
||||
$user = $_SERVER['PHP_AUTH_USER'];
|
||||
|
@ -215,8 +213,9 @@
|
|||
if((! $record) || (! count($record))) {
|
||||
logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
|
||||
header('WWW-Authenticate: Basic realm="Friendica"');
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
die('This api requires login');
|
||||
#header('HTTP/1.0 401 Unauthorized');
|
||||
#die('This api requires login');
|
||||
throw new UnauthorizedException("This API requires login");
|
||||
}
|
||||
|
||||
authenticate_success($record); $_SESSION["allow_api"] = true;
|
||||
|
@ -250,7 +249,7 @@
|
|||
*/
|
||||
function api_call(&$a){
|
||||
GLOBAL $API, $called_api;
|
||||
|
||||
|
||||
$type="json";
|
||||
if (strpos($a->query_string, ".xml")>0) $type="xml";
|
||||
if (strpos($a->query_string, ".json")>0) $type="json";
|
||||
|
@ -330,7 +329,8 @@
|
|||
*
|
||||
* @param Api $a
|
||||
* @param string $type Return type (xml, json, rss, as)
|
||||
* @param string $error Error message
|
||||
* @param HTTPException $error Error object
|
||||
* @return strin error message formatted as $type
|
||||
*/
|
||||
function api_error(&$a, $type, $e) {
|
||||
$error = ($e->getMessage()!==""?$e->getMessage():$e->httpdesc);
|
||||
|
@ -680,6 +680,34 @@
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief transform $data array in xml without a template
|
||||
*
|
||||
* @param array $data
|
||||
* @return string xml string
|
||||
*/
|
||||
function api_array_to_xml($data, $ename="") {
|
||||
$attrs="";
|
||||
$childs="";
|
||||
if (count($data)==1 && !is_array($data[0])) {
|
||||
$ename = array_keys($data)[0];
|
||||
$v = $data[$ename];
|
||||
return "<$ename>$v</$ename>";
|
||||
}
|
||||
foreach($data as $k=>$v) {
|
||||
$k=trim($k,'$');
|
||||
if (!is_array($v)) {
|
||||
$attrs .= sprintf('%s="%s" ', $k, $v);
|
||||
} else {
|
||||
if (is_numeric($k)) $k=trim($ename,'s');
|
||||
$childs.=api_array_to_xml($v, $k);
|
||||
}
|
||||
}
|
||||
$res = $childs;
|
||||
if ($ename!="") $res = "<$ename $attrs>$res</$ename>";
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* load api $templatename for $type and replace $data array
|
||||
*/
|
||||
|
@ -692,13 +720,17 @@
|
|||
case "rss":
|
||||
case "xml":
|
||||
$data = array_xmlify($data);
|
||||
$tpl = get_markup_template("api_".$templatename."_".$type.".tpl");
|
||||
if(! $tpl) {
|
||||
header ("Content-Type: text/xml");
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<status><error>not implemented</error></status>';
|
||||
killme();
|
||||
if ($templatename==="<auto>") {
|
||||
$ret = api_array_to_xml($data);
|
||||
} else {
|
||||
$tpl = get_markup_template("api_".$templatename."_".$type.".tpl");
|
||||
if(! $tpl) {
|
||||
header ("Content-Type: text/xml");
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n".'<status><error>not implemented</error></status>';
|
||||
killme();
|
||||
}
|
||||
$ret = replace_macros($tpl, $data);
|
||||
}
|
||||
$ret = replace_macros($tpl, $data);
|
||||
break;
|
||||
case "json":
|
||||
$ret = $data;
|
||||
|
@ -781,8 +813,6 @@
|
|||
|
||||
if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) {
|
||||
|
||||
require_once('library/HTMLPurifier.auto.php');
|
||||
|
||||
$txt = html2bb_video($txt);
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$config->set('Cache.DefinitionImpl', null);
|
||||
|
@ -822,9 +852,6 @@
|
|||
if(requestdata('htmlstatus')) {
|
||||
$txt = requestdata('htmlstatus');
|
||||
if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) {
|
||||
|
||||
require_once('library/HTMLPurifier.auto.php');
|
||||
|
||||
$txt = html2bb_video($txt);
|
||||
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
|
@ -875,7 +902,8 @@
|
|||
|
||||
if ($posts_day > $throttle_day) {
|
||||
logger('Daily posting limit reached for user '.api_user(), LOGGER_DEBUG);
|
||||
die(api_error($a, $type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day)));
|
||||
#die(api_error($a, $type, sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day)));
|
||||
throw new TooManyRequestsException(sprintf(t("Daily posting limit of %d posts reached. The post was rejected."), $throttle_day));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -894,7 +922,9 @@
|
|||
|
||||
if ($posts_week > $throttle_week) {
|
||||
logger('Weekly posting limit reached for user '.api_user(), LOGGER_DEBUG);
|
||||
die(api_error($a, $type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week)));
|
||||
#die(api_error($a, $type, sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week)));
|
||||
throw new TooManyRequestsException(sprintf(t("Weekly posting limit of %d posts reached. The post was rejected."), $throttle_week));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -913,7 +943,8 @@
|
|||
|
||||
if ($posts_month > $throttle_month) {
|
||||
logger('Monthly posting limit reached for user '.api_user(), LOGGER_DEBUG);
|
||||
die(api_error($a, $type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month)));
|
||||
#die(api_error($a, $type, sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month)));
|
||||
throw new TooManyRequestsException(sprintf(t("Monthly posting limit of %d posts reached. The post was rejected."), $throttle_month));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1781,7 +1812,7 @@
|
|||
$action_argv_id=2;
|
||||
if ($a->argv[1]=="1.1") $action_argv_id=3;
|
||||
|
||||
if ($a->argc<=$action_argv_id) die(api_error($a, $type, t("Invalid request.")));
|
||||
if ($a->argc<=$action_argv_id) throw new BadRequestException("Invalid request.");
|
||||
$action = str_replace(".".$type,"",$a->argv[$action_argv_id]);
|
||||
if ($a->argc==$action_argv_id+2) {
|
||||
$itemid = intval($a->argv[$action_argv_id+1]);
|
||||
|
@ -3386,6 +3417,64 @@
|
|||
api_register_func('api/friendica/activity/unattendno', 'api_friendica_activity', true, API_METHOD_POST);
|
||||
api_register_func('api/friendica/activity/unattendmaybe', 'api_friendica_activity', true, API_METHOD_POST);
|
||||
|
||||
/**
|
||||
* @brief Returns notifications
|
||||
*
|
||||
* @param App $a
|
||||
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
|
||||
* @return string
|
||||
*/
|
||||
function api_friendica_notification(&$a, $type) {
|
||||
if (api_user()===false) throw new ForbiddenException();
|
||||
if ($a->argc!==3) throw new BadRequestException("Invalid argument count");
|
||||
$nm = new NotificationsManager();
|
||||
|
||||
$notes = $nm->getAll(array(), "+seen -date", 50);
|
||||
return api_apply_template("<auto>", $type, array('$notes' => $notes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set notification as seen and returns associated item (if possible)
|
||||
*
|
||||
* POST request with 'id' param as notification id
|
||||
*
|
||||
* @param App $a
|
||||
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
|
||||
* @return string
|
||||
*/
|
||||
function api_friendica_notification_seen(&$a, $type){
|
||||
if (api_user()===false) throw new ForbiddenException();
|
||||
if ($a->argc!==4) throw new BadRequestException("Invalid argument count");
|
||||
|
||||
$id = (x($_REQUEST, 'id') ? intval($_REQUEST['id']) : 0);
|
||||
|
||||
$nm = new NotificationsManager();
|
||||
$note = $nm->getByID($id);
|
||||
if (is_null($note)) throw new BadRequestException("Invalid argument");
|
||||
|
||||
$nm->setSeen($note);
|
||||
if ($note['otype']=='item') {
|
||||
// would be really better with an ItemsManager and $im->getByID() :-P
|
||||
$r = q("SELECT * FROM `item` WHERE `id`=%d AND `uid`=%d",
|
||||
intval($note['iid']),
|
||||
intval(local_user())
|
||||
);
|
||||
if ($r!==false) {
|
||||
// we found the item, return it to the user
|
||||
$user_info = api_get_user($a);
|
||||
$ret = api_format_items($r,$user_info);
|
||||
$data = array('$statuses' => $ret);
|
||||
return api_apply_template("timeline", $type, $data);
|
||||
}
|
||||
// the item can't be found, but we set the note as seen, so we count this as a success
|
||||
}
|
||||
return api_apply_template('<auto>', $type, array('status' => "success"));
|
||||
}
|
||||
|
||||
api_register_func('api/friendica/notification/seen', 'api_friendica_notification_seen', true, API_METHOD_POST);
|
||||
api_register_func('api/friendica/notification', 'api_friendica_notification', true, API_METHOD_GET);
|
||||
|
||||
|
||||
/*
|
||||
To.Do:
|
||||
[pagename] => api/1.1/statuses/lookup.json
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/autoloader.php
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief composer-derived autoloader init
|
||||
**/
|
||||
class FriendicaAutoloaderInit
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/autoloader/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('FriendicaAutoloaderInit', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('FriendicaAutoloaderInit', 'loadClassLoader'));
|
||||
|
||||
// library
|
||||
$map = require __DIR__ . '/autoloader/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoloader/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoloader/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$includeFiles = require __DIR__ . '/autoloader/autoload_files.php';
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
friendicaRequire($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function friendicaRequire($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return FriendicaAutoloaderInit::getLoader();
|
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE.composer
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0 class loader
|
||||
*
|
||||
* See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if ($file === null && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if ($file === null) {
|
||||
// Remember that this class does not exist.
|
||||
return $this->classMap[$class] = false;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2015 Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the Software), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, andor sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
|
||||
);
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
|
@ -858,6 +858,8 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
|
|||
$Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text);
|
||||
$Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text);
|
||||
|
||||
// Remove the abstract element. It is a non visible element.
|
||||
$Text = remove_abstract($Text);
|
||||
|
||||
// Move all spaces out of the tags
|
||||
$Text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $Text);
|
||||
|
@ -1300,4 +1302,43 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
|
|||
|
||||
return trim($Text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the "abstract" element from the text
|
||||
*
|
||||
* @param string $text The text with BBCode
|
||||
* @return string The same text - but without "abstract" element
|
||||
*/
|
||||
function remove_abstract($text) {
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", '', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", '', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the value of the "abstract" element
|
||||
*
|
||||
* @param string $text The text that maybe contains the element
|
||||
* @param string $addon The addon for which the abstract is meant for
|
||||
* @return string The abstract
|
||||
*/
|
||||
function fetch_abstract($text, $addon = "") {
|
||||
$abstract = "";
|
||||
$abstracts = array();
|
||||
$addon = strtolower($addon);
|
||||
|
||||
if (preg_match_all("/\[abstract=(.*?)\](.*?)\[\/abstract\]/ism",$text, $results, PREG_SET_ORDER))
|
||||
foreach ($results AS $result)
|
||||
$abstracts[strtolower($result[1])] = $result[2];
|
||||
|
||||
if (isset($abstracts[$addon]))
|
||||
$abstract = $abstracts[$addon];
|
||||
|
||||
if ($abstract == "")
|
||||
if (preg_match("/\[abstract\](.*?)\[\/abstract\]/ism",$text, $result))
|
||||
$abstract = $result[1];
|
||||
|
||||
return $abstract;
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -99,8 +99,16 @@ function network_to_name($s, $profile = "") {
|
|||
|
||||
$networkname = str_replace($search,$replace,$s);
|
||||
|
||||
if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora_is_redmatrix($profile))
|
||||
$networkname = t("Redmatrix");
|
||||
if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora_is_redmatrix($profile)) {
|
||||
$networkname = t("Hubzilla/Redmatrix");
|
||||
|
||||
$r = q("SELECT `gserver`.`platform` FROM `gcontact`
|
||||
INNER JOIN `gserver` ON `gserver`.`nurl` = `gcontact`.`server_url`
|
||||
WHERE `gcontact`.`nurl` = '%s' AND `platform` != ''",
|
||||
dbesc(normalise_link($profile)));
|
||||
if ($r)
|
||||
$networkname = $r[0]["platform"];
|
||||
}
|
||||
|
||||
return $networkname;
|
||||
}
|
||||
|
|
|
@ -614,7 +614,7 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
|
|||
if(($normalised != 'mailbox') && (x($a->contacts[$normalised])))
|
||||
$profile_avatar = $a->contacts[$normalised]['thumb'];
|
||||
else
|
||||
$profile_avatar = ((strlen($item['author-avatar'])) ? $a->get_cached_avatar_image($item['author-avatar']) : $item['thumb']);
|
||||
$profile_avatar = $a->remove_baseurl(((strlen($item['author-avatar'])) ? $item['author-avatar'] : $item['thumb']));
|
||||
|
||||
$locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => '');
|
||||
call_hooks('render_location',$locate);
|
||||
|
@ -707,8 +707,8 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
|
|||
'like' => '',
|
||||
'dislike' => '',
|
||||
'comment' => '',
|
||||
//'conv' => (($preview) ? '' : array('href'=> $a->get_baseurl($ssl_state) . '/display/' . $nickname . '/' . $item['id'], 'title'=> t('View in context'))),
|
||||
'conv' => (($preview) ? '' : array('href'=> $a->get_baseurl($ssl_state) . '/display/'.$item['guid'], 'title'=> t('View in context'))),
|
||||
//'conv' => (($preview) ? '' : array('href'=> 'display/' . $nickname . '/' . $item['id'], 'title'=> t('View in context'))),
|
||||
'conv' => (($preview) ? '' : array('href'=> 'display/'.$item['guid'], 'title'=> t('View in context'))),
|
||||
'previewing' => $previewing,
|
||||
'wait' => t('Please wait'),
|
||||
'thread_level' => 1,
|
||||
|
@ -868,7 +868,7 @@ function item_photo_menu($item){
|
|||
$status_link = $profile_link . "?url=status";
|
||||
$photos_link = $profile_link . "?url=photos";
|
||||
$profile_link = $profile_link . "?url=profile";
|
||||
$pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
|
||||
$pm_url = 'message/new/' . $cid;
|
||||
$zurl = '';
|
||||
}
|
||||
else {
|
||||
|
@ -882,23 +882,23 @@ function item_photo_menu($item){
|
|||
$cid = $r[0]["id"];
|
||||
|
||||
if ($r[0]["network"] == NETWORK_DIASPORA)
|
||||
$pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
|
||||
$pm_url = 'message/new/' . $cid;
|
||||
|
||||
} else
|
||||
$cid = 0;
|
||||
}
|
||||
}
|
||||
if(($cid) && (! $item['self'])) {
|
||||
$poke_link = $a->get_baseurl($ssl_state) . '/poke/?f=&c=' . $cid;
|
||||
$contact_url = $a->get_baseurl($ssl_state) . '/contacts/' . $cid;
|
||||
$posts_link = $a->get_baseurl($ssl_state) . '/contacts/' . $cid . '/posts';
|
||||
$poke_link = 'poke/?f=&c=' . $cid;
|
||||
$contact_url = 'contacts/' . $cid;
|
||||
$posts_link = 'contacts/' . $cid . '/posts';
|
||||
|
||||
$clean_url = normalise_link($item['author-link']);
|
||||
|
||||
if((local_user()) && (local_user() == $item['uid'])) {
|
||||
if(isset($a->contacts) && x($a->contacts,$clean_url)) {
|
||||
if($a->contacts[$clean_url]['network'] === NETWORK_DIASPORA) {
|
||||
$pm_url = $a->get_baseurl($ssl_state) . '/message/new/' . $cid;
|
||||
$pm_url = 'message/new/' . $cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -921,7 +921,7 @@ function item_photo_menu($item){
|
|||
|
||||
if ((($cid == 0) OR ($a->contacts[$clean_url]['rel'] == CONTACT_IS_FOLLOWER)) AND
|
||||
in_array($item['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
|
||||
$menu[t("Connect/Follow")] = $a->get_baseurl($ssl_state)."/follow?url=".urlencode($item['author-link']);
|
||||
$menu[t("Connect/Follow")] = "follow?url=".urlencode($item['author-link']);
|
||||
} else
|
||||
$menu = array(t("View Profile") => $item['author-link']);
|
||||
|
||||
|
@ -980,7 +980,7 @@ function builtin_activity_puller($item, &$conv_responses) {
|
|||
if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) {
|
||||
$url = $item['author-link'];
|
||||
if((local_user()) && (local_user() == $item['uid']) && ($item['network'] === NETWORK_DFRN) && (! $item['self']) && (link_compare($item['author-link'],$item['url']))) {
|
||||
$url = z_root(true) . '/redir/' . $item['contact-id'];
|
||||
$url = 'redir/' . $item['contact-id'];
|
||||
$sparkle = ' class="sparkle" ';
|
||||
}
|
||||
else
|
||||
|
@ -1178,7 +1178,7 @@ function status_editor($a,$x, $notes_cid = 0, $popup=false) {
|
|||
|
||||
$o .= replace_macros($tpl,array(
|
||||
'$return_path' => $query_str,
|
||||
'$action' => $a->get_baseurl(true) . '/item',
|
||||
'$action' => 'item',
|
||||
'$share' => (x($x,'button') ? $x['button'] : t('Share')),
|
||||
'$upload' => t('Upload photo'),
|
||||
'$shortupload' => t('upload photo'),
|
||||
|
|
|
@ -748,21 +748,6 @@ function db_definition() {
|
|||
"nurl" => array("nurl"),
|
||||
)
|
||||
);
|
||||
$database["guid"] = array(
|
||||
"fields" => array(
|
||||
"id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
|
||||
"guid" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"plink" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"uri" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"network" => array("type" => "varchar(32)", "not null" => "1", "default" => ""),
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"guid" => array("guid"),
|
||||
"plink" => array("plink"),
|
||||
"uri" => array("uri"),
|
||||
)
|
||||
);
|
||||
$database["hook"] = array(
|
||||
"fields" => array(
|
||||
"id" => array("type" => "int(11)", "not null" => "1", "extra" => "auto_increment", "primary" => "1"),
|
||||
|
|
|
@ -18,7 +18,7 @@ require_once("include/event.php");
|
|||
require_once("include/text.php");
|
||||
require_once("include/oembed.php");
|
||||
require_once("include/html2bbcode.php");
|
||||
require_once("library/HTMLPurifier.auto.php");
|
||||
require_once("include/bbcode.php");
|
||||
|
||||
/**
|
||||
* @brief This class contain functions to create and send DFRN XML files
|
||||
|
@ -96,7 +96,7 @@ class dfrn {
|
|||
|
||||
$sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' ";
|
||||
|
||||
$r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
|
||||
$r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
|
||||
dbesc($owner_nick)
|
||||
|
@ -106,7 +106,7 @@ class dfrn {
|
|||
killme();
|
||||
|
||||
$owner = $r[0];
|
||||
$owner_id = $owner['user_uid'];
|
||||
$owner_id = $owner['uid'];
|
||||
$owner_nick = $owner['nickname'];
|
||||
|
||||
$sql_post_table = "";
|
||||
|
@ -484,7 +484,7 @@ class dfrn {
|
|||
"media:width" => 175, "media:height" => 175, "href" => $owner['photo']);
|
||||
xml_add_element($doc, $author, "link", "", $attributes);
|
||||
|
||||
$birthday = feed_birthday($owner['user_uid'], $owner['timezone']);
|
||||
$birthday = feed_birthday($owner['uid'], $owner['timezone']);
|
||||
|
||||
if ($birthday)
|
||||
xml_add_element($doc, $author, "dfrn:birthday", $birthday);
|
||||
|
@ -499,7 +499,7 @@ class dfrn {
|
|||
FROM `profile`
|
||||
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
|
||||
WHERE `profile`.`is-default` AND NOT `user`.`hidewall` AND `user`.`uid` = %d",
|
||||
intval($owner['user_uid']));
|
||||
intval($owner['uid']));
|
||||
if ($r) {
|
||||
$profile = $r[0];
|
||||
xml_add_element($doc, $author, "poco:displayName", $profile["name"]);
|
||||
|
@ -721,6 +721,9 @@ class dfrn {
|
|||
else
|
||||
$body = $item['body'];
|
||||
|
||||
// Remove the abstract element. It is only locally important.
|
||||
$body = remove_abstract($body);
|
||||
|
||||
if ($type == 'html') {
|
||||
$htmlbody = $body;
|
||||
|
||||
|
@ -1115,13 +1118,13 @@ class dfrn {
|
|||
*
|
||||
* @return Returns an array with relevant data of the author
|
||||
*/
|
||||
private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch) {
|
||||
private function fetchauthor($xpath, $context, $importer, $element, $onlyfetch, $xml = "") {
|
||||
|
||||
$author = array();
|
||||
$author["name"] = $xpath->evaluate($element."/atom:name/text()", $context)->item(0)->nodeValue;
|
||||
$author["link"] = $xpath->evaluate($element."/atom:uri/text()", $context)->item(0)->nodeValue;
|
||||
|
||||
$r = q("SELECT `id`, `uid`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`,
|
||||
$r = q("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`,
|
||||
`name`, `nick`, `about`, `location`, `keywords`, `bdyear`, `bd`
|
||||
FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'",
|
||||
intval($importer["uid"]), dbesc(normalise_link($author["link"])), dbesc(NETWORK_STATUSNET));
|
||||
|
@ -1130,6 +1133,9 @@ class dfrn {
|
|||
$author["contact-id"] = $r[0]["id"];
|
||||
$author["network"] = $r[0]["network"];
|
||||
} else {
|
||||
if (!$onlyfetch)
|
||||
logger("Contact ".$author["link"]." wasn't found for user ".$importer["uid"]." XML: ".$xml, LOGGER_DEBUG);
|
||||
|
||||
$author["contact-id"] = $importer["id"];
|
||||
$author["network"] = $importer["network"];
|
||||
$onlyfetch = true;
|
||||
|
@ -1159,38 +1165,41 @@ class dfrn {
|
|||
}
|
||||
|
||||
if ($r AND !$onlyfetch) {
|
||||
logger("Check if contact details for contact ".$r[0]["id"]." (".$r[0]["nick"].") have to be updated.", LOGGER_DEBUG);
|
||||
|
||||
$poco = array("url" => $contact["url"]);
|
||||
|
||||
// When was the last change to name or uri?
|
||||
$name_element = $xpath->query($element."/atom:name", $context)->item(0);
|
||||
foreach($name_element->attributes AS $attributes)
|
||||
if ($attributes->name == "updated")
|
||||
$contact["name-date"] = $attributes->textContent;
|
||||
$poco["name-date"] = $attributes->textContent;
|
||||
|
||||
$link_element = $xpath->query($element."/atom:link", $context)->item(0);
|
||||
foreach($link_element->attributes AS $attributes)
|
||||
if ($attributes->name == "updated")
|
||||
$contact["uri-date"] = $attributes->textContent;
|
||||
$poco["uri-date"] = $attributes->textContent;
|
||||
|
||||
// Update contact data
|
||||
$value = $xpath->evaluate($element."/dfrn:handle/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$contact["addr"] = $value;
|
||||
$poco["addr"] = $value;
|
||||
|
||||
$value = $xpath->evaluate($element."/poco:displayName/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$contact["name"] = $value;
|
||||
$poco["name"] = $value;
|
||||
|
||||
$value = $xpath->evaluate($element."/poco:preferredUsername/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$contact["nick"] = $value;
|
||||
$poco["nick"] = $value;
|
||||
|
||||
$value = $xpath->evaluate($element."/poco:note/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$contact["about"] = $value;
|
||||
$poco["about"] = $value;
|
||||
|
||||
$value = $xpath->evaluate($element."/poco:address/poco:formatted/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$contact["location"] = $value;
|
||||
$poco["location"] = $value;
|
||||
|
||||
/// @todo Add support for the following fields that we don't support by now in the contact table:
|
||||
/// - poco:utcOffset
|
||||
|
@ -1207,7 +1216,7 @@ class dfrn {
|
|||
$tags[$tag->nodeValue] = $tag->nodeValue;
|
||||
|
||||
if (count($tags))
|
||||
$contact["keywords"] = implode(", ", $tags);
|
||||
$poco["keywords"] = implode(", ", $tags);
|
||||
|
||||
// "dfrn:birthday" contains the birthday converted to UTC
|
||||
$old_bdyear = $contact["bdyear"];
|
||||
|
@ -1217,7 +1226,7 @@ class dfrn {
|
|||
if (strtotime($birthday) > time()) {
|
||||
$bd_timestamp = strtotime($birthday);
|
||||
|
||||
$contact["bdyear"] = date("Y", $bd_timestamp);
|
||||
$poco["bdyear"] = date("Y", $bd_timestamp);
|
||||
}
|
||||
|
||||
// "poco:birthday" is the birthday in the format "yyyy-mm-dd"
|
||||
|
@ -1232,9 +1241,11 @@ class dfrn {
|
|||
$bdyear = $bdyear + 1;
|
||||
}
|
||||
|
||||
$contact["bd"] = $value;
|
||||
$poco["bd"] = $value;
|
||||
}
|
||||
|
||||
$contact = array_merge($contact, $poco);
|
||||
|
||||
if ($old_bdyear != $contact["bdyear"])
|
||||
self::birthday_event($contact, $birthday);
|
||||
|
||||
|
@ -1245,6 +1256,7 @@ class dfrn {
|
|||
|
||||
unset($fields["id"]);
|
||||
unset($fields["uid"]);
|
||||
unset($fields["url"]);
|
||||
unset($fields["avatar-date"]);
|
||||
unset($fields["name-date"]);
|
||||
unset($fields["uri-date"]);
|
||||
|
@ -1252,8 +1264,10 @@ class dfrn {
|
|||
// Update check for this field has to be done differently
|
||||
$datefields = array("name-date", "uri-date");
|
||||
foreach ($datefields AS $field)
|
||||
if (strtotime($contact[$field]) > strtotime($r[0][$field]))
|
||||
if (strtotime($contact[$field]) > strtotime($r[0][$field])) {
|
||||
logger("Difference for contact ".$contact["id"]." in field '".$field."'. Old value: '".$contact[$field]."', new value '".$r[0][$field]."'", LOGGER_DEBUG);
|
||||
$update = true;
|
||||
}
|
||||
|
||||
foreach ($fields AS $field => $data)
|
||||
if ($contact[$field] != $r[0][$field]) {
|
||||
|
@ -1262,7 +1276,7 @@ class dfrn {
|
|||
}
|
||||
|
||||
if ($update) {
|
||||
logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG);
|
||||
logger("Update contact data for contact ".$contact["id"]." (".$contact["nick"].")", LOGGER_DEBUG);
|
||||
|
||||
q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s',
|
||||
`addr` = '%s', `keywords` = '%s', `bdyear` = '%s', `bd` = '%s',
|
||||
|
@ -1281,9 +1295,10 @@ class dfrn {
|
|||
// It is used in the socgraph.php to prevent that old contact data
|
||||
// that was relayed over several servers can overwrite contact
|
||||
// data that we received directly.
|
||||
$contact["generation"] = 2;
|
||||
$contact["photo"] = $author["avatar"];
|
||||
update_gcontact($contact);
|
||||
|
||||
$poco["generation"] = 2;
|
||||
$poco["photo"] = $author["avatar"];
|
||||
update_gcontact($poco);
|
||||
}
|
||||
|
||||
return($author);
|
||||
|
@ -1953,6 +1968,8 @@ class dfrn {
|
|||
$item['body'] = @html2bbcode($item['body']);
|
||||
}
|
||||
|
||||
/// @todo We should check for a repeated post and if we know the repeated author.
|
||||
|
||||
// We don't need the content element since "dfrn:env" is always present
|
||||
//$item["body"] = $xpath->query("atom:content/text()", $entry)->item(0)->nodeValue;
|
||||
|
||||
|
@ -2051,10 +2068,14 @@ class dfrn {
|
|||
if (($item["network"] != $author["network"]) AND ($author["network"] != ""))
|
||||
$item["network"] = $author["network"];
|
||||
|
||||
if($importer["rel"] == CONTACT_IS_FOLLOWER) {
|
||||
logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
// This code was taken from the old DFRN code
|
||||
// When activated, forums don't work.
|
||||
// And: Why should we disallow commenting by followers?
|
||||
// the behaviour is now similar to the Diaspora part.
|
||||
//if($importer["rel"] == CONTACT_IS_FOLLOWER) {
|
||||
// logger("Contact ".$importer["id"]." is only follower. Quitting", LOGGER_DEBUG);
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
|
||||
if ($entrytype == DFRN_REPLY_RC) {
|
||||
|
@ -2363,8 +2384,14 @@ class dfrn {
|
|||
$header["contact-id"] = $importer["id"];
|
||||
|
||||
// Update the contact table if the data has changed
|
||||
|
||||
// The "atom:author" is only present in feeds
|
||||
if ($xpath->query("/atom:feed/atom:author")->length > 0)
|
||||
self::fetchauthor($xpath, $doc->firstChild, $importer, "atom:author", false, $xml);
|
||||
|
||||
// Only the "dfrn:owner" in the head section contains all data
|
||||
self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false);
|
||||
if ($xpath->query("/atom:feed/dfrn:owner")->length > 0)
|
||||
self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml);
|
||||
|
||||
logger("Import DFRN message for user ".$importer["uid"]." from contact ".$importer["id"], LOGGER_DEBUG);
|
||||
|
||||
|
|
|
@ -76,7 +76,6 @@ function format_event_html($ev, $simple = false) {
|
|||
function parse_event($h) {
|
||||
|
||||
require_once('include/Scrape.php');
|
||||
require_once('library/HTMLPurifier.auto.php');
|
||||
require_once('include/html2bbcode');
|
||||
|
||||
$h = '<html><body>' . $h . '</body></html>';
|
||||
|
|
113
include/feed.php
113
include/feed.php
|
@ -2,7 +2,18 @@
|
|||
require_once("include/html2bbcode.php");
|
||||
require_once("include/items.php");
|
||||
|
||||
function feed_import($xml,$importer,&$contact, &$hub) {
|
||||
/**
|
||||
* @brief Read a RSS/RDF/Atom feed and create an item entry for it
|
||||
*
|
||||
* @param string $xml The feed data
|
||||
* @param array $importer The user record of the importer
|
||||
* @param array $contact The contact record of the feed
|
||||
* @param string $hub Unused dummy value for compatibility reasons
|
||||
* @param bool $simulate If enabled, no data is imported
|
||||
*
|
||||
* @return array In simulation mode it returns the header and the first item
|
||||
*/
|
||||
function feed_import($xml,$importer,&$contact, &$hub, $simulate = false) {
|
||||
|
||||
$a = get_app();
|
||||
|
||||
|
@ -14,18 +25,19 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
$doc = new DOMDocument();
|
||||
@$doc->loadXML($xml);
|
||||
$xpath = new DomXPath($doc);
|
||||
$xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom");
|
||||
$xpath->registerNamespace('atom', NAMESPACE_ATOM1);
|
||||
$xpath->registerNamespace('dc', "http://purl.org/dc/elements/1.1/");
|
||||
$xpath->registerNamespace('content', "http://purl.org/rss/1.0/modules/content/");
|
||||
$xpath->registerNamespace('rdf', "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
|
||||
$xpath->registerNamespace('rss', "http://purl.org/rss/1.0/");
|
||||
$xpath->registerNamespace('media', "http://search.yahoo.com/mrss/");
|
||||
$xpath->registerNamespace('poco', NAMESPACE_POCO);
|
||||
|
||||
$author = array();
|
||||
|
||||
// Is it RDF?
|
||||
if ($xpath->query('/rdf:RDF/rss:channel')->length > 0) {
|
||||
//$author["author-link"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:link/text()')->item(0)->nodeValue;
|
||||
$author["author-link"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:link/text()')->item(0)->nodeValue;
|
||||
$author["author-name"] = $xpath->evaluate('/rdf:RDF/rss:channel/rss:title/text()')->item(0)->nodeValue;
|
||||
|
||||
if ($author["author-name"] == "")
|
||||
|
@ -36,19 +48,27 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
|
||||
// Is it Atom?
|
||||
if ($xpath->query('/atom:feed/atom:entry')->length > 0) {
|
||||
//$self = $xpath->query("/atom:feed/atom:link[@rel='self']")->item(0)->attributes;
|
||||
//if (is_object($self))
|
||||
// foreach($self AS $attributes)
|
||||
// if ($attributes->name == "href")
|
||||
// $author["author-link"] = $attributes->textContent;
|
||||
$alternate = $xpath->query("atom:link[@rel='alternate']")->item(0)->attributes;
|
||||
if (is_object($alternate))
|
||||
foreach($alternate AS $attributes)
|
||||
if ($attributes->name == "href")
|
||||
$author["author-link"] = $attributes->textContent;
|
||||
|
||||
//if ($author["author-link"] == "") {
|
||||
// $alternate = $xpath->query("/atom:feed/atom:link[@rel='alternate']")->item(0)->attributes;
|
||||
// if (is_object($alternate))
|
||||
// foreach($alternate AS $attributes)
|
||||
// if ($attributes->name == "href")
|
||||
// $author["author-link"] = $attributes->textContent;
|
||||
//}
|
||||
if ($author["author-link"] == "")
|
||||
$author["author-link"] = $xpath->evaluate('/atom:feed/atom:author/atom:uri/text()')->item(0)->nodeValue;
|
||||
|
||||
if ($author["author-link"] == "") {
|
||||
$self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes;
|
||||
if (is_object($self))
|
||||
foreach($self AS $attributes)
|
||||
if ($attributes->name == "href")
|
||||
$author["author-link"] = $attributes->textContent;
|
||||
}
|
||||
|
||||
if ($author["author-link"] == "")
|
||||
$author["author-link"] = $xpath->evaluate('/atom:feed/atom:id/text()')->item(0)->nodeValue;
|
||||
|
||||
$author["author-avatar"] = $xpath->evaluate('/atom:feed/atom:logo/text()')->item(0)->nodeValue;
|
||||
|
||||
$author["author-name"] = $xpath->evaluate('/atom:feed/atom:title/text()')->item(0)->nodeValue;
|
||||
|
||||
|
@ -58,7 +78,13 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
if ($author["author-name"] == "")
|
||||
$author["author-name"] = $xpath->evaluate('/atom:feed/atom:author/atom:name/text()')->item(0)->nodeValue;
|
||||
|
||||
//$author["author-avatar"] = $xpath->evaluate('/atom:feed/atom:logo/text()')->item(0)->nodeValue;
|
||||
$value = $xpath->evaluate('atom:author/poco:displayName/text()')->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$author["author-name"] = $value;
|
||||
|
||||
$value = $xpath->evaluate('atom:author/poco:preferredUsername/text()')->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$author["author-nick"] = $value;
|
||||
|
||||
$author["edited"] = $author["created"] = $xpath->query('/atom:feed/atom:updated/text()')->item(0)->nodeValue;
|
||||
|
||||
|
@ -69,9 +95,10 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
|
||||
// Is it RSS?
|
||||
if ($xpath->query('/rss/channel')->length > 0) {
|
||||
//$author["author-link"] = $xpath->evaluate('/rss/channel/link/text()')->item(0)->nodeValue;
|
||||
$author["author-link"] = $xpath->evaluate('/rss/channel/link/text()')->item(0)->nodeValue;
|
||||
|
||||
$author["author-name"] = $xpath->evaluate('/rss/channel/title/text()')->item(0)->nodeValue;
|
||||
//$author["author-avatar"] = $xpath->evaluate('/rss/channel/image/url/text()')->item(0)->nodeValue;
|
||||
$author["author-avatar"] = $xpath->evaluate('/rss/channel/image/url/text()')->item(0)->nodeValue;
|
||||
|
||||
if ($author["author-name"] == "")
|
||||
$author["author-name"] = $xpath->evaluate('/rss/channel/copyright/text()')->item(0)->nodeValue;
|
||||
|
@ -86,18 +113,21 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
$entries = $xpath->query('/rss/channel/item');
|
||||
}
|
||||
|
||||
//if ($author["author-link"] == "")
|
||||
if (!$simulate) {
|
||||
$author["author-link"] = $contact["url"];
|
||||
|
||||
if ($author["author-name"] == "")
|
||||
$author["author-name"] = $contact["name"];
|
||||
if ($author["author-name"] == "")
|
||||
$author["author-name"] = $contact["name"];
|
||||
|
||||
//if ($author["author-avatar"] == "")
|
||||
$author["author-avatar"] = $contact["thumb"];
|
||||
|
||||
$author["owner-link"] = $contact["url"];
|
||||
$author["owner-name"] = $contact["name"];
|
||||
$author["owner-avatar"] = $contact["thumb"];
|
||||
$author["owner-link"] = $contact["url"];
|
||||
$author["owner-name"] = $contact["name"];
|
||||
$author["owner-avatar"] = $contact["thumb"];
|
||||
|
||||
// This is no field in the item table. So we have to unset it.
|
||||
unset($author["author-nick"]);
|
||||
}
|
||||
|
||||
$header = array();
|
||||
$header["uid"] = $importer["uid"];
|
||||
|
@ -120,6 +150,8 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
if (!is_object($entries))
|
||||
return;
|
||||
|
||||
$items = array();
|
||||
|
||||
$entrylist = array();
|
||||
|
||||
foreach ($entries AS $entry)
|
||||
|
@ -201,13 +233,13 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
if ($creator != "")
|
||||
$item["author-name"] = $creator;
|
||||
|
||||
//$item["object"] = $xml;
|
||||
|
||||
$r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s', '%s')",
|
||||
intval($importer["uid"]), dbesc($item["uri"]), dbesc(NETWORK_FEED), dbesc(NETWORK_DFRN));
|
||||
if ($r) {
|
||||
logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
|
||||
continue;
|
||||
if (!$simulate) {
|
||||
$r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s', '%s')",
|
||||
intval($importer["uid"]), dbesc($item["uri"]), dbesc(NETWORK_FEED), dbesc(NETWORK_DFRN));
|
||||
if ($r) {
|
||||
logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/// @TODO ?
|
||||
|
@ -272,14 +304,21 @@ function feed_import($xml,$importer,&$contact, &$hub) {
|
|||
$item["body"] = html2bbcode($body);
|
||||
}
|
||||
|
||||
logger("Stored feed: ".print_r($item, true), LOGGER_DEBUG);
|
||||
if (!$simulate) {
|
||||
logger("Stored feed: ".print_r($item, true), LOGGER_DEBUG);
|
||||
|
||||
$notify = item_is_remote_self($contact, $item);
|
||||
$id = item_store($item, false, $notify);
|
||||
$notify = item_is_remote_self($contact, $item);
|
||||
$id = item_store($item, false, $notify);
|
||||
|
||||
//print_r($item);
|
||||
logger("Feed for contact ".$contact["url"]." stored under id ".$id);
|
||||
} else
|
||||
$items[] = $item;
|
||||
|
||||
logger("Feed for contact ".$contact["url"]." stored under id ".$id);
|
||||
if ($simulate)
|
||||
break;
|
||||
}
|
||||
|
||||
if ($simulate)
|
||||
return array("header" => $author, "items" => $items);
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
require_once("include/Scrape.php");
|
||||
require_once("include/socgraph.php");
|
||||
|
||||
function update_contact($id) {
|
||||
/*
|
||||
|
@ -43,6 +44,9 @@ function update_contact($id) {
|
|||
intval($id)
|
||||
);
|
||||
|
||||
// Update the corresponding gcontact entry
|
||||
poco_last_updated($ret["url"]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ function mini_group_select($uid,$gid = 0) {
|
|||
|
||||
/**
|
||||
* @brief Create group sidebar widget
|
||||
*
|
||||
*
|
||||
* @param string $every
|
||||
* @param string $each
|
||||
* @param string $editmode
|
||||
|
@ -234,7 +234,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
|
|||
return '';
|
||||
|
||||
$groups = array();
|
||||
|
||||
|
||||
$groups[] = array(
|
||||
'text' => t('Everybody'),
|
||||
'id' => 0,
|
||||
|
@ -255,7 +255,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
|
|||
if(count($r)) {
|
||||
foreach($r as $rr) {
|
||||
$selected = (($group_id == $rr['id']) ? ' group-selected' : '');
|
||||
|
||||
|
||||
if ($editmode == "full") {
|
||||
$groupedit = array(
|
||||
'href' => "group/".$rr['id'],
|
||||
|
@ -264,7 +264,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro
|
|||
} else {
|
||||
$groupedit = null;
|
||||
}
|
||||
|
||||
|
||||
$groups[] = array(
|
||||
'id' => $rr['id'],
|
||||
'cid' => $cid,
|
||||
|
@ -362,14 +362,13 @@ function groups_containing($uid,$c) {
|
|||
*/
|
||||
function groups_count_unseen() {
|
||||
|
||||
$r = q("SELECT `group`.`id`, `group`.`name`, COUNT(`item`.`id`) AS `count` FROM `group`, `group_member`, `item`
|
||||
WHERE `group`.`uid` = %d
|
||||
AND `item`.`uid` = %d
|
||||
AND `item`.`unseen` AND `item`.`visible`
|
||||
AND NOT `item`.`deleted`
|
||||
AND `item`.`contact-id` = `group_member`.`contact-id`
|
||||
AND `group_member`.`gid` = `group`.`id`
|
||||
GROUP BY `group`.`id` ",
|
||||
$r = q("SELECT `group`.`id`, `group`.`name`,
|
||||
(SELECT COUNT(*) FROM `item`
|
||||
WHERE `uid` = %d AND `unseen` AND
|
||||
`contact-id` IN (SELECT `contact-id` FROM `group_member`
|
||||
WHERE `group_member`.`gid` = `group`.`id` AND `group_member`.`uid` = %d)) AS `count`
|
||||
FROM `group` WHERE `group`.`uid` = %d;",
|
||||
intval(local_user()),
|
||||
intval(local_user()),
|
||||
intval(local_user())
|
||||
);
|
||||
|
|
|
@ -332,9 +332,9 @@ function profile_sidebar($profile, $block = 0) {
|
|||
'fullname' => $profile['name'],
|
||||
'firstname' => $firstname,
|
||||
'lastname' => $lastname,
|
||||
'photo300' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg'),
|
||||
'photo100' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg'),
|
||||
'photo50' => $a->get_cached_avatar_image($a->get_baseurl() . '/photo/custom/50/' . $profile['uid'] . '.jpg'),
|
||||
'photo300' => $a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg',
|
||||
'photo100' => $a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg',
|
||||
'photo50' => $a->get_baseurl() . '/photo/custom/50/' . $profile['uid'] . '.jpg',
|
||||
);
|
||||
|
||||
if (!$block){
|
||||
|
|
|
@ -291,16 +291,6 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) {
|
|||
return $body;
|
||||
}
|
||||
|
||||
function add_guid($item) {
|
||||
$r = q("SELECT `guid` FROM `guid` WHERE `guid` = '%s' LIMIT 1", dbesc($item["guid"]));
|
||||
if ($r)
|
||||
return;
|
||||
|
||||
q("INSERT INTO `guid` (`guid`,`plink`,`uri`,`network`) VALUES ('%s','%s','%s','%s')",
|
||||
dbesc($item["guid"]), dbesc($item["plink"]),
|
||||
dbesc($item["uri"]), dbesc($item["network"]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "lang" specification in a "postopts" element of given $arr,
|
||||
* if possible and not already present.
|
||||
|
@ -510,14 +500,8 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$arr['file'] = ((x($arr,'file')) ? trim($arr['file']) : '');
|
||||
|
||||
|
||||
if (($arr['author-link'] == "") AND ($arr['owner-link'] == "")) {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
||||
foreach ($trace AS $func)
|
||||
$function[] = $func["function"];
|
||||
|
||||
$function = implode(", ", $function);
|
||||
logger("Both author-link and owner-link are empty. Called by: ".$function, LOGGER_DEBUG);
|
||||
}
|
||||
if (($arr['author-link'] == "") AND ($arr['owner-link'] == ""))
|
||||
logger("Both author-link and owner-link are empty. Called by: ".App::callstack(), LOGGER_DEBUG);
|
||||
|
||||
if ($arr['plink'] == "") {
|
||||
$a = get_app();
|
||||
|
@ -778,9 +762,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
return 0;
|
||||
} elseif(count($r)) {
|
||||
|
||||
// Store the guid and other relevant data
|
||||
add_guid($arr);
|
||||
|
||||
$current_post = $r[0]['id'];
|
||||
logger('item_store: created item ' . $current_post);
|
||||
|
||||
|
@ -901,9 +882,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
logger('item_store: new item not found in DB, id ' . $current_post);
|
||||
}
|
||||
|
||||
// Add every contact of the post to the global contact table
|
||||
poco_store($arr);
|
||||
|
||||
create_tags_from_item($current_post);
|
||||
create_files_from_item($current_post);
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ function nav_info(&$a) {
|
|||
// user info
|
||||
$r = q("SELECT micro FROM contact WHERE uid=%d AND self=1", intval($a->user['uid']));
|
||||
$userinfo = array(
|
||||
'icon' => (count($r) ? $a->get_cached_avatar_image($r[0]['micro']) : $a->get_baseurl($ssl_state)."/images/person-48.jpg"),
|
||||
'icon' => (count($r) ? $a->remove_baseurl($r[0]['micro']) : "images/person-48.jpg"),
|
||||
'name' => $a->user['username'],
|
||||
);
|
||||
|
||||
|
@ -107,7 +107,7 @@ function nav_info(&$a) {
|
|||
if(($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user()))
|
||||
$nav['register'] = array('register',t('Register'), "", t('Create an account'));
|
||||
|
||||
$help_url = $a->get_baseurl($ssl_state) . '/help';
|
||||
$help_url = 'help';
|
||||
|
||||
if(! get_config('system','hide_help'))
|
||||
$nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'));
|
||||
|
|
|
@ -164,8 +164,6 @@ function ostatus_fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch)
|
|||
update_contact_avatar($author["author-avatar"], $importer["uid"], $contact["id"]);
|
||||
}
|
||||
|
||||
|
||||
/// @todo Add the "addr" field
|
||||
$contact["generation"] = 2;
|
||||
$contact["photo"] = $author["author-avatar"];
|
||||
update_gcontact($contact);
|
||||
|
@ -626,6 +624,59 @@ function check_conversations($mentions = false, $override = false) {
|
|||
set_config('system','ostatus_last_poll', time());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the gcontact table with actor data from the conversation
|
||||
*
|
||||
* @param object $actor The actor object that contains the contact data
|
||||
*/
|
||||
function ostatus_conv_fetch_actor($actor) {
|
||||
|
||||
// We set the generation to "3" since the data here is not as reliable as the data we get on other occasions
|
||||
$contact = array("network" => NETWORK_OSTATUS, "generation" => 3);
|
||||
|
||||
if (isset($actor->url))
|
||||
$contact["url"] = $actor->url;
|
||||
|
||||
if (isset($actor->displayName))
|
||||
$contact["name"] = $actor->displayName;
|
||||
|
||||
if (isset($actor->portablecontacts_net->displayName))
|
||||
$contact["name"] = $actor->portablecontacts_net->displayName;
|
||||
|
||||
if (isset($actor->portablecontacts_net->preferredUsername))
|
||||
$contact["nick"] = $actor->portablecontacts_net->preferredUsername;
|
||||
|
||||
if (isset($actor->id))
|
||||
$contact["alias"] = $actor->id;
|
||||
|
||||
if (isset($actor->summary))
|
||||
$contact["about"] = $actor->summary;
|
||||
|
||||
if (isset($actor->portablecontacts_net->note))
|
||||
$contact["about"] = $actor->portablecontacts_net->note;
|
||||
|
||||
if (isset($actor->portablecontacts_net->addresses->formatted))
|
||||
$contact["location"] = $actor->portablecontacts_net->addresses->formatted;
|
||||
|
||||
|
||||
if (isset($actor->image->url))
|
||||
$contact["photo"] = $actor->image->url;
|
||||
|
||||
if (isset($actor->image->width))
|
||||
$avatarwidth = $actor->image->width;
|
||||
|
||||
if (is_array($actor->status_net->avatarLinks))
|
||||
foreach ($actor->status_net->avatarLinks AS $avatar) {
|
||||
if ($avatarsize < $avatar->width) {
|
||||
$contact["photo"] = $avatar->url;
|
||||
$avatarsize = $avatar->width;
|
||||
}
|
||||
}
|
||||
|
||||
update_gcontact($contact);
|
||||
}
|
||||
|
||||
|
||||
function ostatus_completion($conversation_url, $uid, $item = array()) {
|
||||
|
||||
$a = get_app();
|
||||
|
@ -729,6 +780,9 @@ function ostatus_completion($conversation_url, $uid, $item = array()) {
|
|||
|
||||
foreach ($items as $single_conv) {
|
||||
|
||||
// Update the gcontact table
|
||||
ostatus_conv_fetch_actor($single_conv->actor);
|
||||
|
||||
// Test - remove before flight
|
||||
//$tempfile = tempnam(get_temppath(), "conversation");
|
||||
//file_put_contents($tempfile, json_encode($single_conv));
|
||||
|
|
|
@ -132,7 +132,19 @@ function shortenmsg($msg, $limit, $twitter = false) {
|
|||
return($msg);
|
||||
}
|
||||
|
||||
function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
|
||||
/**
|
||||
* @brief Convert a message into plaintext for connectors to other networks
|
||||
*
|
||||
* @param App $a The application class
|
||||
* @param array $b The message array that is about to be posted
|
||||
* @param int $limit The maximum number of characters when posting to that network
|
||||
* @param bool $includedlinks Has an attached link to be included into the message?
|
||||
* @param int $htmlmode This triggers the behaviour of the bbcode conversion
|
||||
* @param string $target_network Name of the network where the post should go to.
|
||||
*
|
||||
* @return string The converted message
|
||||
*/
|
||||
function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = "") {
|
||||
require_once("include/bbcode.php");
|
||||
require_once("include/html2plain.php");
|
||||
require_once("include/network.php");
|
||||
|
@ -144,6 +156,9 @@ function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
|
|||
// Add an URL element if the text contains a raw link
|
||||
$body = preg_replace("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url]$2[/url]', $body);
|
||||
|
||||
// Remove the abstract
|
||||
$body = remove_abstract($body);
|
||||
|
||||
// At first look at data that is attached via "type-..." stuff
|
||||
// This will hopefully replaced with a dedicated bbcode later
|
||||
//$post = get_attached_data($b["body"]);
|
||||
|
@ -154,6 +169,44 @@ function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2) {
|
|||
elseif ($b["title"] != "")
|
||||
$post["text"] = trim($b["title"]);
|
||||
|
||||
$abstract = "";
|
||||
|
||||
// Fetch the abstract from the given target network
|
||||
if ($target_network != "") {
|
||||
$default_abstract = fetch_abstract($b["body"]);
|
||||
$abstract = fetch_abstract($b["body"], $target_network);
|
||||
|
||||
// If we post to a network with no limit we only fetch
|
||||
// an abstract exactly for this network
|
||||
if (($limit == 0) AND ($abstract == $default_abstract))
|
||||
$abstract = "";
|
||||
|
||||
} else // Try to guess the correct target network
|
||||
switch ($htmlmode) {
|
||||
case 8:
|
||||
$abstract = fetch_abstract($b["body"], NETWORK_TWITTER);
|
||||
break;
|
||||
case 7:
|
||||
$abstract = fetch_abstract($b["body"], NETWORK_STATUSNET);
|
||||
break;
|
||||
case 6:
|
||||
$abstract = fetch_abstract($b["body"], NETWORK_APPNET);
|
||||
break;
|
||||
default: // We don't know the exact target.
|
||||
// We fetch an abstract since there is a posting limit.
|
||||
if ($limit > 0)
|
||||
$abstract = fetch_abstract($b["body"]);
|
||||
}
|
||||
|
||||
if ($abstract != "") {
|
||||
$post["text"] = $abstract;
|
||||
|
||||
if ($post["type"] == "text") {
|
||||
$post["type"] = "link";
|
||||
$post["url"] = $b["plink"];
|
||||
}
|
||||
}
|
||||
|
||||
$html = bbcode($post["text"], false, false, $htmlmode);
|
||||
$msg = html2plain($html, 0, true);
|
||||
$msg = trim(html_entity_decode($msg,ENT_QUOTES,'UTF-8'));
|
||||
|
|
|
@ -68,6 +68,10 @@ function poller_run(&$argv, &$argc){
|
|||
|
||||
while ($r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `created` LIMIT 1")) {
|
||||
|
||||
// Constantly check the number of available database connections to let the frontend be accessible at any time
|
||||
if (poller_max_connections_reached())
|
||||
return;
|
||||
|
||||
// Count active workers and compare them with a maximum value that depends on the load
|
||||
if (poller_too_much_workers(3))
|
||||
return;
|
||||
|
@ -126,6 +130,48 @@ function poller_run(&$argv, &$argc){
|
|||
* @return bool Are more than 3/4 of the maximum connections used?
|
||||
*/
|
||||
function poller_max_connections_reached() {
|
||||
|
||||
// Fetch the max value from the config. This is needed when the system cannot detect the correct value by itself.
|
||||
$max = get_config("system", "max_connections");
|
||||
|
||||
if ($max == 0) {
|
||||
// the maximum number of possible user connections can be a system variable
|
||||
$r = q("SHOW VARIABLES WHERE `variable_name` = 'max_user_connections'");
|
||||
if ($r)
|
||||
$max = $r[0]["Value"];
|
||||
|
||||
// Or it can be granted. This overrides the system variable
|
||||
$r = q("SHOW GRANTS");
|
||||
if ($r)
|
||||
foreach ($r AS $grants) {
|
||||
$grant = array_pop($grants);
|
||||
if (stristr($grant, "GRANT USAGE ON"))
|
||||
if (preg_match("/WITH MAX_USER_CONNECTIONS (\d*)/", $grant, $match))
|
||||
$max = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
// If $max is set we will use the processlist to determine the current number of connections
|
||||
// The processlist only shows entries of the current user
|
||||
if ($max != 0) {
|
||||
$r = q("SHOW PROCESSLIST");
|
||||
if (!$r)
|
||||
return false;
|
||||
|
||||
$used = count($r);
|
||||
|
||||
logger("Connection usage (user values): ".$used."/".$max, LOGGER_DEBUG);
|
||||
|
||||
$level = $used / $max;
|
||||
|
||||
if ($level >= (3/4)) {
|
||||
logger("Maximum level (3/4) of user connections reached: ".$used."/".$max);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We will now check for the system values.
|
||||
// This limit could be reached although the user limits are fine.
|
||||
$r = q("SHOW VARIABLES WHERE `variable_name` = 'max_connections'");
|
||||
if (!$r)
|
||||
return false;
|
||||
|
@ -138,20 +184,19 @@ function poller_max_connections_reached() {
|
|||
if (!$r)
|
||||
return false;
|
||||
|
||||
$connected = intval($r[0]["Value"]);
|
||||
if ($connected == 0)
|
||||
$used = intval($r[0]["Value"]);
|
||||
if ($used == 0)
|
||||
return false;
|
||||
|
||||
$level = $connected / $max;
|
||||
logger("Connection usage (system values): ".$used."/".$max, LOGGER_DEBUG);
|
||||
|
||||
logger("Connection usage: ".$connected."/".$max, LOGGER_DEBUG);
|
||||
$level = $used / $max;
|
||||
|
||||
if ($level < (3/4))
|
||||
return false;
|
||||
|
||||
logger("Maximum level (3/4) of connections reached: ".$connected."/".$max);
|
||||
logger("Maximum level (3/4) of system connections reached: ".$used."/".$max);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
require_once('include/datetime.php');
|
||||
require_once("include/Scrape.php");
|
||||
require_once("include/html2bbcode.php");
|
||||
|
||||
require_once("include/Contact.php");
|
||||
require_once("include/Photo.php");
|
||||
|
||||
/*
|
||||
* poco_load
|
||||
|
@ -139,15 +140,16 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
|
||||
|
||||
// Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
|
||||
if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
|
||||
q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
|
||||
WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
|
||||
dbesc($location),
|
||||
dbesc($about),
|
||||
dbesc($keywords),
|
||||
dbesc($gender),
|
||||
dbesc(normalise_link($profile_url)),
|
||||
dbesc(NETWORK_DFRN));
|
||||
// Deactivated because we now update Friendica contacts in dfrn.php
|
||||
//if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
|
||||
// q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
|
||||
// WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
|
||||
// dbesc($location),
|
||||
// dbesc($about),
|
||||
// dbesc($keywords),
|
||||
// dbesc($gender),
|
||||
// dbesc(normalise_link($profile_url)),
|
||||
// dbesc(NETWORK_DFRN));
|
||||
}
|
||||
logger("poco_load: loaded $total entries",LOGGER_DEBUG);
|
||||
|
||||
|
@ -427,7 +429,7 @@ function poco_last_updated($profile, $force = false) {
|
|||
if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) {
|
||||
|
||||
// Use noscrape if possible
|
||||
$server = q("SELECT `noscrape` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
|
||||
$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
|
||||
|
||||
if ($server) {
|
||||
$noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
|
||||
|
@ -436,69 +438,47 @@ function poco_last_updated($profile, $force = false) {
|
|||
|
||||
$noscrape = json_decode($noscraperet["body"], true);
|
||||
|
||||
if (($noscrape["fn"] != "") AND ($noscrape["fn"] != $gcontacts[0]["name"]))
|
||||
q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["fn"]), dbesc(normalise_link($profile)));
|
||||
if (is_array($noscrape)) {
|
||||
$contact = array("url" => $profile,
|
||||
"network" => $server[0]["network"],
|
||||
"generation" => $gcontacts[0]["generation"]);
|
||||
|
||||
if (($noscrape["photo"] != "") AND ($noscrape["photo"] != $gcontacts[0]["photo"]))
|
||||
q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["photo"]), dbesc(normalise_link($profile)));
|
||||
$contact["name"] = $noscrape["fn"];
|
||||
$contact["community"] = $noscrape["comm"];
|
||||
|
||||
if (($noscrape["updated"] != "") AND ($noscrape["updated"] != $gcontacts[0]["updated"]))
|
||||
q("UPDATE `gcontact` SET `updated` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["updated"]), dbesc(normalise_link($profile)));
|
||||
if (isset($noscrape["tags"])) {
|
||||
$keywords = implode(" ", $noscrape["tags"]);
|
||||
if ($keywords != "")
|
||||
$contact["keywords"] = $keywords;
|
||||
}
|
||||
|
||||
if (($noscrape["gender"] != "") AND ($noscrape["gender"] != $gcontacts[0]["gender"]))
|
||||
q("UPDATE `gcontact` SET `gender` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["gender"]), dbesc(normalise_link($profile)));
|
||||
$location = formatted_location($noscrape);
|
||||
if ($location)
|
||||
$contact["location"] = $location;
|
||||
|
||||
if (($noscrape["pdesc"] != "") AND ($noscrape["pdesc"] != $gcontacts[0]["about"]))
|
||||
q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["pdesc"]), dbesc(normalise_link($profile)));
|
||||
$contact["notify"] = $noscrape["dfrn-notify"];
|
||||
|
||||
if (($noscrape["about"] != "") AND ($noscrape["about"] != $gcontacts[0]["about"]))
|
||||
q("UPDATE `gcontact` SET `about` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($noscrape["about"]), dbesc(normalise_link($profile)));
|
||||
// Remove all fields that are not present in the gcontact table
|
||||
unset($noscrape["fn"]);
|
||||
unset($noscrape["key"]);
|
||||
unset($noscrape["homepage"]);
|
||||
unset($noscrape["comm"]);
|
||||
unset($noscrape["tags"]);
|
||||
unset($noscrape["locality"]);
|
||||
unset($noscrape["region"]);
|
||||
unset($noscrape["country-name"]);
|
||||
unset($noscrape["contacts"]);
|
||||
unset($noscrape["dfrn-request"]);
|
||||
unset($noscrape["dfrn-confirm"]);
|
||||
unset($noscrape["dfrn-notify"]);
|
||||
unset($noscrape["dfrn-poll"]);
|
||||
|
||||
if (isset($noscrape["comm"]) AND ($noscrape["comm"] != $gcontacts[0]["community"]))
|
||||
q("UPDATE `gcontact` SET `community` = %d WHERE `nurl` = '%s'",
|
||||
intval($noscrape["comm"]), dbesc(normalise_link($profile)));
|
||||
$contact = array_merge($contact, $noscrape);
|
||||
|
||||
if (isset($noscrape["tags"]))
|
||||
$keywords = implode(" ", $noscrape["tags"]);
|
||||
else
|
||||
$keywords = "";
|
||||
update_gcontact($contact);
|
||||
|
||||
if (($keywords != "") AND ($keywords != $gcontacts[0]["keywords"]))
|
||||
q("UPDATE `gcontact` SET `keywords` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($keywords), dbesc(normalise_link($profile)));
|
||||
|
||||
$location = $noscrape["locality"];
|
||||
|
||||
if ($noscrape["region"] != "") {
|
||||
if ($location != "")
|
||||
$location .= ", ";
|
||||
|
||||
$location .= $noscrape["region"];
|
||||
return $noscrape["updated"];
|
||||
}
|
||||
|
||||
if ($noscrape["country-name"] != "") {
|
||||
if ($location != "")
|
||||
$location .= ", ";
|
||||
|
||||
$location .= $noscrape["country-name"];
|
||||
}
|
||||
|
||||
if (($location != "") AND ($location != $gcontacts[0]["location"]))
|
||||
q("UPDATE `gcontact` SET `location` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($location), dbesc(normalise_link($profile)));
|
||||
|
||||
// If we got data from noscrape then mark the contact as reachable
|
||||
if (is_array($noscrape) AND count($noscrape))
|
||||
q("UPDATE `gcontact` SET `last_contact` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
|
||||
|
||||
return $noscrape["updated"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -533,25 +513,22 @@ function poco_last_updated($profile, $force = false) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (($data["name"] != "") AND ($data["name"] != $gcontacts[0]["name"]))
|
||||
q("UPDATE `gcontact` SET `name` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($data["name"]), dbesc(normalise_link($profile)));
|
||||
$contact = array("generation" => $gcontacts[0]["generation"]);
|
||||
|
||||
if (($data["nick"] != "") AND ($data["nick"] != $gcontacts[0]["nick"]))
|
||||
q("UPDATE `gcontact` SET `nick` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($data["nick"]), dbesc(normalise_link($profile)));
|
||||
$contact = array_merge($contact, $data);
|
||||
|
||||
if (($data["addr"] != "") AND ($data["addr"] != $gcontacts[0]["connect"]))
|
||||
q("UPDATE `gcontact` SET `connect` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($data["addr"]), dbesc(normalise_link($profile)));
|
||||
$contact["server_url"] = $data["baseurl"];
|
||||
|
||||
if (($data["photo"] != "") AND ($data["photo"] != $gcontacts[0]["photo"]))
|
||||
q("UPDATE `gcontact` SET `photo` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($data["photo"]), dbesc(normalise_link($profile)));
|
||||
unset($contact["batch"]);
|
||||
unset($contact["poll"]);
|
||||
unset($contact["request"]);
|
||||
unset($contact["confirm"]);
|
||||
unset($contact["poco"]);
|
||||
unset($contact["priority"]);
|
||||
unset($contact["pubkey"]);
|
||||
unset($contact["baseurl"]);
|
||||
|
||||
if (($data["baseurl"] != "") AND ($data["baseurl"] != $gcontacts[0]["server_url"]))
|
||||
q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($data["baseurl"]), dbesc(normalise_link($profile)));
|
||||
update_gcontact($contact);
|
||||
|
||||
$feedret = z_fetch_url($data["poll"]);
|
||||
|
||||
|
@ -745,7 +722,8 @@ function poco_check_server($server_url, $network = "", $force = false) {
|
|||
// Will also return data for Friendica and GNU Social - but it will be overwritten later
|
||||
// The "not implemented" is a special treatment for really, really old Friendica versions
|
||||
$serverret = z_fetch_url($server_url."/api/statusnet/version.json");
|
||||
if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
|
||||
if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND
|
||||
($serverret["body"] != '') AND (strlen($serverret["body"]) < 30)) {
|
||||
$platform = "StatusNet";
|
||||
$version = trim($serverret["body"], '"');
|
||||
$network = NETWORK_OSTATUS;
|
||||
|
@ -753,7 +731,8 @@ function poco_check_server($server_url, $network = "", $force = false) {
|
|||
|
||||
// Test for GNU Social
|
||||
$serverret = z_fetch_url($server_url."/api/gnusocial/version.json");
|
||||
if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND ($serverret["body"] != '') AND (strlen($serverret["body"]) < 250)) {
|
||||
if ($serverret["success"] AND ($serverret["body"] != '{"error":"not implemented"}') AND
|
||||
($serverret["body"] != '') AND (strlen($serverret["body"]) < 30)) {
|
||||
$platform = "GNU Social";
|
||||
$version = trim($serverret["body"], '"');
|
||||
$network = NETWORK_OSTATUS;
|
||||
|
@ -880,6 +859,11 @@ function poco_check_server($server_url, $network = "", $force = false) {
|
|||
// Check again if the server exists
|
||||
$servers = q("SELECT `nurl` FROM `gserver` WHERE `nurl` = '%s'", dbesc(normalise_link($server_url)));
|
||||
|
||||
$version = strip_tags($version);
|
||||
$site_name = strip_tags($site_name);
|
||||
$info = strip_tags($info);
|
||||
$platform = strip_tags($platform);
|
||||
|
||||
if ($servers)
|
||||
q("UPDATE `gserver` SET `url` = '%s', `version` = '%s', `site_name` = '%s', `info` = '%s', `register_policy` = %d, `poco` = '%s', `noscrape` = '%s',
|
||||
`network` = '%s', `platform` = '%s', `last_contact` = '%s', `last_failure` = '%s' WHERE `nurl` = '%s'",
|
||||
|
@ -920,88 +904,6 @@ function poco_check_server($server_url, $network = "", $force = false) {
|
|||
return !$failure;
|
||||
}
|
||||
|
||||
function poco_contact_from_body($body, $created, $cid, $uid) {
|
||||
preg_replace_callback("/\[share(.*?)\].*?\[\/share\]/ism",
|
||||
function ($match) use ($created, $cid, $uid){
|
||||
return(sub_poco_from_share($match, $created, $cid, $uid));
|
||||
}, $body);
|
||||
}
|
||||
|
||||
function sub_poco_from_share($share, $created, $cid, $uid) {
|
||||
$profile = "";
|
||||
preg_match("/profile='(.*?)'/ism", $share[1], $matches);
|
||||
if ($matches[1] != "")
|
||||
$profile = $matches[1];
|
||||
|
||||
preg_match('/profile="(.*?)"/ism', $share[1], $matches);
|
||||
if ($matches[1] != "")
|
||||
$profile = $matches[1];
|
||||
|
||||
if ($profile == "")
|
||||
return;
|
||||
|
||||
logger("prepare poco_check for profile ".$profile, LOGGER_DEBUG);
|
||||
poco_check($profile, "", "", "", "", "", "", "", "", $created, 3, $cid, $uid);
|
||||
}
|
||||
|
||||
function poco_store($item) {
|
||||
|
||||
// Isn't it public?
|
||||
if ($item['private'])
|
||||
return;
|
||||
|
||||
// Or is it from a network where we don't store the global contacts?
|
||||
if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_STATUSNET, "")))
|
||||
return;
|
||||
|
||||
// Is it a global copy?
|
||||
$store_gcontact = ($item["uid"] == 0);
|
||||
|
||||
// Is it a comment on a global copy?
|
||||
if (!$store_gcontact AND ($item["uri"] != $item["parent-uri"])) {
|
||||
$q = q("SELECT `id` FROM `item` WHERE `uri`='%s' AND `uid` = 0", $item["parent-uri"]);
|
||||
$store_gcontact = count($q);
|
||||
}
|
||||
|
||||
if (!$store_gcontact)
|
||||
return;
|
||||
|
||||
// "3" means: We don't know this contact directly (Maybe a reshared item)
|
||||
$generation = 3;
|
||||
$network = "";
|
||||
$profile_url = $item["author-link"];
|
||||
|
||||
// Is it a user from our server?
|
||||
$q = q("SELECT `id` FROM `contact` WHERE `self` AND `nurl` = '%s' LIMIT 1",
|
||||
dbesc(normalise_link($item["author-link"])));
|
||||
if (count($q)) {
|
||||
logger("Our user (generation 1): ".$item["author-link"], LOGGER_DEBUG);
|
||||
$generation = 1;
|
||||
$network = NETWORK_DFRN;
|
||||
} else { // Is it a contact from a user on our server?
|
||||
$q = q("SELECT `network`, `url` FROM `contact` WHERE `uid` != 0 AND `network` != ''
|
||||
AND (`nurl` = '%s' OR `alias` IN ('%s', '%s')) AND `network` != '%s' LIMIT 1",
|
||||
dbesc(normalise_link($item["author-link"])),
|
||||
dbesc(normalise_link($item["author-link"])),
|
||||
dbesc($item["author-link"]),
|
||||
dbesc(NETWORK_STATUSNET));
|
||||
if (count($q)) {
|
||||
$generation = 2;
|
||||
$network = $q[0]["network"];
|
||||
$profile_url = $q[0]["url"];
|
||||
logger("Known contact (generation 2): ".$profile_url, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
if ($generation == 3)
|
||||
logger("Unknown contact (generation 3): ".$item["author-link"], LOGGER_DEBUG);
|
||||
|
||||
poco_check($profile_url, $item["author-name"], $network, $item["author-avatar"], "", "", "", "", "", $item["received"], $generation, $item["contact-id"], $item["uid"]);
|
||||
|
||||
// Maybe its a body with a shared item? Then extract a global contact from it.
|
||||
poco_contact_from_body($item["body"], $item["received"], $item["contact-id"], $item["uid"]);
|
||||
}
|
||||
|
||||
function count_common_friends($uid,$cid) {
|
||||
|
||||
$r = q("SELECT count(*) as `total`
|
||||
|
@ -1530,9 +1432,17 @@ function update_gcontact($contact) {
|
|||
unset($fields["url"]);
|
||||
unset($fields["updated"]);
|
||||
|
||||
// Bugfix: We had an error in the storing of keywords which lead to the "0"
|
||||
// This value is still transmitted via poco.
|
||||
if ($contact["keywords"] == "0")
|
||||
unset($contact["keywords"]);
|
||||
|
||||
if ($r[0]["keywords"] == "0")
|
||||
$r[0]["keywords"] = "";
|
||||
|
||||
// assign all unassigned fields from the database entry
|
||||
foreach ($fields AS $field => $data)
|
||||
if (!isset($contact[$field]))
|
||||
if (!isset($contact[$field]) OR ($contact[$field] == ""))
|
||||
$contact[$field] = $r[0][$field];
|
||||
|
||||
if ($contact["network"] == NETWORK_STATUSNET)
|
||||
|
@ -1541,20 +1451,50 @@ function update_gcontact($contact) {
|
|||
if (!isset($contact["updated"]))
|
||||
$contact["updated"] = datetime_convert();
|
||||
|
||||
if ($contact["server_url"] == "") {
|
||||
$server_url = $contact["url"];
|
||||
|
||||
$server_url = matching_url($server_url, $contact["alias"]);
|
||||
if ($server_url != "")
|
||||
$contact["server_url"] = $server_url;
|
||||
|
||||
$server_url = matching_url($server_url, $contact["photo"]);
|
||||
if ($server_url != "")
|
||||
$contact["server_url"] = $server_url;
|
||||
|
||||
$server_url = matching_url($server_url, $contact["notify"]);
|
||||
if ($server_url != "")
|
||||
$contact["server_url"] = $server_url;
|
||||
} else
|
||||
$contact["server_url"] = normalise_link($contact["server_url"]);
|
||||
|
||||
if (($contact["addr"] == "") AND ($contact["server_url"] != "") AND ($contact["nick"] != "")) {
|
||||
$hostname = str_replace("http://", "", $contact["server_url"]);
|
||||
$contact["addr"] = $contact["nick"]."@".$hostname;
|
||||
}
|
||||
|
||||
// Check if any field changed
|
||||
$update = false;
|
||||
unset($fields["generation"]);
|
||||
|
||||
foreach ($fields AS $field => $data)
|
||||
if ($contact[$field] != $r[0][$field])
|
||||
$update = true;
|
||||
if ((($contact["generation"] > 0) AND ($contact["generation"] <= $r[0]["generation"])) OR ($r[0]["generation"] == 0)) {
|
||||
foreach ($fields AS $field => $data)
|
||||
if ($contact[$field] != $r[0][$field]) {
|
||||
logger("Difference for contact ".$contact["url"]." in field '".$field."'. New value: '".$contact[$field]."', old value '".$r[0][$field]."'", LOGGER_DEBUG);
|
||||
$update = true;
|
||||
}
|
||||
|
||||
if ($contact["generation"] < $r[0]["generation"])
|
||||
$update = true;
|
||||
if ($contact["generation"] < $r[0]["generation"]) {
|
||||
logger("Difference for contact ".$contact["url"]." in field 'generation'. new value: '".$contact["generation"]."', old value '".$r[0]["generation"]."'", LOGGER_DEBUG);
|
||||
$update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
logger("Update gcontact for ".$contact["url"]." Callstack: ".App::callstack(), LOGGER_DEBUG);
|
||||
|
||||
q("UPDATE `gcontact` SET `photo` = '%s', `name` = '%s', `nick` = '%s', `addr` = '%s', `network` = '%s',
|
||||
`birthday` = '%s', `gender` = '%s', `keywords` = %d, `hide` = %d, `nsfw` = %d,
|
||||
`birthday` = '%s', `gender` = '%s', `keywords` = '%s', `hide` = %d, `nsfw` = %d,
|
||||
`alias` = '%s', `notify` = '%s', `url` = '%s',
|
||||
`location` = '%s', `about` = '%s', `generation` = %d, `updated` = '%s',
|
||||
`server_url` = '%s', `connect` = '%s'
|
||||
|
@ -1567,6 +1507,28 @@ function update_gcontact($contact) {
|
|||
intval($contact["generation"]), dbesc($contact["updated"]),
|
||||
dbesc($contact["server_url"]), dbesc($contact["connect"]),
|
||||
dbesc(normalise_link($contact["url"])), intval($contact["generation"]));
|
||||
|
||||
|
||||
// Now update the contact entry with the user id "0" as well.
|
||||
// This is used for the shadow copies of public items.
|
||||
$r = q("SELECT `id` FROM `contact` WHERE `nurl` = '%s' AND `uid` = 0 ORDER BY `id` LIMIT 1",
|
||||
dbesc(normalise_link($contact["url"])));
|
||||
|
||||
if ($r) {
|
||||
logger("Update shadow contact ".$r[0]["id"], LOGGER_DEBUG);
|
||||
|
||||
update_contact_avatar($contact["photo"], 0, $r[0]["id"]);
|
||||
|
||||
q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s',
|
||||
`network` = '%s', `bd` = '%s', `gender` = '%s',
|
||||
`keywords` = '%s', `alias` = '%s', `url` = '%s',
|
||||
`location` = '%s', `about` = '%s'
|
||||
WHERE `id` = %d",
|
||||
dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["addr"]),
|
||||
dbesc($contact["network"]), dbesc($contact["birthday"]), dbesc($contact["gender"]),
|
||||
dbesc($contact["keywords"]), dbesc($contact["alias"]), dbesc($contact["url"]),
|
||||
dbesc($contact["location"]), dbesc($contact["about"]), intval($r[0]["id"]));
|
||||
}
|
||||
}
|
||||
|
||||
return $gcontact_id;
|
||||
|
@ -1580,8 +1542,10 @@ function update_gcontact($contact) {
|
|||
function update_gcontact_from_probe($url) {
|
||||
$data = probe_url($url);
|
||||
|
||||
if ($data["network"] != NETWORK_PHANTOM)
|
||||
update_gcontact($data);
|
||||
if ($data["network"] == NETWORK_PHANTOM)
|
||||
return;
|
||||
|
||||
update_gcontact($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -285,7 +285,7 @@ function paginate_data(&$a, $count=null) {
|
|||
if (($a->page_offset != "") AND !preg_match('/[?&].offset=/', $stripped))
|
||||
$stripped .= "&offset=".urlencode($a->page_offset);
|
||||
|
||||
$url = z_root() . '/' . $stripped;
|
||||
$url = $stripped;
|
||||
|
||||
$data = array();
|
||||
function _l(&$d, $name, $url, $text, $class="") {
|
||||
|
@ -923,7 +923,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) {
|
|||
|
||||
if($redirect) {
|
||||
$a = get_app();
|
||||
$redirect_url = z_root() . '/redir/' . $contact['id'];
|
||||
$redirect_url = 'redir/' . $contact['id'];
|
||||
if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) {
|
||||
$redir = true;
|
||||
$url = $redirect_url;
|
||||
|
@ -964,13 +964,13 @@ if(! function_exists('search')) {
|
|||
* @param string $url search url
|
||||
* @param boolean $savedsearch show save search button
|
||||
*/
|
||||
function search($s,$id='search-box',$url='/search',$save = false, $aside = true) {
|
||||
function search($s,$id='search-box',$url='search',$save = false, $aside = true) {
|
||||
$a = get_app();
|
||||
|
||||
$values = array(
|
||||
'$s' => $s,
|
||||
'$id' => $id,
|
||||
'$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url,
|
||||
'$action_url' => $url,
|
||||
'$search_label' => t('Search'),
|
||||
'$save_label' => t('Save'),
|
||||
'$savedsearch' => feature_enabled(local_user(),'savedsearch'),
|
||||
|
@ -1305,7 +1305,7 @@ function redir_private_images($a, &$item) {
|
|||
|
||||
if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
|
||||
//logger("redir_private_images: redir");
|
||||
$img_url = z_root() . '/redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
|
||||
$img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
|
||||
$item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']);
|
||||
}
|
||||
}
|
||||
|
@ -1421,7 +1421,7 @@ function prepare_body(&$item,$attach = false, $preview = false) {
|
|||
$mime = $mtch[3];
|
||||
|
||||
if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN))
|
||||
$the_url = z_root() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
|
||||
$the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
|
||||
else
|
||||
$the_url = $mtch[1];
|
||||
|
||||
|
@ -1596,7 +1596,7 @@ function get_cats_and_terms($item) {
|
|||
$categories[] = array(
|
||||
'name' => xmlify(file_tag_decode($mtch[1])),
|
||||
'url' => "#",
|
||||
'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
|
||||
'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""),
|
||||
'first' => $first,
|
||||
'last' => false
|
||||
);
|
||||
|
@ -1614,7 +1614,7 @@ function get_cats_and_terms($item) {
|
|||
$folders[] = array(
|
||||
'name' => xmlify(file_tag_decode($mtch[1])),
|
||||
'url' => "#",
|
||||
'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
|
||||
'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""),
|
||||
'first' => $first,
|
||||
'last' => false
|
||||
);
|
||||
|
@ -1639,15 +1639,15 @@ function get_plink($item) {
|
|||
|
||||
if ($a->user['nickname'] != "") {
|
||||
$ret = array(
|
||||
//'href' => z_root()."/display/".$a->user['nickname']."/".$item['id'],
|
||||
'href' => z_root()."/display/".$item['guid'],
|
||||
'orig' => z_root()."/display/".$item['guid'],
|
||||
//'href' => "display/".$a->user['nickname']."/".$item['id'],
|
||||
'href' => "display/".$item['guid'],
|
||||
'orig' => "display/".$item['guid'],
|
||||
'title' => t('View on separate page'),
|
||||
'orig_title' => t('view on separate page'),
|
||||
);
|
||||
|
||||
if (x($item,'plink')) {
|
||||
$ret["href"] = $item['plink'];
|
||||
$ret["href"] = $a->remove_baseurl($item['plink']);
|
||||
$ret["title"] = t('link to source');
|
||||
}
|
||||
|
||||
|
|
15
index.php
15
index.php
|
@ -371,7 +371,7 @@ $a->init_page_end();
|
|||
if(x($_SESSION,'visitor_home'))
|
||||
$homebase = $_SESSION['visitor_home'];
|
||||
elseif(local_user())
|
||||
$homebase = $a->get_baseurl() . '/profile/' . $a->user['nickname'];
|
||||
$homebase = 'profile/' . $a->user['nickname'];
|
||||
|
||||
if(isset($homebase))
|
||||
$a->page['content'] .= '<script>var homebase="' . $homebase . '" ; </script>';
|
||||
|
@ -407,15 +407,6 @@ if(x($_SESSION,'sysmsg_info')) {
|
|||
call_hooks('page_end', $a->page['content']);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Add a place for the pause/resume Ajax indicator
|
||||
*
|
||||
*/
|
||||
|
||||
$a->page['content'] .= '<div id="pause"></div>';
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Add the navigation (menu) template
|
||||
|
@ -432,10 +423,10 @@ if($a->module != 'install' && $a->module != 'maintenance') {
|
|||
|
||||
if($a->is_mobile || $a->is_tablet) {
|
||||
if(isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
|
||||
$link = $a->get_baseurl() . '/toggle_mobile?address=' . curPageURL();
|
||||
$link = 'toggle_mobile?address=' . curPageURL();
|
||||
}
|
||||
else {
|
||||
$link = $a->get_baseurl() . '/toggle_mobile?off=1&address=' . curPageURL();
|
||||
$link = 'toggle_mobile?off=1&address=' . curPageURL();
|
||||
}
|
||||
$a->page['footer'] = replace_macros(get_markup_template("toggle_mobile_footer.tpl"), array(
|
||||
'$toggle_link' => $link,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
|
||||
{
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(false); // opacity is non-negative, but we will clamp it
|
||||
}
|
||||
|
||||
public function validate($number, $config, $context) {
|
||||
$result = parent::validate($number, $config, $context);
|
||||
if ($result === false) return $result;
|
||||
$float = (float) $result;
|
||||
if ($float < 0.0) $result = '0';
|
||||
if ($float > 1.0) $result = '1';
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Decorator which enables CSS properties to be disabled for specific elements.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
|
||||
{
|
||||
public $def, $element;
|
||||
|
||||
/**
|
||||
* @param $def Definition to wrap
|
||||
* @param $element Element to deny
|
||||
*/
|
||||
public function __construct($def, $element) {
|
||||
$this->def = $def;
|
||||
$this->element = $element;
|
||||
}
|
||||
/**
|
||||
* Checks if CurrentToken is set and equal to $this->element
|
||||
*/
|
||||
public function validate($string, $config, $context) {
|
||||
$token = $context->get('CurrentToken', true);
|
||||
if ($token && $token->name == $this->element) return false;
|
||||
return $this->def->validate($string, $config, $context);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a font family list according to CSS spec
|
||||
* @todo whitelisting allowed fonts would be nice
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
static $generic_names = array(
|
||||
'serif' => true,
|
||||
'sans-serif' => true,
|
||||
'monospace' => true,
|
||||
'fantasy' => true,
|
||||
'cursive' => true
|
||||
);
|
||||
|
||||
// assume that no font names contain commas in them
|
||||
$fonts = explode(',', $string);
|
||||
$final = '';
|
||||
foreach($fonts as $font) {
|
||||
$font = trim($font);
|
||||
if ($font === '') continue;
|
||||
// match a generic name
|
||||
if (isset($generic_names[$font])) {
|
||||
$final .= $font . ', ';
|
||||
continue;
|
||||
}
|
||||
// match a quoted name
|
||||
if ($font[0] === '"' || $font[0] === "'") {
|
||||
$length = strlen($font);
|
||||
if ($length <= 2) continue;
|
||||
$quote = $font[0];
|
||||
if ($font[$length - 1] !== $quote) continue;
|
||||
$font = substr($font, 1, $length - 2);
|
||||
}
|
||||
|
||||
$font = $this->expandCSSEscape($font);
|
||||
|
||||
// $font is a pure representation of the font name
|
||||
|
||||
if (ctype_alnum($font) && $font !== '') {
|
||||
// very simple font, allow it in unharmed
|
||||
$final .= $font . ', ';
|
||||
continue;
|
||||
}
|
||||
|
||||
// bugger out on whitespace. form feed (0C) really
|
||||
// shouldn't show up regardless
|
||||
$font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
|
||||
|
||||
// These ugly transforms don't pose a security
|
||||
// risk (as \\ and \" might). We could try to be clever and
|
||||
// use single-quote wrapping when there is a double quote
|
||||
// present, but I have choosen not to implement that.
|
||||
// (warning: this code relies on the selection of quotation
|
||||
// mark below)
|
||||
$font = str_replace('\\', '\\5C ', $font);
|
||||
$font = str_replace('"', '\\22 ', $font);
|
||||
|
||||
// complicated font, requires quoting
|
||||
$final .= "\"$font\", "; // note that this will later get turned into "
|
||||
}
|
||||
$final = rtrim($final, ', ');
|
||||
if ($final === '') return false;
|
||||
return $final;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents a Length as defined by CSS.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
protected $min, $max;
|
||||
|
||||
/**
|
||||
* @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable.
|
||||
* @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable.
|
||||
*/
|
||||
public function __construct($min = null, $max = null) {
|
||||
$this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
|
||||
$this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
|
||||
}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
$string = $this->parseCDATA($string);
|
||||
|
||||
// Optimizations
|
||||
if ($string === '') return false;
|
||||
if ($string === '0') return '0';
|
||||
if (strlen($string) === 1) return false;
|
||||
|
||||
$length = HTMLPurifier_Length::make($string);
|
||||
if (!$length->isValid()) return false;
|
||||
|
||||
if ($this->min) {
|
||||
$c = $length->compareTo($this->min);
|
||||
if ($c === false) return false;
|
||||
if ($c < 0) return false;
|
||||
}
|
||||
if ($this->max) {
|
||||
$c = $length->compareTo($this->max);
|
||||
if ($c === false) return false;
|
||||
if ($c > 0) return false;
|
||||
}
|
||||
|
||||
return $length->toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,78 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates shorthand CSS property list-style.
|
||||
* @warning Does not support url tokens that have internal spaces.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
/**
|
||||
* Local copy of component validators.
|
||||
* @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
|
||||
*/
|
||||
protected $info;
|
||||
|
||||
public function __construct($config) {
|
||||
$def = $config->getCSSDefinition();
|
||||
$this->info['list-style-type'] = $def->info['list-style-type'];
|
||||
$this->info['list-style-position'] = $def->info['list-style-position'];
|
||||
$this->info['list-style-image'] = $def->info['list-style-image'];
|
||||
}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
// regular pre-processing
|
||||
$string = $this->parseCDATA($string);
|
||||
if ($string === '') return false;
|
||||
|
||||
// assumes URI doesn't have spaces in it
|
||||
$bits = explode(' ', strtolower($string)); // bits to process
|
||||
|
||||
$caught = array();
|
||||
$caught['type'] = false;
|
||||
$caught['position'] = false;
|
||||
$caught['image'] = false;
|
||||
|
||||
$i = 0; // number of catches
|
||||
$none = false;
|
||||
|
||||
foreach ($bits as $bit) {
|
||||
if ($i >= 3) return; // optimization bit
|
||||
if ($bit === '') continue;
|
||||
foreach ($caught as $key => $status) {
|
||||
if ($status !== false) continue;
|
||||
$r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
|
||||
if ($r === false) continue;
|
||||
if ($r === 'none') {
|
||||
if ($none) continue;
|
||||
else $none = true;
|
||||
if ($key == 'image') continue;
|
||||
}
|
||||
$caught[$key] = $r;
|
||||
$i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$i) return false;
|
||||
|
||||
$ret = array();
|
||||
|
||||
// construct type
|
||||
if ($caught['type']) $ret[] = $caught['type'];
|
||||
|
||||
// construct image
|
||||
if ($caught['image']) $ret[] = $caught['image'];
|
||||
|
||||
// construct position
|
||||
if ($caught['position']) $ret[] = $caught['position'];
|
||||
|
||||
if (empty($ret)) return false;
|
||||
return implode(' ', $ret);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a Percentage as defined by the CSS spec.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
/**
|
||||
* Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation
|
||||
*/
|
||||
protected $number_def;
|
||||
|
||||
/**
|
||||
* @param Bool indicating whether to forbid negative values
|
||||
*/
|
||||
public function __construct($non_negative = false) {
|
||||
$this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
|
||||
}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
$string = $this->parseCDATA($string);
|
||||
|
||||
if ($string === '') return false;
|
||||
$length = strlen($string);
|
||||
if ($length === 1) return false;
|
||||
if ($string[$length - 1] !== '%') return false;
|
||||
|
||||
$number = substr($string, 0, $length - 1);
|
||||
$number = $this->number_def->validate($number, $config, $context);
|
||||
|
||||
if ($number === false) return false;
|
||||
return "$number%";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a boolean attribute
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
protected $name;
|
||||
public $minimized = true;
|
||||
|
||||
public function __construct($name = false) {$this->name = $name;}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
if (empty($string)) return false;
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string Name of attribute
|
||||
*/
|
||||
public function make($string) {
|
||||
return new HTMLPurifier_AttrDef_HTML_Bool($string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a color according to the HTML spec.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
static $colors = null;
|
||||
if ($colors === null) $colors = $config->get('Core.ColorKeywords');
|
||||
|
||||
$string = trim($string);
|
||||
|
||||
if (empty($string)) return false;
|
||||
if (isset($colors[$string])) return $colors[$string];
|
||||
if ($string[0] === '#') $hex = substr($string, 1);
|
||||
else $hex = $string;
|
||||
|
||||
$length = strlen($hex);
|
||||
if ($length !== 3 && $length !== 6) return false;
|
||||
if (!ctype_xdigit($hex)) return false;
|
||||
if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
|
||||
|
||||
return "#$hex";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Special-case enum attribute definition that lazy loads allowed frame targets
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
|
||||
{
|
||||
|
||||
public $valid_values = false; // uninitialized value
|
||||
protected $case_sensitive = false;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets');
|
||||
return parent::validate($string, $config, $context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates the HTML attribute ID.
|
||||
* @warning Even though this is the id processor, it
|
||||
* will ignore the directive Attr:IDBlacklist, since it will only
|
||||
* go according to the ID accumulator. Since the accumulator is
|
||||
* automatically generated, it will have already absorbed the
|
||||
* blacklist. If you're hacking around, make sure you use load()!
|
||||
*/
|
||||
|
||||
class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
// ref functionality disabled, since we also have to verify
|
||||
// whether or not the ID it refers to exists
|
||||
|
||||
public function validate($id, $config, $context) {
|
||||
|
||||
if (!$config->get('Attr.EnableID')) return false;
|
||||
|
||||
$id = trim($id); // trim it first
|
||||
|
||||
if ($id === '') return false;
|
||||
|
||||
$prefix = $config->get('Attr.IDPrefix');
|
||||
if ($prefix !== '') {
|
||||
$prefix .= $config->get('Attr.IDPrefixLocal');
|
||||
// prevent re-appending the prefix
|
||||
if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
|
||||
} elseif ($config->get('Attr.IDPrefixLocal') !== '') {
|
||||
trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
|
||||
'%Attr.IDPrefix is set', E_USER_WARNING);
|
||||
}
|
||||
|
||||
//if (!$this->ref) {
|
||||
$id_accumulator =& $context->get('IDAccumulator');
|
||||
if (isset($id_accumulator->ids[$id])) return false;
|
||||
//}
|
||||
|
||||
// we purposely avoid using regex, hopefully this is faster
|
||||
|
||||
if (ctype_alpha($id)) {
|
||||
$result = true;
|
||||
} else {
|
||||
if (!ctype_alpha(@$id[0])) return false;
|
||||
$trim = trim( // primitive style of regexps, I suppose
|
||||
$id,
|
||||
'A..Za..z0..9:-._'
|
||||
);
|
||||
$result = ($trim === '');
|
||||
}
|
||||
|
||||
$regexp = $config->get('Attr.IDBlacklistRegexp');
|
||||
if ($regexp && preg_match($regexp, $id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/*!$this->ref && */$result) $id_accumulator->add($id);
|
||||
|
||||
// if no change was made to the ID, return the result
|
||||
// else, return the new id if stripping whitespace made it
|
||||
// valid, or return false.
|
||||
return $result ? $id : false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates the HTML type length (not to be confused with CSS's length).
|
||||
*
|
||||
* This accepts integer pixels or percentages as lengths for certain
|
||||
* HTML attributes.
|
||||
*/
|
||||
|
||||
class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
|
||||
{
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
$string = trim($string);
|
||||
if ($string === '') return false;
|
||||
|
||||
$parent_result = parent::validate($string, $config, $context);
|
||||
if ($parent_result !== false) return $parent_result;
|
||||
|
||||
$length = strlen($string);
|
||||
$last_char = $string[$length - 1];
|
||||
|
||||
if ($last_char !== '%') return false;
|
||||
|
||||
$points = substr($string, 0, $length - 1);
|
||||
|
||||
if (!is_numeric($points)) return false;
|
||||
|
||||
$points = (int) $points;
|
||||
|
||||
if ($points < 0) return '0%';
|
||||
if ($points > 100) return '100%';
|
||||
|
||||
return ((string) $points) . '%';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a MultiLength as defined by the HTML spec.
|
||||
*
|
||||
* A multilength is either a integer (pixel count), a percentage, or
|
||||
* a relative number.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
|
||||
{
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
$string = trim($string);
|
||||
if ($string === '') return false;
|
||||
|
||||
$parent_result = parent::validate($string, $config, $context);
|
||||
if ($parent_result !== false) return $parent_result;
|
||||
|
||||
$length = strlen($string);
|
||||
$last_char = $string[$length - 1];
|
||||
|
||||
if ($last_char !== '*') return false;
|
||||
|
||||
$int = substr($string, 0, $length - 1);
|
||||
|
||||
if ($int == '') return '*';
|
||||
if (!is_numeric($int)) return false;
|
||||
|
||||
$int = (int) $int;
|
||||
|
||||
if ($int < 0) return false;
|
||||
if ($int == 0) return '0';
|
||||
if ($int == 1) return '*';
|
||||
return ((string) $int) . '*';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates an integer representation of pixels according to the HTML spec.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
protected $max;
|
||||
|
||||
public function __construct($max = null) {
|
||||
$this->max = $max;
|
||||
}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
|
||||
$string = trim($string);
|
||||
if ($string === '0') return $string;
|
||||
if ($string === '') return false;
|
||||
$length = strlen($string);
|
||||
if (substr($string, $length - 2) == 'px') {
|
||||
$string = substr($string, 0, $length - 2);
|
||||
}
|
||||
if (!is_numeric($string)) return false;
|
||||
$int = (int) $string;
|
||||
|
||||
if ($int < 0) return '0';
|
||||
|
||||
// upper-bound value, extremely high values can
|
||||
// crash operating systems, see <http://ha.ckers.org/imagecrash.html>
|
||||
// WARNING, above link WILL crash you if you're using Windows
|
||||
|
||||
if ($this->max !== null && $int > $this->max) return (string) $this->max;
|
||||
|
||||
return (string) $int;
|
||||
|
||||
}
|
||||
|
||||
public function make($string) {
|
||||
if ($string === '') $max = null;
|
||||
else $max = (int) $string;
|
||||
$class = get_class($this);
|
||||
return new $class($max);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates arbitrary text according to the HTML spec.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
return $this->parseCDATA($string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
|
||||
{
|
||||
|
||||
/**
|
||||
* Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator
|
||||
*/
|
||||
protected $ipv4;
|
||||
|
||||
/**
|
||||
* Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator
|
||||
*/
|
||||
protected $ipv6;
|
||||
|
||||
public function __construct() {
|
||||
$this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
|
||||
$this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
|
||||
}
|
||||
|
||||
public function validate($string, $config, $context) {
|
||||
$length = strlen($string);
|
||||
if ($string === '') return '';
|
||||
if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') {
|
||||
//IPv6
|
||||
$ip = substr($string, 1, $length - 2);
|
||||
$valid = $this->ipv6->validate($ip, $config, $context);
|
||||
if ($valid === false) return false;
|
||||
return '['. $valid . ']';
|
||||
}
|
||||
|
||||
// need to do checks on unusual encodings too
|
||||
$ipv4 = $this->ipv4->validate($string, $config, $context);
|
||||
if ($ipv4 !== false) return $ipv4;
|
||||
|
||||
// A regular domain name.
|
||||
|
||||
// This breaks I18N domain names, but we don't have proper IRI support,
|
||||
// so force users to insert Punycode. If there's complaining we'll
|
||||
// try to fix things into an international friendly form.
|
||||
|
||||
// The productions describing this are:
|
||||
$a = '[a-z]'; // alpha
|
||||
$an = '[a-z0-9]'; // alphanum
|
||||
$and = '[a-z0-9-]'; // alphanum | "-"
|
||||
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||
$domainlabel = "$an($and*$an)?";
|
||||
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
||||
$toplabel = "$a($and*$an)?";
|
||||
// hostname = *( domainlabel "." ) toplabel [ "." ]
|
||||
$match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string);
|
||||
if (!$match) return false;
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validates an IPv6 address.
|
||||
* @author Feyd @ forums.devnetwork.net (public domain)
|
||||
* @note This function requires brackets to have been removed from address
|
||||
* in URI.
|
||||
*/
|
||||
class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
|
||||
{
|
||||
|
||||
public function validate($aIP, $config, $context) {
|
||||
|
||||
if (!$this->ip4) $this->_loadRegex();
|
||||
|
||||
$original = $aIP;
|
||||
|
||||
$hex = '[0-9a-fA-F]';
|
||||
$blk = '(?:' . $hex . '{1,4})';
|
||||
$pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
|
||||
|
||||
// prefix check
|
||||
if (strpos($aIP, '/') !== false)
|
||||
{
|
||||
if (preg_match('#' . $pre . '$#s', $aIP, $find))
|
||||
{
|
||||
$aIP = substr($aIP, 0, 0-strlen($find[0]));
|
||||
unset($find);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// IPv4-compatiblity check
|
||||
if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find))
|
||||
{
|
||||
$aIP = substr($aIP, 0, 0-strlen($find[0]));
|
||||
$ip = explode('.', $find[0]);
|
||||
$ip = array_map('dechex', $ip);
|
||||
$aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
|
||||
unset($find, $ip);
|
||||
}
|
||||
|
||||
// compression check
|
||||
$aIP = explode('::', $aIP);
|
||||
$c = count($aIP);
|
||||
if ($c > 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
elseif ($c == 2)
|
||||
{
|
||||
list($first, $second) = $aIP;
|
||||
$first = explode(':', $first);
|
||||
$second = explode(':', $second);
|
||||
|
||||
if (count($first) + count($second) > 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while(count($first) < 8)
|
||||
{
|
||||
array_push($first, '0');
|
||||
}
|
||||
|
||||
array_splice($first, 8 - count($second), 8, $second);
|
||||
$aIP = $first;
|
||||
unset($first,$second);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aIP = explode(':', $aIP[0]);
|
||||
}
|
||||
$c = count($aIP);
|
||||
|
||||
if ($c != 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// All the pieces should be 16-bit hex strings. Are they?
|
||||
foreach ($aIP as $piece)
|
||||
{
|
||||
if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $original;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Pre-transform that changes converts a boolean attribute to fixed CSS
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform {
|
||||
|
||||
/**
|
||||
* Name of boolean attribute that is trigger
|
||||
*/
|
||||
protected $attr;
|
||||
|
||||
/**
|
||||
* CSS declarations to add to style, needs trailing semicolon
|
||||
*/
|
||||
protected $css;
|
||||
|
||||
/**
|
||||
* @param $attr string attribute name to convert from
|
||||
* @param $css string CSS declarations to add to style (needs semicolon)
|
||||
*/
|
||||
public function __construct($attr, $css) {
|
||||
$this->attr = $attr;
|
||||
$this->css = $css;
|
||||
}
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
if (!isset($attr[$this->attr])) return $attr;
|
||||
unset($attr[$this->attr]);
|
||||
$this->prependCSS($attr, $this->css);
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Generic pre-transform that converts an attribute with a fixed number of
|
||||
* values (enumerated) to CSS.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform {
|
||||
|
||||
/**
|
||||
* Name of attribute to transform from
|
||||
*/
|
||||
protected $attr;
|
||||
|
||||
/**
|
||||
* Lookup array of attribute values to CSS
|
||||
*/
|
||||
protected $enumToCSS = array();
|
||||
|
||||
/**
|
||||
* Case sensitivity of the matching
|
||||
* @warning Currently can only be guaranteed to work with ASCII
|
||||
* values.
|
||||
*/
|
||||
protected $caseSensitive = false;
|
||||
|
||||
/**
|
||||
* @param $attr String attribute name to transform from
|
||||
* @param $enumToCSS Lookup array of attribute values to CSS
|
||||
* @param $case_sensitive Boolean case sensitivity indicator, default false
|
||||
*/
|
||||
public function __construct($attr, $enum_to_css, $case_sensitive = false) {
|
||||
$this->attr = $attr;
|
||||
$this->enumToCSS = $enum_to_css;
|
||||
$this->caseSensitive = (bool) $case_sensitive;
|
||||
}
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
|
||||
if (!isset($attr[$this->attr])) return $attr;
|
||||
|
||||
$value = trim($attr[$this->attr]);
|
||||
unset($attr[$this->attr]);
|
||||
|
||||
if (!$this->caseSensitive) $value = strtolower($value);
|
||||
|
||||
if (!isset($this->enumToCSS[$value])) {
|
||||
return $attr;
|
||||
}
|
||||
|
||||
$this->prependCSS($attr, $this->enumToCSS[$value]);
|
||||
|
||||
return $attr;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class for handling width/height length attribute transformations to CSS
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
|
||||
protected $name;
|
||||
protected $cssName;
|
||||
|
||||
public function __construct($name, $css_name = null) {
|
||||
$this->name = $name;
|
||||
$this->cssName = $css_name ? $css_name : $name;
|
||||
}
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
if (!isset($attr[$this->name])) return $attr;
|
||||
$length = $this->confiscateAttr($attr, $this->name);
|
||||
if(ctype_digit($length)) $length .= 'px';
|
||||
$this->prependCSS($attr, $this->cssName . ":$length;");
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Pre-transform that changes deprecated name attribute to ID if necessary
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
// Abort early if we're using relaxed definition of name
|
||||
if ($config->get('HTML.Attr.Name.UseCDATA')) return $attr;
|
||||
if (!isset($attr['name'])) return $attr;
|
||||
$id = $this->confiscateAttr($attr, 'name');
|
||||
if ( isset($attr['id'])) return $attr;
|
||||
$attr['id'] = $id;
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Post-transform that performs validation to the name attribute; if
|
||||
* it is present with an equivalent id attribute, it is passed through;
|
||||
* otherwise validation is performed.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
|
||||
public function __construct() {
|
||||
$this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
|
||||
}
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
if (!isset($attr['name'])) return $attr;
|
||||
$name = $attr['name'];
|
||||
if (isset($attr['id']) && $attr['id'] === $name) return $attr;
|
||||
$result = $this->idDef->validate($name, $config, $context);
|
||||
if ($result === false) unset($attr['name']);
|
||||
else $attr['name'] = $result;
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Writes default type for all objects. Currently only supports flash.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
public $name = "SafeObject";
|
||||
|
||||
function transform($attr, $config, $context) {
|
||||
if (!isset($attr['type'])) $attr['type'] = 'application/x-shockwave-flash';
|
||||
return $attr;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Sets height/width defaults for <textarea>
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
// Calculated from Firefox
|
||||
if (!isset($attr['cols'])) $attr['cols'] = '22';
|
||||
if (!isset($attr['rows'])) $attr['rows'] = '3';
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
|
||||
// constants are slow, so we use as few as possible
|
||||
if (!defined('HTMLPURIFIER_PREFIX')) {
|
||||
define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));
|
||||
}
|
||||
|
||||
// accomodations for versions earlier than 5.0.2
|
||||
// borrowed from PHP_Compat, LGPL licensed, by Aidan Lister <aidan@php.net>
|
||||
if (!defined('PHP_EOL')) {
|
||||
switch (strtoupper(substr(PHP_OS, 0, 3))) {
|
||||
case 'WIN':
|
||||
define('PHP_EOL', "\r\n");
|
||||
break;
|
||||
case 'DAR':
|
||||
define('PHP_EOL', "\r");
|
||||
break;
|
||||
default:
|
||||
define('PHP_EOL', "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap class that contains meta-functionality for HTML Purifier such as
|
||||
* the autoload function.
|
||||
*
|
||||
* @note
|
||||
* This class may be used without any other files from HTML Purifier.
|
||||
*/
|
||||
class HTMLPurifier_Bootstrap
|
||||
{
|
||||
|
||||
/**
|
||||
* Autoload function for HTML Purifier
|
||||
* @param $class Class to load
|
||||
*/
|
||||
public static function autoload($class) {
|
||||
$file = HTMLPurifier_Bootstrap::getPath($class);
|
||||
if (!$file) return false;
|
||||
require HTMLPURIFIER_PREFIX . '/' . $file;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path for a specific class.
|
||||
*/
|
||||
public static function getPath($class) {
|
||||
if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
|
||||
// Custom implementations
|
||||
if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
|
||||
$code = str_replace('_', '-', substr($class, 22));
|
||||
$file = 'HTMLPurifier/Language/classes/' . $code . '.php';
|
||||
} else {
|
||||
$file = str_replace('_', '/', $class) . '.php';
|
||||
}
|
||||
if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Pre-registers" our autoloader on the SPL stack.
|
||||
*/
|
||||
public static function registerAutoload() {
|
||||
$autoload = array('HTMLPurifier_Bootstrap', 'autoload');
|
||||
if ( ($funcs = spl_autoload_functions()) === false ) {
|
||||
spl_autoload_register($autoload);
|
||||
} elseif (function_exists('spl_autoload_unregister')) {
|
||||
$compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
|
||||
version_compare(PHP_VERSION, '5.1.0', '>=');
|
||||
foreach ($funcs as $func) {
|
||||
if (is_array($func)) {
|
||||
// :TRICKY: There are some compatibility issues and some
|
||||
// places where we need to error out
|
||||
$reflector = new ReflectionMethod($func[0], $func[1]);
|
||||
if (!$reflector->isStatic()) {
|
||||
throw new Exception('
|
||||
HTML Purifier autoloader registrar is not compatible
|
||||
with non-static object methods due to PHP Bug #44144;
|
||||
Please do not use HTMLPurifier.autoload.php (or any
|
||||
file that includes this file); instead, place the code:
|
||||
spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
|
||||
after your own autoloaders.
|
||||
');
|
||||
}
|
||||
// Suprisingly, spl_autoload_register supports the
|
||||
// Class::staticMethod callback format, although call_user_func doesn't
|
||||
if ($compat) $func = implode('::', $func);
|
||||
}
|
||||
spl_autoload_unregister($func);
|
||||
}
|
||||
spl_autoload_register($autoload);
|
||||
foreach ($funcs as $func) spl_autoload_register($func);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,292 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Defines allowed CSS attributes and what their values are.
|
||||
* @see HTMLPurifier_HTMLDefinition
|
||||
*/
|
||||
class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
||||
{
|
||||
|
||||
public $type = 'CSS';
|
||||
|
||||
/**
|
||||
* Assoc array of attribute name to definition object.
|
||||
*/
|
||||
public $info = array();
|
||||
|
||||
/**
|
||||
* Constructs the info array. The meat of this class.
|
||||
*/
|
||||
protected function doSetup($config) {
|
||||
|
||||
$this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('left', 'right', 'center', 'justify'), false);
|
||||
|
||||
$border_style =
|
||||
$this->info['border-bottom-style'] =
|
||||
$this->info['border-right-style'] =
|
||||
$this->info['border-left-style'] =
|
||||
$this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
|
||||
'groove', 'ridge', 'inset', 'outset'), false);
|
||||
|
||||
$this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
|
||||
|
||||
$this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('none', 'left', 'right', 'both'), false);
|
||||
$this->info['float'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('none', 'left', 'right'), false);
|
||||
$this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('normal', 'italic', 'oblique'), false);
|
||||
$this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('normal', 'small-caps'), false);
|
||||
|
||||
$uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
|
||||
array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('none')),
|
||||
new HTMLPurifier_AttrDef_CSS_URI()
|
||||
)
|
||||
);
|
||||
|
||||
$this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('inside', 'outside'), false);
|
||||
$this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('disc', 'circle', 'square', 'decimal', 'lower-roman',
|
||||
'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
|
||||
$this->info['list-style-image'] = $uri_or_none;
|
||||
|
||||
$this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
|
||||
|
||||
$this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('capitalize', 'uppercase', 'lowercase', 'none'), false);
|
||||
$this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
|
||||
$this->info['background-image'] = $uri_or_none;
|
||||
$this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
|
||||
);
|
||||
$this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('scroll', 'fixed')
|
||||
);
|
||||
$this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
|
||||
|
||||
$border_color =
|
||||
$this->info['border-top-color'] =
|
||||
$this->info['border-bottom-color'] =
|
||||
$this->info['border-left-color'] =
|
||||
$this->info['border-right-color'] =
|
||||
$this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('transparent')),
|
||||
new HTMLPurifier_AttrDef_CSS_Color()
|
||||
));
|
||||
|
||||
$this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
|
||||
|
||||
$this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
|
||||
|
||||
$border_width =
|
||||
$this->info['border-top-width'] =
|
||||
$this->info['border-bottom-width'] =
|
||||
$this->info['border-left-width'] =
|
||||
$this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
|
||||
));
|
||||
|
||||
$this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
|
||||
|
||||
$this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
||||
new HTMLPurifier_AttrDef_CSS_Length()
|
||||
));
|
||||
|
||||
$this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
||||
new HTMLPurifier_AttrDef_CSS_Length()
|
||||
));
|
||||
|
||||
$this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
|
||||
'small', 'medium', 'large', 'x-large', 'xx-large',
|
||||
'larger', 'smaller')),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(),
|
||||
new HTMLPurifier_AttrDef_CSS_Length()
|
||||
));
|
||||
|
||||
$this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('normal')),
|
||||
new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true)
|
||||
));
|
||||
|
||||
$margin =
|
||||
$this->info['margin-top'] =
|
||||
$this->info['margin-bottom'] =
|
||||
$this->info['margin-left'] =
|
||||
$this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(),
|
||||
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
||||
));
|
||||
|
||||
$this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
|
||||
|
||||
// non-negative
|
||||
$padding =
|
||||
$this->info['padding-top'] =
|
||||
$this->info['padding-bottom'] =
|
||||
$this->info['padding-left'] =
|
||||
$this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true)
|
||||
));
|
||||
|
||||
$this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
|
||||
|
||||
$this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage()
|
||||
));
|
||||
|
||||
$trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0'),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage(true),
|
||||
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
||||
));
|
||||
$max = $config->get('CSS.MaxImgLength');
|
||||
|
||||
$this->info['width'] =
|
||||
$this->info['height'] =
|
||||
$max === null ?
|
||||
$trusted_wh :
|
||||
new HTMLPurifier_AttrDef_Switch('img',
|
||||
// For img tags:
|
||||
new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_CSS_Length('0', $max),
|
||||
new HTMLPurifier_AttrDef_Enum(array('auto'))
|
||||
)),
|
||||
// For everyone else:
|
||||
$trusted_wh
|
||||
);
|
||||
|
||||
$this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
|
||||
|
||||
$this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
|
||||
|
||||
// this could use specialized code
|
||||
$this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
|
||||
array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
|
||||
'400', '500', '600', '700', '800', '900'), false);
|
||||
|
||||
// MUST be called after other font properties, as it references
|
||||
// a CSSDefinition object
|
||||
$this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
|
||||
|
||||
// same here
|
||||
$this->info['border'] =
|
||||
$this->info['border-bottom'] =
|
||||
$this->info['border-top'] =
|
||||
$this->info['border-left'] =
|
||||
$this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
|
||||
|
||||
$this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
|
||||
'collapse', 'separate'));
|
||||
|
||||
$this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
|
||||
'top', 'bottom'));
|
||||
|
||||
$this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
|
||||
'auto', 'fixed'));
|
||||
|
||||
$this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
|
||||
new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
|
||||
'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
|
||||
new HTMLPurifier_AttrDef_CSS_Length(),
|
||||
new HTMLPurifier_AttrDef_CSS_Percentage()
|
||||
));
|
||||
|
||||
$this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
|
||||
|
||||
// partial support
|
||||
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
|
||||
|
||||
if ($config->get('CSS.Proprietary')) {
|
||||
$this->doSetupProprietary($config);
|
||||
}
|
||||
|
||||
if ($config->get('CSS.AllowTricky')) {
|
||||
$this->doSetupTricky($config);
|
||||
}
|
||||
|
||||
$allow_important = $config->get('CSS.AllowImportant');
|
||||
// wrap all attr-defs with decorator that handles !important
|
||||
foreach ($this->info as $k => $v) {
|
||||
$this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
|
||||
}
|
||||
|
||||
$this->setupConfigStuff($config);
|
||||
}
|
||||
|
||||
protected function doSetupProprietary($config) {
|
||||
// Internet Explorer only scrollbar colors
|
||||
$this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
$this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
|
||||
|
||||
// technically not proprietary, but CSS3, and no one supports it
|
||||
$this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
$this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
$this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||
|
||||
// only opacity, for now
|
||||
$this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
|
||||
|
||||
}
|
||||
|
||||
protected function doSetupTricky($config) {
|
||||
$this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
|
||||
'inline', 'block', 'list-item', 'run-in', 'compact',
|
||||
'marker', 'table', 'inline-table', 'table-row-group',
|
||||
'table-header-group', 'table-footer-group', 'table-row',
|
||||
'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
|
||||
));
|
||||
$this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
|
||||
'visible', 'hidden', 'collapse'
|
||||
));
|
||||
$this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs extra config-based processing. Based off of
|
||||
* HTMLPurifier_HTMLDefinition.
|
||||
* @todo Refactor duplicate elements into common class (probably using
|
||||
* composition, not inheritance).
|
||||
*/
|
||||
protected function setupConfigStuff($config) {
|
||||
|
||||
// setup allowed elements
|
||||
$support = "(for information on implementing this, see the ".
|
||||
"support forums) ";
|
||||
$allowed_attributes = $config->get('CSS.AllowedProperties');
|
||||
if ($allowed_attributes !== null) {
|
||||
foreach ($this->info as $name => $d) {
|
||||
if(!isset($allowed_attributes[$name])) unset($this->info[$name]);
|
||||
unset($allowed_attributes[$name]);
|
||||
}
|
||||
// emit errors
|
||||
foreach ($allowed_attributes as $name => $d) {
|
||||
// :TODO: Is this htmlspecialchars() call really necessary?
|
||||
$name = htmlspecialchars($name);
|
||||
trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Definition that allows a set of elements, and allows no children.
|
||||
* @note This is a hack to reuse code from HTMLPurifier_ChildDef_Required,
|
||||
* really, one shouldn't inherit from the other. Only altered behavior
|
||||
* is to overload a returned false with an array. Thus, it will never
|
||||
* return false.
|
||||
*/
|
||||
class HTMLPurifier_ChildDef_Optional extends HTMLPurifier_ChildDef_Required
|
||||
{
|
||||
public $allow_empty = true;
|
||||
public $type = 'optional';
|
||||
public function validateChildren($tokens_of_children, $config, $context) {
|
||||
$result = parent::validateChildren($tokens_of_children, $config, $context);
|
||||
// we assume that $tokens_of_children is not modified
|
||||
if ($result === false) {
|
||||
if (empty($tokens_of_children)) return true;
|
||||
elseif ($this->whitespace) return $tokens_of_children;
|
||||
else return array();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,117 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Definition that allows a set of elements, but disallows empty children.
|
||||
*/
|
||||
class HTMLPurifier_ChildDef_Required extends HTMLPurifier_ChildDef
|
||||
{
|
||||
/**
|
||||
* Lookup table of allowed elements.
|
||||
* @public
|
||||
*/
|
||||
public $elements = array();
|
||||
/**
|
||||
* Whether or not the last passed node was all whitespace.
|
||||
*/
|
||||
protected $whitespace = false;
|
||||
/**
|
||||
* @param $elements List of allowed element names (lowercase).
|
||||
*/
|
||||
public function __construct($elements) {
|
||||
if (is_string($elements)) {
|
||||
$elements = str_replace(' ', '', $elements);
|
||||
$elements = explode('|', $elements);
|
||||
}
|
||||
$keys = array_keys($elements);
|
||||
if ($keys == array_keys($keys)) {
|
||||
$elements = array_flip($elements);
|
||||
foreach ($elements as $i => $x) {
|
||||
$elements[$i] = true;
|
||||
if (empty($i)) unset($elements[$i]); // remove blank
|
||||
}
|
||||
}
|
||||
$this->elements = $elements;
|
||||
}
|
||||
public $allow_empty = false;
|
||||
public $type = 'required';
|
||||
public function validateChildren($tokens_of_children, $config, $context) {
|
||||
// Flag for subclasses
|
||||
$this->whitespace = false;
|
||||
|
||||
// if there are no tokens, delete parent node
|
||||
if (empty($tokens_of_children)) return false;
|
||||
|
||||
// the new set of children
|
||||
$result = array();
|
||||
|
||||
// current depth into the nest
|
||||
$nesting = 0;
|
||||
|
||||
// whether or not we're deleting a node
|
||||
$is_deleting = false;
|
||||
|
||||
// whether or not parsed character data is allowed
|
||||
// this controls whether or not we silently drop a tag
|
||||
// or generate escaped HTML from it
|
||||
$pcdata_allowed = isset($this->elements['#PCDATA']);
|
||||
|
||||
// a little sanity check to make sure it's not ALL whitespace
|
||||
$all_whitespace = true;
|
||||
|
||||
// some configuration
|
||||
$escape_invalid_children = $config->get('Core.EscapeInvalidChildren');
|
||||
|
||||
// generator
|
||||
$gen = new HTMLPurifier_Generator($config, $context);
|
||||
|
||||
foreach ($tokens_of_children as $token) {
|
||||
if (!empty($token->is_whitespace)) {
|
||||
$result[] = $token;
|
||||
continue;
|
||||
}
|
||||
$all_whitespace = false; // phew, we're not talking about whitespace
|
||||
|
||||
$is_child = ($nesting == 0);
|
||||
|
||||
if ($token instanceof HTMLPurifier_Token_Start) {
|
||||
$nesting++;
|
||||
} elseif ($token instanceof HTMLPurifier_Token_End) {
|
||||
$nesting--;
|
||||
}
|
||||
|
||||
if ($is_child) {
|
||||
$is_deleting = false;
|
||||
if (!isset($this->elements[$token->name])) {
|
||||
$is_deleting = true;
|
||||
if ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text) {
|
||||
$result[] = $token;
|
||||
} elseif ($pcdata_allowed && $escape_invalid_children) {
|
||||
$result[] = new HTMLPurifier_Token_Text(
|
||||
$gen->generateFromToken($token)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!$is_deleting || ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text)) {
|
||||
$result[] = $token;
|
||||
} elseif ($pcdata_allowed && $escape_invalid_children) {
|
||||
$result[] =
|
||||
new HTMLPurifier_Token_Text(
|
||||
$gen->generateFromToken($token)
|
||||
);
|
||||
} else {
|
||||
// drop silently
|
||||
}
|
||||
}
|
||||
if (empty($result)) return false;
|
||||
if ($all_whitespace) {
|
||||
$this->whitespace = true;
|
||||
return false;
|
||||
}
|
||||
if ($tokens_of_children == $result) return true;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Takes the contents of blockquote when in strict and reformats for validation.
|
||||
*/
|
||||
class HTMLPurifier_ChildDef_StrictBlockquote extends HTMLPurifier_ChildDef_Required
|
||||
{
|
||||
protected $real_elements;
|
||||
protected $fake_elements;
|
||||
public $allow_empty = true;
|
||||
public $type = 'strictblockquote';
|
||||
protected $init = false;
|
||||
|
||||
/**
|
||||
* @note We don't want MakeWellFormed to auto-close inline elements since
|
||||
* they might be allowed.
|
||||
*/
|
||||
public function getAllowedElements($config) {
|
||||
$this->init($config);
|
||||
return $this->fake_elements;
|
||||
}
|
||||
|
||||
public function validateChildren($tokens_of_children, $config, $context) {
|
||||
|
||||
$this->init($config);
|
||||
|
||||
// trick the parent class into thinking it allows more
|
||||
$this->elements = $this->fake_elements;
|
||||
$result = parent::validateChildren($tokens_of_children, $config, $context);
|
||||
$this->elements = $this->real_elements;
|
||||
|
||||
if ($result === false) return array();
|
||||
if ($result === true) $result = $tokens_of_children;
|
||||
|
||||
$def = $config->getHTMLDefinition();
|
||||
$block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper);
|
||||
$block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper);
|
||||
$is_inline = false;
|
||||
$depth = 0;
|
||||
$ret = array();
|
||||
|
||||
// assuming that there are no comment tokens
|
||||
foreach ($result as $i => $token) {
|
||||
$token = $result[$i];
|
||||
// ifs are nested for readability
|
||||
if (!$is_inline) {
|
||||
if (!$depth) {
|
||||
if (
|
||||
($token instanceof HTMLPurifier_Token_Text && !$token->is_whitespace) ||
|
||||
(!$token instanceof HTMLPurifier_Token_Text && !isset($this->elements[$token->name]))
|
||||
) {
|
||||
$is_inline = true;
|
||||
$ret[] = $block_wrap_start;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$depth) {
|
||||
// starting tokens have been inline text / empty
|
||||
if ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) {
|
||||
if (isset($this->elements[$token->name])) {
|
||||
// ended
|
||||
$ret[] = $block_wrap_end;
|
||||
$is_inline = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$ret[] = $token;
|
||||
if ($token instanceof HTMLPurifier_Token_Start) $depth++;
|
||||
if ($token instanceof HTMLPurifier_Token_End) $depth--;
|
||||
}
|
||||
if ($is_inline) $ret[] = $block_wrap_end;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function init($config) {
|
||||
if (!$this->init) {
|
||||
$def = $config->getHTMLDefinition();
|
||||
// allow all inline elements
|
||||
$this->real_elements = $this->elements;
|
||||
$this->fake_elements = $def->info_content_sets['Flow'];
|
||||
$this->fake_elements['#PCDATA'] = true;
|
||||
$this->init = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,142 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Definition for tables
|
||||
*/
|
||||
class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
|
||||
{
|
||||
public $allow_empty = false;
|
||||
public $type = 'table';
|
||||
public $elements = array('tr' => true, 'tbody' => true, 'thead' => true,
|
||||
'tfoot' => true, 'caption' => true, 'colgroup' => true, 'col' => true);
|
||||
public function __construct() {}
|
||||
public function validateChildren($tokens_of_children, $config, $context) {
|
||||
if (empty($tokens_of_children)) return false;
|
||||
|
||||
// this ensures that the loop gets run one last time before closing
|
||||
// up. It's a little bit of a hack, but it works! Just make sure you
|
||||
// get rid of the token later.
|
||||
$tokens_of_children[] = false;
|
||||
|
||||
// only one of these elements is allowed in a table
|
||||
$caption = false;
|
||||
$thead = false;
|
||||
$tfoot = false;
|
||||
|
||||
// as many of these as you want
|
||||
$cols = array();
|
||||
$content = array();
|
||||
|
||||
$nesting = 0; // current depth so we can determine nodes
|
||||
$is_collecting = false; // are we globbing together tokens to package
|
||||
// into one of the collectors?
|
||||
$collection = array(); // collected nodes
|
||||
$tag_index = 0; // the first node might be whitespace,
|
||||
// so this tells us where the start tag is
|
||||
|
||||
foreach ($tokens_of_children as $token) {
|
||||
$is_child = ($nesting == 0);
|
||||
|
||||
if ($token === false) {
|
||||
// terminating sequence started
|
||||
} elseif ($token instanceof HTMLPurifier_Token_Start) {
|
||||
$nesting++;
|
||||
} elseif ($token instanceof HTMLPurifier_Token_End) {
|
||||
$nesting--;
|
||||
}
|
||||
|
||||
// handle node collection
|
||||
if ($is_collecting) {
|
||||
if ($is_child) {
|
||||
// okay, let's stash the tokens away
|
||||
// first token tells us the type of the collection
|
||||
switch ($collection[$tag_index]->name) {
|
||||
case 'tr':
|
||||
case 'tbody':
|
||||
$content[] = $collection;
|
||||
break;
|
||||
case 'caption':
|
||||
if ($caption !== false) break;
|
||||
$caption = $collection;
|
||||
break;
|
||||
case 'thead':
|
||||
case 'tfoot':
|
||||
// access the appropriate variable, $thead or $tfoot
|
||||
$var = $collection[$tag_index]->name;
|
||||
if ($$var === false) {
|
||||
$$var = $collection;
|
||||
} else {
|
||||
// transmutate the first and less entries into
|
||||
// tbody tags, and then put into content
|
||||
$collection[$tag_index]->name = 'tbody';
|
||||
$collection[count($collection)-1]->name = 'tbody';
|
||||
$content[] = $collection;
|
||||
}
|
||||
break;
|
||||
case 'colgroup':
|
||||
$cols[] = $collection;
|
||||
break;
|
||||
}
|
||||
$collection = array();
|
||||
$is_collecting = false;
|
||||
$tag_index = 0;
|
||||
} else {
|
||||
// add the node to the collection
|
||||
$collection[] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
// terminate
|
||||
if ($token === false) break;
|
||||
|
||||
if ($is_child) {
|
||||
// determine what we're dealing with
|
||||
if ($token->name == 'col') {
|
||||
// the only empty tag in the possie, we can handle it
|
||||
// immediately
|
||||
$cols[] = array_merge($collection, array($token));
|
||||
$collection = array();
|
||||
$tag_index = 0;
|
||||
continue;
|
||||
}
|
||||
switch($token->name) {
|
||||
case 'caption':
|
||||
case 'colgroup':
|
||||
case 'thead':
|
||||
case 'tfoot':
|
||||
case 'tbody':
|
||||
case 'tr':
|
||||
$is_collecting = true;
|
||||
$collection[] = $token;
|
||||
continue;
|
||||
default:
|
||||
if (!empty($token->is_whitespace)) {
|
||||
$collection[] = $token;
|
||||
$tag_index++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($content)) return false;
|
||||
|
||||
$ret = array();
|
||||
if ($caption !== false) $ret = array_merge($ret, $caption);
|
||||
if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array);
|
||||
if ($thead !== false) $ret = array_merge($ret, $thead);
|
||||
if ($tfoot !== false) $ret = array_merge($ret, $tfoot);
|
||||
foreach ($content as $token_array) $ret = array_merge($ret, $token_array);
|
||||
if (!empty($collection) && $is_collecting == false){
|
||||
// grab the trailing space
|
||||
$ret = array_merge($ret, $collection);
|
||||
}
|
||||
|
||||
array_pop($tokens_of_children); // remove phantom token
|
||||
|
||||
return ($ret === $tokens_of_children) ? true : $ret;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,580 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Configuration object that triggers customizable behavior.
|
||||
*
|
||||
* @warning This class is strongly defined: that means that the class
|
||||
* will fail if an undefined directive is retrieved or set.
|
||||
*
|
||||
* @note Many classes that could (although many times don't) use the
|
||||
* configuration object make it a mandatory parameter. This is
|
||||
* because a configuration object should always be forwarded,
|
||||
* otherwise, you run the risk of missing a parameter and then
|
||||
* being stumped when a configuration directive doesn't work.
|
||||
*
|
||||
* @todo Reconsider some of the public member variables
|
||||
*/
|
||||
class HTMLPurifier_Config
|
||||
{
|
||||
|
||||
/**
|
||||
* HTML Purifier's version
|
||||
*/
|
||||
public $version = '4.1.1';
|
||||
|
||||
/**
|
||||
* Bool indicator whether or not to automatically finalize
|
||||
* the object if a read operation is done
|
||||
*/
|
||||
public $autoFinalize = true;
|
||||
|
||||
// protected member variables
|
||||
|
||||
/**
|
||||
* Namespace indexed array of serials for specific namespaces (see
|
||||
* getSerial() for more info).
|
||||
*/
|
||||
protected $serials = array();
|
||||
|
||||
/**
|
||||
* Serial for entire configuration object
|
||||
*/
|
||||
protected $serial;
|
||||
|
||||
/**
|
||||
* Parser for variables
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Reference HTMLPurifier_ConfigSchema for value checking
|
||||
* @note This is public for introspective purposes. Please don't
|
||||
* abuse!
|
||||
*/
|
||||
public $def;
|
||||
|
||||
/**
|
||||
* Indexed array of definitions
|
||||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* Bool indicator whether or not config is finalized
|
||||
*/
|
||||
protected $finalized = false;
|
||||
|
||||
/**
|
||||
* Property list containing configuration directives.
|
||||
*/
|
||||
protected $plist;
|
||||
|
||||
/**
|
||||
* Whether or not a set is taking place due to an
|
||||
* alias lookup.
|
||||
*/
|
||||
private $aliasMode;
|
||||
|
||||
/**
|
||||
* Set to false if you do not want line and file numbers in errors
|
||||
* (useful when unit testing)
|
||||
*/
|
||||
public $chatty = true;
|
||||
|
||||
/**
|
||||
* Current lock; only gets to this namespace are allowed.
|
||||
*/
|
||||
private $lock;
|
||||
|
||||
/**
|
||||
* @param $definition HTMLPurifier_ConfigSchema that defines what directives
|
||||
* are allowed.
|
||||
*/
|
||||
public function __construct($definition, $parent = null) {
|
||||
$parent = $parent ? $parent : $definition->defaultPlist;
|
||||
$this->plist = new HTMLPurifier_PropertyList($parent);
|
||||
$this->def = $definition; // keep a copy around for checking
|
||||
$this->parser = new HTMLPurifier_VarParser_Flexible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor that creates a config object based on a mixed var
|
||||
* @param mixed $config Variable that defines the state of the config
|
||||
* object. Can be: a HTMLPurifier_Config() object,
|
||||
* an array of directives based on loadArray(),
|
||||
* or a string filename of an ini file.
|
||||
* @param HTMLPurifier_ConfigSchema Schema object
|
||||
* @return Configured HTMLPurifier_Config object
|
||||
*/
|
||||
public static function create($config, $schema = null) {
|
||||
if ($config instanceof HTMLPurifier_Config) {
|
||||
// pass-through
|
||||
return $config;
|
||||
}
|
||||
if (!$schema) {
|
||||
$ret = HTMLPurifier_Config::createDefault();
|
||||
} else {
|
||||
$ret = new HTMLPurifier_Config($schema);
|
||||
}
|
||||
if (is_string($config)) $ret->loadIni($config);
|
||||
elseif (is_array($config)) $ret->loadArray($config);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new config object that inherits from a previous one.
|
||||
* @param HTMLPurifier_Config $config Configuration object to inherit
|
||||
* from.
|
||||
* @return HTMLPurifier_Config object with $config as its parent.
|
||||
*/
|
||||
public static function inherit(HTMLPurifier_Config $config) {
|
||||
return new HTMLPurifier_Config($config->def, $config->plist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor that creates a default configuration object.
|
||||
* @return Default HTMLPurifier_Config object.
|
||||
*/
|
||||
public static function createDefault() {
|
||||
$definition = HTMLPurifier_ConfigSchema::instance();
|
||||
$config = new HTMLPurifier_Config($definition);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a value from the configuration.
|
||||
* @param $key String key
|
||||
*/
|
||||
public function get($key, $a = null) {
|
||||
if ($a !== null) {
|
||||
$this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING);
|
||||
$key = "$key.$a";
|
||||
}
|
||||
if (!$this->finalized) $this->autoFinalize();
|
||||
if (!isset($this->def->info[$key])) {
|
||||
// can't add % due to SimpleTest bug
|
||||
$this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
if (isset($this->def->info[$key]->isAlias)) {
|
||||
$d = $this->def->info[$key];
|
||||
$this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key,
|
||||
E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
if ($this->lock) {
|
||||
list($ns) = explode('.', $key);
|
||||
if ($ns !== $this->lock) {
|
||||
$this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return $this->plist->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives an array of directives to values from a given namespace
|
||||
* @param $namespace String namespace
|
||||
*/
|
||||
public function getBatch($namespace) {
|
||||
if (!$this->finalized) $this->autoFinalize();
|
||||
$full = $this->getAll();
|
||||
if (!isset($full[$namespace])) {
|
||||
$this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
return $full[$namespace];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a md5 signature of a segment of the configuration object
|
||||
* that uniquely identifies that particular configuration
|
||||
* @note Revision is handled specially and is removed from the batch
|
||||
* before processing!
|
||||
* @param $namespace Namespace to get serial for
|
||||
*/
|
||||
public function getBatchSerial($namespace) {
|
||||
if (empty($this->serials[$namespace])) {
|
||||
$batch = $this->getBatch($namespace);
|
||||
unset($batch['DefinitionRev']);
|
||||
$this->serials[$namespace] = md5(serialize($batch));
|
||||
}
|
||||
return $this->serials[$namespace];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a md5 signature for the entire configuration object
|
||||
* that uniquely identifies that particular configuration
|
||||
*/
|
||||
public function getSerial() {
|
||||
if (empty($this->serial)) {
|
||||
$this->serial = md5(serialize($this->getAll()));
|
||||
}
|
||||
return $this->serial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all directives, organized by namespace
|
||||
* @warning This is a pretty inefficient function, avoid if you can
|
||||
*/
|
||||
public function getAll() {
|
||||
if (!$this->finalized) $this->autoFinalize();
|
||||
$ret = array();
|
||||
foreach ($this->plist->squash() as $name => $value) {
|
||||
list($ns, $key) = explode('.', $name, 2);
|
||||
$ret[$ns][$key] = $value;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value to configuration.
|
||||
* @param $key String key
|
||||
* @param $value Mixed value
|
||||
*/
|
||||
public function set($key, $value, $a = null) {
|
||||
if (strpos($key, '.') === false) {
|
||||
$namespace = $key;
|
||||
$directive = $value;
|
||||
$value = $a;
|
||||
$key = "$key.$directive";
|
||||
$this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);
|
||||
} else {
|
||||
list($namespace) = explode('.', $key);
|
||||
}
|
||||
if ($this->isFinalized('Cannot set directive after finalization')) return;
|
||||
if (!isset($this->def->info[$key])) {
|
||||
$this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
|
||||
E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
$def = $this->def->info[$key];
|
||||
|
||||
if (isset($def->isAlias)) {
|
||||
if ($this->aliasMode) {
|
||||
$this->triggerError('Double-aliases not allowed, please fix '.
|
||||
'ConfigSchema bug with' . $key, E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
$this->aliasMode = true;
|
||||
$this->set($def->key, $value);
|
||||
$this->aliasMode = false;
|
||||
$this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Raw type might be negative when using the fully optimized form
|
||||
// of stdclass, which indicates allow_null == true
|
||||
$rtype = is_int($def) ? $def : $def->type;
|
||||
if ($rtype < 0) {
|
||||
$type = -$rtype;
|
||||
$allow_null = true;
|
||||
} else {
|
||||
$type = $rtype;
|
||||
$allow_null = isset($def->allow_null);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = $this->parser->parse($value, $type, $allow_null);
|
||||
} catch (HTMLPurifier_VarParserException $e) {
|
||||
$this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
if (is_string($value) && is_object($def)) {
|
||||
// resolve value alias if defined
|
||||
if (isset($def->aliases[$value])) {
|
||||
$value = $def->aliases[$value];
|
||||
}
|
||||
// check to see if the value is allowed
|
||||
if (isset($def->allowed) && !isset($def->allowed[$value])) {
|
||||
$this->triggerError('Value not supported, valid values are: ' .
|
||||
$this->_listify($def->allowed), E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->plist->set($key, $value);
|
||||
|
||||
// reset definitions if the directives they depend on changed
|
||||
// this is a very costly process, so it's discouraged
|
||||
// with finalization
|
||||
if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {
|
||||
$this->definitions[$namespace] = null;
|
||||
}
|
||||
|
||||
$this->serials[$namespace] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for error reporting
|
||||
*/
|
||||
private function _listify($lookup) {
|
||||
$list = array();
|
||||
foreach ($lookup as $name => $b) $list[] = $name;
|
||||
return implode(', ', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves object reference to the HTML definition.
|
||||
* @param $raw Return a copy that has not been setup yet. Must be
|
||||
* called before it's been setup, otherwise won't work.
|
||||
*/
|
||||
public function getHTMLDefinition($raw = false) {
|
||||
return $this->getDefinition('HTML', $raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves object reference to the CSS definition
|
||||
* @param $raw Return a copy that has not been setup yet. Must be
|
||||
* called before it's been setup, otherwise won't work.
|
||||
*/
|
||||
public function getCSSDefinition($raw = false) {
|
||||
return $this->getDefinition('CSS', $raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a definition
|
||||
* @param $type Type of definition: HTML, CSS, etc
|
||||
* @param $raw Whether or not definition should be returned raw
|
||||
*/
|
||||
public function getDefinition($type, $raw = false) {
|
||||
if (!$this->finalized) $this->autoFinalize();
|
||||
// temporarily suspend locks, so we can handle recursive definition calls
|
||||
$lock = $this->lock;
|
||||
$this->lock = null;
|
||||
$factory = HTMLPurifier_DefinitionCacheFactory::instance();
|
||||
$cache = $factory->create($type, $this);
|
||||
$this->lock = $lock;
|
||||
if (!$raw) {
|
||||
// see if we can quickly supply a definition
|
||||
if (!empty($this->definitions[$type])) {
|
||||
if (!$this->definitions[$type]->setup) {
|
||||
$this->definitions[$type]->setup($this);
|
||||
$cache->set($this->definitions[$type], $this);
|
||||
}
|
||||
return $this->definitions[$type];
|
||||
}
|
||||
// memory check missed, try cache
|
||||
$this->definitions[$type] = $cache->get($this);
|
||||
if ($this->definitions[$type]) {
|
||||
// definition in cache, return it
|
||||
return $this->definitions[$type];
|
||||
}
|
||||
} elseif (
|
||||
!empty($this->definitions[$type]) &&
|
||||
!$this->definitions[$type]->setup
|
||||
) {
|
||||
// raw requested, raw in memory, quick return
|
||||
return $this->definitions[$type];
|
||||
}
|
||||
// quick checks failed, let's create the object
|
||||
if ($type == 'HTML') {
|
||||
$this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
|
||||
} elseif ($type == 'CSS') {
|
||||
$this->definitions[$type] = new HTMLPurifier_CSSDefinition();
|
||||
} elseif ($type == 'URI') {
|
||||
$this->definitions[$type] = new HTMLPurifier_URIDefinition();
|
||||
} else {
|
||||
throw new HTMLPurifier_Exception("Definition of $type type not supported");
|
||||
}
|
||||
// quick abort if raw
|
||||
if ($raw) {
|
||||
if (is_null($this->get($type . '.DefinitionID'))) {
|
||||
// fatally error out if definition ID not set
|
||||
throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
|
||||
}
|
||||
return $this->definitions[$type];
|
||||
}
|
||||
// set it up
|
||||
$this->lock = $type;
|
||||
$this->definitions[$type]->setup($this);
|
||||
$this->lock = null;
|
||||
// save in cache
|
||||
$cache->set($this->definitions[$type], $this);
|
||||
return $this->definitions[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration values from an array with the following structure:
|
||||
* Namespace.Directive => Value
|
||||
* @param $config_array Configuration associative array
|
||||
*/
|
||||
public function loadArray($config_array) {
|
||||
if ($this->isFinalized('Cannot load directives after finalization')) return;
|
||||
foreach ($config_array as $key => $value) {
|
||||
$key = str_replace('_', '.', $key);
|
||||
if (strpos($key, '.') !== false) {
|
||||
$this->set($key, $value);
|
||||
} else {
|
||||
$namespace = $key;
|
||||
$namespace_values = $value;
|
||||
foreach ($namespace_values as $directive => $value) {
|
||||
$this->set($namespace .'.'. $directive, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of array(namespace, directive) for all directives
|
||||
* that are allowed in a web-form context as per an allowed
|
||||
* namespaces/directives list.
|
||||
* @param $allowed List of allowed namespaces/directives
|
||||
*/
|
||||
public static function getAllowedDirectivesForForm($allowed, $schema = null) {
|
||||
if (!$schema) {
|
||||
$schema = HTMLPurifier_ConfigSchema::instance();
|
||||
}
|
||||
if ($allowed !== true) {
|
||||
if (is_string($allowed)) $allowed = array($allowed);
|
||||
$allowed_ns = array();
|
||||
$allowed_directives = array();
|
||||
$blacklisted_directives = array();
|
||||
foreach ($allowed as $ns_or_directive) {
|
||||
if (strpos($ns_or_directive, '.') !== false) {
|
||||
// directive
|
||||
if ($ns_or_directive[0] == '-') {
|
||||
$blacklisted_directives[substr($ns_or_directive, 1)] = true;
|
||||
} else {
|
||||
$allowed_directives[$ns_or_directive] = true;
|
||||
}
|
||||
} else {
|
||||
// namespace
|
||||
$allowed_ns[$ns_or_directive] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$ret = array();
|
||||
foreach ($schema->info as $key => $def) {
|
||||
list($ns, $directive) = explode('.', $key, 2);
|
||||
if ($allowed !== true) {
|
||||
if (isset($blacklisted_directives["$ns.$directive"])) continue;
|
||||
if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
|
||||
}
|
||||
if (isset($def->isAlias)) continue;
|
||||
if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
|
||||
$ret[] = array($ns, $directive);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration values from $_GET/$_POST that were posted
|
||||
* via ConfigForm
|
||||
* @param $array $_GET or $_POST array to import
|
||||
* @param $index Index/name that the config variables are in
|
||||
* @param $allowed List of allowed namespaces/directives
|
||||
* @param $mq_fix Boolean whether or not to enable magic quotes fix
|
||||
* @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy
|
||||
*/
|
||||
public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
|
||||
$ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
|
||||
$config = HTMLPurifier_Config::create($ret, $schema);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
|
||||
* @note Same parameters as loadArrayFromForm
|
||||
*/
|
||||
public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {
|
||||
$ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
|
||||
$this->loadArray($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an array from a form into something usable for the more
|
||||
* strict parts of HTMLPurifier_Config
|
||||
*/
|
||||
public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
|
||||
if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
|
||||
$mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
|
||||
|
||||
$allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);
|
||||
$ret = array();
|
||||
foreach ($allowed as $key) {
|
||||
list($ns, $directive) = $key;
|
||||
$skey = "$ns.$directive";
|
||||
if (!empty($array["Null_$skey"])) {
|
||||
$ret[$ns][$directive] = null;
|
||||
continue;
|
||||
}
|
||||
if (!isset($array[$skey])) continue;
|
||||
$value = $mq ? stripslashes($array[$skey]) : $array[$skey];
|
||||
$ret[$ns][$directive] = $value;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads configuration values from an ini file
|
||||
* @param $filename Name of ini file
|
||||
*/
|
||||
public function loadIni($filename) {
|
||||
if ($this->isFinalized('Cannot load directives after finalization')) return;
|
||||
$array = parse_ini_file($filename, true);
|
||||
$this->loadArray($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the configuration object is finalized.
|
||||
* @param $error String error message, or false for no error
|
||||
*/
|
||||
public function isFinalized($error = false) {
|
||||
if ($this->finalized && $error) {
|
||||
$this->triggerError($error, E_USER_ERROR);
|
||||
}
|
||||
return $this->finalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes configuration only if auto finalize is on and not
|
||||
* already finalized
|
||||
*/
|
||||
public function autoFinalize() {
|
||||
if ($this->autoFinalize) {
|
||||
$this->finalize();
|
||||
} else {
|
||||
$this->plist->squash(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes a configuration object, prohibiting further change
|
||||
*/
|
||||
public function finalize() {
|
||||
$this->finalized = true;
|
||||
unset($this->parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a nicely formatted error message by supplying the
|
||||
* stack frame information from two levels up and OUTSIDE of
|
||||
* HTMLPurifier_Config.
|
||||
*/
|
||||
protected function triggerError($msg, $no) {
|
||||
// determine previous stack frame
|
||||
$backtrace = debug_backtrace();
|
||||
if ($this->chatty && isset($backtrace[1])) {
|
||||
$frame = $backtrace[1];
|
||||
$extra = " on line {$frame['line']} in file {$frame['file']}";
|
||||
} else {
|
||||
$extra = '';
|
||||
}
|
||||
trigger_error($msg . $extra, $no);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a serialized form of the configuration object that can
|
||||
* be reconstituted.
|
||||
*/
|
||||
public function serialize() {
|
||||
$this->getDefinition('HTML');
|
||||
$this->getDefinition('CSS');
|
||||
$this->getDefinition('URI');
|
||||
return serialize($this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Fluent interface for validating the contents of member variables.
|
||||
* This should be immutable. See HTMLPurifier_ConfigSchema_Validator for
|
||||
* use-cases. We name this an 'atom' because it's ONLY for validations that
|
||||
* are independent and usually scalar.
|
||||
*/
|
||||
class HTMLPurifier_ConfigSchema_ValidatorAtom
|
||||
{
|
||||
|
||||
protected $context, $obj, $member, $contents;
|
||||
|
||||
public function __construct($context, $obj, $member) {
|
||||
$this->context = $context;
|
||||
$this->obj = $obj;
|
||||
$this->member = $member;
|
||||
$this->contents =& $obj->$member;
|
||||
}
|
||||
|
||||
public function assertIsString() {
|
||||
if (!is_string($this->contents)) $this->error('must be a string');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertIsBool() {
|
||||
if (!is_bool($this->contents)) $this->error('must be a boolean');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertIsArray() {
|
||||
if (!is_array($this->contents)) $this->error('must be an array');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertNotNull() {
|
||||
if ($this->contents === null) $this->error('must not be null');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertAlnum() {
|
||||
$this->assertIsString();
|
||||
if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertNotEmpty() {
|
||||
if (empty($this->contents)) $this->error('must not be empty');
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assertIsLookup() {
|
||||
$this->assertIsArray();
|
||||
foreach ($this->contents as $v) {
|
||||
if ($v !== true) $this->error('must be a lookup array');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function error($msg) {
|
||||
throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
Binary file not shown.
|
@ -1,18 +0,0 @@
|
|||
HTML.AllowedElements
|
||||
TYPE: lookup/null
|
||||
VERSION: 1.3.0
|
||||
DEFAULT: NULL
|
||||
--DESCRIPTION--
|
||||
<p>
|
||||
If HTML Purifier's tag set is unsatisfactory for your needs, you
|
||||
can overload it with your own list of tags to allow. Note that this
|
||||
method is subtractive: it does its job by taking away from HTML Purifier
|
||||
usual feature set, so you cannot add a tag that HTML Purifier never
|
||||
supported in the first place (like embed, form or head). If you
|
||||
change this, you probably also want to change %HTML.AllowedAttributes.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Warning:</strong> If another directive conflicts with the
|
||||
elements here, <em>that</em> directive will win and override.
|
||||
</p>
|
||||
--# vim: et sw=4 sts=4
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Registry object that contains information about the current context.
|
||||
* @warning Is a bit buggy when variables are set to null: it thinks
|
||||
* they don't exist! So use false instead, please.
|
||||
* @note Since the variables Context deals with may not be objects,
|
||||
* references are very important here! Do not remove!
|
||||
*/
|
||||
class HTMLPurifier_Context
|
||||
{
|
||||
|
||||
/**
|
||||
* Private array that stores the references.
|
||||
*/
|
||||
private $_storage = array();
|
||||
|
||||
/**
|
||||
* Registers a variable into the context.
|
||||
* @param $name String name
|
||||
* @param $ref Reference to variable to be registered
|
||||
*/
|
||||
public function register($name, &$ref) {
|
||||
if (isset($this->_storage[$name])) {
|
||||
trigger_error("Name $name produces collision, cannot re-register",
|
||||
E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
$this->_storage[$name] =& $ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a variable reference from the context.
|
||||
* @param $name String name
|
||||
* @param $ignore_error Boolean whether or not to ignore error
|
||||
*/
|
||||
public function &get($name, $ignore_error = false) {
|
||||
if (!isset($this->_storage[$name])) {
|
||||
if (!$ignore_error) {
|
||||
trigger_error("Attempted to retrieve non-existent variable $name",
|
||||
E_USER_ERROR);
|
||||
}
|
||||
$var = null; // so we can return by reference
|
||||
return $var;
|
||||
}
|
||||
return $this->_storage[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Destorys a variable in the context.
|
||||
* @param $name String name
|
||||
*/
|
||||
public function destroy($name) {
|
||||
if (!isset($this->_storage[$name])) {
|
||||
trigger_error("Attempted to destroy non-existent variable $name",
|
||||
E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
unset($this->_storage[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the variable exists.
|
||||
* @param $name String name
|
||||
*/
|
||||
public function exists($name) {
|
||||
return isset($this->_storage[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a series of variables from an associative array
|
||||
* @param $context_array Assoc array of variables to load
|
||||
*/
|
||||
public function loadArray($context_array) {
|
||||
foreach ($context_array as $key => $discard) {
|
||||
$this->register($key, $context_array[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Super-class for definition datatype objects, implements serialization
|
||||
* functions for the class.
|
||||
*/
|
||||
abstract class HTMLPurifier_Definition
|
||||
{
|
||||
|
||||
/**
|
||||
* Has setup() been called yet?
|
||||
*/
|
||||
public $setup = false;
|
||||
|
||||
/**
|
||||
* What type of definition is it?
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Sets up the definition object into the final form, something
|
||||
* not done by the constructor
|
||||
* @param $config HTMLPurifier_Config instance
|
||||
*/
|
||||
abstract protected function doSetup($config);
|
||||
|
||||
/**
|
||||
* Setup function that aborts if already setup
|
||||
* @param $config HTMLPurifier_Config instance
|
||||
*/
|
||||
public function setup($config) {
|
||||
if ($this->setup) return;
|
||||
$this->setup = true;
|
||||
$this->doSetup($config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_DefinitionCache_Decorator extends HTMLPurifier_DefinitionCache
|
||||
{
|
||||
|
||||
/**
|
||||
* Cache object we are decorating
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Lazy decorator function
|
||||
* @param $cache Reference to cache object to decorate
|
||||
*/
|
||||
public function decorate(&$cache) {
|
||||
$decorator = $this->copy();
|
||||
// reference is necessary for mocks in PHP 4
|
||||
$decorator->cache =& $cache;
|
||||
$decorator->type = $cache->type;
|
||||
return $decorator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cross-compatible clone substitute
|
||||
*/
|
||||
public function copy() {
|
||||
return new HTMLPurifier_DefinitionCache_Decorator();
|
||||
}
|
||||
|
||||
public function add($def, $config) {
|
||||
return $this->cache->add($def, $config);
|
||||
}
|
||||
|
||||
public function set($def, $config) {
|
||||
return $this->cache->set($def, $config);
|
||||
}
|
||||
|
||||
public function replace($def, $config) {
|
||||
return $this->cache->replace($def, $config);
|
||||
}
|
||||
|
||||
public function get($config) {
|
||||
return $this->cache->get($config);
|
||||
}
|
||||
|
||||
public function remove($config) {
|
||||
return $this->cache->remove($config);
|
||||
}
|
||||
|
||||
public function flush($config) {
|
||||
return $this->cache->flush($config);
|
||||
}
|
||||
|
||||
public function cleanup($config) {
|
||||
return $this->cache->cleanup($config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Definition cache decorator class that cleans up the cache
|
||||
* whenever there is a cache miss.
|
||||
*/
|
||||
class HTMLPurifier_DefinitionCache_Decorator_Cleanup extends
|
||||
HTMLPurifier_DefinitionCache_Decorator
|
||||
{
|
||||
|
||||
public $name = 'Cleanup';
|
||||
|
||||
public function copy() {
|
||||
return new HTMLPurifier_DefinitionCache_Decorator_Cleanup();
|
||||
}
|
||||
|
||||
public function add($def, $config) {
|
||||
$status = parent::add($def, $config);
|
||||
if (!$status) parent::cleanup($config);
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function set($def, $config) {
|
||||
$status = parent::set($def, $config);
|
||||
if (!$status) parent::cleanup($config);
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function replace($def, $config) {
|
||||
$status = parent::replace($def, $config);
|
||||
if (!$status) parent::cleanup($config);
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function get($config) {
|
||||
$ret = parent::get($config);
|
||||
if (!$ret) parent::cleanup($config);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Definition cache decorator class that saves all cache retrievals
|
||||
* to PHP's memory; good for unit tests or circumstances where
|
||||
* there are lots of configuration objects floating around.
|
||||
*/
|
||||
class HTMLPurifier_DefinitionCache_Decorator_Memory extends
|
||||
HTMLPurifier_DefinitionCache_Decorator
|
||||
{
|
||||
|
||||
protected $definitions;
|
||||
public $name = 'Memory';
|
||||
|
||||
public function copy() {
|
||||
return new HTMLPurifier_DefinitionCache_Decorator_Memory();
|
||||
}
|
||||
|
||||
public function add($def, $config) {
|
||||
$status = parent::add($def, $config);
|
||||
if ($status) $this->definitions[$this->generateKey($config)] = $def;
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function set($def, $config) {
|
||||
$status = parent::set($def, $config);
|
||||
if ($status) $this->definitions[$this->generateKey($config)] = $def;
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function replace($def, $config) {
|
||||
$status = parent::replace($def, $config);
|
||||
if ($status) $this->definitions[$this->generateKey($config)] = $def;
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function get($config) {
|
||||
$key = $this->generateKey($config);
|
||||
if (isset($this->definitions[$key])) return $this->definitions[$key];
|
||||
$this->definitions[$key] = parent::get($config);
|
||||
return $this->definitions[$key];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
|
||||
require_once 'HTMLPurifier/DefinitionCache/Decorator.php';
|
||||
|
||||
/**
|
||||
* Definition cache decorator template.
|
||||
*/
|
||||
class HTMLPurifier_DefinitionCache_Decorator_Template extends
|
||||
HTMLPurifier_DefinitionCache_Decorator
|
||||
{
|
||||
|
||||
var $name = 'Template'; // replace this
|
||||
|
||||
function copy() {
|
||||
// replace class name with yours
|
||||
return new HTMLPurifier_DefinitionCache_Decorator_Template();
|
||||
}
|
||||
|
||||
// remove methods you don't need
|
||||
|
||||
function add($def, $config) {
|
||||
return parent::add($def, $config);
|
||||
}
|
||||
|
||||
function set($def, $config) {
|
||||
return parent::set($def, $config);
|
||||
}
|
||||
|
||||
function replace($def, $config) {
|
||||
return parent::replace($def, $config);
|
||||
}
|
||||
|
||||
function get($config) {
|
||||
return parent::get($config);
|
||||
}
|
||||
|
||||
function flush() {
|
||||
return parent::flush();
|
||||
}
|
||||
|
||||
function cleanup($config) {
|
||||
return parent::cleanup($config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Null cache object to use when no caching is on.
|
||||
*/
|
||||
class HTMLPurifier_DefinitionCache_Null extends HTMLPurifier_DefinitionCache
|
||||
{
|
||||
|
||||
public function add($def, $config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function set($def, $config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function replace($def, $config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function remove($config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get($config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function flush($config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function cleanup($config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,172 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_DefinitionCache_Serializer extends
|
||||
HTMLPurifier_DefinitionCache
|
||||
{
|
||||
|
||||
public function add($def, $config) {
|
||||
if (!$this->checkDefType($def)) return;
|
||||
$file = $this->generateFilePath($config);
|
||||
if (file_exists($file)) return false;
|
||||
if (!$this->_prepareDir($config)) return false;
|
||||
return $this->_write($file, serialize($def));
|
||||
}
|
||||
|
||||
public function set($def, $config) {
|
||||
if (!$this->checkDefType($def)) return;
|
||||
$file = $this->generateFilePath($config);
|
||||
if (!$this->_prepareDir($config)) return false;
|
||||
return $this->_write($file, serialize($def));
|
||||
}
|
||||
|
||||
public function replace($def, $config) {
|
||||
if (!$this->checkDefType($def)) return;
|
||||
$file = $this->generateFilePath($config);
|
||||
if (!file_exists($file)) return false;
|
||||
if (!$this->_prepareDir($config)) return false;
|
||||
return $this->_write($file, serialize($def));
|
||||
}
|
||||
|
||||
public function get($config) {
|
||||
$file = $this->generateFilePath($config);
|
||||
if (!file_exists($file)) return false;
|
||||
return unserialize(file_get_contents($file));
|
||||
}
|
||||
|
||||
public function remove($config) {
|
||||
$file = $this->generateFilePath($config);
|
||||
if (!file_exists($file)) return false;
|
||||
return unlink($file);
|
||||
}
|
||||
|
||||
public function flush($config) {
|
||||
if (!$this->_prepareDir($config)) return false;
|
||||
$dir = $this->generateDirectoryPath($config);
|
||||
$dh = opendir($dir);
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
if (empty($filename)) continue;
|
||||
if ($filename[0] === '.') continue;
|
||||
unlink($dir . '/' . $filename);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanup($config) {
|
||||
if (!$this->_prepareDir($config)) return false;
|
||||
$dir = $this->generateDirectoryPath($config);
|
||||
$dh = opendir($dir);
|
||||
while (false !== ($filename = readdir($dh))) {
|
||||
if (empty($filename)) continue;
|
||||
if ($filename[0] === '.') continue;
|
||||
$key = substr($filename, 0, strlen($filename) - 4);
|
||||
if ($this->isOld($key, $config)) unlink($dir . '/' . $filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the file path to the serial file corresponding to
|
||||
* the configuration and definition name
|
||||
* @todo Make protected
|
||||
*/
|
||||
public function generateFilePath($config) {
|
||||
$key = $this->generateKey($config);
|
||||
return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the path to the directory contain this cache's serial files
|
||||
* @note No trailing slash
|
||||
* @todo Make protected
|
||||
*/
|
||||
public function generateDirectoryPath($config) {
|
||||
$base = $this->generateBaseDirectoryPath($config);
|
||||
return $base . '/' . $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates path to base directory that contains all definition type
|
||||
* serials
|
||||
* @todo Make protected
|
||||
*/
|
||||
public function generateBaseDirectoryPath($config) {
|
||||
$base = $config->get('Cache.SerializerPath');
|
||||
$base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper function for file_put_contents
|
||||
* @param $file File name to write to
|
||||
* @param $data Data to write into file
|
||||
* @return Number of bytes written if success, or false if failure.
|
||||
*/
|
||||
private function _write($file, $data) {
|
||||
return file_put_contents($file, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the directory that this type stores the serials in
|
||||
* @return True if successful
|
||||
*/
|
||||
private function _prepareDir($config) {
|
||||
$directory = $this->generateDirectoryPath($config);
|
||||
if (!is_dir($directory)) {
|
||||
$base = $this->generateBaseDirectoryPath($config);
|
||||
if (!is_dir($base)) {
|
||||
trigger_error('Base directory '.$base.' does not exist,
|
||||
please create or change using %Cache.SerializerPath',
|
||||
E_USER_WARNING);
|
||||
return false;
|
||||
} elseif (!$this->_testPermissions($base)) {
|
||||
return false;
|
||||
}
|
||||
$old = umask(0022); // disable group and world writes
|
||||
mkdir($directory);
|
||||
umask($old);
|
||||
} elseif (!$this->_testPermissions($directory)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests permissions on a directory and throws out friendly
|
||||
* error messages and attempts to chmod it itself if possible
|
||||
*/
|
||||
private function _testPermissions($dir) {
|
||||
// early abort, if it is writable, everything is hunky-dory
|
||||
if (is_writable($dir)) return true;
|
||||
if (!is_dir($dir)) {
|
||||
// generally, you'll want to handle this beforehand
|
||||
// so a more specific error message can be given
|
||||
trigger_error('Directory '.$dir.' does not exist',
|
||||
E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
if (function_exists('posix_getuid')) {
|
||||
// POSIX system, we can give more specific advice
|
||||
if (fileowner($dir) === posix_getuid()) {
|
||||
// we can chmod it ourselves
|
||||
chmod($dir, 0755);
|
||||
return true;
|
||||
} elseif (filegroup($dir) === posix_getgid()) {
|
||||
$chmod = '775';
|
||||
} else {
|
||||
// PHP's probably running as nobody, so we'll
|
||||
// need to give global permissions
|
||||
$chmod = '777';
|
||||
}
|
||||
trigger_error('Directory '.$dir.' not writable, '.
|
||||
'please chmod to ' . $chmod,
|
||||
E_USER_WARNING);
|
||||
} else {
|
||||
// generic error message
|
||||
trigger_error('Directory '.$dir.' not writable, '.
|
||||
'please alter file permissions',
|
||||
E_USER_WARNING);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
File diff suppressed because one or more lines are too long
|
@ -1,135 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This filter extracts <style> blocks from input HTML, cleans them up
|
||||
* using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
|
||||
* so they can be used elsewhere in the document.
|
||||
*
|
||||
* @note
|
||||
* See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
|
||||
* sample usage.
|
||||
*
|
||||
* @note
|
||||
* This filter can also be used on stylesheets not included in the
|
||||
* document--something purists would probably prefer. Just directly
|
||||
* call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
|
||||
*/
|
||||
class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
|
||||
{
|
||||
|
||||
public $name = 'ExtractStyleBlocks';
|
||||
private $_styleMatches = array();
|
||||
private $_tidy;
|
||||
|
||||
public function __construct() {
|
||||
$this->_tidy = new csstidy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the contents of CSS blocks to style matches
|
||||
* @param $matches preg_replace style $matches array
|
||||
*/
|
||||
protected function styleCallback($matches) {
|
||||
$this->_styleMatches[] = $matches[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes inline <style> tags from HTML, saves them for later use
|
||||
* @todo Extend to indicate non-text/css style blocks
|
||||
*/
|
||||
public function preFilter($html, $config, $context) {
|
||||
$tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
|
||||
if ($tidy !== null) $this->_tidy = $tidy;
|
||||
$html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
|
||||
$style_blocks = $this->_styleMatches;
|
||||
$this->_styleMatches = array(); // reset
|
||||
$context->register('StyleBlocks', $style_blocks); // $context must not be reused
|
||||
if ($this->_tidy) {
|
||||
foreach ($style_blocks as &$style) {
|
||||
$style = $this->cleanCSS($style, $config, $context);
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes CSS (the stuff found in <style>) and cleans it.
|
||||
* @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
|
||||
* @param $css CSS styling to clean
|
||||
* @param $config Instance of HTMLPurifier_Config
|
||||
* @param $context Instance of HTMLPurifier_Context
|
||||
* @return Cleaned CSS
|
||||
*/
|
||||
public function cleanCSS($css, $config, $context) {
|
||||
// prepare scope
|
||||
$scope = $config->get('Filter.ExtractStyleBlocks.Scope');
|
||||
if ($scope !== null) {
|
||||
$scopes = array_map('trim', explode(',', $scope));
|
||||
} else {
|
||||
$scopes = array();
|
||||
}
|
||||
// remove comments from CSS
|
||||
$css = trim($css);
|
||||
if (strncmp('<!--', $css, 4) === 0) {
|
||||
$css = substr($css, 4);
|
||||
}
|
||||
if (strlen($css) > 3 && substr($css, -3) == '-->') {
|
||||
$css = substr($css, 0, -3);
|
||||
}
|
||||
$css = trim($css);
|
||||
$this->_tidy->parse($css);
|
||||
$css_definition = $config->getDefinition('CSS');
|
||||
foreach ($this->_tidy->css as $k => $decls) {
|
||||
// $decls are all CSS declarations inside an @ selector
|
||||
$new_decls = array();
|
||||
foreach ($decls as $selector => $style) {
|
||||
$selector = trim($selector);
|
||||
if ($selector === '') continue; // should not happen
|
||||
if ($selector[0] === '+') {
|
||||
if ($selector !== '' && $selector[0] === '+') continue;
|
||||
}
|
||||
if (!empty($scopes)) {
|
||||
$new_selector = array(); // because multiple ones are possible
|
||||
$selectors = array_map('trim', explode(',', $selector));
|
||||
foreach ($scopes as $s1) {
|
||||
foreach ($selectors as $s2) {
|
||||
$new_selector[] = "$s1 $s2";
|
||||
}
|
||||
}
|
||||
$selector = implode(', ', $new_selector); // now it's a string
|
||||
}
|
||||
foreach ($style as $name => $value) {
|
||||
if (!isset($css_definition->info[$name])) {
|
||||
unset($style[$name]);
|
||||
continue;
|
||||
}
|
||||
$def = $css_definition->info[$name];
|
||||
$ret = $def->validate($value, $config, $context);
|
||||
if ($ret === false) unset($style[$name]);
|
||||
else $style[$name] = $ret;
|
||||
}
|
||||
$new_decls[$selector] = $style;
|
||||
}
|
||||
$this->_tidy->css[$k] = $new_decls;
|
||||
}
|
||||
// remove stuff that shouldn't be used, could be reenabled
|
||||
// after security risks are analyzed
|
||||
$this->_tidy->import = array();
|
||||
$this->_tidy->charset = null;
|
||||
$this->_tidy->namespace = null;
|
||||
$css = $this->_tidy->print->plain();
|
||||
// we are going to escape any special characters <>& to ensure
|
||||
// that no funny business occurs (i.e. </style> in a font-family prop).
|
||||
if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
|
||||
$css = str_replace(
|
||||
array('<', '>', '&'),
|
||||
array('\3C ', '\3E ', '\26 '),
|
||||
$css
|
||||
);
|
||||
}
|
||||
return $css;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter
|
||||
{
|
||||
|
||||
public $name = 'YouTube';
|
||||
|
||||
public function preFilter($html, $config, $context) {
|
||||
$pre_regex = '#<object[^>]+>.+?'.
|
||||
'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s';
|
||||
$pre_replace = '<span class="youtube-embed">\1</span>';
|
||||
return preg_replace($pre_regex, $pre_replace, $html);
|
||||
}
|
||||
|
||||
public function postFilter($html, $config, $context) {
|
||||
$post_regex = '#<span class="youtube-embed">((?:v|cp)/[A-Za-z0-9\-_=]+)</span>#';
|
||||
return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html);
|
||||
}
|
||||
|
||||
protected function armorUrl($url) {
|
||||
return str_replace('--', '--', $url);
|
||||
}
|
||||
|
||||
protected function postFilterCallback($matches) {
|
||||
$url = $this->armorUrl($matches[1]);
|
||||
return '<object width="425" height="350" type="application/x-shockwave-flash" '.
|
||||
'data="http://www.youtube.com/'.$url.'">'.
|
||||
'<param name="movie" value="http://www.youtube.com/'.$url.'"></param>'.
|
||||
'<!--[if IE]>'.
|
||||
'<embed src="http://www.youtube.com/'.$url.'"'.
|
||||
'type="application/x-shockwave-flash"'.
|
||||
'wmode="transparent" width="425" height="350" />'.
|
||||
'<![endif]-->'.
|
||||
'</object>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,118 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
|
||||
*/
|
||||
class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
|
||||
{
|
||||
public $name = 'Forms';
|
||||
public $safe = false;
|
||||
|
||||
public $content_sets = array(
|
||||
'Block' => 'Form',
|
||||
'Inline' => 'Formctrl',
|
||||
);
|
||||
|
||||
public function setup($config) {
|
||||
$form = $this->addElement('form', 'Form',
|
||||
'Required: Heading | List | Block | fieldset', 'Common', array(
|
||||
'accept' => 'ContentTypes',
|
||||
'accept-charset' => 'Charsets',
|
||||
'action*' => 'URI',
|
||||
'method' => 'Enum#get,post',
|
||||
// really ContentType, but these two are the only ones used today
|
||||
'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
|
||||
));
|
||||
$form->excludes = array('form' => true);
|
||||
|
||||
$input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array(
|
||||
'accept' => 'ContentTypes',
|
||||
'accesskey' => 'Character',
|
||||
'alt' => 'Text',
|
||||
'checked' => 'Bool#checked',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'maxlength' => 'Number',
|
||||
'name' => 'CDATA',
|
||||
'readonly' => 'Bool#readonly',
|
||||
'size' => 'Number',
|
||||
'src' => 'URI#embeds',
|
||||
'tabindex' => 'Number',
|
||||
'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
$input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
|
||||
|
||||
$this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'multiple' => 'Bool#multiple',
|
||||
'name' => 'CDATA',
|
||||
'size' => 'Number',
|
||||
'tabindex' => 'Number',
|
||||
));
|
||||
|
||||
$this->addElement('option', false, 'Optional: #PCDATA', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'label' => 'Text',
|
||||
'selected' => 'Bool#selected',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
// It's illegal for there to be more than one selected, but not
|
||||
// be multiple. Also, no selected means undefined behavior. This might
|
||||
// be difficult to implement; perhaps an injector, or a context variable.
|
||||
|
||||
$textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
'cols*' => 'Number',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'name' => 'CDATA',
|
||||
'readonly' => 'Bool#readonly',
|
||||
'rows*' => 'Number',
|
||||
'tabindex' => 'Number',
|
||||
));
|
||||
$textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
|
||||
|
||||
$button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'name' => 'CDATA',
|
||||
'tabindex' => 'Number',
|
||||
'type' => 'Enum#button,submit,reset',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
|
||||
// For exclusions, ideally we'd specify content sets, not literal elements
|
||||
$button->excludes = $this->makeLookup(
|
||||
'form', 'fieldset', // Form
|
||||
'input', 'select', 'textarea', 'label', 'button', // Formctrl
|
||||
'a' // as per HTML 4.01 spec, this is omitted by modularization
|
||||
);
|
||||
|
||||
// Extra exclusion: img usemap="" is not permitted within this element.
|
||||
// We'll omit this for now, since we don't have any good way of
|
||||
// indicating it yet.
|
||||
|
||||
// This is HIGHLY user-unfriendly; we need a custom child-def for this
|
||||
$this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
|
||||
|
||||
$label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
// 'for' => 'IDREF', // IDREF not implemented, cannot allow
|
||||
));
|
||||
$label->excludes = array('label' => true);
|
||||
|
||||
$this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
));
|
||||
|
||||
$this->addElement('optgroup', false, 'Required: option', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'label*' => 'Text',
|
||||
));
|
||||
|
||||
// Don't forget an injector for <isindex>. This one's a little complex
|
||||
// because it maps to multiple elements.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_HTMLModule_Tidy_Strict extends HTMLPurifier_HTMLModule_Tidy_XHTMLAndHTML4
|
||||
{
|
||||
public $name = 'Tidy_Strict';
|
||||
public $defaultLevel = 'light';
|
||||
|
||||
public function makeFixes() {
|
||||
$r = parent::makeFixes();
|
||||
$r['blockquote#content_model_type'] = 'strictblockquote';
|
||||
return $r;
|
||||
}
|
||||
|
||||
public $defines_child_def = true;
|
||||
public function getChildDef($def) {
|
||||
if ($def->content_model_type != 'strictblockquote') return parent::getChildDef($def);
|
||||
return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
|
||||
{
|
||||
|
||||
private $context, $config, $attrValidator, $removeNbsp, $removeNbspExceptions;
|
||||
|
||||
public function prepare($config, $context) {
|
||||
parent::prepare($config, $context);
|
||||
$this->config = $config;
|
||||
$this->context = $context;
|
||||
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
|
||||
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
|
||||
$this->attrValidator = new HTMLPurifier_AttrValidator();
|
||||
}
|
||||
|
||||
public function handleElement(&$token) {
|
||||
if (!$token instanceof HTMLPurifier_Token_Start) return;
|
||||
$next = false;
|
||||
for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) {
|
||||
$next = $this->inputTokens[$i];
|
||||
if ($next instanceof HTMLPurifier_Token_Text) {
|
||||
if ($next->is_whitespace) continue;
|
||||
if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
|
||||
$plain = str_replace("\xC2\xA0", "", $next->data);
|
||||
$isWsOrNbsp = $plain === '' || ctype_space($plain);
|
||||
if ($isWsOrNbsp) continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
|
||||
if ($token->name == 'colgroup') return;
|
||||
$this->attrValidator->validateToken($token, $this->config, $this->context);
|
||||
$token->armor['ValidateAttributes'] = true;
|
||||
if (isset($token->attr['id']) || isset($token->attr['name'])) return;
|
||||
$token = $i - $this->inputIndex + 1;
|
||||
for ($b = $this->inputIndex - 1; $b > 0; $b--) {
|
||||
$prev = $this->inputTokens[$b];
|
||||
if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) continue;
|
||||
break;
|
||||
}
|
||||
// This is safe because we removed the token that triggered this.
|
||||
$this->rewind($b - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
$fallback = false;
|
||||
|
||||
$messages = array(
|
||||
|
||||
'HTMLPurifier' => 'HTML Purifier',
|
||||
|
||||
// for unit testing purposes
|
||||
'LanguageFactoryTest: Pizza' => 'Pizza',
|
||||
'LanguageTest: List' => '$1',
|
||||
'LanguageTest: Hash' => '$1.Keys; $1.Values',
|
||||
|
||||
'Item separator' => ', ',
|
||||
'Item separator last' => ' and ', // non-Harvard style
|
||||
|
||||
'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.',
|
||||
'ErrorCollector: At line' => ' at line $line',
|
||||
'ErrorCollector: Incidental errors' => 'Incidental errors',
|
||||
|
||||
'Lexer: Unclosed comment' => 'Unclosed comment',
|
||||
'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <',
|
||||
'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped',
|
||||
'Lexer: Missing attribute key' => 'Attribute declaration has no key',
|
||||
'Lexer: Missing end quote' => 'Attribute declaration has no end quote',
|
||||
'Lexer: Extracted body' => 'Removed document metadata tags',
|
||||
|
||||
'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized',
|
||||
'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1',
|
||||
'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text',
|
||||
'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed',
|
||||
'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed',
|
||||
'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed',
|
||||
'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end',
|
||||
'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed',
|
||||
'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens',
|
||||
|
||||
'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed',
|
||||
'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text',
|
||||
'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact',
|
||||
'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact',
|
||||
'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed',
|
||||
'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text',
|
||||
'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized',
|
||||
'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document',
|
||||
|
||||
'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed',
|
||||
'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element',
|
||||
'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model',
|
||||
'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed',
|
||||
|
||||
'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys',
|
||||
'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed',
|
||||
|
||||
);
|
||||
|
||||
$errorNames = array(
|
||||
E_ERROR => 'Error',
|
||||
E_WARNING => 'Warning',
|
||||
E_NOTICE => 'Notice'
|
||||
);
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,139 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Proof-of-concept lexer that uses the PEAR package XML_HTMLSax3 to parse HTML.
|
||||
*
|
||||
* PEAR, not suprisingly, also has a SAX parser for HTML. I don't know
|
||||
* very much about implementation, but it's fairly well written. However, that
|
||||
* abstraction comes at a price: performance. You need to have it installed,
|
||||
* and if the API changes, it might break our adapter. Not sure whether or not
|
||||
* it's UTF-8 aware, but it has some entity parsing trouble (in all areas,
|
||||
* text and attributes).
|
||||
*
|
||||
* Quite personally, I don't recommend using the PEAR class, and the defaults
|
||||
* don't use it. The unit tests do perform the tests on the SAX parser too, but
|
||||
* whatever it does for poorly formed HTML is up to it.
|
||||
*
|
||||
* @todo Generalize so that XML_HTMLSax is also supported.
|
||||
*
|
||||
* @warning Entity-resolution inside attributes is broken.
|
||||
*/
|
||||
|
||||
class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer
|
||||
{
|
||||
|
||||
/**
|
||||
* Internal accumulator array for SAX parsers.
|
||||
*/
|
||||
protected $tokens = array();
|
||||
protected $last_token_was_empty;
|
||||
|
||||
private $parent_handler;
|
||||
private $stack = array();
|
||||
|
||||
public function tokenizeHTML($string, $config, $context) {
|
||||
|
||||
$this->tokens = array();
|
||||
$this->last_token_was_empty = false;
|
||||
|
||||
$string = $this->normalize($string, $config, $context);
|
||||
|
||||
$this->parent_handler = set_error_handler(array($this, 'muteStrictErrorHandler'));
|
||||
|
||||
$parser = new XML_HTMLSax3();
|
||||
$parser->set_object($this);
|
||||
$parser->set_element_handler('openHandler','closeHandler');
|
||||
$parser->set_data_handler('dataHandler');
|
||||
$parser->set_escape_handler('escapeHandler');
|
||||
|
||||
// doesn't seem to work correctly for attributes
|
||||
$parser->set_option('XML_OPTION_ENTITIES_PARSED', 1);
|
||||
|
||||
$parser->parse($string);
|
||||
|
||||
restore_error_handler();
|
||||
|
||||
return $this->tokens;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Open tag event handler, interface is defined by PEAR package.
|
||||
*/
|
||||
public function openHandler(&$parser, $name, $attrs, $closed) {
|
||||
// entities are not resolved in attrs
|
||||
foreach ($attrs as $key => $attr) {
|
||||
$attrs[$key] = $this->parseData($attr);
|
||||
}
|
||||
if ($closed) {
|
||||
$this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs);
|
||||
$this->last_token_was_empty = true;
|
||||
} else {
|
||||
$this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs);
|
||||
}
|
||||
$this->stack[] = $name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close tag event handler, interface is defined by PEAR package.
|
||||
*/
|
||||
public function closeHandler(&$parser, $name) {
|
||||
// HTMLSax3 seems to always send empty tags an extra close tag
|
||||
// check and ignore if you see it:
|
||||
// [TESTME] to make sure it doesn't overreach
|
||||
if ($this->last_token_was_empty) {
|
||||
$this->last_token_was_empty = false;
|
||||
return true;
|
||||
}
|
||||
$this->tokens[] = new HTMLPurifier_Token_End($name);
|
||||
if (!empty($this->stack)) array_pop($this->stack);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data event handler, interface is defined by PEAR package.
|
||||
*/
|
||||
public function dataHandler(&$parser, $data) {
|
||||
$this->last_token_was_empty = false;
|
||||
$this->tokens[] = new HTMLPurifier_Token_Text($data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escaped text handler, interface is defined by PEAR package.
|
||||
*/
|
||||
public function escapeHandler(&$parser, $data) {
|
||||
if (strpos($data, '--') === 0) {
|
||||
// remove trailing and leading double-dashes
|
||||
$data = substr($data, 2);
|
||||
if (strlen($data) >= 2 && substr($data, -2) == "--") {
|
||||
$data = substr($data, 0, -2);
|
||||
}
|
||||
if (isset($this->stack[sizeof($this->stack) - 1]) &&
|
||||
$this->stack[sizeof($this->stack) - 1] == "style") {
|
||||
$this->tokens[] = new HTMLPurifier_Token_Text($data);
|
||||
} else {
|
||||
$this->tokens[] = new HTMLPurifier_Token_Comment($data);
|
||||
}
|
||||
$this->last_token_was_empty = false;
|
||||
}
|
||||
// CDATA is handled elsewhere, but if it was handled here:
|
||||
//if (strpos($data, '[CDATA[') === 0) {
|
||||
// $this->tokens[] = new HTMLPurifier_Token_Text(
|
||||
// substr($data, 7, strlen($data) - 9) );
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An error handler that mutes strict errors
|
||||
*/
|
||||
public function muteStrictErrorHandler($errno, $errstr, $errfile=null, $errline=null, $errcontext=null) {
|
||||
if ($errno == E_STRICT) return;
|
||||
return call_user_func($this->parent_handler, $errno, $errstr, $errfile, $errline, $errcontext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
File diff suppressed because it is too large
Load Diff
|
@ -1,328 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Takes a well formed list of tokens and fixes their nesting.
|
||||
*
|
||||
* HTML elements dictate which elements are allowed to be their children,
|
||||
* for example, you can't have a p tag in a span tag. Other elements have
|
||||
* much more rigorous definitions: tables, for instance, require a specific
|
||||
* order for their elements. There are also constraints not expressible by
|
||||
* document type definitions, such as the chameleon nature of ins/del
|
||||
* tags and global child exclusions.
|
||||
*
|
||||
* The first major objective of this strategy is to iterate through all the
|
||||
* nodes (not tokens) of the list of tokens and determine whether or not
|
||||
* their children conform to the element's definition. If they do not, the
|
||||
* child definition may optionally supply an amended list of elements that
|
||||
* is valid or require that the entire node be deleted (and the previous
|
||||
* node rescanned).
|
||||
*
|
||||
* The second objective is to ensure that explicitly excluded elements of
|
||||
* an element do not appear in its children. Code that accomplishes this
|
||||
* task is pervasive through the strategy, though the two are distinct tasks
|
||||
* and could, theoretically, be seperated (although it's not recommended).
|
||||
*
|
||||
* @note Whether or not unrecognized children are silently dropped or
|
||||
* translated into text depends on the child definitions.
|
||||
*
|
||||
* @todo Enable nodes to be bubbled out of the structure.
|
||||
*/
|
||||
|
||||
class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy
|
||||
{
|
||||
|
||||
public function execute($tokens, $config, $context) {
|
||||
//####################################################################//
|
||||
// Pre-processing
|
||||
|
||||
// get a copy of the HTML definition
|
||||
$definition = $config->getHTMLDefinition();
|
||||
|
||||
// insert implicit "parent" node, will be removed at end.
|
||||
// DEFINITION CALL
|
||||
$parent_name = $definition->info_parent;
|
||||
array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name));
|
||||
$tokens[] = new HTMLPurifier_Token_End($parent_name);
|
||||
|
||||
// setup the context variable 'IsInline', for chameleon processing
|
||||
// is 'false' when we are not inline, 'true' when it must always
|
||||
// be inline, and an integer when it is inline for a certain
|
||||
// branch of the document tree
|
||||
$is_inline = $definition->info_parent_def->descendants_are_inline;
|
||||
$context->register('IsInline', $is_inline);
|
||||
|
||||
// setup error collector
|
||||
$e =& $context->get('ErrorCollector', true);
|
||||
|
||||
//####################################################################//
|
||||
// Loop initialization
|
||||
|
||||
// stack that contains the indexes of all parents,
|
||||
// $stack[count($stack)-1] being the current parent
|
||||
$stack = array();
|
||||
|
||||
// stack that contains all elements that are excluded
|
||||
// it is organized by parent elements, similar to $stack,
|
||||
// but it is only populated when an element with exclusions is
|
||||
// processed, i.e. there won't be empty exclusions.
|
||||
$exclude_stack = array();
|
||||
|
||||
// variable that contains the start token while we are processing
|
||||
// nodes. This enables error reporting to do its job
|
||||
$start_token = false;
|
||||
$context->register('CurrentToken', $start_token);
|
||||
|
||||
//####################################################################//
|
||||
// Loop
|
||||
|
||||
// iterate through all start nodes. Determining the start node
|
||||
// is complicated so it has been omitted from the loop construct
|
||||
for ($i = 0, $size = count($tokens) ; $i < $size; ) {
|
||||
|
||||
//################################################################//
|
||||
// Gather information on children
|
||||
|
||||
// child token accumulator
|
||||
$child_tokens = array();
|
||||
|
||||
// scroll to the end of this node, report number, and collect
|
||||
// all children
|
||||
for ($j = $i, $depth = 0; ; $j++) {
|
||||
if ($tokens[$j] instanceof HTMLPurifier_Token_Start) {
|
||||
$depth++;
|
||||
// skip token assignment on first iteration, this is the
|
||||
// token we currently are on
|
||||
if ($depth == 1) continue;
|
||||
} elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) {
|
||||
$depth--;
|
||||
// skip token assignment on last iteration, this is the
|
||||
// end token of the token we're currently on
|
||||
if ($depth == 0) break;
|
||||
}
|
||||
$child_tokens[] = $tokens[$j];
|
||||
}
|
||||
|
||||
// $i is index of start token
|
||||
// $j is index of end token
|
||||
|
||||
$start_token = $tokens[$i]; // to make token available via CurrentToken
|
||||
|
||||
//################################################################//
|
||||
// Gather information on parent
|
||||
|
||||
// calculate parent information
|
||||
if ($count = count($stack)) {
|
||||
$parent_index = $stack[$count-1];
|
||||
$parent_name = $tokens[$parent_index]->name;
|
||||
if ($parent_index == 0) {
|
||||
$parent_def = $definition->info_parent_def;
|
||||
} else {
|
||||
$parent_def = $definition->info[$parent_name];
|
||||
}
|
||||
} else {
|
||||
// processing as if the parent were the "root" node
|
||||
// unknown info, it won't be used anyway, in the future,
|
||||
// we may want to enforce one element only (this is
|
||||
// necessary for HTML Purifier to clean entire documents
|
||||
$parent_index = $parent_name = $parent_def = null;
|
||||
}
|
||||
|
||||
// calculate context
|
||||
if ($is_inline === false) {
|
||||
// check if conditions make it inline
|
||||
if (!empty($parent_def) && $parent_def->descendants_are_inline) {
|
||||
$is_inline = $count - 1;
|
||||
}
|
||||
} else {
|
||||
// check if we're out of inline
|
||||
if ($count === $is_inline) {
|
||||
$is_inline = false;
|
||||
}
|
||||
}
|
||||
|
||||
//################################################################//
|
||||
// Determine whether element is explicitly excluded SGML-style
|
||||
|
||||
// determine whether or not element is excluded by checking all
|
||||
// parent exclusions. The array should not be very large, two
|
||||
// elements at most.
|
||||
$excluded = false;
|
||||
if (!empty($exclude_stack)) {
|
||||
foreach ($exclude_stack as $lookup) {
|
||||
if (isset($lookup[$tokens[$i]->name])) {
|
||||
$excluded = true;
|
||||
// no need to continue processing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//################################################################//
|
||||
// Perform child validation
|
||||
|
||||
if ($excluded) {
|
||||
// there is an exclusion, remove the entire node
|
||||
$result = false;
|
||||
$excludes = array(); // not used, but good to initialize anyway
|
||||
} else {
|
||||
// DEFINITION CALL
|
||||
if ($i === 0) {
|
||||
// special processing for the first node
|
||||
$def = $definition->info_parent_def;
|
||||
} else {
|
||||
$def = $definition->info[$tokens[$i]->name];
|
||||
|
||||
}
|
||||
|
||||
if (!empty($def->child)) {
|
||||
// have DTD child def validate children
|
||||
$result = $def->child->validateChildren(
|
||||
$child_tokens, $config, $context);
|
||||
} else {
|
||||
// weird, no child definition, get rid of everything
|
||||
$result = false;
|
||||
}
|
||||
|
||||
// determine whether or not this element has any exclusions
|
||||
$excludes = $def->excludes;
|
||||
}
|
||||
|
||||
// $result is now a bool or array
|
||||
|
||||
//################################################################//
|
||||
// Process result by interpreting $result
|
||||
|
||||
if ($result === true || $child_tokens === $result) {
|
||||
// leave the node as is
|
||||
|
||||
// register start token as a parental node start
|
||||
$stack[] = $i;
|
||||
|
||||
// register exclusions if there are any
|
||||
if (!empty($excludes)) $exclude_stack[] = $excludes;
|
||||
|
||||
// move cursor to next possible start node
|
||||
$i++;
|
||||
|
||||
} elseif($result === false) {
|
||||
// remove entire node
|
||||
|
||||
if ($e) {
|
||||
if ($excluded) {
|
||||
$e->send(E_ERROR, 'Strategy_FixNesting: Node excluded');
|
||||
} else {
|
||||
$e->send(E_ERROR, 'Strategy_FixNesting: Node removed');
|
||||
}
|
||||
}
|
||||
|
||||
// calculate length of inner tokens and current tokens
|
||||
$length = $j - $i + 1;
|
||||
|
||||
// perform removal
|
||||
array_splice($tokens, $i, $length);
|
||||
|
||||
// update size
|
||||
$size -= $length;
|
||||
|
||||
// there is no start token to register,
|
||||
// current node is now the next possible start node
|
||||
// unless it turns out that we need to do a double-check
|
||||
|
||||
// this is a rought heuristic that covers 100% of HTML's
|
||||
// cases and 99% of all other cases. A child definition
|
||||
// that would be tricked by this would be something like:
|
||||
// ( | a b c) where it's all or nothing. Fortunately,
|
||||
// our current implementation claims that that case would
|
||||
// not allow empty, even if it did
|
||||
if (!$parent_def->child->allow_empty) {
|
||||
// we need to do a double-check
|
||||
$i = $parent_index;
|
||||
array_pop($stack);
|
||||
}
|
||||
|
||||
// PROJECTED OPTIMIZATION: Process all children elements before
|
||||
// reprocessing parent node.
|
||||
|
||||
} else {
|
||||
// replace node with $result
|
||||
|
||||
// calculate length of inner tokens
|
||||
$length = $j - $i - 1;
|
||||
|
||||
if ($e) {
|
||||
if (empty($result) && $length) {
|
||||
$e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed');
|
||||
} else {
|
||||
$e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized');
|
||||
}
|
||||
}
|
||||
|
||||
// perform replacement
|
||||
array_splice($tokens, $i + 1, $length, $result);
|
||||
|
||||
// update size
|
||||
$size -= $length;
|
||||
$size += count($result);
|
||||
|
||||
// register start token as a parental node start
|
||||
$stack[] = $i;
|
||||
|
||||
// register exclusions if there are any
|
||||
if (!empty($excludes)) $exclude_stack[] = $excludes;
|
||||
|
||||
// move cursor to next possible start node
|
||||
$i++;
|
||||
|
||||
}
|
||||
|
||||
//################################################################//
|
||||
// Scroll to next start node
|
||||
|
||||
// We assume, at this point, that $i is the index of the token
|
||||
// that is the first possible new start point for a node.
|
||||
|
||||
// Test if the token indeed is a start tag, if not, move forward
|
||||
// and test again.
|
||||
$size = count($tokens);
|
||||
while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) {
|
||||
if ($tokens[$i] instanceof HTMLPurifier_Token_End) {
|
||||
// pop a token index off the stack if we ended a node
|
||||
array_pop($stack);
|
||||
// pop an exclusion lookup off exclusion stack if
|
||||
// we ended node and that node had exclusions
|
||||
if ($i == 0 || $i == $size - 1) {
|
||||
// use specialized var if it's the super-parent
|
||||
$s_excludes = $definition->info_parent_def->excludes;
|
||||
} else {
|
||||
$s_excludes = $definition->info[$tokens[$i]->name]->excludes;
|
||||
}
|
||||
if ($s_excludes) {
|
||||
array_pop($exclude_stack);
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//####################################################################//
|
||||
// Post-processing
|
||||
|
||||
// remove implicit parent tokens at the beginning and end
|
||||
array_shift($tokens);
|
||||
array_pop($tokens);
|
||||
|
||||
// remove context variables
|
||||
$context->destroy('IsInline');
|
||||
$context->destroy('CurrentToken');
|
||||
|
||||
//####################################################################//
|
||||
// Return
|
||||
|
||||
return $tokens;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Abstract base token class that all others inherit from.
|
||||
*/
|
||||
class HTMLPurifier_Token {
|
||||
public $line; /**< Line number node was on in source document. Null if unknown. */
|
||||
public $col; /**< Column of line node was on in source document. Null if unknown. */
|
||||
|
||||
/**
|
||||
* Lookup array of processing that this token is exempt from.
|
||||
* Currently, valid values are "ValidateAttributes" and
|
||||
* "MakeWellFormed_TagClosedError"
|
||||
*/
|
||||
public $armor = array();
|
||||
|
||||
/**
|
||||
* Used during MakeWellFormed.
|
||||
*/
|
||||
public $skip;
|
||||
public $rewind;
|
||||
public $carryover;
|
||||
|
||||
public function __get($n) {
|
||||
if ($n === 'type') {
|
||||
trigger_error('Deprecated type property called; use instanceof', E_USER_NOTICE);
|
||||
switch (get_class($this)) {
|
||||
case 'HTMLPurifier_Token_Start': return 'start';
|
||||
case 'HTMLPurifier_Token_Empty': return 'empty';
|
||||
case 'HTMLPurifier_Token_End': return 'end';
|
||||
case 'HTMLPurifier_Token_Text': return 'text';
|
||||
case 'HTMLPurifier_Token_Comment': return 'comment';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the token in the source document.
|
||||
*/
|
||||
public function position($l = null, $c = null) {
|
||||
$this->line = $l;
|
||||
$this->col = $c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for DirectLex settings line/col position.
|
||||
*/
|
||||
public function rawPosition($l, $c) {
|
||||
if ($c === -1) $l++;
|
||||
$this->line = $l;
|
||||
$this->col = $c;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Concrete comment token class. Generally will be ignored.
|
||||
*/
|
||||
class HTMLPurifier_Token_Comment extends HTMLPurifier_Token
|
||||
{
|
||||
public $data; /**< Character data within comment. */
|
||||
public $is_whitespace = true;
|
||||
/**
|
||||
* Transparent constructor.
|
||||
*
|
||||
* @param $data String comment data.
|
||||
*/
|
||||
public function __construct($data, $line = null, $col = null) {
|
||||
$this->data = $data;
|
||||
$this->line = $line;
|
||||
$this->col = $col;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Factory for token generation.
|
||||
*
|
||||
* @note Doing some benchmarking indicates that the new operator is much
|
||||
* slower than the clone operator (even discounting the cost of the
|
||||
* constructor). This class is for that optimization.
|
||||
* Other then that, there's not much point as we don't
|
||||
* maintain parallel HTMLPurifier_Token hierarchies (the main reason why
|
||||
* you'd want to use an abstract factory).
|
||||
* @todo Port DirectLex to use this
|
||||
*/
|
||||
class HTMLPurifier_TokenFactory
|
||||
{
|
||||
|
||||
/**
|
||||
* Prototypes that will be cloned.
|
||||
* @private
|
||||
*/
|
||||
// p stands for prototype
|
||||
private $p_start, $p_end, $p_empty, $p_text, $p_comment;
|
||||
|
||||
/**
|
||||
* Generates blank prototypes for cloning.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->p_start = new HTMLPurifier_Token_Start('', array());
|
||||
$this->p_end = new HTMLPurifier_Token_End('');
|
||||
$this->p_empty = new HTMLPurifier_Token_Empty('', array());
|
||||
$this->p_text = new HTMLPurifier_Token_Text('');
|
||||
$this->p_comment= new HTMLPurifier_Token_Comment('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTMLPurifier_Token_Start.
|
||||
* @param $name Tag name
|
||||
* @param $attr Associative array of attributes
|
||||
* @return Generated HTMLPurifier_Token_Start
|
||||
*/
|
||||
public function createStart($name, $attr = array()) {
|
||||
$p = clone $this->p_start;
|
||||
$p->__construct($name, $attr);
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTMLPurifier_Token_End.
|
||||
* @param $name Tag name
|
||||
* @return Generated HTMLPurifier_Token_End
|
||||
*/
|
||||
public function createEnd($name) {
|
||||
$p = clone $this->p_end;
|
||||
$p->__construct($name);
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTMLPurifier_Token_Empty.
|
||||
* @param $name Tag name
|
||||
* @param $attr Associative array of attributes
|
||||
* @return Generated HTMLPurifier_Token_Empty
|
||||
*/
|
||||
public function createEmpty($name, $attr = array()) {
|
||||
$p = clone $this->p_empty;
|
||||
$p->__construct($name, $attr);
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTMLPurifier_Token_Text.
|
||||
* @param $data Data of text token
|
||||
* @return Generated HTMLPurifier_Token_Text
|
||||
*/
|
||||
public function createText($data) {
|
||||
$p = clone $this->p_text;
|
||||
$p->__construct($data);
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HTMLPurifier_Token_Comment.
|
||||
* @param $data Data of comment token
|
||||
* @return Generated HTMLPurifier_Token_Comment
|
||||
*/
|
||||
public function createComment($data) {
|
||||
$p = clone $this->p_comment;
|
||||
$p->__construct($data);
|
||||
return $p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,173 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* HTML Purifier's internal representation of a URI.
|
||||
* @note
|
||||
* Internal data-structures are completely escaped. If the data needs
|
||||
* to be used in a non-URI context (which is very unlikely), be sure
|
||||
* to decode it first. The URI may not necessarily be well-formed until
|
||||
* validate() is called.
|
||||
*/
|
||||
class HTMLPurifier_URI
|
||||
{
|
||||
|
||||
public $scheme, $userinfo, $host, $port, $path, $query, $fragment;
|
||||
|
||||
/**
|
||||
* @note Automatically normalizes scheme and port
|
||||
*/
|
||||
public function __construct($scheme, $userinfo, $host, $port, $path, $query, $fragment) {
|
||||
$this->scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme);
|
||||
$this->userinfo = $userinfo;
|
||||
$this->host = $host;
|
||||
$this->port = is_null($port) ? $port : (int) $port;
|
||||
$this->path = $path;
|
||||
$this->query = $query;
|
||||
$this->fragment = $fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a scheme object corresponding to the URI's scheme/default
|
||||
* @param $config Instance of HTMLPurifier_Config
|
||||
* @param $context Instance of HTMLPurifier_Context
|
||||
* @return Scheme object appropriate for validating this URI
|
||||
*/
|
||||
public function getSchemeObj($config, $context) {
|
||||
$registry = HTMLPurifier_URISchemeRegistry::instance();
|
||||
if ($this->scheme !== null) {
|
||||
$scheme_obj = $registry->getScheme($this->scheme, $config, $context);
|
||||
if (!$scheme_obj) return false; // invalid scheme, clean it out
|
||||
} else {
|
||||
// no scheme: retrieve the default one
|
||||
$def = $config->getDefinition('URI');
|
||||
$scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context);
|
||||
if (!$scheme_obj) {
|
||||
// something funky happened to the default scheme object
|
||||
trigger_error(
|
||||
'Default scheme object "' . $def->defaultScheme . '" was not readable',
|
||||
E_USER_WARNING
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $scheme_obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic validation method applicable for all schemes. May modify
|
||||
* this URI in order to get it into a compliant form.
|
||||
* @param $config Instance of HTMLPurifier_Config
|
||||
* @param $context Instance of HTMLPurifier_Context
|
||||
* @return True if validation/filtering succeeds, false if failure
|
||||
*/
|
||||
public function validate($config, $context) {
|
||||
|
||||
// ABNF definitions from RFC 3986
|
||||
$chars_sub_delims = '!$&\'()*+,;=';
|
||||
$chars_gen_delims = ':/?#[]@';
|
||||
$chars_pchar = $chars_sub_delims . ':@';
|
||||
|
||||
// validate scheme (MUST BE FIRST!)
|
||||
if (!is_null($this->scheme) && is_null($this->host)) {
|
||||
$def = $config->getDefinition('URI');
|
||||
if ($def->defaultScheme === $this->scheme) {
|
||||
$this->scheme = null;
|
||||
}
|
||||
}
|
||||
|
||||
// validate host
|
||||
if (!is_null($this->host)) {
|
||||
$host_def = new HTMLPurifier_AttrDef_URI_Host();
|
||||
$this->host = $host_def->validate($this->host, $config, $context);
|
||||
if ($this->host === false) $this->host = null;
|
||||
}
|
||||
|
||||
// validate username
|
||||
if (!is_null($this->userinfo)) {
|
||||
$encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':');
|
||||
$this->userinfo = $encoder->encode($this->userinfo);
|
||||
}
|
||||
|
||||
// validate port
|
||||
if (!is_null($this->port)) {
|
||||
if ($this->port < 1 || $this->port > 65535) $this->port = null;
|
||||
}
|
||||
|
||||
// validate path
|
||||
$path_parts = array();
|
||||
$segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/');
|
||||
if (!is_null($this->host)) {
|
||||
// path-abempty (hier and relative)
|
||||
$this->path = $segments_encoder->encode($this->path);
|
||||
} elseif ($this->path !== '' && $this->path[0] === '/') {
|
||||
// path-absolute (hier and relative)
|
||||
if (strlen($this->path) >= 2 && $this->path[1] === '/') {
|
||||
// This shouldn't ever happen!
|
||||
$this->path = '';
|
||||
} else {
|
||||
$this->path = $segments_encoder->encode($this->path);
|
||||
}
|
||||
} elseif (!is_null($this->scheme) && $this->path !== '') {
|
||||
// path-rootless (hier)
|
||||
// Short circuit evaluation means we don't need to check nz
|
||||
$this->path = $segments_encoder->encode($this->path);
|
||||
} elseif (is_null($this->scheme) && $this->path !== '') {
|
||||
// path-noscheme (relative)
|
||||
// (once again, not checking nz)
|
||||
$segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@');
|
||||
$c = strpos($this->path, '/');
|
||||
if ($c !== false) {
|
||||
$this->path =
|
||||
$segment_nc_encoder->encode(substr($this->path, 0, $c)) .
|
||||
$segments_encoder->encode(substr($this->path, $c));
|
||||
} else {
|
||||
$this->path = $segment_nc_encoder->encode($this->path);
|
||||
}
|
||||
} else {
|
||||
// path-empty (hier and relative)
|
||||
$this->path = ''; // just to be safe
|
||||
}
|
||||
|
||||
// qf = query and fragment
|
||||
$qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?');
|
||||
|
||||
if (!is_null($this->query)) {
|
||||
$this->query = $qf_encoder->encode($this->query);
|
||||
}
|
||||
|
||||
if (!is_null($this->fragment)) {
|
||||
$this->fragment = $qf_encoder->encode($this->fragment);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert URI back to string
|
||||
* @return String URI appropriate for output
|
||||
*/
|
||||
public function toString() {
|
||||
// reconstruct authority
|
||||
$authority = null;
|
||||
if (!is_null($this->host)) {
|
||||
$authority = '';
|
||||
if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@';
|
||||
$authority .= $this->host;
|
||||
if(!is_null($this->port)) $authority .= ':' . $this->port;
|
||||
}
|
||||
|
||||
// reconstruct the result
|
||||
$result = '';
|
||||
if (!is_null($this->scheme)) $result .= $this->scheme . ':';
|
||||
if (!is_null($authority)) $result .= '//' . $authority;
|
||||
$result .= $this->path;
|
||||
if (!is_null($this->query)) $result .= '?' . $this->query;
|
||||
if (!is_null($this->fragment)) $result .= '#' . $this->fragment;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Chainable filters for custom URI processing.
|
||||
*
|
||||
* These filters can perform custom actions on a URI filter object,
|
||||
* including transformation or blacklisting.
|
||||
*
|
||||
* @warning This filter is called before scheme object validation occurs.
|
||||
* Make sure, if you require a specific scheme object, you
|
||||
* you check that it exists. This allows filters to convert
|
||||
* proprietary URI schemes into regular ones.
|
||||
*/
|
||||
abstract class HTMLPurifier_URIFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* Unique identifier of filter
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* True if this filter should be run after scheme validation.
|
||||
*/
|
||||
public $post = false;
|
||||
|
||||
/**
|
||||
* Performs initialization for the filter
|
||||
*/
|
||||
public function prepare($config) {return true;}
|
||||
|
||||
/**
|
||||
* Filter a URI object
|
||||
* @param $uri Reference to URI object variable
|
||||
* @param $config Instance of HTMLPurifier_Config
|
||||
* @param $context Instance of HTMLPurifier_Context
|
||||
* @return bool Whether or not to continue processing: false indicates
|
||||
* URL is no good, true indicates continue processing. Note that
|
||||
* all changes are committed directly on the URI object
|
||||
*/
|
||||
abstract public function filter(&$uri, $config, $context);
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter
|
||||
{
|
||||
public $name = 'DisableExternal';
|
||||
protected $ourHostParts = false;
|
||||
public function prepare($config) {
|
||||
$our_host = $config->getDefinition('URI')->host;
|
||||
if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host));
|
||||
}
|
||||
public function filter(&$uri, $config, $context) {
|
||||
if (is_null($uri->host)) return true;
|
||||
if ($this->ourHostParts === false) return false;
|
||||
$host_parts = array_reverse(explode('.', $uri->host));
|
||||
foreach ($this->ourHostParts as $i => $x) {
|
||||
if (!isset($host_parts[$i])) return false;
|
||||
if ($host_parts[$i] != $this->ourHostParts[$i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal
|
||||
{
|
||||
public $name = 'DisableExternalResources';
|
||||
public function filter(&$uri, $config, $context) {
|
||||
if (!$context->get('EmbeddedURI', true)) return true;
|
||||
return parent::filter($uri, $config, $context);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
|
||||
{
|
||||
public $name = 'HostBlacklist';
|
||||
protected $blacklist = array();
|
||||
public function prepare($config) {
|
||||
$this->blacklist = $config->get('URI.HostBlacklist');
|
||||
return true;
|
||||
}
|
||||
public function filter(&$uri, $config, $context) {
|
||||
foreach($this->blacklist as $blacklisted_host_fragment) {
|
||||
if (strpos($uri->host, $blacklisted_host_fragment) !== false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter
|
||||
{
|
||||
public $name = 'Munge';
|
||||
public $post = true;
|
||||
private $target, $parser, $doEmbed, $secretKey;
|
||||
|
||||
protected $replace = array();
|
||||
|
||||
public function prepare($config) {
|
||||
$this->target = $config->get('URI.' . $this->name);
|
||||
$this->parser = new HTMLPurifier_URIParser();
|
||||
$this->doEmbed = $config->get('URI.MungeResources');
|
||||
$this->secretKey = $config->get('URI.MungeSecretKey');
|
||||
return true;
|
||||
}
|
||||
public function filter(&$uri, $config, $context) {
|
||||
if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true;
|
||||
|
||||
$scheme_obj = $uri->getSchemeObj($config, $context);
|
||||
if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
|
||||
if (is_null($uri->host) || empty($scheme_obj->browsable)) {
|
||||
return true;
|
||||
}
|
||||
// don't redirect if target host is our host
|
||||
if ($uri->host === $config->getDefinition('URI')->host) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->makeReplace($uri, $config, $context);
|
||||
$this->replace = array_map('rawurlencode', $this->replace);
|
||||
|
||||
$new_uri = strtr($this->target, $this->replace);
|
||||
$new_uri = $this->parser->parse($new_uri);
|
||||
// don't redirect if the target host is the same as the
|
||||
// starting host
|
||||
if ($uri->host === $new_uri->host) return true;
|
||||
$uri = $new_uri; // overwrite
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function makeReplace($uri, $config, $context) {
|
||||
$string = $uri->toString();
|
||||
// always available
|
||||
$this->replace['%s'] = $string;
|
||||
$this->replace['%r'] = $context->get('EmbeddedURI', true);
|
||||
$token = $context->get('CurrentToken', true);
|
||||
$this->replace['%n'] = $token ? $token->name : null;
|
||||
$this->replace['%m'] = $context->get('CurrentAttr', true);
|
||||
$this->replace['%p'] = $context->get('CurrentCSSProperty', true);
|
||||
// not always available
|
||||
if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Validator for the components of a URI for a specific scheme
|
||||
*/
|
||||
class HTMLPurifier_URIScheme
|
||||
{
|
||||
|
||||
/**
|
||||
* Scheme's default port (integer)
|
||||
*/
|
||||
public $default_port = null;
|
||||
|
||||
/**
|
||||
* Whether or not URIs of this schem are locatable by a browser
|
||||
* http and ftp are accessible, while mailto and news are not.
|
||||
*/
|
||||
public $browsable = false;
|
||||
|
||||
/**
|
||||
* Whether or not the URI always uses <hier_part>, resolves edge cases
|
||||
* with making relative URIs absolute
|
||||
*/
|
||||
public $hierarchical = false;
|
||||
|
||||
/**
|
||||
* Validates the components of a URI
|
||||
* @note This implementation should be called by children if they define
|
||||
* a default port, as it does port processing.
|
||||
* @param $uri Instance of HTMLPurifier_URI
|
||||
* @param $config HTMLPurifier_Config object
|
||||
* @param $context HTMLPurifier_Context object
|
||||
* @return Bool success or failure
|
||||
*/
|
||||
public function validate(&$uri, $config, $context) {
|
||||
if ($this->default_port == $uri->port) $uri->port = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vim: et sw=4 sts=4
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user