diff --git a/.gitignore b/.gitignore index b300f579e2..5b7e09b507 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ addon *~ robots.txt -#ignore documentation, it should be newly built +#ignore documentation, it should be newly built doc/html #ignore reports, should be generted with every build @@ -23,7 +23,7 @@ report/ .buildpath .externalToolBuilders .settings -#ignore OSX .DS_Store files +#ignore OSX .DS_Store files .DS_Store /nbproject/private/ diff --git a/boot.php b/boot.php index 4ef30eadac..58b4bc0983 100644 --- a/boot.php +++ b/boot.php @@ -6,17 +6,19 @@ /** * Friendica - * + * * Friendica is a communications platform for integrated social communications * utilising decentralised communications and linkage to several indie social * projects - as well as popular mainstream providers. - * + * * Our mission is to free our friends and families from the clutches of * data-harvesting corporations, and pave the way to a future where social * communications are free and open and flow between alternate providers as * 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'); @@ -28,7 +30,7 @@ require_once('include/cache.php'); require_once('library/Mobile_Detect/Mobile_Detect.php'); require_once('include/features.php'); require_once('include/identity.php'); - +require_once('include/pidfile.php'); require_once('update.php'); require_once('include/dbstructure.php'); @@ -463,11 +465,12 @@ class App { public $plugins; public $apps = array(); public $identities; - public $is_mobile; - public $is_tablet; + public $is_mobile = false; + public $is_tablet = false; public $is_friendica_app; public $performance = array(); public $callstack = array(); + public $theme_info = array(); public $nav_sel; @@ -588,15 +591,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 +856,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 +939,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 * @@ -1034,22 +1047,42 @@ class App { function save_timestamp($stamp, $value) { $duration = (float)(microtime(true)-$stamp); + if (!isset($this->performance[$value])) { + // Prevent ugly E_NOTICE + $this->performance[$value] = 0; + } + $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(); + if (!isset($this->callstack[$value][$callstack])) { + // Prevent ugly E_NOTICE + $this->callstack[$value][$callstack] = 0; + } + + $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) { @@ -1065,6 +1098,55 @@ class App { return($this->is_friendica_app); } + /** + * @brief Checks if the maximum load is reached + * + * @return bool Is the load reached? + */ + function maxload_reached() { + + $maxsysload = intval(get_config('system', 'maxloadavg')); + if ($maxsysload < 1) + $maxsysload = 50; + + $load = current_load(); + if ($load) { + if (intval($load) > $maxsysload) { + logger('system: load '.$load.' too high.'); + return true; + } + } + return false; + } + + /** + * @brief Checks if the process is already running + * + * @param string $taskname The name of the task that will be used for the name of the lockfile + * @param string $task The path and name of the php script + * @param int $timeout The timeout after which a task should be killed + * + * @return bool Is the process running? + */ + function is_already_running($taskname, $task = "", $timeout = 540) { + + $lockpath = get_lockpath(); + if ($lockpath != '') { + $pidfile = new pidfile($lockpath, $taskname); + if ($pidfile->is_already_running()) { + logger("Already running"); + if ($pidfile->running_time() > $timeout) { + $pidfile->kill(); + logger("killed stale process"); + // Calling a new instance + if ($task != "") + proc_run('php', $task); + } + return true; + } + } + return false; + } } /** @@ -1416,7 +1498,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"); @@ -1476,6 +1558,9 @@ function killme() { * @brief Redirect to another URL and terminate this process. */ function goaway($s) { + if (!strstr(normalise_link($s), "http://")) + $s = App::get_baseurl()."/".$s; + header("Location: $s"); killme(); } @@ -1735,9 +1820,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) { diff --git a/database.sql b/database.sql index 70b315ea24..89b821e23a 100644 --- a/database.sql +++ b/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, @@ -200,17 +201,6 @@ CREATE TABLE IF NOT EXISTS `deliverq` ( PRIMARY KEY(`id`) ) DEFAULT CHARSET=utf8; --- --- TABLE dsprphotoq --- -CREATE TABLE IF NOT EXISTS `dsprphotoq` ( - `id` int(10) unsigned NOT NULL auto_increment, - `uid` int(11) NOT NULL DEFAULT 0, - `msg` mediumtext NOT NULL, - `attempt` tinyint(4) NOT NULL DEFAULT 0, - PRIMARY KEY(`id`) -) DEFAULT CHARSET=utf8; - -- -- TABLE event -- @@ -411,21 +401,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 -- @@ -926,13 +901,11 @@ CREATE TABLE IF NOT EXISTS `session` ( CREATE TABLE IF NOT EXISTS `sign` ( `id` int(10) unsigned NOT NULL auto_increment, `iid` int(10) unsigned NOT NULL DEFAULT 0, - `retract_iid` int(10) unsigned NOT NULL DEFAULT 0, `signed_text` mediumtext NOT NULL, `signature` text NOT NULL, `signer` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY(`id`), - INDEX `iid` (`iid`), - INDEX `retract_iid` (`retract_iid`) + INDEX `iid` (`iid`) ) DEFAULT CHARSET=utf8; -- diff --git a/doc/Accesskeys.md b/doc/Accesskeys.md index c49e79c0ab..4f16ba2536 100644 --- a/doc/Accesskeys.md +++ b/doc/Accesskeys.md @@ -37,10 +37,7 @@ General * o: Profile * t: Contacts * d: Common friends -* b: Toggle Blocked status -* i: Toggle Ignored status -* v: Toggle Archive status -* r: Repair +* r: Advanced /message -------- diff --git a/doc/BBCode.md b/doc/BBCode.md index fe7c1481f6..186b1cda93 100644 --- a/doc/BBCode.md +++ b/doc/BBCode.md @@ -143,6 +143,56 @@ Map You can embed maps from coordinates or addresses. This require "openstreetmap" addon version 1.3 or newer. +----------------------------------------------------------- + +Abstract for longer posts +------------------------- + +If you want to spread your post to several third party networks you can have the problem that these networks have (for example) a length limitation. +(Like on Twitter) + +Friendica is using a semi intelligent mechanism to generate a fitting abstract. +But it can be interesting to define an own abstract that will only be displayed on the external network. +This is done with the [abstract]-element. +Example: + +
[abstract]Totally interesting! A must-see! Please click the link![/abstract]
+I want to tell you a really boring story that you really never wanted 
+to hear.
+ +Twitter would display the text "Totally interesting! A must-see! Please click the link!". +On Friendica you would only see the text after "I want to tell you a really ..." + +It is even possible to define abstracts for separate networks: + +
+[abstract]Hi friends Here are my newest pictures![abstract]
+[abstract=twit]Hi my dear Twitter followers. Do you want to see my new 
+pictures?[abstract]
+[abstract=apdn]Helly my dear followers on ADN. I made sone new pictures 
+that I wanted to share with you.[abstract]
+Today I was in the woods and took some real cool pictures ...
+
+ +For Twitter and App.net the system will use the defined abstracts. +For other networks (e.g. when you are using the "statusnet" connector that is used to post to GNU Social) the general abstract element will be used. + +If you use (for example) the "buffer" connector to post to Facebook or Google+ you can use this element to define an abstract for a longer blogpost that you don't want to post completely to these networks. + +Networks like Facebook or Google+ aren't length limited. +For this reason the [abstract] element isn't used. +Instead you have to name the explicit network: + +
+[abstract]These days I had a strange encounter ...[abstract]
+[abstract=goog]Helly my dear Google+ followers. You have to read my 
+newest blog post![abstract]
+[abstract=face]Hello my Facebook friends. These days happened something 
+really cool.[abstract]
+While taking pictures in the woods I had a really strange encounter ... 
+ +The [abstract] element isn't working with the native OStatus connection or with connectors where we post the HTML. +(Like Tumblr, Wordpress or Pump.io) Special ------- @@ -150,5 +200,3 @@ Special If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
[noparse][b]bold[/b][/noparse]
: [b]bold[/b] - - diff --git a/doc/Bugs-and-Issues.md b/doc/Bugs-and-Issues.md index 366b2ed662..0ece265a24 100644 --- a/doc/Bugs-and-Issues.md +++ b/doc/Bugs-and-Issues.md @@ -6,6 +6,8 @@ Bugs and Issues If your server has a support page, you should report any bugs/issues you encounter there first. Reporting to your support page before reporting to the developers makes their job easier, as they don't have to deal with bug reports that might not have anything to do with them. This helps us get new features faster. +You can also contact the [friendica support forum](https://helpers.pyxis.uberspace.de/profile/helpers) and report your problem there. +Maybe someone from another node encountered the problem as well and can help you. If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](http://bugs.friendica.com/). Please perform a search to see if there's already an open bug that matches yours before submitting anything. diff --git a/doc/Connectors.md b/doc/Connectors.md index cd4b643f14..148352c552 100644 --- a/doc/Connectors.md +++ b/doc/Connectors.md @@ -57,13 +57,15 @@ All that the pages need to have is a discoverable feed using either the RSS or A Twitter --- -To follow a Twitter member, put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page. +To follow a Twitter member, the Twitter-Connector (Addon) needs to be configured on your node. +If this is the case put the URL of the Twitter member's main page into the Connect box on your [Contacts](contacts) page. To reply, you must have the Twitter connector installed, and reply using your own status editor. Begin the message with @twitterperson replacing with the Twitter username. Email --- +If the php module for IMAP support is available on your server, Friendica can connect to email contacts as well. Configure the email connector from your [Settings](settings) page. Once this has been done, you may enter an email address to connect with using the Connect box on your [Contacts](contacts) page. They must be the sender of a message which is currently in your INBOX for the connection to succeed. diff --git a/doc/Developers-Intro.md b/doc/Developers-Intro.md index 10bbd5632a..8e3cd03b18 100644 --- a/doc/Developers-Intro.md +++ b/doc/Developers-Intro.md @@ -83,11 +83,11 @@ Ask us to find out whom to talk to about their experiences. Do not worry about cross-posting. ###Client software -There are free software clients that do somehow work with Friendica but most of them need love and maintenance. -Also, they were mostly made for other platforms using the GNU Social API. -This means they lack the features that are really specific to Friendica. -Popular clients you might want to have a look at are: +As Friendica is using a [Twitter/GNU Social compatible API](help/api) any of the clients for those platforms should work with Friendica as well. +Furthermore there are several client projects, especially for use with Friendica. +If you are interested in improving those clients, please contact the developers of the clients directly. -* [Hotot (Linux)](http://hotot.org/) - abandoned -* [Friendica for Android](https://github.com/max-weller/friendica-for-android) - abandoned -* You can find more working client software in [Wikipedia](https://en.wikipedia.org/wiki/Friendica). +* Android / CynogenMod: **Friendica for Android** [src](https://github.com/max-weller/friendica-for-android), [homepage](http://friendica.android.max-weller.de/) - abandoned +* iOS: *currently no client* +* SailfishOS: **Friendiy** [src](https://kirgroup.com/projects/fabrixxm/harbour-friendly) - developed by [Fabio](https://kirgroup.com/profile/fabrixxm/?tab=profile) +* Windows: **Friendica Mobile** for Windows versions [before 8.1](http://windowsphone.com/s?appid=e3257730-c9cf-4935-9620-5261e3505c67) and [Windows 10](https://www.microsoft.com/store/apps/9nblggh0fhmn) - developed by [Gerhard Seeber](http://mozartweg.dyndns.org/friendica/profile/gerhard/?tab=profile) diff --git a/doc/Home.md b/doc/Home.md index 3b6442867c..1f9b0cfab7 100644 --- a/doc/Home.md +++ b/doc/Home.md @@ -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) diff --git a/doc/Plugins.md b/doc/Plugins.md index 24d403e1f6..a30a3f4a71 100644 --- a/doc/Plugins.md +++ b/doc/Plugins.md @@ -1,5 +1,7 @@ Friendica Addon/Plugin development -========================== +============== + +* [Home](help) Please see the sample addon 'randplace' for a working example of using some of these features. Addons work by intercepting event hooks - which must be registered. @@ -16,12 +18,12 @@ Future extensions may provide for "setup" amd "remove". Plugins should contain a comment block with the four following parameters: - /* - * Name: My Great Plugin - * Description: This is what my plugin does. It's really cool - * Version: 1.0 - * Author: John Q. Public - */ + /* + * Name: My Great Plugin + * Description: This is what my plugin does. It's really cool. + * Version: 1.0 + * Author: John Q. Public + */ Register your plugin hooks during installation. @@ -45,7 +47,7 @@ Your hook callback functions will be called with at least one and possibly two a If you wish to make changes to the calling data, you must declare them as reference variables (with '&') during function declaration. -###$a +#### $a $a is the Friendica 'App' class. It contains a wealth of information about the current state of Friendica: @@ -56,13 +58,13 @@ It contains a wealth of information about the current state of Friendica: It is recommeded you call this '$a' to match its usage elsewhere. -###$b +#### $b $b can be called anything you like. This is information specific to the hook currently being processed, and generally contains information that is being immediately processed or acted on that you can use, display, or alter. Remember to declare it with '&' if you wish to alter it. Modules --------- +--- Plugins/addons may also act as "modules" and intercept all page requests for a given URL path. In order for a plugin to act as a module it needs to define a function "plugin_name_module()" which takes no arguments and needs not do anything. @@ -72,15 +74,15 @@ These are parsed into an array $a->argv, with a corresponding $a->argc indicatin So http://my.web.site/plugin/arg1/arg2 would look for a module named "plugin" and pass its module functions the $a App structure (which is available to many components). This will include: - $a->argc = 3 - $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); + $a->argc = 3 + $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); Your module functions will often contain the function plugin_name_content(&$a), which defines and returns the page body content. They may also contain plugin_name_post(&$a) which is called before the _content function and typically handles the results of POST forms. You may also have plugin_name_init(&$a) which is called very early on and often does module initialisation. Templates ----------- +--- If your plugin needs some template, you can use the Friendica template system. Friendica uses [smarty3](http://www.smarty.net/) as a template engine. @@ -104,140 +106,140 @@ See also the wiki page [Quick Template Guide](https://github.com/friendica/frien Current hooks ------------- -###'authenticate' +### 'authenticate' 'authenticate' is called when a user attempts to login. $b is an array containing: - 'username' => the supplied username - 'password' => the supplied password + 'username' => the supplied username + 'password' => the supplied password 'authenticated' => set this to non-zero to authenticate the user. 'user_record' => successful authentication must also return a valid user record from the database -###'logged_in' +### 'logged_in' 'logged_in' is called after a user has successfully logged in. $b contains the $a->user array. -###'display_item' +### 'display_item' 'display_item' is called when formatting a post for display. $b is an array: 'item' => The item (array) details pulled from the database 'output' => the (string) HTML representation of this item prior to adding it to the page -###'post_local' +### 'post_local' * called when a status post or comment is entered on the local system * $b is the item array of the information to be stored in the database * Please note: body contents are bbcode - not HTML -###'post_local_end' +### 'post_local_end' * called when a local status post or comment has been stored on the local system * $b is the item array of the information which has just been stored in the database * Please note: body contents are bbcode - not HTML -###'post_remote' +### 'post_remote' * called when receiving a post from another source. This may also be used to post local activity or system generated messages. * $b is the item array of information to be stored in the database and the item body is bbcode. -###'settings_form' +### 'settings_form' * called when generating the HTML for the user Settings page * $b is the (string) HTML of the settings page before the final '' tag. -###'settings_post' +### 'settings_post' * called when the Settings pages are submitted * $b is the $_POST array -###'plugin_settings' +### 'plugin_settings' * called when generating the HTML for the addon settings page * $b is the (string) HTML of the addon settings page before the final '' tag. -###'plugin_settings_post' +### 'plugin_settings_post' * called when the Addon Settings pages are submitted * $b is the $_POST array -###'profile_post' +### 'profile_post' * called when posting a profile page * $b is the $_POST array -###'profile_edit' +### 'profile_edit' 'profile_edit' is called prior to output of profile edit page. $b is an array containing: 'profile' => profile (array) record from the database 'entry' => the (string) HTML of the generated entry -###'profile_advanced' +### 'profile_advanced' * called when the HTML is generated for the 'Advanced profile', corresponding to the 'Profile' tab within a person's profile page * $b is the (string) HTML representation of the generated profile * The profile array details are in $a->profile. -###'directory_item' +### 'directory_item' 'directory_item' is called from the Directory page when formatting an item for display. $b is an array: 'contact' => contact (array) record for the person from the database 'entry' => the (string) HTML of the generated entry -###'profile_sidebar_enter' +### 'profile_sidebar_enter' * called prior to generating the sidebar "short" profile for a page * $b is the person's profile array -###'profile_sidebar' +### 'profile_sidebar' 'profile_sidebar is called when generating the sidebar "short" profile for a page. $b is an array: 'profile' => profile (array) record for the person from the database 'entry' => the (string) HTML of the generated entry -###'contact_block_end' +### 'contact_block_end' is called when formatting the block of contacts/friends on a profile sidebar has completed. $b is an array: 'contacts' => array of contacts 'output' => the (string) generated HTML of the contact block -###'bbcode' +### 'bbcode' * called during conversion of bbcode to html * $b is a string converted text -###'html2bbcode' +### 'html2bbcode' * called during conversion of html to bbcode (e.g. remote message posting) * $b is a string converted text -###'page_header' +### 'page_header' * called after building the page navigation section * $b is a string HTML of nav region -###'personal_xrd' +### 'personal_xrd' 'personal_xrd' is called prior to output of personal XRD file. $b is an array: 'user' => the user record for the person 'xml' => the complete XML to be output -###'home_content' +### 'home_content' * called prior to output home page content, shown to unlogged users * $b is (string) HTML of section region -###'contact_edit' +### 'contact_edit' is called when editing contact details on an individual from the Contacts page. $b is an array: 'contact' => contact record (array) of target contact 'output' => the (string) generated HTML of the contact edit page -###'contact_edit_post' +### 'contact_edit_post' * called when posting the contact edit page. * $b is the $_POST array -###'init_1' +### 'init_1' * called just after DB has been opened and before session start * $b is not used or passed -###'page_end' +### 'page_end' * called after HTML content functions have completed * $b is (string) HTML of content div -###'avatar_lookup' +### 'avatar_lookup' 'avatar_lookup' is called when looking up the avatar. $b is an array: @@ -245,11 +247,11 @@ $b is an array: 'email' => email to look up the avatar for 'url' => the (string) generated URL of the avatar -###'emailer_send_prepare' +### 'emailer_send_prepare' 'emailer_send_prepare' called from Emailer::send() before building the mime message. $b is an array, params to Emailer::send() - 'fromName' => name of the sender + 'fromName' => name of the sender 'fromEmail' => email fo the sender 'replyTo' => replyTo address to direct responses 'toEmail' => destination email address @@ -258,20 +260,20 @@ $b is an array, params to Emailer::send() 'textVersion' => text only version of the message 'additionalMailHeader' => additions to the smtp mail header -###'emailer_send' +### 'emailer_send' is called before calling PHP's mail(). $b is an array, params to mail() - 'to' - 'subject' + 'to' + 'subject' 'body' 'headers' -###'nav_info' +### 'nav_info' is called after the navigational menu is build in include/nav.php. $b is an array containing $nav from nav.php. -###'template_vars' +### 'template_vars' is called before vars are passed to the template engine to render the page. The registered function can add,change or remove variables passed to template. $b is an array with: @@ -463,4 +465,3 @@ mod/cb.php: call_hooks('cb_afterpost'); mod/cb.php: call_hooks('cb_content', $o); mod/directory.php: call_hooks('directory_item', $arr); - diff --git a/doc/SSL.md b/doc/SSL.md index a72eec2a16..bcff929fe5 100644 --- a/doc/SSL.md +++ b/doc/SSL.md @@ -90,8 +90,8 @@ If you run your own server, upload the files and check out the Mozilla wiki link Let's encrypt --- -If you run your own server and you control your name server, the "Let's encrypt" initiative might become an interesting alternative. -Their offer is not ready, yet. +If you run your own server, the "Let's encrypt" initiative might become an interesting alternative. +Their offer is in public beta right now. Check out [their website](https://letsencrypt.org/) for status updates. Web server settings diff --git a/doc/Settings.md b/doc/Settings.md index 86254cb29e..7d909afa09 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -11,8 +11,6 @@ Hot Keys Friendica traps the following keyboard events: * [Pause] - Pauses "Ajax" update activity. This is the process that provides updates without reloading the page. You may wish to pause it to reduce network usage and/or as a debugging aid for javascript developers. A pause indicator will appear at the lower right hand corner of the page. Hit the [pause] key once again to resume. -* [F8] - Displays a language selector - Birthday Notifications --- diff --git a/doc/api.md b/doc/api.md index ced078f556..7d6f440c58 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1,12 +1,27 @@ Friendica API === -The Friendica API aims to be compatible to the [GNU Social API](http://skilledtests.com/wiki/Twitter-compatible_API) and the [Twitter API](https://dev.twitter.com/rest/public). +The Friendica API aims to be compatible to the [GNU Social API](http://wiki.gnusocial.de/gnusocial:api) and the [Twitter API](https://dev.twitter.com/rest/public). 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/ -#### 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 -```true``` - -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": { - "": "url to image" - ... - }, - // if 'scale' is set - "datasize": "size in byte", - "data": "base64 encoded image data" - } -``` - -xml -``` - - photo id - date(YYYY-MM-GG HH:MM:SS) - date(YYYY-MM-GG HH:MM:SS) - photo title - photo description - album name - original file name - mime type - number - number - 1 if is profile 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 -``` - - - "url to thumb sized image" - - ... - -``` - ---- -### 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,28 @@ 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/conversation (*; AUTH) +It shows all direct answers (excluding the original post) to a given id. + +#### Parameter +* id: id of the post +* count: Items per page (default: 20) +* page: page number +* since_id: minimal id +* max_id: maximum id +* include_entities: "true" shows entities for pictures and links (Default: false) + +--- +### statusnet/version (*) #### Unsupported parameters * user_id @@ -507,7 +411,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 +421,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 +437,39 @@ Friendica doesn't allow showing friends of other users. ## Implemented API calls (not compatible with other APIs) + --- -### friendica/group_show +### friendica/activity/ +#### 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 +```true``` + +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 +477,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 +502,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 +526,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": { + "": "url to image" + ... + }, + // if 'scale' is set + "datasize": "size in byte", + "data": "base64 encoded image data" + } +``` + +xml +``` + + photo id + date(YYYY-MM-GG HH:MM:SS) + date(YYYY-MM-GG HH:MM:SS) + photo title + photo description + album name + original file name + mime type + number + number + 1 if is profile 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 +``` + + + "url to thumb sized image" + + ... + +``` + + --- ## Not Implemented API calls The following API calls are implemented in GNU Social but not in Friendica: (incomplete) @@ -702,13 +790,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. diff --git a/doc/autoloader.md b/doc/autoloader.md new file mode 100644 index 0000000000..947eade23c --- /dev/null +++ b/doc/autoloader.md @@ -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 + 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 + 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 + [url]*url*[/url] -Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete -Objekt (z.B. ein Dokument von scribd) eingebunden. +Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete Objekt (z.B. ein Dokument von scribd) eingebunden. Der Titel der Seite mit einem Link zur *url* wird ebenfalls angezeigt. Um eine Karte in einen Beitrag einzubinden, muss das *openstreetmap* Addon aktiviert werden. Ist dies der Fall, kann mit @@ -145,11 +144,54 @@ eine Karte von [OpenStreetmap](http://openstreetmap.org) eingebettet werden. Zur oder eine Adresse in obiger Form verwendet werden. +Zusammenfassung für längere Beiträge +------------------------------------ + +Wenn man seine Beiträge über mehrere Netzwerke verbreiten möchte, hat man häufig das Problem, dass diese Netzwerke z.B. eine Längenbeschränkung haben. +(Z.B. Twitter). + +Friendica benutzt zum Erzeugen eines Anreißtextes eine halbwegs intelligente Logik. +Es kann aber dennoch von Interesse sein, eine eigene Zusammenfassung zu erstellen, die nur auf dem Fremdnetzwerk dargestellt wird. +Dies geschieht mit dem [abstract]-Element. +Beispiel: + +
[abstract]Total spannend! Unbedingt diesen Link anklicken![/abstract]
+Hier erzähle ich euch eine total langweilige Geschichte, die ihr noch 
+nie hören wolltet.
+ +Auf Twitter würde das "Total spannend! Unbedingt diesen Link anklicken!" stehen, auf Friendica würde nur der Text nach "Hier erzähle ..." erscheinen. + +Es ist sogar möglich, für einzelne Netzwerke eigene Zusammenfassungen zu erstellen: + +
+[abstract]Hallo Leute, hier meine neuesten Bilder![abstract]
+[abstract=twit]Hallo Twitter-User, hier meine neuesten Bilder![abstract]
+[abstract=apdn]Hallo App.net-User, hier meine neuesten Bilder![abstract]
+Ich war heute wieder im Wald unterwegs und habe tolle Bilder geschossen ...
+
+ +Für Twitter und App.net nimmt das System die entsprechenden Texte. +Bei anderen Netzwerken, bei denen der Inhalt gekürzt wird (z.B. beim "statusnet"-Connector, der für das Posten nach GNU Social verwendet wird) wird dann die Zusammenfassung unter [abstract] verwendet. + +Wenn man z.B. den "buffer"-Connector verwendet, um nach Facebook oder Google+ zu posten, kann man dieses Element ebenfalls verwenden, wenn man z.B. einen längeren Blogbeitrag erstellt hat, aber ihn nicht komplett in diese Netzwerke posten möchte. + +Netzwerke wie Facebook oder Google+ sind nicht in der Postinglänge beschränkt. +Aus diesem Grund greift nicht die [abstract]-Zusammenfassung. Stattdessen muss man das Netzwerk explizit angeben: + +
+[abstract]Ich habe neulich wieder etwas erlebt, was ich euch mitteilen möchte.[abstract]
+[abstract=goog]Hallo meine Google+-Kreislinge. Ich habe neulich wieder 
+etwas erlebt, was ich euch mitteilen möchte.[abstract]
+[abstract=face]Hallo Facebook-Freunde! Ich habe neulich wieder etwas 
+erlebt, was ich euch mitteilen möchte.[abstract]
+Beim Bildermachen im Wald habe ich neulich eine interessante Person 
+getroffen ... 
+ +Das [abstract]-Element greift nicht bei der nativen OStatus-Verbindung oder bei Connectoren, die den HTML-Text posten wie z.B. die Connectoren zu Tumblr, Wordpress oder Pump.io. + Spezielle Tags ------- Wenn Du über BBCode Tags in einer Nachricht schreiben möchtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu schützen:
[noparse][b]fett[/b][/noparse]
: [b]fett[/b] - - diff --git a/doc/de/Plugins.md b/doc/de/Plugins.md index dcff41a4b6..40be4a0695 100644 --- a/doc/de/Plugins.md +++ b/doc/de/Plugins.md @@ -1,27 +1,28 @@ -**Friendica Addon/Plugin-Entwicklung** +Friendica Addon/Plugin-Entwicklung ============== * [Zur Startseite der Hilfe](help) -Bitte schau dir das Beispiel-Addon "randplace" für ein funktionierendes Beispiel für manche der hier aufgeführten Funktionen an. -Das Facebook-Addon bietet ein Beispiel dafür, die "addon"- und "module"-Funktion gemeinsam zu integrieren. -Addons arbeiten, indem sie Event Hooks abfangen. Module arbeiten, indem bestimmte Seitenanfragen (durch den URL-Pfad) abgefangen werden +Bitte schau dir das Beispiel-Addon "randplace" für ein funktionierendes Beispiel für manche der hier aufgeführten Funktionen an. +Das Facebook-Addon bietet ein Beispiel dafür, die "addon"- und "module"-Funktion gemeinsam zu integrieren. +Addons arbeiten, indem sie Event Hooks abfangen. +Module arbeiten, indem bestimmte Seitenanfragen (durch den URL-Pfad) abgefangen werden. -Plugin-Namen können keine Leerstellen oder andere Interpunktionen enthalten und werden als Datei- und Funktionsnamen genutzt. -Du kannst einen lesbaren Namen im Kommentarblock eintragen. -Jedes Addon muss beides beinhalten - eine Installations- und eine Deinstallationsfunktion, die auf dem Addon-/Plugin-Namen basieren; z.B. "plugin1name_install()". -Diese beiden Funktionen haben keine Argumente und sind dafür verantwortlich, Event Hooks zu registrieren und abzumelden (unregistering), die dein Plugin benötigt. -Die Installations- und Deinstallationsfunktionfunktionen werden auch ausgeführt (z.B. neu installiert), wenn sich das Plugin nach der Installation ändert - somit sollte deine Deinstallationsfunktion keine Daten zerstört und deine Installationsfunktion sollte bestehende Daten berücksichtigen. +Plugin-Namen können keine Leerstellen oder andere Interpunktionen enthalten und werden als Datei- und Funktionsnamen genutzt. +Du kannst einen lesbaren Namen im Kommentarblock eintragen. +Jedes Addon muss beides beinhalten - eine Installations- und eine Deinstallationsfunktion, die auf dem Addon-/Plugin-Namen basieren; z.B. "plugin1name_install()". +Diese beiden Funktionen haben keine Argumente und sind dafür verantwortlich, Event Hooks zu registrieren und abzumelden (unregistering), die dein Plugin benötigt. +Die Installations- und Deinstallationsfunktionfunktionen werden auch ausgeführt (z.B. neu installiert), wenn sich das Plugin nach der Installation ändert - somit sollte deine Deinstallationsfunktion keine Daten zerstört und deine Installationsfunktion sollte bestehende Daten berücksichtigen. Zukünftige Extensions werden möglicherweise "Setup" und "Entfernen" anbieten. Plugins sollten einen Kommentarblock mit den folgenden vier Parametern enthalten: - /* - * Name: My Great Plugin - * Description: This is what my plugin does. It's really cool - * Version: 1.0 - * Author: John Q. Public - */ + /* + * Name: My Great Plugin + * Description: This is what my plugin does. It's really cool. + * Version: 1.0 + * Author: John Q. Public + */ Registriere deine Plugin-Hooks während der Installation. @@ -29,45 +30,50 @@ Registriere deine Plugin-Hooks während der Installation. $hookname ist ein String und entspricht einem bekannten Friendica-Hook. -$file steht für den Pfadnamen, der relativ zum Top-Level-Friendicaverzeichnis liegt. +$file steht für den Pfadnamen, der relativ zum Top-Level-Friendicaverzeichnis liegt. Das *sollte* "addon/plugin_name/plugin_name.php' sein. $function ist ein String und der Name der Funktion, die ausgeführt wird, wenn der Hook aufgerufen wird. +Argumente +--- + Deine Hook-Callback-Funktion wird mit mindestens einem und bis zu zwei Argumenten aufgerufen function myhook_function(&$a, &$b) { } -Wenn du Änderungen an den aufgerufenen Daten vornehmen willst, musst du diese als Referenzvariable (mit "&") während der Funktionsdeklaration deklarieren. +Wenn du Änderungen an den aufgerufenen Daten vornehmen willst, musst du diese als Referenzvariable (mit "&") während der Funktionsdeklaration deklarieren. -$a ist die Friendica "App"-Klasse, die eine Menge an Informationen über den aktuellen Friendica-Status beinhaltet, u.a. welche Module genutzt werden, Konfigurationsinformationen, Inhalte der Seite zum Zeitpunkt des Hook-Aufrufs. -Es ist empfohlen, diese Funktion "$a" zu nennen, um seine Nutzung an den Gebrauch an anderer Stelle anzugleichen. +$a ist die Friendica "App"-Klasse, die eine Menge an Informationen über den aktuellen Friendica-Status beinhaltet, u.a. welche Module genutzt werden, Konfigurationsinformationen, Inhalte der Seite zum Zeitpunkt des Hook-Aufrufs. +Es ist empfohlen, diese Funktion "$a" zu nennen, um seine Nutzung an den Gebrauch an anderer Stelle anzugleichen. -$b kann frei benannt werden. -Diese Information ist speziell auf den Hook bezogen, der aktuell bearbeitet wird, und beinhaltet normalerweise Daten, die du sofort nutzen, anzeigen oder bearbeiten kannst. -Achte darauf, diese mit "&" zu deklarieren, wenn du sie bearbeiten willst. +$b kann frei benannt werden. +Diese Information ist speziell auf den Hook bezogen, der aktuell bearbeitet wird, und beinhaltet normalerweise Daten, die du sofort nutzen, anzeigen oder bearbeiten kannst. +Achte darauf, diese mit "&" zu deklarieren, wenn du sie bearbeiten willst. -**Module** +Module +--- -Plugins/Addons können auch als "Module" agieren und alle Seitenanfragen für eine bestimte URL abfangen. -Um ein Plugin als Modul zu nutzen, ist es nötig, die Funktion "plugin_name_module()" zu definieren, die keine Argumente benötigt und nichts weiter machen muss. +Plugins/Addons können auch als "Module" agieren und alle Seitenanfragen für eine bestimte URL abfangen. +Um ein Plugin als Modul zu nutzen, ist es nötig, die Funktion "plugin_name_module()" zu definieren, die keine Argumente benötigt und nichts weiter machen muss. -Wenn diese Funktion existiert, wirst du nun alle Seitenanfragen für "http://my.web.site/plugin_name" erhalten - mit allen URL-Komponenten als zusätzliche Argumente. -Diese werden in ein Array $a->argv geparst und stimmen mit $a->argc überein, wobei sie die Anzahl der URL-Komponenten abbilden. -So würde http://my.web.site/plugin/arg1/arg2 nach einem Modul "plugin" suchen und seiner Modulfunktion die $a-App-Strukur übergeben (dies ist für viele Komponenten verfügbar). Das umfasst: +Wenn diese Funktion existiert, wirst du nun alle Seitenanfragen für "http://example.com/plugin_name" erhalten - mit allen URL-Komponenten als zusätzliche Argumente. +Diese werden in ein Array $a->argv geparst und stimmen mit $a->argc überein, wobei sie die Anzahl der URL-Komponenten abbilden. +So würde http://example.com/plugin/arg1/arg2 nach einem Modul "plugin" suchen und seiner Modulfunktion die $a-App-Strukur übergeben (dies ist für viele Komponenten verfügbar). Das umfasst: - $a->argc = 3 - $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); + $a->argc = 3 + $a->argv = array(0 => 'plugin', 1 => 'arg1', 2 => 'arg2'); -Deine Modulfunktionen umfassen oft die Funktion plugin_name_content(&$a), welche den Seiteninhalt definiert und zurückgibt. -Sie können auch plugin_name_post(&$a) umfassen, welches vor der content-Funktion aufgerufen wird und normalerweise die Resultate der POST-Formulare handhabt. +Deine Modulfunktionen umfassen oft die Funktion plugin_name_content(&$a), welche den Seiteninhalt definiert und zurückgibt. +Sie können auch plugin_name_post(&$a) umfassen, welches vor der content-Funktion aufgerufen wird und normalerweise die Resultate der POST-Formulare handhabt. Du kannst ebenso plugin_name_init(&$a) nutzen, was oft frühzeitig aufgerufen wird und das Modul initialisert. -**Derzeitige Hooks:** +Derzeitige Hooks +--- **'authenticate'** - wird aufgerufen, wenn sich der User einloggt. $b ist ein Array @@ -180,6 +186,9 @@ Du kannst ebenso plugin_name_init(&$a) nutzen, was oft frühzeitig aufgerufen wi - wird aufgerufen nachdem in include/nav,php der Inhalt des Navigations Menüs erzeugt wurde. - $b ist ein Array, das $nav wiederspiegelt. +Komplette Liste der Hook-Callbacks +--- + Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 14-Feb-2012 generiert): Bitte schau in die Quellcodes für Details zu Hooks, die oben nicht dokumentiert sind. boot.php: call_hooks('login_hook',$o); @@ -204,7 +213,7 @@ include/text.php: call_hooks('contact_block_end', $arr); include/text.php: call_hooks('smilie', $s); -include/text.php: call_hooks('prepare_body_init', $item); +include/text.php: call_hooks('prepare_body_init', $item); include/text.php: call_hooks('prepare_body', $prep_arr); @@ -359,4 +368,3 @@ mod/cb.php: call_hooks('cb_afterpost'); mod/cb.php: call_hooks('cb_content', $o); mod/directory.php: call_hooks('directory_item', $arr); - diff --git a/doc/de/Settings.md b/doc/de/Settings.md index 988b3657c0..4ad9f39ba5 100644 --- a/doc/de/Settings.md +++ b/doc/de/Settings.md @@ -14,9 +14,6 @@ Friendica erfasst die folgenden Tastaturbefehle: * [Pause] - Pausiert die Update-Aktivität via "Ajax". Das ist ein Prozess, der Updates durchführt, ohne die Seite neu zu laden. Du kannst diesen Prozess pausieren, um deine Netzwerkauslastung zu reduzieren und/oder um es in der Javascript-Programmierung zum Debuggen zu nutzen. Ein Pausenzeichen erscheint unten links im Fenster. Klicke die [Pause]-Taste ein weiteres Mal, um die Pause zu beenden. -* [F8] - Zeigt eine Sprachauswahl an - - **Geburtstagsbenachrichtigung** Geburtstage erscheinen auf deiner Startseite für alle Freunde, die in den nächsten 6 Tagen Geburtstag haben. diff --git a/doc/htconfig.md b/doc/htconfig.md index 4764c287c8..a36e0bef22 100644 --- a/doc/htconfig.md +++ b/doc/htconfig.md @@ -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. @@ -63,9 +64,6 @@ line to your .htconfig.php: * throttle_limit_week - Maximum number of posts that a user can send per week with the API. * throttle_limit_month - Maximum number of posts that a user can send per month with the API. * wall-to-wall_share (Boolean) - Displays forwarded posts like "wall-to-wall" posts. -* worker (Boolean) - (Experimental) Use the worker system instead of calling several background processes. Reduces the overall load and speeds up item delivery. -* worker_dont_fork (Boolean) - if enabled, the workers are only called from the poller process. Useful on systems that permit the use of "proc_open". -* worker_queues - Number of parallel workers. Default value is 10 queues. * xrd_timeout - Timeout for fetching the XRD links. Default value is 20 seconds. ## service_class ## diff --git a/doc/snarty3-templates.md b/doc/smarty3-templates.md similarity index 100% rename from doc/snarty3-templates.md rename to doc/smarty3-templates.md diff --git a/include/Contact.php b/include/Contact.php index 3799e0b189..79a14ab581 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -129,7 +129,7 @@ function terminate_friendship($user,$self,$contact) { } elseif($contact['network'] === NETWORK_DIASPORA) { require_once('include/diaspora.php'); - diaspora_unshare($user,$contact); + diaspora::send_unshare($user,$contact); } elseif($contact['network'] === NETWORK_DFRN) { require_once('include/dfrn.php'); @@ -555,60 +555,6 @@ function posts_from_gcontact($a, $gcontact_id) { return $o; } -/** - * @brief set the gcontact-id in all item entries - * - * This job has to be started multiple times until all entries are set. - * It isn't started in the update function since it would consume too much time and can be done in the background. - */ -function item_set_gcontact() { - define ('POST_UPDATE_VERSION', 1192); - - // Was the script completed? - if (get_config("system", "post_update_version") >= POST_UPDATE_VERSION) - return; - - // Check if the first step is done (Setting "gcontact-id" in the item table) - $r = q("SELECT `author-link`, `author-name`, `author-avatar`, `uid`, `network` FROM `item` WHERE `gcontact-id` = 0 LIMIT 1000"); - if (!$r) { - // Are there unfinished entries in the thread table? - $r = q("SELECT COUNT(*) AS `total` FROM `thread` - INNER JOIN `item` ON `item`.`id` =`thread`.`iid` - WHERE `thread`.`gcontact-id` = 0 AND - (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)"); - - if ($r AND ($r[0]["total"] == 0)) { - set_config("system", "post_update_version", POST_UPDATE_VERSION); - return false; - } - - // Update the thread table from the item table - q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid` - SET `thread`.`gcontact-id` = `item`.`gcontact-id` - WHERE `thread`.`gcontact-id` = 0 AND - (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)"); - - return false; - } - - $item_arr = array(); - foreach ($r AS $item) { - $index = $item["author-link"]."-".$item["uid"]; - $item_arr[$index] = array("author-link" => $item["author-link"], - "uid" => $item["uid"], - "network" => $item["network"]); - } - - // Set the "gcontact-id" in the item table and add a new gcontact entry if needed - foreach($item_arr AS $item) { - $gcontact_id = get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'], - "photo" => $item['author-avatar'], "name" => $item['author-name'])); - q("UPDATE `item` SET `gcontact-id` = %d WHERE `uid` = %d AND `author-link` = '%s' AND `gcontact-id` = 0", - intval($gcontact_id), intval($item["uid"]), dbesc($item["author-link"])); - } - return true; -} - /** * @brief Returns posts from a given contact * diff --git a/include/ForumManager.php b/include/ForumManager.php new file mode 100644 index 0000000000..17a6b6730b --- /dev/null +++ b/include/ForumManager.php @@ -0,0 +1,190 @@ + forum url + * 'name' => forum name + * 'id' => number of the key from the array + * 'micro' => contact photo in format micro + */ + public static function get_list($uid, $showhidden = true, $lastitem, $showprivate = false) { + + $forumlist = array(); + + $order = (($showhidden) ? '' : ' AND NOT `hidden` '); + $order .= (($lastitem) ? ' ORDER BY `last-item` DESC ' : ' ORDER BY `name` ASC '); + $select = '`forum` '; + if ($showprivate) { + $select = '(`forum` OR `prv`)'; + } + + $contacts = q("SELECT `contact`.`id`, `contact`.`url`, `contact`.`name`, `contact`.`micro` FROM `contact` + WHERE `network`= 'dfrn' AND $select AND `uid` = %d + AND NOT `blocked` AND NOT `hidden` AND NOT `pending` AND NOT `archive` + AND `success_update` > `failure_update` + $order ", + intval($uid) + ); + + if (!$contacts) + return($forumlist); + + foreach($contacts as $contact) { + $forumlist[] = array( + 'url' => $contact['url'], + 'name' => $contact['name'], + 'id' => $contact['id'], + 'micro' => $contact['micro'], + ); + } + return($forumlist); + } + + + /** + * @brief Forumlist widget + * + * Sidebar widget to show subcribed friendica forums. If activated + * in the settings, it appears at the notwork page sidebar + * + * @param int $uid The ID of the User + * @param int $cid + * The contact id which is used to mark a forum as "selected" + * @return string + */ + public static function widget($uid,$cid = 0) { + + if(! intval(feature_enabled(local_user(),'forumlist_widget'))) + return; + + $o = ''; + + //sort by last updated item + $lastitem = true; + + $contacts = self::get_list($uid,true,$lastitem, true); + $total = count($contacts); + $visible_forums = 10; + + if(count($contacts)) { + + $id = 0; + + foreach($contacts as $contact) { + + $selected = (($cid == $contact['id']) ? ' forum-selected' : ''); + + $entry = array( + 'url' => 'network?f=&cid=' . $contact['id'], + 'external_url' => 'redir/' . $contact['id'], + 'name' => $contact['name'], + 'cid' => $contact['id'], + 'selected' => $selected, + 'micro' => App::remove_baseurl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)), + 'id' => ++$id, + ); + $entries[] = $entry; + } + + $tpl = get_markup_template('widget_forumlist.tpl'); + + $o .= replace_macros($tpl,array( + '$title' => t('Forums'), + '$forums' => $entries, + '$link_desc' => t('External link to forum'), + '$total' => $total, + '$visible_forums' => $visible_forums, + '$showmore' => t('show more'), + )); + } + + return $o; + } + + /** + * @brief Format forumlist as contact block + * + * This function is used to show the forumlist in + * the advanced profile. + * + * @param int $uid The ID of the User + * @return string + * + */ + public static function profile_advanced($uid) { + + $profile = intval(feature_enabled($uid,'forumlist_profile')); + if(! $profile) + return; + + $o = ''; + + // place holder in case somebody wants configurability + $show_total = 9999; + + //don't sort by last updated item + $lastitem = false; + + $contacts = self::get_list($uid,false,$lastitem,false); + + $total_shown = 0; + + foreach($contacts as $contact) { + $forumlist .= micropro($contact,false,'forumlist-profile-advanced'); + $total_shown ++; + if($total_shown == $show_total) + break; + } + + if(count($contacts) > 0) + $o .= $forumlist; + return $o; + } + + /** + * @brief count unread forum items + * + * Count unread items of connected forums and private groups + * + * @return array + * 'id' => contact id + * 'name' => contact/forum name + * 'count' => counted unseen forum items + * + */ + public static function count_unseen_items() { + $r = q("SELECT `contact`.`id`, `contact`.`name`, COUNT(*) AS `count` FROM `item` + INNER JOIN `contact` ON `item`.`contact-id` = `contact`.`id` + WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted` AND `item`.`unseen` + AND `contact`.`network`= 'dfrn' AND (`contact`.`forum` OR `contact`.`prv`) + AND NOT `contact`.`blocked` AND NOT `contact`.`hidden` + AND NOT `contact`.`pending` AND NOT `contact`.`archive` + AND `contact`.`success_update` > `failure_update` + GROUP BY `contact`.`id` ", + intval(local_user()) + ); + + return $r; + } + +} diff --git a/include/NotificationsManager.php b/include/NotificationsManager.php new file mode 100644 index 0000000000..5f8211eb87 --- /dev/null +++ b/include/NotificationsManager.php @@ -0,0 +1,136 @@ +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()) + ); + } +} diff --git a/include/Photo.php b/include/Photo.php index 3f1608d3ec..91fce55a86 100644 --- a/include/Photo.php +++ b/include/Photo.php @@ -726,10 +726,11 @@ function guess_image_type($filename, $fromcurl=false) { * @param string $avatar Link to avatar picture * @param int $uid User id of contact owner * @param int $cid Contact id + * @param bool $force force picture update * * @return array Returns array of the different avatar sizes */ -function update_contact_avatar($avatar,$uid,$cid) { +function update_contact_avatar($avatar,$uid,$cid, $force = false) { $r = q("SELECT `avatar`, `photo`, `thumb`, `micro` FROM `contact` WHERE `id` = %d LIMIT 1", intval($cid)); if (!$r) @@ -737,7 +738,7 @@ function update_contact_avatar($avatar,$uid,$cid) { else $data = array($r[0]["photo"], $r[0]["thumb"], $r[0]["micro"]); - if ($r[0]["avatar"] != $avatar) { + if (($r[0]["avatar"] != $avatar) OR $force) { $photos = import_profile_photo($avatar,$uid,$cid, true); if ($photos) { diff --git a/include/Scrape.php b/include/Scrape.php index ca6489b16a..68926a997e 100644 --- a/include/Scrape.php +++ b/include/Scrape.php @@ -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,9 +13,25 @@ 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 + unset($noscrapedata["nick"]); + } else + $noscrapedata = array(); + } + $s = fetch_url($url); - if(! $s) + if (!$s) return $ret; if (!$dont_probe) { @@ -91,8 +108,7 @@ function scrape_dfrn($url, $dont_probe = false) { } } } - - return $ret; + return array_merge($ret, $noscrapedata); }} @@ -342,7 +358,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { $result = array(); - if(! $url) + if (!$url) return $result; $result = Cache::get("probe_url:".$mode.":".$url); @@ -351,6 +367,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { return $result; } + $original_url = $url; $network = null; $diaspora = false; $diaspora_base = ''; @@ -366,8 +383,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); @@ -381,7 +396,12 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { else $links = lrdd($url); - if(count($links)) { + if ((count($links) == 0) AND strstr($url, "/index.php")) { + $url = str_replace("/index.php", "", $url); + $links = lrdd($url); + } + + if (count($links)) { $has_lrdd = true; logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA); @@ -428,12 +448,21 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { // aliases, let's hope we're lucky and get one that matches the feed author-uri because // otherwise we're screwed. + $backup_alias = ""; + foreach($links as $link) { if($link['@attributes']['rel'] === 'alias') { if(strpos($link['@attributes']['href'],'@') === false) { if(isset($profile)) { - if($link['@attributes']['href'] !== $profile) - $alias = unamp($link['@attributes']['href']); + $alias_url = $link['@attributes']['href']; + + if(($alias_url !== $profile) AND ($backup_alias == "") AND + ($alias_url !== str_replace("/index.php", "", $profile))) + $backup_alias = $alias_url; + + if(($alias_url !== $profile) AND !strstr($alias_url, "index.php") AND + ($alias_url !== str_replace("/index.php", "", $profile))) + $alias = $alias_url; } else $profile = unamp($link['@attributes']['href']); @@ -441,6 +470,9 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { } } + if ($alias == "") + $alias = $backup_alias; + // If the profile is different from the url then the url is abviously an alias if (($alias == "") AND ($profile != "") AND !$at_addr AND (normalise_link($profile) != normalise_link($url))) $alias = $url; @@ -604,21 +636,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 +679,41 @@ 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 : ''); - - $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 ($network == NETWORK_OSTATUS) { + if ($data["header"]["author-id"] != "") + $alias = $data["header"]["author-id"]; + + if ($data["header"]["author-link"] != "") + $profile = $data["header"]["author-link"]; + + } elseif(!$profile AND ($data["header"]["author-link"] != "") AND !in_array($network, array("", NETWORK_FEED))) + $profile = $data["header"]["author-link"]; } } @@ -783,27 +756,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 +771,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 +783,21 @@ 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)); + + if (substr($baseurl, -10) == "/index.php") + $baseurl = str_replace("/index.php", "", $baseurl); $baseurl = rtrim($baseurl, "/"); @@ -888,25 +846,82 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { } // Only store into the cache if the value seems to be valid - if ($result['network'] != NETWORK_PHANTOM) - Cache::set("probe_url:".$mode.":".$url,serialize($result), CACHE_DAY); + if ($result['network'] != NETWORK_PHANTOM) { + Cache::set("probe_url:".$mode.":".$original_url,serialize($result), CACHE_DAY); + + /// @todo temporary fix - we need a real contact update function that updates only changing fields + /// The biggest problem is the avatar picture that could have a reduced image size. + /// It should only be updated if the existing picture isn't existing anymore. + if (($result['network'] != NETWORK_FEED) AND ($mode == PROBE_NORMAL) AND + $result["name"] AND $result["nick"] AND $result["url"] AND $result["addr"] AND $result["poll"]) + q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `url` = '%s', `addr` = '%s', + `notify` = '%s', `poll` = '%s', `alias` = '%s', `success_update` = '%s' + WHERE `nurl` = '%s' AND NOT `self` AND `uid` = 0", + dbesc($result["name"]), + dbesc($result["nick"]), + dbesc($result["url"]), + dbesc($result["addr"]), + dbesc($result["notify"]), + dbesc($result["poll"]), + dbesc($result["alias"]), + dbesc(datetime_convert()), + dbesc(normalise_link($result['url'])) + ); + } 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); } diff --git a/include/Smilies.php b/include/Smilies.php index 193f3b555d..9cb2d6f2b5 100644 --- a/include/Smilies.php +++ b/include/Smilies.php @@ -1,7 +1,8 @@ ', - '</3', - '<\\3', - ':-)', - ';-)', - ':-(', - ':-P', - ':-p', - ':-\', - ':-\', - ':-x', - ':-X', - ':-D', - '8-|', - '8-O', - ':-O', - '\\o/', - 'o.O', - 'O.o', - 'o_O', - 'O_o', - ':\'(', - ':-!', - ':-/', - ':-[', - '8-)', - ':beer', - ':homebrew', - ':coffee', - ':facepalm', - ':like', - ':dislike', - '~friendica ~friendica', - 'redredmatrix', - 'redredmatrix' + '<3', + '</3', + '<\\3', + ':-)', + ';-)', + ':-(', + ':-P', + ':-p', + ':-\', + ':-\', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O', + ':-O', + '\\o/', + 'o.O', + 'O.o', + 'o_O', + 'O_o', + ':\'(', + ':-!', + ':-/', + ':-[', + '8-)', + ':beer', + ':homebrew', + ':coffee', + ':facepalm', + ':like', + ':dislike', + '~friendica ~friendica', + 'redred#matrix', + 'redred#matrixmatrix' ); $params = array('texts' => $texts, 'icons' => $icons); diff --git a/include/api.php b/include/api.php index 4d206da28e..305a86ca13 100644 --- a/include/api.php +++ b/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"; + } + 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"; + 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 ''."\n".'not implemented'; - killme(); + if ($templatename==="") { + $ret = api_array_to_xml($data); + } else { + $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); + if(! $tpl) { + header ("Content-Type: text/xml"); + echo ''."\n".'not implemented'; + 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)); } } @@ -1493,15 +1524,21 @@ if ($max_id > 0) $sql_extra = ' AND `item`.`id` <= '.intval($max_id); + // Not sure why this query was so complicated. We should keep it here for a while, + // just to make sure that we really don't need it. + // FROM `item` INNER JOIN (SELECT `uri`,`parent` FROM `item` WHERE `id` = %d) AS `temp1` + // ON (`item`.`thr-parent` = `temp1`.`uri` AND `item`.`parent` = `temp1`.`parent`) + $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` - FROM `item` INNER JOIN (SELECT `uri`,`parent` FROM `item` WHERE `id` = %d) AS `temp1` - ON (`item`.`thr-parent` = `temp1`.`uri` AND `item`.`parent` = `temp1`.`parent`), `contact` - WHERE `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `item`.`uid` = %d AND `item`.`verb` = '%s' AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + WHERE `item`.`parent` = %d AND `item`.`visible` + AND NOT `item`.`moderated` AND NOT `item`.`deleted` + AND `item`.`uid` = %d AND `item`.`verb` = '%s' + AND NOT `contact`.`blocked` AND NOT `contact`.`pending` AND `item`.`id`>%d $sql_extra ORDER BY `item`.`id` DESC LIMIT %d ,%d", intval($id), intval(api_user()), @@ -1519,6 +1556,7 @@ return api_apply_template("timeline", $type, $data); } api_register_func('api/conversation/show','api_conversation_show', true); + api_register_func('api/statusnet/conversation','api_conversation_show', true); /** @@ -1660,13 +1698,13 @@ `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` - FROM `item`, `contact` + FROM `item` FORCE INDEX (`uid_id`), `contact` WHERE `item`.`uid` = %d AND `verb` = '%s' AND NOT (`item`.`author-link` IN ('https://%s', 'http://%s')) - AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0 + AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `item`.`parent` IN (SELECT `iid` from thread where uid = %d AND `mention` AND !`ignored`) + AND NOT `contact`.`blocked` AND NOT `contact`.`pending` + AND `item`.`parent` IN (SELECT `iid` FROM `thread` WHERE `uid` = %d AND `mention` AND !`ignored`) $sql_extra AND `item`.`id`>%d ORDER BY `item`.`id` DESC LIMIT %d ,%d ", @@ -1781,7 +1819,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]); @@ -2027,6 +2065,16 @@ $statushtml = trim(bbcode($body, false, false)); + $search = array("
", "
", "
", + "

", "

", "

", "

", + "

", "

", "

", "

", + "
", "
", "
", "
"); + $replace = array("
\n", "\n
", "
\n", + "\n

", "

\n", "\n

", "

\n", + "\n

", "

\n", "\n

", "

\n", + "\n
", "
\n", "\n
", "
\n"); + $statushtml = str_replace($search, $replace, $statushtml); + if ($item['title'] != "") $statushtml = "

".bbcode($item['title'])."

\n".$statushtml; @@ -3386,6 +3434,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("", $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('', $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 diff --git a/include/auth.php b/include/auth.php index a5b6432fff..4abff19710 100644 --- a/include/auth.php +++ b/include/auth.php @@ -5,35 +5,14 @@ require_once('include/security.php'); require_once('include/datetime.php'); function nuke_session() { - if (get_config('system', 'disable_database_session')) { - session_unset(); - return; - } new_cookie(0); // make sure cookie is deleted on browser close, as a security measure - - unset($_SESSION['authenticated']); - unset($_SESSION['uid']); - unset($_SESSION['visitor_id']); - unset($_SESSION['administrator']); - unset($_SESSION['cid']); - unset($_SESSION['theme']); - unset($_SESSION['mobile-theme']); - unset($_SESSION['page_flags']); - unset($_SESSION['submanage']); - unset($_SESSION['my_url']); - unset($_SESSION['my_address']); - unset($_SESSION['addr']); - unset($_SESSION['return_url']); - + session_unset(); } // login/logout - - - if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-params'))) || ($_POST['auth-params'] !== 'login'))) { if(((x($_POST,'auth-params')) && ($_POST['auth-params'] === 'logout')) || ($a->module === 'logout')) { @@ -41,6 +20,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p // process logout request call_hooks("logging_out"); nuke_session(); + new_cookie(-1); info( t('Logged out.') . EOL); goaway(z_root()); } @@ -90,8 +70,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p } authenticate_success($r[0], false, false, $login_refresh); } -} -else { +} else { if(isset($_SESSION)) { nuke_session(); @@ -209,13 +188,11 @@ else { } function new_cookie($time) { - if (!get_config('system', 'disable_database_session')) - $old_sid = session_id(); - session_set_cookie_params($time); + if ($time != 0) + $time = $time + time(); - if (!get_config('system', 'disable_database_session')) { - session_regenerate_id(false); - q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid)); - } + $params = session_get_cookie_params(); + setcookie(session_name(), session_id(), $time, $params['path'], $params['domain'], $params['secure'], isset($params['httponly'])); + return; } diff --git a/include/autoloader.php b/include/autoloader.php new file mode 100644 index 0000000000..6caa082915 --- /dev/null +++ b/include/autoloader.php @@ -0,0 +1,69 @@ + $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(); diff --git a/include/autoloader/ClassLoader.php b/include/autoloader/ClassLoader.php new file mode 100644 index 0000000000..d916d802fe --- /dev/null +++ b/include/autoloader/ClassLoader.php @@ -0,0 +1,413 @@ + + * Jordi Boggiano + * + * 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 + * @author Jordi Boggiano + */ +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; +} diff --git a/include/autoloader/LICENSE.composer b/include/autoloader/LICENSE.composer new file mode 100644 index 0000000000..b365b1f5a7 --- /dev/null +++ b/include/autoloader/LICENSE.composer @@ -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. \ No newline at end of file diff --git a/include/autoloader/autoload_classmap.php b/include/autoloader/autoload_classmap.php new file mode 100644 index 0000000000..3efd09fc69 --- /dev/null +++ b/include/autoloader/autoload_classmap.php @@ -0,0 +1,9 @@ + $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', +); diff --git a/include/autoloader/autoload_namespaces.php b/include/autoloader/autoload_namespaces.php new file mode 100644 index 0000000000..315a349310 --- /dev/null +++ b/include/autoloader/autoload_namespaces.php @@ -0,0 +1,10 @@ + array($vendorDir . '/ezyang/htmlpurifier/library'), +); diff --git a/include/autoloader/autoload_psr4.php b/include/autoloader/autoload_psr4.php new file mode 100644 index 0000000000..fe93afea21 --- /dev/null +++ b/include/autoloader/autoload_psr4.php @@ -0,0 +1,9 @@ +title = $match[2]; @@ -858,6 +861,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 +1305,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; +} ?> diff --git a/include/contact_selectors.php b/include/contact_selectors.php index f104866232..3bf68f764e 100644 --- a/include/contact_selectors.php +++ b/include/contact_selectors.php @@ -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; } diff --git a/include/conversation.php b/include/conversation.php index 6c33be84fb..a52502ec39 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -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'), diff --git a/include/cron.php b/include/cron.php index 3acf711dd1..00dd500704 100644 --- a/include/cron.php +++ b/include/cron.php @@ -34,22 +34,18 @@ function cron_run(&$argv, &$argc){ require_once('include/Contact.php'); require_once('include/email.php'); require_once('include/socgraph.php'); - require_once('include/pidfile.php'); require_once('mod/nodeinfo.php'); + require_once('include/post_update.php'); load_config('config'); load_config('system'); - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - - $load = current_load(); - if($load) { - if(intval($load) > $maxsysload) { - logger('system: load ' . $load . ' too high. cron deferred to next scheduled run.'); + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") { + if (App::maxload_reached()) + return; + if (App::is_already_running('cron', 'include/cron.php', 540)) return; - } } $last = get_config('system','last_cron'); @@ -66,23 +62,6 @@ function cron_run(&$argv, &$argc){ } } - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'cron'); - if($pidfile->is_already_running()) { - logger("cron: Already running"); - if ($pidfile->running_time() > 9*60) { - $pidfile->kill(); - logger("cron: killed stale process"); - // Calling a new instance - proc_run('php','include/cron.php'); - } - exit; - } - } - - - $a->set_baseurl(get_config('system','url')); load_hooks(); @@ -93,10 +72,6 @@ function cron_run(&$argv, &$argc){ proc_run('php',"include/queue.php"); - // run diaspora photo queue process in the background - - proc_run('php',"include/dsprphotoq.php"); - // run the process to discover global contacts in the background proc_run('php',"include/discover_poco.php"); @@ -127,13 +102,14 @@ function cron_run(&$argv, &$argc){ // Check OStatus conversations // Check only conversations with mentions (for a longer time) - check_conversations(true); + ostatus::check_conversations(true); // Check every conversation - check_conversations(false); + ostatus::check_conversations(false); - // Set the gcontact-id in the item table if missing - item_set_gcontact(); + // Call possible post update functions + // see include/post_update.php for more details + post_update(); // update nodeinfo data nodeinfo_cron(); @@ -361,35 +337,37 @@ function cron_clear_cache(&$a) { if ($max_tablesize == 0) $max_tablesize = 100 * 1000000; // Default are 100 MB - // Minimum fragmentation level in percent - $fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100; - if ($fragmentation_level == 0) - $fragmentation_level = 0.3; // Default value is 30% + if ($max_tablesize > 0) { + // Minimum fragmentation level in percent + $fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100; + if ($fragmentation_level == 0) + $fragmentation_level = 0.3; // Default value is 30% - // Optimize some tables that need to be optimized - $r = q("SHOW TABLE STATUS"); - foreach($r as $table) { + // Optimize some tables that need to be optimized + $r = q("SHOW TABLE STATUS"); + foreach($r as $table) { - // Don't optimize tables that are too large - if ($table["Data_length"] > $max_tablesize) - continue; + // Don't optimize tables that are too large + if ($table["Data_length"] > $max_tablesize) + continue; - // Don't optimize empty tables - if ($table["Data_length"] == 0) - continue; + // Don't optimize empty tables + if ($table["Data_length"] == 0) + continue; - // Calculate fragmentation - $fragmentation = $table["Data_free"] / $table["Data_length"]; + // Calculate fragmentation + $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]); - logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG); + logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG); - // Don't optimize tables that needn't to be optimized - if ($fragmentation < $fragmentation_level) - continue; + // Don't optimize tables that needn't to be optimized + if ($fragmentation < $fragmentation_level) + continue; - // So optimize it - logger("Optimize Table ".$table["Name"], LOGGER_DEBUG); - q("OPTIMIZE TABLE `%s`", dbesc($table["Name"])); + // So optimize it + logger("Optimize Table ".$table["Name"], LOGGER_DEBUG); + q("OPTIMIZE TABLE `%s`", dbesc($table["Name"])); + } } set_config('system','cache_last_cleared', time()); @@ -429,6 +407,9 @@ function cron_repair_database() { // This call is very "cheap" so we can do it at any time without a problem q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0"); + // There was an issue where the nick vanishes from the contact table + q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''"); + /// @todo /// - remove thread entries without item /// - remove sign entries without item diff --git a/include/cronhooks.php b/include/cronhooks.php index 8c70008e45..b6cf0e7237 100644 --- a/include/cronhooks.php +++ b/include/cronhooks.php @@ -19,21 +19,16 @@ function cronhooks_run(&$argv, &$argc){ require_once('include/session.php'); require_once('include/datetime.php'); - require_once('include/pidfile.php'); load_config('config'); load_config('system'); - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - - $load = current_load(); - if($load) { - if(intval($load) > $maxsysload) { - logger('system: load ' . $load . ' too high. Cronhooks deferred to next scheduled run.'); + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") { + if (App::maxload_reached()) + return; + if (App::is_already_running('cronhooks', 'include/cronhooks.php', 1140)) return; - } } $last = get_config('system','last_cronhook'); @@ -50,21 +45,6 @@ function cronhooks_run(&$argv, &$argc){ } } - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'cronhooks'); - if($pidfile->is_already_running()) { - logger("cronhooks: Already running"); - if ($pidfile->running_time() > 19*60) { - $pidfile->kill(); - logger("cronhooks: killed stale process"); - // Calling a new instance - proc_run('php','include/cronhooks.php'); - } - exit; - } - } - $a->set_baseurl(get_config('system','url')); load_hooks(); diff --git a/include/datetime.php b/include/datetime.php index a05af5e38f..89305a2406 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -1,8 +1,17 @@ '; return $o; -}} +} -// return a select using 'field_select_raw' template, with timezones -// groupped (primarily) by continent -// arguments follow convetion as other field_* template array: -// 'name', 'label', $value, 'help' -if (!function_exists('field_timezone')){ + + +/** + * @brief Generating a Timezone selector + * + * Return a select using 'field_select_raw' template, with timezones + * groupped (primarily) by continent + * arguments follow convetion as other field_* template array: + * 'name', 'label', $value, 'help' + * + * @param string $name Name of the selector + * @param string $label Label for the selector + * @param string $current Timezone + * @param string $help Help text + * + * @return string Parsed HTML + */ function field_timezone($name='timezone', $label='', $current = 'America/Los_Angeles', $help){ $options = select_timezone($current); $options = str_replace(''; + // if ($dob && $dob != '0000-00-00') // $o = datesel($f,mktime(0,0,0,0,0,1900),mktime(),mktime(0,0,0,$month,$day,$year),'dob'); // else // $o = datesel($f,mktime(0,0,0,0,0,1900),mktime(),false,'dob'); + return $o; } /** - * returns a date selector - * @param $format - * format string, e.g. 'ymd' or 'mdy'. Not currently supported - * @param $min - * unix timestamp of minimum date - * @param $max - * unix timestap of maximum date - * @param $default - * unix timestamp of default date - * @param $id - * id and name of datetimepicker (defaults to "datetimepicker") + * @brief Returns a date selector + * + * @param string $format + * Format string, e.g. 'ymd' or 'mdy'. Not currently supported + * @param string $min + * Unix timestamp of minimum date + * @param string $max + * Unix timestap of maximum date + * @param string $default + * Unix timestamp of default date + * @param string $id + * ID and name of datetimepicker (defaults to "datetimepicker") + * + * @return string Parsed HTML output. */ -if(! function_exists('datesel')) { function datesel($format, $min, $max, $default, $id = 'datepicker') { return datetimesel($format,$min,$max,$default,$id,true,false, '',''); -}} +} /** - * returns a time selector - * @param $format - * format string, e.g. 'ymd' or 'mdy'. Not currently supported + * @brief Returns a time selector + * + * @param string $format + * Format string, e.g. 'ymd' or 'mdy'. Not currently supported * @param $h - * already selected hour + * Already selected hour * @param $m - * already selected minute - * @param $id - * id and name of datetimepicker (defaults to "timepicker") + * Already selected minute + * @param string $id + * ID and name of datetimepicker (defaults to "timepicker") + * + * @return string Parsed HTML output. */ -if(! function_exists('timesel')) { function timesel($format, $h, $m, $id='timepicker') { return datetimesel($format,new DateTime(),new DateTime(),new DateTime("$h:$m"),$id,false,true); -}} +} /** * @brief Returns a datetime selector. * - * @param $format + * @param string $format * format string, e.g. 'ymd' or 'mdy'. Not currently supported - * @param $min + * @param string $min * unix timestamp of minimum date - * @param $max + * @param string $max * unix timestap of maximum date - * @param $default + * @param string $default * unix timestamp of default date * @param string $id * id and name of datetimepicker (defaults to "datetimepicker") - * @param boolean $pickdate + * @param bool $pickdate * true to show date picker (default) * @param boolean $picktime * true to show time picker (default) @@ -201,17 +244,15 @@ function timesel($format, $h, $m, $id='timepicker') { * set minimum date from picker with id $minfrom (none by default) * @param $maxfrom * set maximum date from picker with id $maxfrom (none by default) - * @param boolean $required default false + * @param bool $required default false + * * @return string Parsed HTML output. * * @todo Once browser support is better this could probably be replaced with * native HTML5 date picker. */ -if(! function_exists('datetimesel')) { function datetimesel($format, $min, $max, $default, $id = 'datetimepicker', $pickdate = true, $picktime = true, $minfrom = '', $maxfrom = '', $required = false) { - $a = get_app(); - // First day of the week (0 = Sunday) $firstDay = get_pconfig(local_user(),'system','first_day_of_week'); if ($firstDay === false) $firstDay=0; @@ -224,43 +265,58 @@ function datetimesel($format, $min, $max, $default, $id = 'datetimepicker', $pic $o = ''; $dateformat = ''; + if($pickdate) $dateformat .= 'Y-m-d'; if($pickdate && $picktime) $dateformat .= ' '; if($picktime) $dateformat .= 'H:i'; + $minjs = $min ? ",minDate: new Date({$min->getTimestamp()}*1000), yearStart: " . $min->format('Y') : ''; $maxjs = $max ? ",maxDate: new Date({$max->getTimestamp()}*1000), yearEnd: " . $max->format('Y') : ''; $input_text = $default ? 'value="' . date($dateformat, $default->getTimestamp()) . '"' : ''; $defaultdatejs = $default ? ",defaultDate: new Date({$default->getTimestamp()}*1000)" : ''; + $pickers = ''; if(!$pickdate) $pickers .= ',datepicker: false'; if(!$picktime) $pickers .= ',timepicker: false'; + $extra_js = ''; $pickers .= ",dayOfWeekStart: ".$firstDay.",lang:'".$lang."'"; if($minfrom != '') $extra_js .= "\$('#$minfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#$id').data('xdsoft_datetimepicker').setOptions({minDate: currentDateTime})}})"; if($maxfrom != '') $extra_js .= "\$('#$maxfrom').data('xdsoft_datetimepicker').setOptions({onChangeDateTime: function (currentDateTime) { \$('#$id').data('xdsoft_datetimepicker').setOptions({maxDate: currentDateTime})}})"; + $readable_format = $dateformat; $readable_format = str_replace('Y','yyyy',$readable_format); $readable_format = str_replace('m','mm',$readable_format); $readable_format = str_replace('d','dd',$readable_format); $readable_format = str_replace('H','HH',$readable_format); $readable_format = str_replace('i','MM',$readable_format); + $o .= "
"; $o .= '
'; $o .= ""; + return $o; -}} +} -// implements "3 seconds ago" etc. -// based on $posted_date, (UTC). -// Results relative to current timezone -// Limited to range of timestamps - -if(! function_exists('relative_date')) { +/** + * @brief Returns a relative date string. + * + * Implements "3 seconds ago" etc. + * Based on $posted_date, (UTC). + * Results relative to current timezone. + * Limited to range of timestamps. + * + * @param string $posted_date + * @param string $format (optional) Parsed with sprintf() + * %1$d %2$s ago, e.g. 22 hours ago, 1 minute ago + * + * @return string with relative date + */ function relative_date($posted_date,$format = null) { $localtime = datetime_convert('UTC',date_default_timezone_get(),$posted_date); @@ -300,23 +356,33 @@ function relative_date($posted_date,$format = null) { // translators - e.g. 22 hours ago, 1 minute ago if(! $format) $format = t('%1$d %2$s ago'); + return sprintf( $format,$r, (($r == 1) ? $str[0] : $str[1])); - } - } -}} - - - -// Returns age in years, given a date of birth, -// the timezone of the person whose date of birth is provided, -// and the timezone of the person viewing the result. -// Why? Bear with me. Let's say I live in Mittagong, Australia, and my -// birthday is on New Year's. You live in San Bruno, California. -// When exactly are you going to see my age increase? -// A: 5:00 AM Dec 31 San Bruno time. That's precisely when I start -// celebrating and become a year older. If you wish me happy birthday -// on January 1 (San Bruno time), you'll be a day late. + } + } +} +/** + * @brief Returns timezone correct age in years. + * + * Returns the age in years, given a date of birth, the timezone of the person + * whose date of birth is provided, and the timezone of the person viewing the + * result. + * + * Why? Bear with me. Let's say I live in Mittagong, Australia, and my birthday + * is on New Year's. You live in San Bruno, California. + * When exactly are you going to see my age increase? + * + * A: 5:00 AM Dec 31 San Bruno time. That's precisely when I start celebrating + * and become a year older. If you wish me happy birthday on January 1 + * (San Bruno time), you'll be a day late. + * + * @param string $dob Date of Birth + * @param string $owner_tz (optional) Timezone of the person of interest + * @param string $viewer_tz (optional) Timezone of the person viewing + * + * @return int Age in years + */ function age($dob,$owner_tz = '',$viewer_tz = '') { if(! intval($dob)) return 0; @@ -333,64 +399,79 @@ function age($dob,$owner_tz = '',$viewer_tz = '') { if(($curr_month < $month) || (($curr_month == $month) && ($curr_day < $day))) $year_diff--; + return $year_diff; } - - -// Get days in month -// get_dim($year, $month); -// returns number of days. -// $month[1] = 'January'; -// to match human usage. - -if(! function_exists('get_dim')) { +/** + * @brief Get days of a month in a given year. + * + * Returns number of days in the month of the given year. + * $m = 1 is 'January' to match human usage. + * + * @param int $y Year + * @param int $m Month (1=January, 12=December) + * + * @return int Number of days in the given month + */ function get_dim($y,$m) { - $dim = array( 0, - 31, 28, 31, 30, 31, 30, - 31, 31, 30, 31, 30, 31); - - if($m != 2) - return $dim[$m]; - if(((($y % 4) == 0) && (($y % 100) != 0)) || (($y % 400) == 0)) - return 29; - return $dim[2]; -}} + $dim = array( 0, + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31); + if($m != 2) + return $dim[$m]; -// Returns the first day in month for a given month, year -// get_first_dim($year,$month) -// returns 0 = Sunday through 6 = Saturday -// Months start at 1. + if(((($y % 4) == 0) && (($y % 100) != 0)) || (($y % 400) == 0)) + return 29; -if(! function_exists('get_first_dim')) { + return $dim[2]; +} + +/** + * @brief Returns the first day in month for a given month, year. + * + * Months start at 1. + * + * @param int $y Year + * @param int $m Month (1=January, 12=December) + * + * @return string day 0 = Sunday through 6 = Saturday + */ function get_first_dim($y,$m) { - $d = sprintf('%04d-%02d-01 00:00', intval($y), intval($m)); - return datetime_convert('UTC','UTC',$d,'w'); -}} + $d = sprintf('%04d-%02d-01 00:00', intval($y), intval($m)); -// output a calendar for the given month, year. -// if $links are provided (array), e.g. $links[12] => 'http://mylink' , -// date 12 will be linked appropriately. Today's date is also noted by -// altering td class. -// Months count from 1. + return datetime_convert('UTC','UTC',$d,'w'); +} - -/// @TODO Provide (prev,next) links, define class variations for different size calendars - - -if(! function_exists('cal')) { +/** + * @brief Output a calendar for the given month, year. + * + * If $links are provided (array), e.g. $links[12] => 'http://mylink' , + * date 12 will be linked appropriately. Today's date is also noted by + * altering td class. + * Months count from 1. + * + * @param int $y Year + * @param int $m Month + * @param bool $links (default false) + * @param string $class + * + * @return string + * + * @todo Provide (prev,next) links, define class variations for different size calendars + */ function cal($y = 0,$m = 0, $links = false, $class='') { // month table - start at 1 to match human usage. $mtab = array(' ', - 'January','February','March', - 'April','May','June', - 'July','August','September', - 'October','November','December' + 'January','February','March', + 'April','May','June', + 'July','August','September', + 'October','November','December' ); $thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); @@ -400,54 +481,63 @@ function cal($y = 0,$m = 0, $links = false, $class='') { if(! $m) $m = intval($thismonth); - $dn = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); - $f = get_first_dim($y,$m); - $l = get_dim($y,$m); - $d = 1; - $dow = 0; - $started = false; + $dn = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); + $f = get_first_dim($y,$m); + $l = get_dim($y,$m); + $d = 1; + $dow = 0; + $started = false; - if(($y == $thisyear) && ($m == $thismonth)) - $tddate = intval(datetime_convert('UTC',date_default_timezone_get(),'now','j')); + if(($y == $thisyear) && ($m == $thismonth)) + $tddate = intval(datetime_convert('UTC',date_default_timezone_get(),'now','j')); $str_month = day_translate($mtab[$m]); - $o = ''; - $o .= ""; - for($a = 0; $a < 7; $a ++) - $o .= ''; - $o .= ''; + $o = '
$str_month $y
' . mb_substr(day_translate($dn[$a]),0,3,'UTF-8') . '
'; + $o .= ""; + for($a = 0; $a < 7; $a ++) + $o .= ''; - while($d <= $l) { - if(($dow == $f) && (! $started)) - $started = true; - $today = (((isset($tddate)) && ($tddate == $d)) ? "class=\"today\" " : ''); - $o .= "'; - $dow ++; - if(($dow == 7) && ($d <= $l)) { - $dow = 0; - $o .= ''; - } - } - if($dow) - for($a = $dow; $a < 7; $a ++) - $o .= ''; - $o .= '
$str_month $y
' . mb_substr(day_translate($dn[$a]),0,3,'UTF-8') . '"; - $day = str_replace(' ',' ',sprintf('%2.2d', $d)); - if($started) { - if(is_array($links) && isset($links[$d])) - $o .= "$day"; - else - $o .= $day; - $d ++; - } - else - $o .= ' '; - $o .= '
 
'."\r\n"; + $o .= ''; - return $o; -}} + while($d <= $l) { + if(($dow == $f) && (! $started)) + $started = true; + $today = (((isset($tddate)) && ($tddate == $d)) ? "class=\"today\" " : ''); + $o .= ""; + $day = str_replace(' ',' ',sprintf('%2.2d', $d)); + if($started) { + if(is_array($links) && isset($links[$d])) + $o .= "$day"; + else + $o .= $day; + $d ++; + } else { + $o .= ' '; + } + + $o .= ''; + $dow ++; + if(($dow == 7) && ($d <= $l)) { + $dow = 0; + $o .= ''; + } + } + if($dow) + for($a = $dow; $a < 7; $a ++) + $o .= ' '; + + $o .= ''."\r\n"; + + return $o; +} + +/** + * @brief Create a birthday event. + * + * Update the year and the birthday. + */ function update_contact_birthdays() { // This only handles foreign or alien networks where a birthday has been provided. @@ -474,8 +564,6 @@ function update_contact_birthdays() { $bdtext = sprintf( t('%s\'s birthday'), $rr['name']); $bdtext2 = sprintf( t('Happy Birthday %s'), ' [url=' . $rr['url'] . ']' . $rr['name'] . '[/url]') ; - - $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`,`adjust`) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d' ) ", intval($rr['uid']), diff --git a/include/dbstructure.php b/include/dbstructure.php index 96d18cd789..e34e409023 100644 --- a/include/dbstructure.php +++ b/include/dbstructure.php @@ -537,17 +537,6 @@ function db_definition() { "PRIMARY" => array("id"), ) ); - $database["dsprphotoq"] = array( - "fields" => array( - "id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"), - "uid" => array("type" => "int(11)", "not null" => "1", "default" => "0"), - "msg" => array("type" => "mediumtext", "not null" => "1"), - "attempt" => array("type" => "tinyint(4)", "not null" => "1", "default" => "0"), - ), - "indexes" => array( - "PRIMARY" => array("id"), - ) - ); $database["event"] = array( "fields" => array( "id" => array("type" => "int(11)", "not null" => "1", "extra" => "auto_increment", "primary" => "1"), @@ -748,21 +737,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"), @@ -1261,7 +1235,6 @@ function db_definition() { "fields" => array( "id" => array("type" => "int(10) unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"), "iid" => array("type" => "int(10) unsigned", "not null" => "1", "default" => "0"), - "retract_iid" => array("type" => "int(10) unsigned", "not null" => "1", "default" => "0"), "signed_text" => array("type" => "mediumtext", "not null" => "1"), "signature" => array("type" => "text", "not null" => "1"), "signer" => array("type" => "varchar(255)", "not null" => "1", "default" => ""), @@ -1269,7 +1242,6 @@ function db_definition() { "indexes" => array( "PRIMARY" => array("id"), "iid" => array("iid"), - "retract_iid" => array("retract_iid"), ) ); $database["spam"] = array( diff --git a/include/delivery.php b/include/delivery.php index 5ef942dd06..fe33774382 100644 --- a/include/delivery.php +++ b/include/delivery.php @@ -10,11 +10,11 @@ require_once("include/dfrn.php"); function delivery_run(&$argv, &$argc){ global $a, $db; - if(is_null($a)){ + if (is_null($a)){ $a = new App; } - if(is_null($db)) { + if (is_null($db)) { @include(".htconfig.php"); require_once("include/dba.php"); $db = new dba($db_host, $db_user, $db_pass, $db_data); @@ -32,12 +32,12 @@ function delivery_run(&$argv, &$argc){ load_hooks(); - if($argc < 3) + if ($argc < 3) return; $a->set_baseurl(get_config('system','url')); - logger('delivery: invoked: ' . print_r($argv,true), LOGGER_DEBUG); + logger('delivery: invoked: '. print_r($argv,true), LOGGER_DEBUG); $cmd = $argv[1]; $item_id = intval($argv[2]); @@ -53,21 +53,12 @@ function delivery_run(&$argv, &$argc){ dbesc($item_id), dbesc($contact_id) ); - if(! count($r)) { + if (!count($r)) { continue; } - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - - $load = current_load(); - if($load) { - if(intval($load) > $maxsysload) { - logger('system: load ' . $load . ' too high. Delivery deferred to next queue run.'); - return; - } - } + if (App::maxload_reached()) + return; // It's ours to deliver. Remove it from the queue. @@ -77,7 +68,7 @@ function delivery_run(&$argv, &$argc){ dbesc($contact_id) ); - if((! $item_id) || (! $contact_id)) + if (!$item_id || !$contact_id) continue; $expire = false; @@ -93,20 +84,20 @@ function delivery_run(&$argv, &$argc){ $recipients[] = $contact_id; - if($cmd === 'mail') { + if ($cmd === 'mail') { $normal_mode = false; $mail = true; $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", intval($item_id) ); - if(! count($message)){ + if (!count($message)){ return; } $uid = $message[0]['uid']; $recipients[] = $message[0]['contact-id']; $item = $message[0]; } - elseif($cmd === 'expire') { + elseif ($cmd === 'expire') { $normal_mode = false; $expire = true; $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 @@ -115,22 +106,22 @@ function delivery_run(&$argv, &$argc){ ); $uid = $item_id; $item_id = 0; - if(! count($items)) + if (!count($items)) continue; } - elseif($cmd === 'suggest') { + elseif ($cmd === 'suggest') { $normal_mode = false; $fsuggest = true; $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item_id) ); - if(! count($suggest)) + if (!count($suggest)) return; $uid = $suggest[0]['uid']; $recipients[] = $suggest[0]['cid']; $item = $suggest[0]; - } elseif($cmd === 'relocate') { + } elseif ($cmd === 'relocate') { $normal_mode = false; $relocate = true; $uid = $item_id; @@ -140,7 +131,7 @@ function delivery_run(&$argv, &$argc){ intval($item_id) ); - if((! count($r)) || (! intval($r[0]['parent']))) { + if ((!count($r)) || (!intval($r[0]['parent']))) { continue; } @@ -154,32 +145,32 @@ function delivery_run(&$argv, &$argc){ intval($parent_id) ); - if(! count($items)) { + if (!count($items)) { continue; } $icontacts = null; $contacts_arr = array(); foreach($items as $item) - if(! in_array($item['contact-id'],$contacts_arr)) + if (!in_array($item['contact-id'],$contacts_arr)) $contacts_arr[] = intval($item['contact-id']); - if(count($contacts_arr)) { + if (count($contacts_arr)) { $str_contacts = implode(',',$contacts_arr); $icontacts = q("SELECT * FROM `contact` WHERE `id` IN ( $str_contacts ) " ); } - if( ! ($icontacts && count($icontacts))) + if ( !($icontacts && count($icontacts))) continue; // avoid race condition with deleting entries - if($items[0]['deleted']) { + if ($items[0]['deleted']) { foreach($items as $item) $item['deleted'] = 1; } - if((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) { + if ((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) { logger('delivery: top level post'); $top_level = true; } @@ -193,7 +184,7 @@ function delivery_run(&$argv, &$argc){ intval($uid) ); - if(! count($r)) + if (!count($r)) continue; $owner = $r[0]; @@ -202,7 +193,7 @@ function delivery_run(&$argv, &$argc){ $public_message = true; - if(! ($mail || $fsuggest || $relocate)) { + if (!($mail || $fsuggest || $relocate)) { require_once('include/group.php'); $parent = $items[0]; @@ -226,7 +217,7 @@ function delivery_run(&$argv, &$argc){ $localhost = $a->get_hostname(); - if(strpos($localhost,':')) + if (strpos($localhost,':')) $localhost = substr($localhost,0,strpos($localhost,':')); /** @@ -239,20 +230,21 @@ function delivery_run(&$argv, &$argc){ $relay_to_owner = false; - if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { + if (!$top_level && ($parent['wall'] == 0) && !$expire && stristr($target_item['uri'],$localhost)) { $relay_to_owner = true; } - if($relay_to_owner) { + if ($relay_to_owner) { logger('followup '.$target_item["guid"], LOGGER_DEBUG); // local followup to remote post $followup = true; } - if((strlen($parent['allow_cid'])) + if ((strlen($parent['allow_cid'])) || (strlen($parent['allow_gid'])) || (strlen($parent['deny_cid'])) - || (strlen($parent['deny_gid']))) { + || (strlen($parent['deny_gid'])) + || $parent["private"]) { $public_message = false; // private recipients, not public } @@ -262,10 +254,10 @@ function delivery_run(&$argv, &$argc){ intval($contact_id) ); - if(count($r)) + if (count($r)) $contact = $r[0]; - if($contact['self']) + if ($contact['self']) continue; $deliver_status = 0; @@ -275,7 +267,7 @@ function delivery_run(&$argv, &$argc){ switch($contact['network']) { case NETWORK_DFRN: - logger('notifier: '.$target_item["guid"].' dfrndelivery: ' . $contact['name']); + logger('notifier: '.$target_item["guid"].' dfrndelivery: '.$contact['name']); if ($mail) { $item['body'] = fix_private_photos($item['body'],$owner['uid'],null,$message[0]['contact-id']); @@ -285,13 +277,13 @@ function delivery_run(&$argv, &$argc){ q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id'])); } elseif ($relocate) $atom = dfrn::relocate($owner, $uid); - elseif($followup) { + elseif ($followup) { $msgitems = array(); foreach($items as $item) { // there is only one item - if(!$item['parent']) + if (!$item['parent']) continue; - if($item['id'] == $item_id) { - logger('followup: item: ' . print_r($item,true), LOGGER_DATA); + if ($item['id'] == $item_id) { + logger('followup: item: '. print_r($item,true), LOGGER_DATA); $msgitems[] = $item; } } @@ -299,19 +291,19 @@ function delivery_run(&$argv, &$argc){ } else { $msgitems = array(); foreach($items as $item) { - if(!$item['parent']) + if (!$item['parent']) continue; // private emails may be in included in public conversations. Filter them. - if(($public_message) && $item['private']) + if ($public_message && $item['private']) continue; $item_contact = get_item_contact($item,$icontacts); - if(!$item_contact) + if (!$item_contact) continue; - if($normal_mode) { - if($item_id == $item['id'] || $item['id'] == $item['parent']) { + if ($normal_mode) { + if ($item_id == $item['id'] || $item['id'] == $item['parent']) { $item["entry:comment-allow"] = true; $item["entry:cid"] = (($top_level) ? $contact['id'] : 0); $msgitems[] = $item; @@ -326,15 +318,15 @@ function delivery_run(&$argv, &$argc){ logger('notifier entry: '.$contact["url"].' '.$target_item["guid"].' entry: '.$atom, LOGGER_DEBUG); - logger('notifier: ' . $atom, LOGGER_DATA); + logger('notifier: '.$atom, LOGGER_DATA); $basepath = implode('/', array_slice(explode('/',$contact['url']),0,3)); // perform local delivery if we are on the same site - if(link_compare($basepath,$a->get_baseurl())) { + if (link_compare($basepath,$a->get_baseurl())) { $nickname = basename($contact['url']); - if($contact['issued-id']) + if ($contact['issued-id']) $sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id'])); else $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id'])); @@ -356,10 +348,10 @@ function delivery_run(&$argv, &$argc){ dbesc($nickname) ); - if($x && count($x)) { + if ($x && count($x)) { $write_flag = ((($x[0]['rel']) && ($x[0]['rel'] != CONTACT_IS_SHARING)) ? true : false); - if((($owner['page-flags'] == PAGE_COMMUNITY) || ($write_flag)) && (! $x[0]['writable'])) { - q("update contact set writable = 1 where id = %d", + if ((($owner['page-flags'] == PAGE_COMMUNITY) || $write_flag) && !$x[0]['writable']) { + q("UPDATE `contact` SET `writable` = 1 WHERE `id` = %d", intval($x[0]['id']) ); $x[0]['writable'] = 1; @@ -374,19 +366,19 @@ function delivery_run(&$argv, &$argc){ break; logger('mod-delivery: local delivery'); - local_delivery($x[0],$atom); + dfrn::import($atom, $x[0]); break; } } - if(! was_recently_delayed($contact['id'])) + if (!was_recently_delayed($contact['id'])) $deliver_status = dfrn::deliver($owner,$contact,$atom); else $deliver_status = (-1); logger('notifier: dfrn_delivery to '.$contact["url"].' with guid '.$target_item["guid"].' returns '.$deliver_status); - if($deliver_status == (-1)) { + if ($deliver_status == (-1)) { logger('notifier: delivery failed: queuing message'); add_to_queue($contact['id'],NETWORK_DFRN,$atom); } @@ -394,9 +386,9 @@ function delivery_run(&$argv, &$argc){ case NETWORK_OSTATUS: // Do not send to otatus if we are not configured to send to public networks - if($owner['prvnets']) + if ($owner['prvnets']) break; - if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only')) + if (get_config('system','ostatus_disabled') || get_config('system','dfrn_only')) break; // There is currently no code here to distribute anything to OStatus. @@ -406,67 +398,67 @@ function delivery_run(&$argv, &$argc){ case NETWORK_MAIL: case NETWORK_MAIL2: - if(get_config('system','dfrn_only')) + if (get_config('system','dfrn_only')) break; // WARNING: does not currently convert to RFC2047 header encodings, etc. $addr = $contact['addr']; - if(! strlen($addr)) + if (!strlen($addr)) break; - if($cmd === 'wall-new' || $cmd === 'comment-new') { + if ($cmd === 'wall-new' || $cmd === 'comment-new') { $it = null; - if($cmd === 'wall-new') + if ($cmd === 'wall-new') $it = $items[0]; else { $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($argv[2]), intval($uid) ); - if(count($r)) + if (count($r)) $it = $r[0]; } - if(! $it) + if (!$it) break; $local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($uid) ); - if(! count($local_user)) + if (!count($local_user)) break; $reply_to = ''; $r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1", intval($uid) ); - if($r1 && $r1[0]['reply_to']) + if ($r1 && $r1[0]['reply_to']) $reply_to = $r1[0]['reply_to']; $subject = (($it['title']) ? email_header_encode($it['title'],'UTF-8') : t("\x28no subject\x29")) ; // only expose our real email address to true friends - if(($contact['rel'] == CONTACT_IS_FRIEND) && (! $contact['blocked'])) { - if($reply_to) { + if (($contact['rel'] == CONTACT_IS_FRIEND) && !$contact['blocked']) { + if ($reply_to) { $headers = 'From: '.email_header_encode($local_user[0]['username'],'UTF-8').' <'.$reply_to.'>'."\n"; $headers .= 'Sender: '.$local_user[0]['email']."\n"; } else $headers = 'From: '.email_header_encode($local_user[0]['username'],'UTF-8').' <'.$local_user[0]['email'].'>'."\n"; } else - $headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . t('noreply') . '@' . $a->get_hostname() . '>' . "\n"; + $headers = 'From: '. email_header_encode($local_user[0]['username'],'UTF-8') .' <'. t('noreply') .'@'.$a->get_hostname() .'>'. "\n"; - //if($reply_to) - // $headers .= 'Reply-to: ' . $reply_to . "\n"; + //if ($reply_to) + // $headers .= 'Reply-to: '.$reply_to . "\n"; - $headers .= 'Message-Id: <' . iri2msgid($it['uri']). '>' . "\n"; + $headers .= 'Message-Id: <'. iri2msgid($it['uri']).'>'. "\n"; //logger("Mail: uri: ".$it['uri']." parent-uri ".$it['parent-uri'], LOGGER_DEBUG); //logger("Mail: Data: ".print_r($it, true), LOGGER_DEBUG); //logger("Mail: Data: ".print_r($it, true), LOGGER_DATA); - if($it['uri'] !== $it['parent-uri']) { + if ($it['uri'] !== $it['parent-uri']) { $headers .= "References: <".iri2msgid($it["parent-uri"]).">"; // If Threading is enabled, write down the correct parent @@ -474,23 +466,23 @@ function delivery_run(&$argv, &$argc){ $headers .= " <".iri2msgid($it["thr-parent"]).">"; $headers .= "\n"; - if(!$it['title']) { + if (!$it['title']) { $r = q("SELECT `title` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($it['parent-uri']), intval($uid)); - if(count($r) AND ($r[0]['title'] != '')) + if (count($r) AND ($r[0]['title'] != '')) $subject = $r[0]['title']; else { $r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($it['parent-uri']), intval($uid)); - if(count($r) AND ($r[0]['title'] != '')) + if (count($r) AND ($r[0]['title'] != '')) $subject = $r[0]['title']; } } - if(strncasecmp($subject,'RE:',3)) + if (strncasecmp($subject,'RE:',3)) $subject = 'Re: '.$subject; } email_send($addr, $subject, $headers, $it); @@ -498,60 +490,59 @@ function delivery_run(&$argv, &$argc){ break; case NETWORK_DIASPORA: - if($public_message) - $loc = 'public batch ' . $contact['batch']; + if ($public_message) + $loc = 'public batch '.$contact['batch']; else $loc = $contact['name']; - logger('delivery: diaspora batch deliver: ' . $loc); + logger('delivery: diaspora batch deliver: '.$loc); - if(get_config('system','dfrn_only') || (!get_config('system','diaspora_enabled'))) + if (get_config('system','dfrn_only') || (!get_config('system','diaspora_enabled'))) break; - if($mail) { - diaspora_send_mail($item,$owner,$contact); + if ($mail) { + diaspora::send_mail($item,$owner,$contact); break; } - if(!$normal_mode) + if (!$normal_mode) break; - if((! $contact['pubkey']) && (! $public_message)) + if (!$contact['pubkey'] && !$public_message) break; $unsupported_activities = array(ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE); //don't transmit activities which are not supported by diaspora foreach($unsupported_activities as $act) { - if(activity_match($target_item['verb'],$act)) { + if (activity_match($target_item['verb'],$act)) { break 2; } } - if(($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) { + if (($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) { // top-level retraction - logger('delivery: diaspora retract: ' . $loc); - - diaspora_send_retraction($target_item,$owner,$contact,$public_message); + logger('diaspora retract: '.$loc); + diaspora::send_retraction($target_item,$owner,$contact,$public_message); break; - } elseif($followup) { + } elseif ($followup) { // send comments and likes to owner to relay - diaspora_send_followup($target_item,$owner,$contact,$public_message); + logger('diaspora followup: '.$loc); + diaspora::send_followup($target_item,$owner,$contact,$public_message); break; - } elseif($target_item['uri'] !== $target_item['parent-uri']) { + } elseif ($target_item['uri'] !== $target_item['parent-uri']) { // we are the relay - send comments, likes and relayable_retractions to our conversants - logger('delivery: diaspora relay: ' . $loc); - - diaspora_send_relay($target_item,$owner,$contact,$public_message); + logger('diaspora relay: '.$loc); + diaspora::send_relay($target_item,$owner,$contact,$public_message); break; - } elseif(($top_level) && (! $walltowall)) { + } elseif ($top_level && !$walltowall) { // currently no workable solution for sending walltowall - logger('delivery: diaspora status: ' . $loc); - diaspora_send_status($target_item,$owner,$contact,$public_message); + logger('diaspora status: '.$loc); + diaspora::send_status($target_item,$owner,$contact,$public_message); break; } - logger('delivery: diaspora unknown mode: ' . $contact['name']); + logger('delivery: diaspora unknown mode: '.$contact['name']); break; diff --git a/include/dfrn.php b/include/dfrn.php index 2c8e8ce38f..14be747305 100644 --- a/include/dfrn.php +++ b/include/dfrn.php @@ -6,9 +6,20 @@ * https://github.com/friendica/friendica/wiki/Protocol */ -require_once('include/items.php'); -require_once('include/Contact.php'); -require_once('include/ostatus.php'); +require_once("include/Contact.php"); +require_once("include/ostatus.php"); +require_once("include/enotify.php"); +require_once("include/threads.php"); +require_once("include/socgraph.php"); +require_once("include/items.php"); +require_once("include/tags.php"); +require_once("include/files.php"); +require_once("include/event.php"); +require_once("include/text.php"); +require_once("include/oembed.php"); +require_once("include/html2bbcode.php"); +require_once("include/bbcode.php"); +require_once("include/xml.php"); /** * @brief This class contain functions to create and send DFRN XML files @@ -16,6 +27,10 @@ require_once('include/ostatus.php'); */ class dfrn { + const DFRN_TOP_LEVEL = 0; // Top level posting + const DFRN_REPLY = 1; // Regular reply that is stored locally + const DFRN_REPLY_RC = 2; // Reply that will be relayed + /** * @brief Generates the atom entries for delivery.php * @@ -26,7 +41,7 @@ class dfrn { * * @return string DFRN entries */ - function entries($items,$owner) { + public static function entries($items,$owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -56,7 +71,7 @@ class dfrn { * * @return string DFRN feed entries */ - function feed($dfrn_id, $owner_nick, $last_update, $direction = 0) { + public static function feed($dfrn_id, $owner_nick, $last_update, $direction = 0) { $a = get_app(); @@ -71,7 +86,7 @@ class dfrn { $converse = true; if($a->argv[$x] == 'starred') $starred = true; - if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) + if($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) $category = $a->argv[$x+1]; } } @@ -82,7 +97,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) @@ -92,7 +107,7 @@ class dfrn { killme(); $owner = $r[0]; - $owner_id = $owner['user_uid']; + $owner_id = $owner['uid']; $owner_nick = $owner['nickname']; $sql_post_table = ""; @@ -230,7 +245,7 @@ class dfrn { foreach($items as $item) { // prevent private email from leaking. - if($item['network'] === NETWORK_MAIL) + if($item['network'] == NETWORK_MAIL) continue; // public feeds get html, our own nodes use bbcode @@ -263,7 +278,7 @@ class dfrn { * * @return string DFRN mail */ - function mail($item, $owner) { + public static function mail($item, $owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -272,17 +287,17 @@ class dfrn { $mail = $doc->createElement("dfrn:mail"); $sender = $doc->createElement("dfrn:sender"); - xml_add_element($doc, $sender, "dfrn:name", $owner['name']); - xml_add_element($doc, $sender, "dfrn:uri", $owner['url']); - xml_add_element($doc, $sender, "dfrn:avatar", $owner['thumb']); + xml::add_element($doc, $sender, "dfrn:name", $owner['name']); + xml::add_element($doc, $sender, "dfrn:uri", $owner['url']); + xml::add_element($doc, $sender, "dfrn:avatar", $owner['thumb']); $mail->appendChild($sender); - xml_add_element($doc, $mail, "dfrn:id", $item['uri']); - xml_add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']); - xml_add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)); - xml_add_element($doc, $mail, "dfrn:subject", $item['title']); - xml_add_element($doc, $mail, "dfrn:content", $item['body']); + xml::add_element($doc, $mail, "dfrn:id", $item['uri']); + xml::add_element($doc, $mail, "dfrn:in-reply-to", $item['parent-uri']); + xml::add_element($doc, $mail, "dfrn:sentdate", datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)); + xml::add_element($doc, $mail, "dfrn:subject", $item['title']); + xml::add_element($doc, $mail, "dfrn:content", $item['body']); $root->appendChild($mail); @@ -297,7 +312,7 @@ class dfrn { * * @return string DFRN suggestions */ - function fsuggest($item, $owner) { + public static function fsuggest($item, $owner) { $doc = new DOMDocument('1.0', 'utf-8'); $doc->formatOutput = true; @@ -305,11 +320,11 @@ class dfrn { $suggest = $doc->createElement("dfrn:suggest"); - xml_add_element($doc, $suggest, "dfrn:url", $item['url']); - xml_add_element($doc, $suggest, "dfrn:name", $item['name']); - xml_add_element($doc, $suggest, "dfrn:photo", $item['photo']); - xml_add_element($doc, $suggest, "dfrn:request", $item['request']); - xml_add_element($doc, $suggest, "dfrn:note", $item['note']); + xml::add_element($doc, $suggest, "dfrn:url", $item['url']); + xml::add_element($doc, $suggest, "dfrn:name", $item['name']); + xml::add_element($doc, $suggest, "dfrn:photo", $item['photo']); + xml::add_element($doc, $suggest, "dfrn:request", $item['request']); + xml::add_element($doc, $suggest, "dfrn:note", $item['note']); $root->appendChild($suggest); @@ -324,7 +339,7 @@ class dfrn { * * @return string DFRN relocations */ - function relocate($owner, $uid) { + public static function relocate($owner, $uid) { /* get site pubkey. this could be a new installation with no site keys*/ $pubkey = get_config('system','site_pubkey'); @@ -351,16 +366,16 @@ class dfrn { $relocate = $doc->createElement("dfrn:relocate"); - xml_add_element($doc, $relocate, "dfrn:url", $owner['url']); - xml_add_element($doc, $relocate, "dfrn:name", $owner['name']); - xml_add_element($doc, $relocate, "dfrn:photo", $photos[4]); - xml_add_element($doc, $relocate, "dfrn:thumb", $photos[5]); - xml_add_element($doc, $relocate, "dfrn:micro", $photos[6]); - xml_add_element($doc, $relocate, "dfrn:request", $owner['request']); - xml_add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']); - xml_add_element($doc, $relocate, "dfrn:notify", $owner['notify']); - xml_add_element($doc, $relocate, "dfrn:poll", $owner['poll']); - xml_add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey')); + xml::add_element($doc, $relocate, "dfrn:url", $owner['url']); + xml::add_element($doc, $relocate, "dfrn:name", $owner['name']); + xml::add_element($doc, $relocate, "dfrn:photo", $photos[4]); + xml::add_element($doc, $relocate, "dfrn:thumb", $photos[5]); + xml::add_element($doc, $relocate, "dfrn:micro", $photos[6]); + xml::add_element($doc, $relocate, "dfrn:request", $owner['request']); + xml::add_element($doc, $relocate, "dfrn:confirm", $owner['confirm']); + xml::add_element($doc, $relocate, "dfrn:notify", $owner['notify']); + xml::add_element($doc, $relocate, "dfrn:poll", $owner['poll']); + xml::add_element($doc, $relocate, "dfrn:sitepubkey", get_config('system','site_pubkey')); $root->appendChild($relocate); @@ -383,49 +398,52 @@ class dfrn { if ($alternatelink == "") $alternatelink = $owner['url']; - $root = $doc->createElementNS(NS_ATOM, 'feed'); + $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed'); $doc->appendChild($root); - $root->setAttribute("xmlns:thr", NS_THR); - $root->setAttribute("xmlns:at", "http://purl.org/atompub/tombstones/1.0"); - $root->setAttribute("xmlns:media", NS_MEDIA); - $root->setAttribute("xmlns:dfrn", "http://purl.org/macgirvin/dfrn/1.0"); - $root->setAttribute("xmlns:activity", NS_ACTIVITY); - $root->setAttribute("xmlns:georss", NS_GEORSS); - $root->setAttribute("xmlns:poco", NS_POCO); - $root->setAttribute("xmlns:ostatus", NS_OSTATUS); - $root->setAttribute("xmlns:statusnet", NS_STATUSNET); + $root->setAttribute("xmlns:thr", NAMESPACE_THREAD); + $root->setAttribute("xmlns:at", NAMESPACE_TOMB); + $root->setAttribute("xmlns:media", NAMESPACE_MEDIA); + $root->setAttribute("xmlns:dfrn", NAMESPACE_DFRN); + $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); + $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS); + $root->setAttribute("xmlns:poco", NAMESPACE_POCO); + $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); + $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - //xml_add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); - xml_add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); - xml_add_element($doc, $root, "title", $owner["name"]); + xml::add_element($doc, $root, "id", app::get_baseurl()."/profile/".$owner["nick"]); + xml::add_element($doc, $root, "title", $owner["name"]); $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); - xml_add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); + xml::add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); $attributes = array("rel" => "license", "href" => "http://creativecommons.org/licenses/by/3.0/"); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $alternatelink); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); - ostatus_hublinks($doc, $root); if ($public) { + // DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed. + ostatus::hublinks($doc, $root); + $attributes = array("rel" => "salmon", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-replies", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); $attributes = array("rel" => "http://salmon-protocol.org/ns/salmon-mention", "href" => app::get_baseurl()."/salmon/".$owner["nick"]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); } if ($owner['page-flags'] == PAGE_COMMUNITY) - xml_add_element($doc, $root, "dfrn:community", 1); + xml::add_element($doc, $root, "dfrn:community", 1); - xml_add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); + /// @todo We need a way to transmit the different page flags like "PAGE_PRVGROUP" + + xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); $author = self::add_author($doc, $owner, $authorelement, $public); $root->appendChild($author); @@ -451,26 +469,26 @@ class dfrn { $picdate = datetime_convert('UTC', 'UTC', $owner['avatar-date'].'+00:00', ATOM_TIME); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "name", $owner["name"], $attributes); + xml::add_element($doc, $author, "name", $owner["name"], $attributes); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes); + xml::add_element($doc, $author, "uri", app::get_baseurl().'/profile/'.$owner["nickname"], $attributes); $attributes = array("dfrn:updated" => $namdate); - xml_add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); + xml::add_element($doc, $author, "dfrn:handle", $owner["addr"], $attributes); $attributes = array("rel" => "photo", "type" => "image/jpeg", "dfrn:updated" => $picdate, "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); $attributes = array("rel" => "avatar", "type" => "image/jpeg", "dfrn:updated" => $picdate, "media:width" => 175, "media:height" => 175, "href" => $owner['photo']); - xml_add_element($doc, $author, "link", "", $attributes); + 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); + xml::add_element($doc, $author, "dfrn:birthday", $birthday); // The following fields will only be generated if this isn't for a public feed if ($public) @@ -482,28 +500,28 @@ 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"]); - xml_add_element($doc, $author, "poco:updated", $namdate); + xml::add_element($doc, $author, "poco:displayName", $profile["name"]); + xml::add_element($doc, $author, "poco:updated", $namdate); if (trim($profile["dob"]) != "0000-00-00") - xml_add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"]))); + xml::add_element($doc, $author, "poco:birthday", "0000-".date("m-d", strtotime($profile["dob"]))); - xml_add_element($doc, $author, "poco:note", $profile["about"]); - xml_add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]); + xml::add_element($doc, $author, "poco:note", $profile["about"]); + xml::add_element($doc, $author, "poco:preferredUsername", $profile["nickname"]); $savetz = date_default_timezone_get(); date_default_timezone_set($profile["timezone"]); - xml_add_element($doc, $author, "poco:utcOffset", date("P")); + xml::add_element($doc, $author, "poco:utcOffset", date("P")); date_default_timezone_set($savetz); if (trim($profile["homepage"]) != "") { $urls = $doc->createElement("poco:urls"); - xml_add_element($doc, $urls, "poco:type", "homepage"); - xml_add_element($doc, $urls, "poco:value", $profile["homepage"]); - xml_add_element($doc, $urls, "poco:primary", "true"); + xml::add_element($doc, $urls, "poco:type", "homepage"); + xml::add_element($doc, $urls, "poco:value", $profile["homepage"]); + xml::add_element($doc, $urls, "poco:primary", "true"); $author->appendChild($urls); } @@ -511,7 +529,7 @@ class dfrn { $keywords = explode(",", $profile["pub_keywords"]); foreach ($keywords AS $keyword) - xml_add_element($doc, $author, "poco:tags", trim($keyword)); + xml::add_element($doc, $author, "poco:tags", trim($keyword)); } @@ -519,25 +537,25 @@ class dfrn { $xmpp = ""; if (trim($xmpp) != "") { $ims = $doc->createElement("poco:ims"); - xml_add_element($doc, $ims, "poco:type", "xmpp"); - xml_add_element($doc, $ims, "poco:value", $xmpp); - xml_add_element($doc, $ims, "poco:primary", "true"); + xml::add_element($doc, $ims, "poco:type", "xmpp"); + xml::add_element($doc, $ims, "poco:value", $xmpp); + xml::add_element($doc, $ims, "poco:primary", "true"); $author->appendChild($ims); } if (trim($profile["locality"].$profile["region"].$profile["country-name"]) != "") { $element = $doc->createElement("poco:address"); - xml_add_element($doc, $element, "poco:formatted", formatted_location($profile)); + xml::add_element($doc, $element, "poco:formatted", formatted_location($profile)); if (trim($profile["locality"]) != "") - xml_add_element($doc, $element, "poco:locality", $profile["locality"]); + xml::add_element($doc, $element, "poco:locality", $profile["locality"]); if (trim($profile["region"]) != "") - xml_add_element($doc, $element, "poco:region", $profile["region"]); + xml::add_element($doc, $element, "poco:region", $profile["region"]); if (trim($profile["country-name"]) != "") - xml_add_element($doc, $element, "poco:country", $profile["country-name"]); + xml::add_element($doc, $element, "poco:country", $profile["country-name"]); $author->appendChild($element); } @@ -561,9 +579,9 @@ class dfrn { $contact = get_contact_details_by_url($contact_url, $item["uid"]); $author = $doc->createElement($element); - xml_add_element($doc, $author, "name", $contact["name"]); - xml_add_element($doc, $author, "uri", $contact["url"]); - xml_add_element($doc, $author, "dfrn:handle", $contact["addr"]); + xml::add_element($doc, $author, "name", $contact["name"]); + xml::add_element($doc, $author, "uri", $contact["url"]); + xml::add_element($doc, $author, "dfrn:handle", $contact["addr"]); /// @Todo /// - Check real image type and image size @@ -574,7 +592,7 @@ class dfrn { "media:width" => 80, "media:height" => 80, "href" => $contact["photo"]); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); $attributes = array( "rel" => "avatar", @@ -582,7 +600,7 @@ class dfrn { "media:width" => 80, "media:height" => 80, "href" => $contact["photo"]); - xml_add_element($doc, $author, "link", "", $attributes); + xml::add_element($doc, $author, "link", "", $attributes); return $author; } @@ -605,28 +623,35 @@ class dfrn { if(!$r) return false; if($r->type) - xml_add_element($doc, $entry, "activity:object-type", $r->type); + xml::add_element($doc, $entry, "activity:object-type", $r->type); if($r->id) - xml_add_element($doc, $entry, "id", $r->id); + xml::add_element($doc, $entry, "id", $r->id); if($r->title) - xml_add_element($doc, $entry, "title", $r->title); + xml::add_element($doc, $entry, "title", $r->title); if($r->link) { - if(substr($r->link,0,1) === '<') { + if(substr($r->link,0,1) == '<') { if(strstr($r->link,'&') && (! strstr($r->link,'&'))) $r->link = str_replace('&','&', $r->link); $r->link = preg_replace('/\/','',$r->link); - $data = parse_xml_string($r->link, false); - foreach ($data->attributes() AS $parameter => $value) - $attributes[$parameter] = $value; - } else + // XML does need a single element as root element so we add a dummy element here + $data = parse_xml_string("".$r->link."", false); + if (is_object($data)) { + foreach ($data->link AS $link) { + $attributes = array(); + foreach ($link->attributes() AS $parameter => $value) + $attributes[$parameter] = $value; + xml::add_element($doc, $entry, "link", "", $attributes); + } + } + } else { $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $r->link); - - xml_add_element($doc, $entry, "link", "", $attributes); + xml::add_element($doc, $entry, "link", "", $attributes); + } } if($r->content) - xml_add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html")); + xml::add_element($doc, $entry, "content", bbcode($r->content), array("type" => "html")); return $entry; } @@ -660,7 +685,7 @@ class dfrn { if(trim($matches[4]) != "") $attributes["title"] = trim($matches[4]); - xml_add_element($doc, $root, "link", "", $attributes); + xml::add_element($doc, $root, "link", "", $attributes); } } } @@ -687,7 +712,7 @@ class dfrn { if($item['deleted']) { $attributes = array("ref" => $item['uri'], "when" => datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)); - return xml_create_element($doc, "at:deleted-entry", "", $attributes); + return xml::create_element($doc, "at:deleted-entry", "", $attributes); } $entry = $doc->createElement("entry"); @@ -697,6 +722,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; @@ -718,61 +746,66 @@ class dfrn { $attributes = array("ref" => $parent_item, "type" => "text/html", "href" => app::get_baseurl().'/display/'.$parent[0]['guid'], "dfrn:diaspora_guid" => $parent[0]['guid']); - xml_add_element($doc, $entry, "thr:in-reply-to", "", $attributes); + xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes); } - xml_add_element($doc, $entry, "id", $item["uri"]); - xml_add_element($doc, $entry, "title", $item["title"]); + xml::add_element($doc, $entry, "id", $item["uri"]); + xml::add_element($doc, $entry, "title", $item["title"]); - xml_add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); - xml_add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); + xml::add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); + xml::add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); - xml_add_element($doc, $entry, "dfrn:env", base64url_encode($body, true)); - xml_add_element($doc, $entry, "content", (($type === 'html') ? $htmlbody : $body), array("type" => $type)); + // "dfrn:env" is used to read the content + xml::add_element($doc, $entry, "dfrn:env", base64url_encode($body, true)); - xml_add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", + // The "content" field is not read by the receiver. We could remove it when the type is "text" + // We keep it at the moment, maybe there is some old version that doesn't read "dfrn:env" + xml::add_element($doc, $entry, "content", (($type == 'html') ? $htmlbody : $body), array("type" => $type)); + + // We save this value in "plink". Maybe we should read it from there as well? + xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", "href" => app::get_baseurl()."/display/".$item["guid"])); // "comment-allow" is some old fashioned stuff for old Friendica versions. // It is included in the rewritten code for completeness if ($comment) - xml_add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child'])); + xml::add_element($doc, $entry, "dfrn:comment-allow", intval($item['last-child'])); if($item['location']) - xml_add_element($doc, $entry, "dfrn:location", $item['location']); + xml::add_element($doc, $entry, "dfrn:location", $item['location']); if($item['coord']) - xml_add_element($doc, $entry, "georss:point", $item['coord']); + xml::add_element($doc, $entry, "georss:point", $item['coord']); if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) - xml_add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1)); + xml::add_element($doc, $entry, "dfrn:private", (($item['private']) ? $item['private'] : 1)); if($item['extid']) - xml_add_element($doc, $entry, "dfrn:extid", $item['extid']); + xml::add_element($doc, $entry, "dfrn:extid", $item['extid']); if($item['bookmark']) - xml_add_element($doc, $entry, "dfrn:bookmark", "true"); + xml::add_element($doc, $entry, "dfrn:bookmark", "true"); if($item['app']) - xml_add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app'])); + xml::add_element($doc, $entry, "statusnet:notice_info", "", array("local_id" => $item['id'], "source" => $item['app'])); - xml_add_element($doc, $entry, "dfrn:diaspora_guid", $item["guid"]); + xml::add_element($doc, $entry, "dfrn:diaspora_guid", $item["guid"]); // The signed text contains the content in Markdown, the sender handle and the signatur for the content // It is needed for relayed comments to Diaspora. if($item['signed_text']) { $sign = base64_encode(json_encode(array('signed_text' => $item['signed_text'],'signature' => $item['signature'],'signer' => $item['signer']))); - xml_add_element($doc, $entry, "dfrn:diaspora_signature", $sign); + xml::add_element($doc, $entry, "dfrn:diaspora_signature", $sign); } - xml_add_element($doc, $entry, "activity:verb", construct_verb($item)); + xml::add_element($doc, $entry, "activity:verb", construct_verb($item)); if ($item['object-type'] != "") - xml_add_element($doc, $entry, "activity:object-type", $item['object-type']); + xml::add_element($doc, $entry, "activity:object-type", $item['object-type']); elseif ($item['id'] == $item['parent']) - xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); + xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); else - xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT); + xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT); $actobj = self::create_activity($doc, "activity:object", $item['object']); if ($actobj) @@ -787,7 +820,7 @@ class dfrn { if(count($tags)) { foreach($tags as $t) if (($type != 'html') OR ($t[0] != "@")) - xml_add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2])); + xml::add_element($doc, $entry, "category", "", array("scheme" => "X-DFRN:".$t[0].":".$t[1], "term" => $t[2])); } if(count($tags)) @@ -800,11 +833,11 @@ class dfrn { intval($owner["uid"]), dbesc(normalise_link($mention))); if ($r[0]["forum"] OR $r[0]["prv"]) - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_GROUP, "href" => $mention)); else - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", "ostatus:object-type" => ACTIVITY_OBJ_PERSON, "href" => $mention)); } @@ -824,7 +857,7 @@ class dfrn { * * @return int Deliver status. -1 means an error. */ - function deliver($owner,$contact,$atom, $dissolve = false) { + public static function deliver($owner,$contact,$atom, $dissolve = false) { $a = get_app(); @@ -1045,4 +1078,1387 @@ class dfrn { return $res->status; } + + /** + * @brief Add new birthday event for this person + * + * @param array $contact Contact record + * @param string $birthday Birthday of the contact + * + */ + private function birthday_event($contact, $birthday) { + + logger("updating birthday: ".$birthday." for contact ".$contact["id"]); + + $bdtext = sprintf(t("%s\'s birthday"), $contact["name"]); + $bdtext2 = sprintf(t("Happy Birthday %s"), " [url=".$contact["url"]."]".$contact["name"]."[/url]") ; + + + $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + intval($contact["uid"]), + intval($contact["id"]), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert("UTC","UTC", $birthday)), + dbesc(datetime_convert("UTC","UTC", $birthday." + 1 day ")), + dbesc($bdtext), + dbesc($bdtext2), + dbesc("birthday") + ); + } + + /** + * @brief Fetch the author data from head or entry items + * + * @param object $xpath XPath object + * @param object $context In which context should the data be searched + * @param array $importer Record of the importer user mixed with contact of the content + * @param string $element Element name from which the data is fetched + * @param bool $onlyfetch Should the data only be fetched or should it update the contact record as well + * + * @return Returns an array with relevant data of the author + */ + 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`, `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)); + if ($r) { + $contact = $r[0]; + $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; + } + + // Until now we aren't serving different sizes - but maybe later + $avatarlist = array(); + // @todo check if "avatar" or "photo" would be the best field in the specification + $avatars = $xpath->query($element."/atom:link[@rel='avatar']", $context); + foreach($avatars AS $avatar) { + $href = ""; + $width = 0; + foreach($avatar->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "width") + $width = $attributes->textContent; + if ($attributes->name == "updated") + $contact["avatar-date"] = $attributes->textContent; + } + if (($width > 0) AND ($href != "")) + $avatarlist[$width] = $href; + } + if (count($avatarlist) > 0) { + krsort($avatarlist); + $author["avatar"] = current($avatarlist); + } + + 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") + $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") + $poco["uri-date"] = $attributes->textContent; + + // Update contact data + $value = $xpath->evaluate($element."/dfrn:handle/text()", $context)->item(0)->nodeValue; + if ($value != "") + $poco["addr"] = $value; + + $value = $xpath->evaluate($element."/poco:displayName/text()", $context)->item(0)->nodeValue; + if ($value != "") + $poco["name"] = $value; + + $value = $xpath->evaluate($element."/poco:preferredUsername/text()", $context)->item(0)->nodeValue; + if ($value != "") + $poco["nick"] = $value; + + $value = $xpath->evaluate($element."/poco:note/text()", $context)->item(0)->nodeValue; + if ($value != "") + $poco["about"] = $value; + + $value = $xpath->evaluate($element."/poco:address/poco:formatted/text()", $context)->item(0)->nodeValue; + if ($value != "") + $poco["location"] = $value; + + /// @todo Add support for the following fields that we don't support by now in the contact table: + /// - poco:utcOffset + /// - poco:ims + /// - poco:urls + /// - poco:locality + /// - poco:region + /// - poco:country + + // Save the keywords into the contact table + $tags = array(); + $tagelements = $xpath->evaluate($element."/poco:tags/text()", $context); + foreach($tagelements AS $tag) + $tags[$tag->nodeValue] = $tag->nodeValue; + + if (count($tags)) + $poco["keywords"] = implode(", ", $tags); + + // "dfrn:birthday" contains the birthday converted to UTC + $old_bdyear = $contact["bdyear"]; + + $birthday = $xpath->evaluate($element."/dfrn:birthday/text()", $context)->item(0)->nodeValue; + + if (strtotime($birthday) > time()) { + $bd_timestamp = strtotime($birthday); + + $poco["bdyear"] = date("Y", $bd_timestamp); + } + + // "poco:birthday" is the birthday in the format "yyyy-mm-dd" + $value = $xpath->evaluate($element."/poco:birthday/text()", $context)->item(0)->nodeValue; + + if (!in_array($value, array("", "0000-00-00"))) { + $bdyear = date("Y"); + $value = str_replace("0000", $bdyear, $value); + + if (strtotime($value) < time()) { + $value = str_replace($bdyear, $bdyear + 1, $value); + $bdyear = $bdyear + 1; + } + + $poco["bd"] = $value; + } + + $contact = array_merge($contact, $poco); + + if ($old_bdyear != $contact["bdyear"]) + self::birthday_event($contact, $birthday); + + // Get all field names + $fields = array(); + foreach ($r[0] AS $field => $data) + $fields[$field] = $data; + + unset($fields["id"]); + unset($fields["uid"]); + unset($fields["url"]); + unset($fields["avatar-date"]); + unset($fields["name-date"]); + unset($fields["uri-date"]); + + // 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])) { + 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]) { + logger("Difference for contact ".$contact["id"]." in field '".$field."'. Old value: '".$contact[$field]."', new value '".$r[0][$field]."'", LOGGER_DEBUG); + $update = true; + } + + if ($update) { + 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', + `name-date` = '%s', `uri-date` = '%s' + WHERE `id` = %d AND `network` = '%s'", + dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]), + dbesc($contact["addr"]), dbesc($contact["keywords"]), dbesc($contact["bdyear"]), + dbesc($contact["bd"]), dbesc($contact["name-date"]), dbesc($contact["uri-date"]), + intval($contact["id"]), dbesc($contact["network"])); + } + + update_contact_avatar($author["avatar"], $importer["uid"], $contact["id"], + (strtotime($contact["avatar-date"]) > strtotime($r[0]["avatar-date"]))); + + // The generation is a sign for the reliability of the provided data. + // 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. + + $poco["generation"] = 2; + $poco["photo"] = $author["avatar"]; + update_gcontact($poco); + } + + return($author); + } + + /** + * @brief Transforms activity objects into an XML string + * + * @param object $xpath XPath object + * @param object $activity Activity object + * @param text $element element name + * + * @return string XML string + */ + private function transform_activity($xpath, $activity, $element) { + if (!is_object($activity)) + return ""; + + $obj_doc = new DOMDocument("1.0", "utf-8"); + $obj_doc->formatOutput = true; + + $obj_element = $obj_doc->createElementNS(NAMESPACE_ATOM1, $element); + + $activity_type = $xpath->query("activity:object-type/text()", $activity)->item(0)->nodeValue; + xml::add_element($obj_doc, $obj_element, "type", $activity_type); + + $id = $xpath->query("atom:id", $activity)->item(0); + if (is_object($id)) + $obj_element->appendChild($obj_doc->importNode($id, true)); + + $title = $xpath->query("atom:title", $activity)->item(0); + if (is_object($title)) + $obj_element->appendChild($obj_doc->importNode($title, true)); + + $links = $xpath->query("atom:link", $activity); + if (is_object($links)) + foreach ($links AS $link) + $obj_element->appendChild($obj_doc->importNode($link, true)); + + $content = $xpath->query("atom:content", $activity)->item(0); + if (is_object($content)) + $obj_element->appendChild($obj_doc->importNode($content, true)); + + $obj_doc->appendChild($obj_element); + + $objxml = $obj_doc->saveXML($obj_element); + + // @todo This isn't totally clean. We should find a way to transform the namespaces + $objxml = str_replace("<".$element.' xmlns="http://www.w3.org/2005/Atom">', "<".$element.">", $objxml); + return($objxml); + } + + /** + * @brief Processes the mail elements + * + * @param object $xpath XPath object + * @param object $mail mail elements + * @param array $importer Record of the importer user mixed with contact of the content + */ + private function process_mail($xpath, $mail, $importer) { + + logger("Processing mails"); + + $msg = array(); + $msg["uid"] = $importer["importer_uid"]; + $msg["from-name"] = $xpath->query("dfrn:sender/dfrn:name/text()", $mail)->item(0)->nodeValue; + $msg["from-url"] = $xpath->query("dfrn:sender/dfrn:uri/text()", $mail)->item(0)->nodeValue; + $msg["from-photo"] = $xpath->query("dfrn:sender/dfrn:avatar/text()", $mail)->item(0)->nodeValue; + $msg["contact-id"] = $importer["id"]; + $msg["uri"] = $xpath->query("dfrn:id/text()", $mail)->item(0)->nodeValue; + $msg["parent-uri"] = $xpath->query("dfrn:in-reply-to/text()", $mail)->item(0)->nodeValue; + $msg["created"] = $xpath->query("dfrn:sentdate/text()", $mail)->item(0)->nodeValue; + $msg["title"] = $xpath->query("dfrn:subject/text()", $mail)->item(0)->nodeValue; + $msg["body"] = $xpath->query("dfrn:content/text()", $mail)->item(0)->nodeValue; + $msg["seen"] = 0; + $msg["replied"] = 0; + + dbesc_array($msg); + + $r = dbq("INSERT INTO `mail` (`".implode("`, `", array_keys($msg))."`) VALUES ('".implode("', '", array_values($msg))."')"); + + // send notifications. + + $notif_params = array( + "type" => NOTIFY_MAIL, + "notify_flags" => $importer["notify-flags"], + "language" => $importer["language"], + "to_name" => $importer["username"], + "to_email" => $importer["email"], + "uid" => $importer["importer_uid"], + "item" => $msg, + "source_name" => $msg["from-name"], + "source_link" => $importer["url"], + "source_photo" => $importer["thumb"], + "verb" => ACTIVITY_POST, + "otype" => "mail" + ); + + notification($notif_params); + + logger("Mail is processed, notification was sent."); + } + + /** + * @brief Processes the suggestion elements + * + * @param object $xpath XPath object + * @param object $suggestion suggestion elements + * @param array $importer Record of the importer user mixed with contact of the content + */ + private function process_suggestion($xpath, $suggestion, $importer) { + + logger("Processing suggestions"); + + $suggest = array(); + $suggest["uid"] = $importer["importer_uid"]; + $suggest["cid"] = $importer["id"]; + $suggest["url"] = $xpath->query("dfrn:url/text()", $suggestion)->item(0)->nodeValue; + $suggest["name"] = $xpath->query("dfrn:name/text()", $suggestion)->item(0)->nodeValue; + $suggest["photo"] = $xpath->query("dfrn:photo/text()", $suggestion)->item(0)->nodeValue; + $suggest["request"] = $xpath->query("dfrn:request/text()", $suggestion)->item(0)->nodeValue; + $suggest["body"] = $xpath->query("dfrn:note/text()", $suggestion)->item(0)->nodeValue; + + // Does our member already have a friend matching this description? + + $r = q("SELECT `id` FROM `contact` WHERE `name` = '%s' AND `nurl` = '%s' AND `uid` = %d LIMIT 1", + dbesc($suggest["name"]), + dbesc(normalise_link($suggest["url"])), + intval($suggest["uid"]) + ); + if(count($r)) + return false; + + // Do we already have an fcontact record for this person? + + $fid = 0; + $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", + dbesc($suggest["url"]), + dbesc($suggest["name"]), + dbesc($suggest["request"]) + ); + if(count($r)) { + $fid = $r[0]["id"]; + + // OK, we do. Do we already have an introduction for this person ? + $r = q("SELECT `id` FROM `intro` WHERE `uid` = %d AND `fid` = %d LIMIT 1", + intval($suggest["uid"]), + intval($fid) + ); + if(count($r)) + return false; + } + if(!$fid) + $r = q("INSERT INTO `fcontact` (`name`,`url`,`photo`,`request`) VALUES ('%s', '%s', '%s', '%s')", + dbesc($suggest["name"]), + dbesc($suggest["url"]), + dbesc($suggest["photo"]), + dbesc($suggest["request"]) + ); + $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", + dbesc($suggest["url"]), + dbesc($suggest["name"]), + dbesc($suggest["request"]) + ); + if(count($r)) + $fid = $r[0]["id"]; + else + // database record did not get created. Quietly give up. + return false; + + + $hash = random_string(); + + $r = q("INSERT INTO `intro` (`uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked`) + VALUES(%d, %d, %d, '%s', '%s', '%s', %d)", + intval($suggest["uid"]), + intval($fid), + intval($suggest["cid"]), + dbesc($suggest["body"]), + dbesc($hash), + dbesc(datetime_convert()), + intval(0) + ); + + notification(array( + "type" => NOTIFY_SUGGEST, + "notify_flags" => $importer["notify-flags"], + "language" => $importer["language"], + "to_name" => $importer["username"], + "to_email" => $importer["email"], + "uid" => $importer["importer_uid"], + "item" => $suggest, + "link" => App::get_baseurl()."/notifications/intros", + "source_name" => $importer["name"], + "source_link" => $importer["url"], + "source_photo" => $importer["photo"], + "verb" => ACTIVITY_REQ_FRIEND, + "otype" => "intro" + )); + + return true; + + } + + /** + * @brief Processes the relocation elements + * + * @param object $xpath XPath object + * @param object $relocation relocation elements + * @param array $importer Record of the importer user mixed with contact of the content + */ + private function process_relocation($xpath, $relocation, $importer) { + + logger("Processing relocations"); + + $relocate = array(); + $relocate["uid"] = $importer["importer_uid"]; + $relocate["cid"] = $importer["id"]; + $relocate["url"] = $xpath->query("dfrn:url/text()", $relocation)->item(0)->nodeValue; + $relocate["name"] = $xpath->query("dfrn:name/text()", $relocation)->item(0)->nodeValue; + $relocate["photo"] = $xpath->query("dfrn:photo/text()", $relocation)->item(0)->nodeValue; + $relocate["thumb"] = $xpath->query("dfrn:thumb/text()", $relocation)->item(0)->nodeValue; + $relocate["micro"] = $xpath->query("dfrn:micro/text()", $relocation)->item(0)->nodeValue; + $relocate["request"] = $xpath->query("dfrn:request/text()", $relocation)->item(0)->nodeValue; + $relocate["confirm"] = $xpath->query("dfrn:confirm/text()", $relocation)->item(0)->nodeValue; + $relocate["notify"] = $xpath->query("dfrn:notify/text()", $relocation)->item(0)->nodeValue; + $relocate["poll"] = $xpath->query("dfrn:poll/text()", $relocation)->item(0)->nodeValue; + $relocate["sitepubkey"] = $xpath->query("dfrn:sitepubkey/text()", $relocation)->item(0)->nodeValue; + + // update contact + $r = q("SELECT `photo`, `url` FROM `contact` WHERE `id` = %d AND `uid` = %d;", + intval($importer["id"]), + intval($importer["importer_uid"])); + if (!$r) + return false; + + $old = $r[0]; + + $x = q("UPDATE `contact` SET + `name` = '%s', + `photo` = '%s', + `thumb` = '%s', + `micro` = '%s', + `url` = '%s', + `nurl` = '%s', + `request` = '%s', + `confirm` = '%s', + `notify` = '%s', + `poll` = '%s', + `site-pubkey` = '%s' + WHERE `id` = %d AND `uid` = %d;", + dbesc($relocate["name"]), + dbesc($relocate["photo"]), + dbesc($relocate["thumb"]), + dbesc($relocate["micro"]), + dbesc($relocate["url"]), + dbesc(normalise_link($relocate["url"])), + dbesc($relocate["request"]), + dbesc($relocate["confirm"]), + dbesc($relocate["notify"]), + dbesc($relocate["poll"]), + dbesc($relocate["sitepubkey"]), + intval($importer["id"]), + intval($importer["importer_uid"])); + + if ($x === false) + return false; + + // update items + $fields = array( + 'owner-link' => array($old["url"], $relocate["url"]), + 'author-link' => array($old["url"], $relocate["url"]), + 'owner-avatar' => array($old["photo"], $relocate["photo"]), + 'author-avatar' => array($old["photo"], $relocate["photo"]), + ); + foreach ($fields as $n=>$f){ + $x = q("UPDATE `item` SET `%s` = '%s' WHERE `%s` = '%s' AND `uid` = %d", + $n, dbesc($f[1]), + $n, dbesc($f[0]), + intval($importer["importer_uid"])); + if ($x === false) + return false; + } + + /// @TODO + /// merge with current record, current contents have priority + /// update record, set url-updated + /// update profile photos + /// schedule a scan? + return true; + } + + /** + * @brief Updates an item + * + * @param array $current the current item record + * @param array $item the new item record + * @param array $importer Record of the importer user mixed with contact of the content + * @param int $entrytype Is it a toplevel entry, a comment or a relayed comment? + */ + private function update_content($current, $item, $importer, $entrytype) { + $changed = false; + + if (edited_timestamp_is_newer($current, $item)) { + + // do not accept (ignore) an earlier edit than one we currently have. + if(datetime_convert("UTC","UTC",$item["edited"]) < $current["edited"]) + return(false); + + $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", + dbesc($item["title"]), + dbesc($item["body"]), + dbesc($item["tag"]), + dbesc(datetime_convert("UTC","UTC",$item["edited"])), + dbesc(datetime_convert()), + dbesc($item["uri"]), + intval($importer["importer_uid"]) + ); + create_tags_from_itemuri($item["uri"], $importer["importer_uid"]); + update_thread_uri($item["uri"], $importer["importer_uid"]); + + $changed = true; + + if ($entrytype == DFRN_REPLY_RC) + proc_run("php", "include/notifier.php","comment-import", $current["id"]); + } + + // update last-child if it changes + if($item["last-child"] AND ($item["last-child"] != $current["last-child"])) { + $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc(datetime_convert()), + dbesc($item["parent-uri"]), + intval($importer["importer_uid"]) + ); + $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", + intval($item["last-child"]), + dbesc(datetime_convert()), + dbesc($item["uri"]), + intval($importer["importer_uid"]) + ); + } + return $changed; + } + + /** + * @brief Detects the entry type of the item + * + * @param array $importer Record of the importer user mixed with contact of the content + * @param array $item the new item record + * + * @return int Is it a toplevel entry, a comment or a relayed comment? + */ + private function get_entry_type($importer, $item) { + if ($item["parent-uri"] != $item["uri"]) { + $community = false; + + if($importer["page-flags"] == PAGE_COMMUNITY || $importer["page-flags"] == PAGE_PRVGROUP) { + $sql_extra = ""; + $community = true; + logger("possible community action"); + } else + $sql_extra = " AND `contact`.`self` AND `item`.`wall` "; + + // was the top-level post for this action written by somebody on this site? + // Specifically, the recipient? + + $is_a_remote_action = false; + + $r = q("SELECT `item`.`parent-uri` FROM `item` + WHERE `item`.`uri` = '%s' + LIMIT 1", + dbesc($item["parent-uri"]) + ); + if($r && count($r)) { + $r = q("SELECT `item`.`forum_mode`, `item`.`wall` FROM `item` + INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` + WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' OR `item`.`thr-parent` = '%s') + AND `item`.`uid` = %d + $sql_extra + LIMIT 1", + dbesc($r[0]["parent-uri"]), + dbesc($r[0]["parent-uri"]), + dbesc($r[0]["parent-uri"]), + intval($importer["importer_uid"]) + ); + if($r && count($r)) + $is_a_remote_action = true; + } + + // Does this have the characteristics of a community or private group action? + // If it's an action to a wall post on a community/prvgroup page it's a + // valid community action. Also forum_mode makes it valid for sure. + // If neither, it's not. + + if($is_a_remote_action && $community) { + if((!$r[0]["forum_mode"]) && (!$r[0]["wall"])) { + $is_a_remote_action = false; + logger("not a community action"); + } + } + + if ($is_a_remote_action) + return DFRN_REPLY_RC; + else + return DFRN_REPLY; + + } else + return DFRN_TOP_LEVEL; + + } + + /** + * @brief Send a "poke" + * + * @param array $item the new item record + * @param array $importer Record of the importer user mixed with contact of the content + * @param int $posted_id The record number of item record that was just posted + */ + private function do_poke($item, $importer, $posted_id) { + $verb = urldecode(substr($item["verb"],strpos($item["verb"], "#")+1)); + if(!$verb) + return; + $xo = parse_xml_string($item["object"],false); + + if(($xo->type == ACTIVITY_OBJ_PERSON) && ($xo->id)) { + + // somebody was poked/prodded. Was it me? + foreach($xo->link as $l) { + $atts = $l->attributes(); + switch($atts["rel"]) { + case "alternate": + $Blink = $atts["href"]; + break; + default: + break; + } + } + + if($Blink && link_compare($Blink,App::get_baseurl()."/profile/".$importer["nickname"])) { + + // send a notification + notification(array( + "type" => NOTIFY_POKE, + "notify_flags" => $importer["notify-flags"], + "language" => $importer["language"], + "to_name" => $importer["username"], + "to_email" => $importer["email"], + "uid" => $importer["importer_uid"], + "item" => $item, + "link" => App::get_baseurl()."/display/".urlencode(get_item_guid($posted_id)), + "source_name" => stripslashes($item["author-name"]), + "source_link" => $item["author-link"], + "source_photo" => ((link_compare($item["author-link"],$importer["url"])) + ? $importer["thumb"] : $item["author-avatar"]), + "verb" => $item["verb"], + "otype" => "person", + "activity" => $verb, + "parent" => $item["parent"] + )); + } + } + } + + /** + * @brief Processes several actions, depending on the verb + * + * @param int $entrytype Is it a toplevel entry, a comment or a relayed comment? + * @param array $importer Record of the importer user mixed with contact of the content + * @param array $item the new item record + * @param bool $is_like Is the verb a "like"? + * + * @return bool Should the processing of the entries be continued? + */ + private function process_verbs($entrytype, $importer, &$item, &$is_like) { + + logger("Process verb ".$item["verb"]." and object-type ".$item["object-type"]." for entrytype ".$entrytype, LOGGER_DEBUG); + + if (($entrytype == DFRN_TOP_LEVEL)) { + // The filling of the the "contact" variable is done for legcy reasons + // The functions below are partly used by ostatus.php as well - where we have this variable + $r = q("SELECT * FROM `contact` WHERE `id` = %d", intval($importer["id"])); + $contact = $r[0]; + $nickname = $contact["nick"]; + + // Big question: Do we need these functions? They were part of the "consume_feed" function. + // This function once was responsible for DFRN and OStatus. + if(activity_match($item["verb"],ACTIVITY_FOLLOW)) { + logger("New follower"); + new_follower($importer, $contact, $item, $nickname); + return false; + } + if(activity_match($item["verb"],ACTIVITY_UNFOLLOW)) { + logger("Lost follower"); + lose_follower($importer, $contact, $item); + return false; + } + if(activity_match($item["verb"],ACTIVITY_REQ_FRIEND)) { + logger("New friend request"); + new_follower($importer, $contact, $item, $nickname, true); + return false; + } + if(activity_match($item["verb"],ACTIVITY_UNFRIEND)) { + logger("Lost sharer"); + lose_sharer($importer, $contact, $item); + return false; + } + } else { + if(($item["verb"] == ACTIVITY_LIKE) + || ($item["verb"] == ACTIVITY_DISLIKE) + || ($item["verb"] == ACTIVITY_ATTEND) + || ($item["verb"] == ACTIVITY_ATTENDNO) + || ($item["verb"] == ACTIVITY_ATTENDMAYBE)) { + $is_like = true; + $item["type"] = "activity"; + $item["gravity"] = GRAVITY_LIKE; + // only one like or dislike per person + // splitted into two queries for performance issues + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", + intval($item["uid"]), + dbesc($item["author-link"]), + dbesc($item["verb"]), + dbesc($item["parent-uri"]) + ); + if($r && count($r)) + return false; + + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", + intval($item["uid"]), + dbesc($item["author-link"]), + dbesc($item["verb"]), + dbesc($item["parent-uri"]) + ); + if($r && count($r)) + return false; + } else + $is_like = false; + + if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) { + + $xo = parse_xml_string($item["object"],false); + $xt = parse_xml_string($item["target"],false); + + if($xt->type == ACTIVITY_OBJ_NOTE) { + $r = q("SELECT `id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($xt->id), + intval($importer["importer_uid"]) + ); + + if(!count($r)) + return false; + + // extract tag, if not duplicate, add to parent item + if($xo->content) { + if(!(stristr($r[0]["tag"],trim($xo->content)))) { + q("UPDATE `item` SET `tag` = '%s' WHERE `id` = %d", + dbesc($r[0]["tag"] . (strlen($r[0]["tag"]) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'), + intval($r[0]["id"]) + ); + create_tags_from_item($r[0]["id"]); + } + } + } + } + } + return true; + } + + /** + * @brief Processes the link elements + * + * @param object $links link elements + * @param array $item the item record + */ + private function parse_links($links, &$item) { + $rel = ""; + $href = ""; + $type = ""; + $length = "0"; + $title = ""; + foreach ($links AS $link) { + foreach($link->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "rel") + $rel = $attributes->textContent; + if ($attributes->name == "type") + $type = $attributes->textContent; + if ($attributes->name == "length") + $length = $attributes->textContent; + if ($attributes->name == "title") + $title = $attributes->textContent; + } + if (($rel != "") AND ($href != "")) + switch($rel) { + case "alternate": + $item["plink"] = $href; + break; + case "enclosure": + $enclosure = $href; + if(strlen($item["attach"])) + $item["attach"] .= ","; + + $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; + break; + } + } + } + + /** + * @brief Processes the entry elements which contain the items and comments + * + * @param array $header Array of the header elements that always stay the same + * @param object $xpath XPath object + * @param object $entry entry elements + * @param array $importer Record of the importer user mixed with contact of the content + */ + private function process_entry($header, $xpath, $entry, $importer) { + + logger("Processing entries"); + + $item = $header; + + // Get the uri + $item["uri"] = $xpath->query("atom:id/text()", $entry)->item(0)->nodeValue; + + // Fetch the owner + $owner = self::fetchauthor($xpath, $entry, $importer, "dfrn:owner", true); + + $item["owner-name"] = $owner["name"]; + $item["owner-link"] = $owner["link"]; + $item["owner-avatar"] = $owner["avatar"]; + + // fetch the author + $author = self::fetchauthor($xpath, $entry, $importer, "atom:author", true); + + $item["author-name"] = $author["name"]; + $item["author-link"] = $author["link"]; + $item["author-avatar"] = $author["avatar"]; + + $item["title"] = $xpath->query("atom:title/text()", $entry)->item(0)->nodeValue; + + $item["created"] = $xpath->query("atom:published/text()", $entry)->item(0)->nodeValue; + $item["edited"] = $xpath->query("atom:updated/text()", $entry)->item(0)->nodeValue; + + $item["body"] = $xpath->query("dfrn:env/text()", $entry)->item(0)->nodeValue; + $item["body"] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$item["body"]); + // make sure nobody is trying to sneak some html tags by us + $item["body"] = notags(base64url_decode($item["body"])); + + $item["body"] = limit_body_size($item["body"]); + + /// @todo Do we really need this check for HTML elements? (It was copied from the old function) + if((strpos($item['body'],'<') !== false) && (strpos($item['body'],'>') !== false)) { + + $item['body'] = reltoabs($item['body'],$base_url); + + $item['body'] = html2bb_video($item['body']); + + $item['body'] = oembed_html2bbcode($item['body']); + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Cache.DefinitionImpl', null); + + // we shouldn't need a whitelist, because the bbcode converter + // will strip out any unsupported tags. + + $purifier = new HTMLPurifier($config); + $item['body'] = $purifier->purify($item['body']); + + $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; + + $item["last-child"] = $xpath->query("dfrn:comment-allow/text()", $entry)->item(0)->nodeValue; + $item["location"] = $xpath->query("dfrn:location/text()", $entry)->item(0)->nodeValue; + + $georsspoint = $xpath->query("georss:point", $entry); + if ($georsspoint) + $item["coord"] = $georsspoint->item(0)->nodeValue; + + $item["private"] = $xpath->query("dfrn:private/text()", $entry)->item(0)->nodeValue; + + $item["extid"] = $xpath->query("dfrn:extid/text()", $entry)->item(0)->nodeValue; + + if ($xpath->query("dfrn:extid/text()", $entry)->item(0)->nodeValue == "true") + $item["bookmark"] = true; + + $notice_info = $xpath->query("statusnet:notice_info", $entry); + if ($notice_info AND ($notice_info->length > 0)) { + foreach($notice_info->item(0)->attributes AS $attributes) { + if ($attributes->name == "source") + $item["app"] = strip_tags($attributes->textContent); + } + } + + $item["guid"] = $xpath->query("dfrn:diaspora_guid/text()", $entry)->item(0)->nodeValue; + + // We store the data from "dfrn:diaspora_signature" in a different table, this is done in "item_store" + $dsprsig = unxmlify($xpath->query("dfrn:diaspora_signature/text()", $entry)->item(0)->nodeValue); + if ($dsprsig != "") + $item["dsprsig"] = $dsprsig; + + $item["verb"] = $xpath->query("activity:verb/text()", $entry)->item(0)->nodeValue; + + if ($xpath->query("activity:object-type/text()", $entry)->item(0)->nodeValue != "") + $item["object-type"] = $xpath->query("activity:object-type/text()", $entry)->item(0)->nodeValue; + + $object = $xpath->query("activity:object", $entry)->item(0); + $item["object"] = self::transform_activity($xpath, $object, "object"); + + if (trim($item["object"]) != "") { + $r = parse_xml_string($item["object"], false); + if (isset($r->type)) + $item["object-type"] = $r->type; + } + + $target = $xpath->query("activity:target", $entry)->item(0); + $item["target"] = self::transform_activity($xpath, $target, "target"); + + $categories = $xpath->query("atom:category", $entry); + if ($categories) { + foreach ($categories AS $category) { + $term = ""; + $scheme = ""; + foreach($category->attributes AS $attributes) { + if ($attributes->name == "term") + $term = $attributes->textContent; + + if ($attributes->name == "scheme") + $scheme = $attributes->textContent; + } + + if (($term != "") AND ($scheme != "")) { + $parts = explode(":", $scheme); + if ((count($parts) >= 4) AND (array_shift($parts) == "X-DFRN")) { + $termhash = array_shift($parts); + $termurl = implode(":", $parts); + + if(strlen($item["tag"])) + $item["tag"] .= ","; + + $item["tag"] .= $termhash."[url=".$termurl."]".$term."[/url]"; + } + } + } + } + + $enclosure = ""; + + $links = $xpath->query("atom:link", $entry); + if ($links) + self::parse_links($links, $item); + + // Is it a reply or a top level posting? + $item["parent-uri"] = $item["uri"]; + + $inreplyto = $xpath->query("thr:in-reply-to", $entry); + if (is_object($inreplyto->item(0))) + foreach($inreplyto->item(0)->attributes AS $attributes) + if ($attributes->name == "ref") + $item["parent-uri"] = $attributes->textContent; + + // Get the type of the item (Top level post, reply or remote reply) + $entrytype = self::get_entry_type($importer, $item); + + // Now assign the rest of the values that depend on the type of the message + if (in_array($entrytype, array(DFRN_REPLY, DFRN_REPLY_RC))) { + if (!isset($item["object-type"])) + $item["object-type"] = ACTIVITY_OBJ_COMMENT; + + if ($item["contact-id"] != $owner["contact-id"]) + $item["contact-id"] = $owner["contact-id"]; + + if (($item["network"] != $owner["network"]) AND ($owner["network"] != "")) + $item["network"] = $owner["network"]; + + if ($item["contact-id"] != $author["contact-id"]) + $item["contact-id"] = $author["contact-id"]; + + if (($item["network"] != $author["network"]) AND ($author["network"] != "")) + $item["network"] = $author["network"]; + + // 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) { + $item["type"] = "remote-comment"; + $item["wall"] = 1; + } elseif ($entrytype == DFRN_TOP_LEVEL) { + if (!isset($item["object-type"])) + $item["object-type"] = ACTIVITY_OBJ_NOTE; + + // Is it an event? + if ($item["object-type"] == ACTIVITY_OBJ_EVENT) { + logger("Item ".$item["uri"]." seems to contain an event.", LOGGER_DEBUG); + $ev = bbtoevent($item["body"]); + if((x($ev, "desc") || x($ev, "summary")) && x($ev, "start")) { + logger("Event in item ".$item["uri"]." was found.", LOGGER_DEBUG); + $ev["cid"] = $importer["id"]; + $ev["uid"] = $importer["uid"]; + $ev["uri"] = $item["uri"]; + $ev["edited"] = $item["edited"]; + $ev['private'] = $item['private']; + $ev["guid"] = $item["guid"]; + + $r = q("SELECT `id` FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($item["uri"]), + intval($importer["uid"]) + ); + if(count($r)) + $ev["id"] = $r[0]["id"]; + + $event_id = event_store($ev); + logger("Event ".$event_id." was stored", LOGGER_DEBUG); + return; + } + } + } + + $r = q("SELECT `id`, `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($item["uri"]), + intval($importer["importer_uid"]) + ); + + if (!self::process_verbs($entrytype, $importer, $item, $is_like)) { + logger("Exiting because 'process_verbs' told us so", LOGGER_DEBUG); + return; + } + + // Update content if 'updated' changes + if(count($r)) { + if (self::update_content($r[0], $item, $importer, $entrytype)) + logger("Item ".$item["uri"]." was updated.", LOGGER_DEBUG); + else + logger("Item ".$item["uri"]." already existed.", LOGGER_DEBUG); + return; + } + + if (in_array($entrytype, array(DFRN_REPLY, DFRN_REPLY_RC))) { + $posted_id = item_store($item); + $parent = 0; + + if($posted_id) { + + logger("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id, LOGGER_DEBUG); + + $item["id"] = $posted_id; + + $r = q("SELECT `parent`, `parent-uri` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($posted_id), + intval($importer["importer_uid"]) + ); + if(count($r)) { + $parent = $r[0]["parent"]; + $parent_uri = $r[0]["parent-uri"]; + } + + if(!$is_like) { + $r1 = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `uid` = %d AND `parent` = %d", + dbesc(datetime_convert()), + intval($importer["importer_uid"]), + intval($r[0]["parent"]) + ); + + $r2 = q("UPDATE `item` SET `last-child` = 1, `changed` = '%s' WHERE `uid` = %d AND `id` = %d", + dbesc(datetime_convert()), + intval($importer["importer_uid"]), + intval($posted_id) + ); + } + + if($posted_id AND $parent AND ($entrytype == DFRN_REPLY_RC)) { + logger("Notifying followers about comment ".$posted_id, LOGGER_DEBUG); + proc_run("php", "include/notifier.php", "comment-import", $posted_id); + } + + return true; + } + } else { // $entrytype == DFRN_TOP_LEVEL + if(!link_compare($item["owner-link"],$importer["url"])) { + // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, + // but otherwise there's a possible data mixup on the sender's system. + // the tgroup delivery code called from item_store will correct it if it's a forum, + // but we're going to unconditionally correct it here so that the post will always be owned by our contact. + logger('Correcting item owner.', LOGGER_DEBUG); + $item["owner-name"] = $importer["senderName"]; + $item["owner-link"] = $importer["url"]; + $item["owner-avatar"] = $importer["thumb"]; + } + + if(($importer["rel"] == CONTACT_IS_FOLLOWER) && (!tgroup_check($importer["importer_uid"], $item))) { + logger("Contact ".$importer["id"]." is only follower and tgroup check was negative.", LOGGER_DEBUG); + return; + } + + // This is my contact on another system, but it's really me. + // Turn this into a wall post. + $notify = item_is_remote_self($importer, $item); + + $posted_id = item_store($item, false, $notify); + + logger("Item was stored with id ".$posted_id, LOGGER_DEBUG); + + if(stristr($item["verb"],ACTIVITY_POKE)) + self::do_poke($item, $importer, $posted_id); + } + } + + /** + * @brief Deletes items + * + * @param object $xpath XPath object + * @param object $deletion deletion elements + * @param array $importer Record of the importer user mixed with contact of the content + */ + private function process_deletion($xpath, $deletion, $importer) { + + logger("Processing deletions"); + + foreach($deletion->attributes AS $attributes) { + if ($attributes->name == "ref") + $uri = $attributes->textContent; + if ($attributes->name == "when") + $when = $attributes->textContent; + } + if ($when) + $when = datetime_convert("UTC", "UTC", $when, "Y-m-d H:i:s"); + else + $when = datetime_convert("UTC", "UTC", "now", "Y-m-d H:i:s"); + + if (!$uri OR !$importer["id"]) + return false; + + /// @todo Only select the used fields + $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN `contact` on `item`.`contact-id` = `contact`.`id` + WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", + dbesc($uri), + intval($importer["uid"]), + intval($importer["id"]) + ); + if(!count($r)) { + logger("Item with uri ".$uri." from contact ".$importer["id"]." for user ".$importer["uid"]." wasn't found.", LOGGER_DEBUG); + return; + } else { + + $item = $r[0]; + + $entrytype = self::get_entry_type($importer, $item); + + if(!$item["deleted"]) + logger('deleting item '.$item["id"].' uri='.$uri, LOGGER_DEBUG); + else + return; + + if($item["object-type"] == ACTIVITY_OBJ_EVENT) { + logger("Deleting event ".$item["event-id"], LOGGER_DEBUG); + event_delete($item["event-id"]); + } + + if(($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) { + + $xo = parse_xml_string($item["object"],false); + $xt = parse_xml_string($item["target"],false); + + if($xt->type == ACTIVITY_OBJ_NOTE) { + $i = q("SELECT `id`, `contact-id`, `tag` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($xt->id), + intval($importer["importer_uid"]) + ); + if(count($i)) { + + // For tags, the owner cannot remove the tag on the author's copy of the post. + + $owner_remove = (($item["contact-id"] == $i[0]["contact-id"]) ? true: false); + $author_remove = (($item["origin"] && $item["self"]) ? true : false); + $author_copy = (($item["origin"]) ? true : false); + + if($owner_remove && $author_copy) + return; + if($author_remove || $owner_remove) { + $tags = explode(',',$i[0]["tag"]); + $newtags = array(); + if(count($tags)) { + foreach($tags as $tag) + if(trim($tag) !== trim($xo->body)) + $newtags[] = trim($tag); + } + q("UPDATE `item` SET `tag` = '%s' WHERE `id` = %d", + dbesc(implode(',',$newtags)), + intval($i[0]["id"]) + ); + create_tags_from_item($i[0]["id"]); + } + } + } + } + + if($entrytype == DFRN_TOP_LEVEL) { + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + `body` = '', `title` = '' + WHERE `parent-uri` = '%s' AND `uid` = %d", + dbesc($when), + dbesc(datetime_convert()), + dbesc($uri), + intval($importer["uid"]) + ); + create_tags_from_itemuri($uri, $importer["uid"]); + create_files_from_itemuri($uri, $importer["uid"]); + update_thread_uri($uri, $importer["uid"]); + } else { + $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + `body` = '', `title` = '' + WHERE `uri` = '%s' AND `uid` = %d", + dbesc($when), + dbesc(datetime_convert()), + dbesc($uri), + intval($importer["uid"]) + ); + create_tags_from_itemuri($uri, $importer["uid"]); + create_files_from_itemuri($uri, $importer["uid"]); + update_thread_uri($uri, $importer["importer_uid"]); + if($item["last-child"]) { + // ensure that last-child is set in case the comment that had it just got wiped. + q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", + dbesc(datetime_convert()), + dbesc($item["parent-uri"]), + intval($item["uid"]) + ); + // who is the last child now? + $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `moderated` = 0 AND `uid` = %d + ORDER BY `created` DESC LIMIT 1", + dbesc($item["parent-uri"]), + intval($importer["uid"]) + ); + if(count($r)) { + q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d", + intval($r[0]["id"]) + ); + } + } + // if this is a relayed delete, propagate it to other recipients + + if($entrytype == DFRN_REPLY_RC) { + logger("Notifying followers about deletion of post ".$item["id"], LOGGER_DEBUG); + proc_run("php", "include/notifier.php","drop", $item["id"]); + } + } + } + } + + /** + * @brief Imports a DFRN message + * + * @param text $xml The DFRN message + * @param array $importer Record of the importer user mixed with contact of the content + * @param bool $sort_by_date Is used when feeds are polled + */ + public static function import($xml,$importer, $sort_by_date = false) { + + if ($xml == "") + return; + + if($importer["readonly"]) { + // We aren't receiving stuff from this person. But we will quietly ignore them + // rather than a blatant "go away" message. + logger('ignoring contact '.$importer["id"]); + return; + } + + $doc = new DOMDocument(); + @$doc->loadXML($xml); + + $xpath = new DomXPath($doc); + $xpath->registerNamespace("atom", NAMESPACE_ATOM1); + $xpath->registerNamespace("thr", NAMESPACE_THREAD); + $xpath->registerNamespace("at", NAMESPACE_TOMB); + $xpath->registerNamespace("media", NAMESPACE_MEDIA); + $xpath->registerNamespace("dfrn", NAMESPACE_DFRN); + $xpath->registerNamespace("activity", NAMESPACE_ACTIVITY); + $xpath->registerNamespace("georss", NAMESPACE_GEORSS); + $xpath->registerNamespace("poco", NAMESPACE_POCO); + $xpath->registerNamespace("ostatus", NAMESPACE_OSTATUS); + $xpath->registerNamespace("statusnet", NAMESPACE_STATUSNET); + + $header = array(); + $header["uid"] = $importer["uid"]; + $header["network"] = NETWORK_DFRN; + $header["type"] = "remote"; + $header["wall"] = 0; + $header["origin"] = 0; + $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 + 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); + + // is it a public forum? Private forums aren't supported by now with this method + $forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()", $context)->item(0)->nodeValue); + + if ($forum != $importer["forum"]) + q("UPDATE `contact` SET `forum` = %d WHERE `forum` != %d AND `id` = %d", + intval($forum), intval($forum), + intval($importer["id"]) + ); + + $mails = $xpath->query("/atom:feed/dfrn:mail"); + foreach ($mails AS $mail) + self::process_mail($xpath, $mail, $importer); + + $suggestions = $xpath->query("/atom:feed/dfrn:suggest"); + foreach ($suggestions AS $suggestion) + self::process_suggestion($xpath, $suggestion, $importer); + + $relocations = $xpath->query("/atom:feed/dfrn:relocate"); + foreach ($relocations AS $relocation) + self::process_relocation($xpath, $relocation, $importer); + + $deletions = $xpath->query("/atom:feed/at:deleted-entry"); + foreach ($deletions AS $deletion) + self::process_deletion($xpath, $deletion, $importer); + + if (!$sort_by_date) { + $entries = $xpath->query("/atom:feed/atom:entry"); + foreach ($entries AS $entry) + self::process_entry($header, $xpath, $entry, $importer); + } else { + $newentries = array(); + $entries = $xpath->query("/atom:feed/atom:entry"); + foreach ($entries AS $entry) { + $created = $xpath->query("atom:published/text()", $entry)->item(0)->nodeValue; + $newentries[strtotime($created)] = $entry; + } + + // Now sort after the publishing date + ksort($newentries); + + foreach ($newentries AS $entry) + self::process_entry($header, $xpath, $entry, $importer); + } + logger("Import done for user ".$importer["uid"]." from contact ".$importer["id"], LOGGER_DEBUG); + } } +?> diff --git a/include/diaspora.php b/include/diaspora.php index 93fe2a472f..e3a3dcd78c 100644 --- a/include/diaspora.php +++ b/include/diaspora.php @@ -1,1663 +1,1122 @@ 0, "page-flags" => PAGE_FREELOVE); - $result = diaspora_dispatch($importer,$msg); - logger("Dispatcher reported ".$result, LOGGER_DEBUG); + $serverdata = get_config("system", "relay_server"); + if ($serverdata == "") + return array(); - // Now distribute it to the followers - $r = q("SELECT `user`.* FROM `user` WHERE `user`.`uid` IN - ( SELECT `contact`.`uid` FROM `contact` WHERE `contact`.`network` = '%s' AND `contact`.`addr` = '%s' ) - AND `account_expired` = 0 AND `account_removed` = 0 ", - dbesc(NETWORK_DIASPORA), - dbesc($msg['author']) - ); - if(count($r)) { - foreach($r as $rr) { - logger('diaspora_public: delivering to: ' . $rr['username']); - diaspora_dispatch($rr,$msg); + $relay = array(); + + $servers = explode(",", $serverdata); + + foreach($servers AS $server) { + $server = trim($server); + $batch = $server."/receive/public"; + + $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch)); + + if (!$relais) { + $addr = "relay@".str_replace("http://", "", normalise_link($server)); + + $r = q("INSERT INTO `contact` (`uid`, `created`, `name`, `nick`, `addr`, `url`, `nurl`, `batch`, `network`, `rel`, `blocked`, `pending`, `writable`, `name-date`, `uri-date`, `avatar-date`) + VALUES (0, '%s', '%s', 'relay', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, '%s', '%s', '%s')", + datetime_convert(), + dbesc($addr), + dbesc($addr), + dbesc($server), + dbesc(normalise_link($server)), + dbesc($batch), + dbesc(NETWORK_DIASPORA), + intval(CONTACT_IS_FOLLOWER), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert()) + ); + + $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch)); + if ($relais) + $relay[] = $relais[0]; + } else + $relay[] = $relais[0]; } - } - else - logger('diaspora_public: no subscribers for '.$msg["author"].' '.print_r($msg, true)); -} - - -function diaspora_dispatch($importer,$msg,$attempt=1) { - - $ret = 0; - - $enabled = intval(get_config('system','diaspora_enabled')); - if(! $enabled) { - logger('mod-diaspora: disabled'); - return; + return $relay; } - // php doesn't like dashes in variable names + /** + * @brief repairs a signature that was double encoded + * + * The function is unused at the moment. It was copied from the old implementation. + * + * @param string $signature The signature + * @param string $handle The handle of the signature owner + * @param integer $level This value is only set inside this function to avoid endless loops + * + * @return string the repaired signature + */ + private function repair_signature($signature, $handle = "", $level = 1) { - $msg['message'] = str_replace( - array('',''), - array('',''), - $msg['message']); + if ($signature == "") + return ($signature); + if (base64_encode(base64_decode(base64_decode($signature))) == base64_decode($signature)) { + $signature = base64_decode($signature); + logger("Repaired double encoded signature from Diaspora/Hubzilla handle ".$handle." - level ".$level, LOGGER_DEBUG); - $parsed_xml = parse_xml_string($msg['message'],false); - - $xmlbase = $parsed_xml->post; - - logger('diaspora_dispatch: ' . print_r($xmlbase,true), LOGGER_DEBUG); - - - if($xmlbase->request) { - $ret = diaspora_request($importer,$xmlbase->request); - } - elseif($xmlbase->status_message) { - $ret = diaspora_post($importer,$xmlbase->status_message,$msg); - } - elseif($xmlbase->profile) { - $ret = diaspora_profile($importer,$xmlbase->profile,$msg); - } - elseif($xmlbase->comment) { - $ret = diaspora_comment($importer,$xmlbase->comment,$msg); - } - elseif($xmlbase->like) { - $ret = diaspora_like($importer,$xmlbase->like,$msg); - } - elseif($xmlbase->asphoto) { - $ret = diaspora_asphoto($importer,$xmlbase->asphoto,$msg); - } - elseif($xmlbase->reshare) { - $ret = diaspora_reshare($importer,$xmlbase->reshare,$msg); - } - elseif($xmlbase->retraction) { - $ret = diaspora_retraction($importer,$xmlbase->retraction,$msg); - } - elseif($xmlbase->signed_retraction) { - $ret = diaspora_signed_retraction($importer,$xmlbase->signed_retraction,$msg); - } - elseif($xmlbase->relayable_retraction) { - $ret = diaspora_signed_retraction($importer,$xmlbase->relayable_retraction,$msg); - } - elseif($xmlbase->photo) { - $ret = diaspora_photo($importer,$xmlbase->photo,$msg,$attempt); - } - elseif($xmlbase->conversation) { - $ret = diaspora_conversation($importer,$xmlbase->conversation,$msg); - } - elseif($xmlbase->message) { - $ret = diaspora_message($importer,$xmlbase->message,$msg); - } - elseif($xmlbase->participation) { - $ret = diaspora_participation($importer,$xmlbase->participation); - } - else { - logger('diaspora_dispatch: unknown message type: ' . print_r($xmlbase,true)); - } - return $ret; -} - -function diaspora_handle_from_contact($contact_id) { - $handle = False; - - logger("diaspora_handle_from_contact: contact id is " . $contact_id, LOGGER_DEBUG); - - $r = q("SELECT network, addr, self, url, nick FROM contact WHERE id = %d", - intval($contact_id) - ); - if($r) { - $contact = $r[0]; - - logger("diaspora_handle_from_contact: contact 'self' = " . $contact['self'] . " 'url' = " . $contact['url'], LOGGER_DEBUG); - - if($contact['network'] === NETWORK_DIASPORA) { - $handle = $contact['addr']; - -// logger("diaspora_handle_from_contact: contact id is a Diaspora person, handle = " . $handle, LOGGER_DEBUG); + // Do a recursive call to be able to fix even multiple levels + if ($level < 10) + $signature = self::repair_signature($signature, $handle, ++$level); } - elseif(($contact['network'] === NETWORK_DFRN) || ($contact['self'] == 1)) { - $baseurl_start = strpos($contact['url'],'://') + 3; - $baseurl_length = strpos($contact['url'],'/profile') - $baseurl_start; // allows installations in a subdirectory--not sure how Diaspora will handle - $baseurl = substr($contact['url'], $baseurl_start, $baseurl_length); - $handle = $contact['nick'] . '@' . $baseurl; -// logger("diaspora_handle_from_contact: contact id is a DFRN person, handle = " . $handle, LOGGER_DEBUG); - } + return($signature); } - return $handle; -} + /** + * @brief: Decodes incoming Diaspora message + * + * @param array $importer Array of the importer user + * @param string $xml urldecoded Diaspora salmon + * + * @return array + * 'message' -> decoded Diaspora XML message + * 'author' -> author diaspora handle + * 'key' -> author public key (converted to pkcs#8) + */ + public static function decode($importer, $xml) { -function diaspora_get_contact_by_handle($uid,$handle) { - $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND `uid` = %d AND `addr` = '%s' LIMIT 1", - dbesc(NETWORK_DIASPORA), - intval($uid), - dbesc($handle) - ); - if($r && count($r)) - return $r[0]; + $public = false; + $basedom = parse_xml_string($xml); - $handle_parts = explode("@", $handle); - $nurl_sql = '%%://' . $handle_parts[1] . '%%/profile/' . $handle_parts[0]; - $r = q("SELECT * FROM contact WHERE network = '%s' AND uid = %d AND nurl LIKE '%s' LIMIT 1", - dbesc(NETWORK_DFRN), - intval($uid), - dbesc($nurl_sql) - ); - if($r && count($r)) - return $r[0]; + if (!is_object($basedom)) + return false; - return false; -} + $children = $basedom->children('https://joindiaspora.com/protocol'); -function find_diaspora_person_by_handle($handle) { + if($children->header) { + $public = true; + $author_link = str_replace('acct:','',$children->header->author_id); + } else { - $person = false; - $update = false; - $got_lock = false; + $encrypted_header = json_decode(base64_decode($children->encrypted_header)); - $endlessloop = 0; - $maxloops = 10; + $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key); + $ciphertext = base64_decode($encrypted_header->ciphertext); - do { - $r = q("select * from fcontact where network = '%s' and addr = '%s' limit 1", + $outer_key_bundle = ''; + openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$importer['prvkey']); + + $j_outer_key_bundle = json_decode($outer_key_bundle); + + $outer_iv = base64_decode($j_outer_key_bundle->iv); + $outer_key = base64_decode($j_outer_key_bundle->key); + + $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv); + + + $decrypted = pkcs5_unpad($decrypted); + + logger('decrypted: '.$decrypted, LOGGER_DEBUG); + $idom = parse_xml_string($decrypted,false); + + $inner_iv = base64_decode($idom->iv); + $inner_aes_key = base64_decode($idom->aes_key); + + $author_link = str_replace('acct:','',$idom->author_id); + } + + $dom = $basedom->children(NAMESPACE_SALMON_ME); + + // figure out where in the DOM tree our data is hiding + + if($dom->provenance->data) + $base = $dom->provenance; + elseif($dom->env->data) + $base = $dom->env; + elseif($dom->data) + $base = $dom; + + if (!$base) { + logger('unable to locate salmon data in xml'); + http_status_exit(400); + } + + + // Stash the signature away for now. We have to find their key or it won't be good for anything. + $signature = base64url_decode($base->sig); + + // unpack the data + + // strip whitespace so our data element will return to one big base64 blob + $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data); + + + // stash away some other stuff for later + + $type = $base->data[0]->attributes()->type[0]; + $keyhash = $base->sig[0]->attributes()->keyhash[0]; + $encoding = $base->encoding; + $alg = $base->alg; + + + $signed_data = $data.'.'.base64url_encode($type).'.'.base64url_encode($encoding).'.'.base64url_encode($alg); + + + // decode the data + $data = base64url_decode($data); + + + if($public) + $inner_decrypted = $data; + else { + + // Decode the encrypted blob + + $inner_encrypted = base64_decode($data); + $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv); + $inner_decrypted = pkcs5_unpad($inner_decrypted); + } + + if (!$author_link) { + logger('Could not retrieve author URI.'); + http_status_exit(400); + } + // Once we have the author URI, go to the web and try to find their public key + // (first this will look it up locally if it is in the fcontact cache) + // This will also convert diaspora public key from pkcs#1 to pkcs#8 + + logger('Fetching key for '.$author_link); + $key = self::key($author_link); + + if (!$key) { + logger('Could not retrieve author key.'); + http_status_exit(400); + } + + $verify = rsa_verify($signed_data,$signature,$key); + + if (!$verify) { + logger('Message did not verify. Discarding.'); + http_status_exit(400); + } + + logger('Message verified.'); + + return array('message' => (string)$inner_decrypted, + 'author' => unxmlify($author_link), + 'key' => (string)$key); + + } + + + /** + * @brief Dispatches public messages and find the fitting receivers + * + * @param array $msg The post that will be dispatched + * + * @return int The message id of the generated message, "true" or "false" if there was an error + */ + public static function dispatch_public($msg) { + + $enabled = intval(get_config("system", "diaspora_enabled")); + if (!$enabled) { + logger("diaspora is disabled"); + return false; + } + + // Use a dummy importer to import the data for the public copy + $importer = array("uid" => 0, "page-flags" => PAGE_FREELOVE); + $message_id = self::dispatch($importer,$msg); + + // Now distribute it to the followers + $r = q("SELECT `user`.* FROM `user` WHERE `user`.`uid` IN + (SELECT `contact`.`uid` FROM `contact` WHERE `contact`.`network` = '%s' AND `contact`.`addr` = '%s') + AND NOT `account_expired` AND NOT `account_removed`", + dbesc(NETWORK_DIASPORA), + dbesc($msg["author"]) + ); + if($r) { + foreach($r as $rr) { + logger("delivering to: ".$rr["username"]); + self::dispatch($rr,$msg); + } + } else + logger("No subscribers for ".$msg["author"]." ".print_r($msg, true)); + + return $message_id; + } + + /** + * @brief Dispatches the different message types to the different functions + * + * @param array $importer Array of the importer user + * @param array $msg The post that will be dispatched + * + * @return int The message id of the generated message, "true" or "false" if there was an error + */ + public static function dispatch($importer, $msg) { + + // The sender is the handle of the contact that sent the message. + // This will often be different with relayed messages (for example "like" and "comment") + $sender = $msg["author"]; + + if (!diaspora::valid_posting($msg, $fields)) { + logger("Invalid posting"); + return false; + } + + $type = $fields->getName(); + + logger("Received message type ".$type." from ".$sender." for user ".$importer["uid"], LOGGER_DEBUG); + + switch ($type) { + case "account_deletion": + return self::receive_account_deletion($importer, $fields); + + case "comment": + return self::receive_comment($importer, $sender, $fields, $msg["message"]); + + case "contact": + return self::receive_contact_request($importer, $fields); + + case "conversation": + return self::receive_conversation($importer, $msg, $fields); + + case "like": + return self::receive_like($importer, $sender, $fields); + + case "message": + return self::receive_message($importer, $fields); + + case "participation": // Not implemented + return self::receive_participation($importer, $fields); + + case "photo": // Not implemented + return self::receive_photo($importer, $fields); + + case "poll_participation": // Not implemented + return self::receive_poll_participation($importer, $fields); + + case "profile": + return self::receive_profile($importer, $fields); + + case "reshare": + return self::receive_reshare($importer, $fields, $msg["message"]); + + case "retraction": + return self::receive_retraction($importer, $sender, $fields); + + case "status_message": + return self::receive_status_message($importer, $fields, $msg["message"]); + + default: + logger("Unknown message type ".$type); + return false; + } + + return true; + } + + /** + * @brief Checks if a posting is valid and fetches the data fields. + * + * This function does not only check the signature. + * It also does the conversion between the old and the new diaspora format. + * + * @param array $msg Array with the XML, the sender handle and the sender signature + * @param object $fields SimpleXML object that contains the posting when it is valid + * + * @return bool Is the posting valid? + */ + private function valid_posting($msg, &$fields) { + + $data = parse_xml_string($msg["message"], false); + + if (!is_object($data)) + return false; + + $first_child = $data->getName(); + + // Is this the new or the old version? + if ($data->getName() == "XML") { + $oldXML = true; + foreach ($data->post->children() as $child) + $element = $child; + } else { + $oldXML = false; + $element = $data; + } + + $type = $element->getName(); + $orig_type = $type; + + // All retractions are handled identically from now on. + // In the new version there will only be "retraction". + if (in_array($type, array("signed_retraction", "relayable_retraction"))) + $type = "retraction"; + + if ($type == "request") + $type = "contact"; + + $fields = new SimpleXMLElement("<".$type."/>"); + + $signed_data = ""; + + foreach ($element->children() AS $fieldname => $entry) { + if ($oldXML) { + // Translation for the old XML structure + if ($fieldname == "diaspora_handle") + $fieldname = "author"; + + if ($fieldname == "participant_handles") + $fieldname = "participants"; + + if (in_array($type, array("like", "participation"))) { + if ($fieldname == "target_type") + $fieldname = "parent_type"; + } + + if ($fieldname == "sender_handle") + $fieldname = "author"; + + if ($fieldname == "recipient_handle") + $fieldname = "recipient"; + + if ($fieldname == "root_diaspora_id") + $fieldname = "root_author"; + + if ($type == "retraction") { + if ($fieldname == "post_guid") + $fieldname = "target_guid"; + + if ($fieldname == "type") + $fieldname = "target_type"; + } + } + + if ($fieldname == "author_signature") + $author_signature = base64_decode($entry); + elseif ($fieldname == "parent_author_signature") + $parent_author_signature = base64_decode($entry); + elseif ($fieldname != "target_author_signature") { + if ($signed_data != "") { + $signed_data .= ";"; + $signed_data_parent .= ";"; + } + + $signed_data .= $entry; + } + if (!in_array($fieldname, array("parent_author_signature", "target_author_signature")) OR + ($orig_type == "relayable_retraction")) + xml::copy($entry, $fields, $fieldname); + } + + // This is something that shouldn't happen at all. + if (in_array($type, array("status_message", "reshare", "profile"))) + if ($msg["author"] != $fields->author) { + logger("Message handle is not the same as envelope sender. Quitting this message."); + return false; + } + + // Only some message types have signatures. So we quit here for the other types. + if (!in_array($type, array("comment", "message", "like"))) + return true; + + // No author_signature? This is a must, so we quit. + if (!isset($author_signature)) + return false; + + if (isset($parent_author_signature)) { + $key = self::key($msg["author"]); + + if (!rsa_verify($signed_data, $parent_author_signature, $key, "sha256")) + return false; + } + + $key = self::key($fields->author); + + return rsa_verify($signed_data, $author_signature, $key, "sha256"); + } + + /** + * @brief Fetches the public key for a given handle + * + * @param string $handle The handle + * + * @return string The public key + */ + private function key($handle) { + $handle = strval($handle); + + logger("Fetching diaspora key for: ".$handle); + + $r = self::person_by_handle($handle); + if($r) + return $r["pubkey"]; + + return ""; + } + + /** + * @brief Fetches data for a given handle + * + * @param string $handle The handle + * + * @return array the queried data + */ + private function person_by_handle($handle) { + + $r = q("SELECT * FROM `fcontact` WHERE `network` = '%s' AND `addr` = '%s' LIMIT 1", dbesc(NETWORK_DIASPORA), dbesc($handle) ); - if(count($r)) { + if ($r) { $person = $r[0]; - logger('find_diaspora_person_by handle: in cache ' . print_r($r,true), LOGGER_DEBUG); + logger("In cache ".print_r($r,true), LOGGER_DEBUG); // update record occasionally so it doesn't get stale - $d = strtotime($person['updated'] . ' +00:00'); - if($d < strtotime('now - 14 days')) + $d = strtotime($person["updated"]." +00:00"); + if ($d < strtotime("now - 14 days")) $update = true; } + if (!$person OR $update) { + logger("create or refresh", LOGGER_DEBUG); + $r = probe_url($handle, PROBE_DIASPORA); - // FETCHING PERSON INFORMATION FROM REMOTE SERVER - // - // If the person isn't in our 'fcontact' table, or if he/she is but - // his/her information hasn't been updated for more than 14 days, then - // we want to fetch the person's information from the remote server. - // - // Note that $person isn't changed by this block of code unless the - // person's information has been successfully fetched from the remote - // server. So if $person was 'false' to begin with (because he/she wasn't - // in the local cache), it'll stay false, and if $person held the local - // cache information to begin with, it'll keep that information. That way - // if there's a problem with the remote fetch, we can at least use our - // cached information--it's better than nothing. - - if((! $person) || ($update)) { - // Lock the function to prevent race conditions if multiple items - // come in at the same time from a person who doesn't exist in - // fcontact - // - // Don't loop forever. On the last loop, try to create the contact - // whether the function is locked or not. Maybe the locking thread - // has died or something. At any rate, a duplicate in 'fcontact' - // is a much smaller problem than a deadlocked thread - $got_lock = lock_function('find_diaspora_person_by_handle', false); - if(($endlessloop + 1) >= $maxloops) - $got_lock = true; - - if($got_lock) { - logger('find_diaspora_person_by_handle: create or refresh', LOGGER_DEBUG); - require_once('include/Scrape.php'); - $r = probe_url($handle, PROBE_DIASPORA); - - // Note that Friendica contacts can return a "Diaspora person" - // if Diaspora connectivity is enabled on their server - if((count($r)) && ($r['network'] === NETWORK_DIASPORA)) { - add_fcontact($r,$update); - $person = ($r); - } - - unlock_function('find_diaspora_person_by_handle'); - } - else { - logger('find_diaspora_person_by_handle: couldn\'t lock function', LOGGER_DEBUG); - if(! $person) - block_on_function_lock('find_diaspora_person_by_handle'); + // Note that Friendica contacts will return a "Diaspora person" + // if Diaspora connectivity is enabled on their server + if ($r AND ($r["network"] === NETWORK_DIASPORA)) { + self::add_fcontact($r, $update); + $person = $r; } } - } while((! $person) && (! $got_lock) && (++$endlessloop < $maxloops)); - // We need to try again if the person wasn't in 'fcontact' but the function was locked. - // The fact that the function was locked may mean that another process was creating the - // person's record. It could also mean another process was creating or updating an unrelated - // person. - // - // At any rate, we need to keep trying until we've either got the person or had a chance to - // try to fetch his/her remote information. But we don't want to block on locking the - // function, because if the other process is creating the record, then when we acquire the lock - // we'll dive right into creating another, duplicate record. We DO want to at least wait - // until the lock is released, so we don't flood the database with requests. - // - // If the person was in the 'fcontact' table, don't try again. It's not worth the time, since - // we do have some information for the person - - return $person; -} - - -function get_diaspora_key($uri) { - logger('Fetching diaspora key for: ' . $uri); - - $r = find_diaspora_person_by_handle($uri); - if($r) - return $r['pubkey']; - return ''; -} - - -function diaspora_pubmsg_build($msg,$user,$contact,$prvkey,$pubkey) { - $a = get_app(); - - logger('diaspora_pubmsg_build: ' . $msg, LOGGER_DATA); - - - $handle = $user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - -// $b64_data = base64_encode($msg); -// $b64url_data = base64url_encode($b64_data); - - $b64url_data = base64url_encode($msg); - - $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data); - - $type = 'application/xml'; - $encoding = 'base64url'; - $alg = 'RSA-SHA256'; - - $signable_data = $data . '.' . base64url_encode($type) . '.' - . base64url_encode($encoding) . '.' . base64url_encode($alg) ; - - $signature = rsa_sign($signable_data,$prvkey); - $sig = base64url_encode($signature); - -$magic_env = <<< EOT - - -
- $handle -
- - base64url - RSA-SHA256 - $data - $sig - -
-EOT; - - logger('diaspora_pubmsg_build: magic_env: ' . $magic_env, LOGGER_DATA); - return $magic_env; - -} - - - - -function diaspora_msg_build($msg,$user,$contact,$prvkey,$pubkey,$public = false) { - $a = get_app(); - - if($public) - return diaspora_pubmsg_build($msg,$user,$contact,$prvkey,$pubkey); - - logger('diaspora_msg_build: ' . $msg, LOGGER_DATA); - - // without a public key nothing will work - - if(! $pubkey) { - logger('diaspora_msg_build: pubkey missing: contact id: ' . $contact['id']); - return ''; + return $person; } - $inner_aes_key = random_string(32); - $b_inner_aes_key = base64_encode($inner_aes_key); - $inner_iv = random_string(16); - $b_inner_iv = base64_encode($inner_iv); + /** + * @brief Updates the fcontact table + * + * @param array $arr The fcontact data + * @param bool $update Update or insert? + * + * @return string The id of the fcontact entry + */ + private function add_fcontact($arr, $update = false) { - $outer_aes_key = random_string(32); - $b_outer_aes_key = base64_encode($outer_aes_key); - $outer_iv = random_string(16); - $b_outer_iv = base64_encode($outer_iv); - - $handle = $user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - - $padded_data = pkcs5_pad($msg,16); - $inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv); - - $b64_data = base64_encode($inner_encrypted); - - - $b64url_data = base64url_encode($b64_data); - $data = str_replace(array("\n","\r"," ","\t"),array('','','',''),$b64url_data); - - $type = 'application/xml'; - $encoding = 'base64url'; - $alg = 'RSA-SHA256'; - - $signable_data = $data . '.' . base64url_encode($type) . '.' - . base64url_encode($encoding) . '.' . base64url_encode($alg) ; - - $signature = rsa_sign($signable_data,$prvkey); - $sig = base64url_encode($signature); - -$decrypted_header = <<< EOT - - $b_inner_iv - $b_inner_aes_key - $handle - -EOT; - - $decrypted_header = pkcs5_pad($decrypted_header,16); - - $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv); - - $outer_json = json_encode(array('iv' => $b_outer_iv,'key' => $b_outer_aes_key)); - - $encrypted_outer_key_bundle = ''; - openssl_public_encrypt($outer_json,$encrypted_outer_key_bundle,$pubkey); - - $b64_encrypted_outer_key_bundle = base64_encode($encrypted_outer_key_bundle); - - logger('outer_bundle: ' . $b64_encrypted_outer_key_bundle . ' key: ' . $pubkey, LOGGER_DATA); - - $encrypted_header_json_object = json_encode(array('aes_key' => base64_encode($encrypted_outer_key_bundle), - 'ciphertext' => base64_encode($ciphertext))); - $cipher_json = base64_encode($encrypted_header_json_object); - - $encrypted_header = '' . $cipher_json . ''; - -$magic_env = <<< EOT - - - $encrypted_header - - base64url - RSA-SHA256 - $data - $sig - - -EOT; - - logger('diaspora_msg_build: magic_env: ' . $magic_env, LOGGER_DATA); - return $magic_env; - -} - -/** - * - * diaspora_decode($importer,$xml) - * array $importer -> from user table - * string $xml -> urldecoded Diaspora salmon - * - * Returns array - * 'message' -> decoded Diaspora XML message - * 'author' -> author diaspora handle - * 'key' -> author public key (converted to pkcs#8) - * - * Author and key are used elsewhere to save a lookup for verifying replies and likes - */ - - -function diaspora_decode($importer,$xml) { - - $public = false; - $basedom = parse_xml_string($xml); - - $children = $basedom->children('https://joindiaspora.com/protocol'); - - if($children->header) { - $public = true; - $author_link = str_replace('acct:','',$children->header->author_id); - } - else { - - $encrypted_header = json_decode(base64_decode($children->encrypted_header)); - - $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key); - $ciphertext = base64_decode($encrypted_header->ciphertext); - - $outer_key_bundle = ''; - openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$importer['prvkey']); - - $j_outer_key_bundle = json_decode($outer_key_bundle); - - $outer_iv = base64_decode($j_outer_key_bundle->iv); - $outer_key = base64_decode($j_outer_key_bundle->key); - - $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv); - - - $decrypted = pkcs5_unpad($decrypted); - - /** - * $decrypted now contains something like - * - * - * 8e+G2+ET8l5BPuW0sVTnQw== - * UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU= - -***** OBSOLETE - - * - * Ryan Hughes - * acct:galaxor@diaspora.pirateship.org - * - -***** CURRENT - - * galaxor@diaspora.priateship.org - -***** END DIFFS - - * - */ - - logger('decrypted: ' . $decrypted, LOGGER_DEBUG); - $idom = parse_xml_string($decrypted,false); - - $inner_iv = base64_decode($idom->iv); - $inner_aes_key = base64_decode($idom->aes_key); - - $author_link = str_replace('acct:','',$idom->author_id); + if($update) { + $r = q("UPDATE `fcontact` SET + `name` = '%s', + `photo` = '%s', + `request` = '%s', + `nick` = '%s', + `addr` = '%s', + `batch` = '%s', + `notify` = '%s', + `poll` = '%s', + `confirm` = '%s', + `alias` = '%s', + `pubkey` = '%s', + `updated` = '%s' + WHERE `url` = '%s' AND `network` = '%s'", + dbesc($arr["name"]), + dbesc($arr["photo"]), + dbesc($arr["request"]), + dbesc($arr["nick"]), + dbesc($arr["addr"]), + dbesc($arr["batch"]), + dbesc($arr["notify"]), + dbesc($arr["poll"]), + dbesc($arr["confirm"]), + dbesc($arr["alias"]), + dbesc($arr["pubkey"]), + dbesc(datetime_convert()), + dbesc($arr["url"]), + dbesc($arr["network"]) + ); + } else { + $r = q("INSERT INTO `fcontact` (`url`,`name`,`photo`,`request`,`nick`,`addr`, + `batch`, `notify`,`poll`,`confirm`,`network`,`alias`,`pubkey`,`updated`) + VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", + dbesc($arr["url"]), + dbesc($arr["name"]), + dbesc($arr["photo"]), + dbesc($arr["request"]), + dbesc($arr["nick"]), + dbesc($arr["addr"]), + dbesc($arr["batch"]), + dbesc($arr["notify"]), + dbesc($arr["poll"]), + dbesc($arr["confirm"]), + dbesc($arr["network"]), + dbesc($arr["alias"]), + dbesc($arr["pubkey"]), + dbesc(datetime_convert()) + ); + } + return $r; } - $dom = $basedom->children(NAMESPACE_SALMON_ME); + /** + * @brief get a handle (user@domain.tld) from a given contact id or gcontact id + * + * @param int $contact_id The id in the contact table + * @param int $gcontact_id The id in the gcontact table + * + * @return string the handle + */ + public static function handle_from_contact($contact_id, $gcontact_id = 0) { + $handle = False; - // figure out where in the DOM tree our data is hiding + logger("contact id is ".$contact_id." - gcontact id is ".$gcontact_id, LOGGER_DEBUG); - if($dom->provenance->data) - $base = $dom->provenance; - elseif($dom->env->data) - $base = $dom->env; - elseif($dom->data) - $base = $dom; + if ($gcontact_id != 0) { + $r = q("SELECT `addr` FROM `gcontact` WHERE `id` = %d AND `addr` != ''", + intval($gcontact_id)); + if ($r) + return $r[0]["addr"]; + } - if(! $base) { - logger('mod-diaspora: unable to locate salmon data in xml '); - http_status_exit(400); + $r = q("SELECT `network`, `addr`, `self`, `url`, `nick` FROM `contact` WHERE `id` = %d", + intval($contact_id)); + if ($r) { + $contact = $r[0]; + + logger("contact 'self' = ".$contact['self']." 'url' = ".$contact['url'], LOGGER_DEBUG); + + if($contact['addr'] != "") + $handle = $contact['addr']; + else { + $baseurl_start = strpos($contact['url'],'://') + 3; + $baseurl_length = strpos($contact['url'],'/profile') - $baseurl_start; // allows installations in a subdirectory--not sure how Diaspora will handle + $baseurl = substr($contact['url'], $baseurl_start, $baseurl_length); + $handle = $contact['nick'].'@'.$baseurl; + } + } + + return $handle; } + /** + * @brief Get a contact id for a given handle + * + * @param int $uid The user id + * @param string $handle The handle in the format user@domain.tld + * + * @return The contact id + */ + private function contact_by_handle($uid, $handle) { + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `addr` = '%s' LIMIT 1", + intval($uid), + dbesc($handle) + ); - // Stash the signature away for now. We have to find their key or it won't be good for anything. - $signature = base64url_decode($base->sig); + if ($r) + return $r[0]; - // unpack the data + $handle_parts = explode("@", $handle); + $nurl_sql = "%%://".$handle_parts[1]."%%/profile/".$handle_parts[0]; + $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND `uid` = %d AND `nurl` LIKE '%s' LIMIT 1", + dbesc(NETWORK_DFRN), + intval($uid), + dbesc($nurl_sql) + ); + if($r) + return $r[0]; - // strip whitespace so our data element will return to one big base64 blob - $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data); - - - // stash away some other stuff for later - - $type = $base->data[0]->attributes()->type[0]; - $keyhash = $base->sig[0]->attributes()->keyhash[0]; - $encoding = $base->encoding; - $alg = $base->alg; - - - $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg); - - - // decode the data - $data = base64url_decode($data); - - - if($public) { - $inner_decrypted = $data; - } - else { - - // Decode the encrypted blob - - $inner_encrypted = base64_decode($data); - $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv); - $inner_decrypted = pkcs5_unpad($inner_decrypted); + return false; } - if(! $author_link) { - logger('mod-diaspora: Could not retrieve author URI.'); - http_status_exit(400); - } - - // Once we have the author URI, go to the web and try to find their public key - // (first this will look it up locally if it is in the fcontact cache) - // This will also convert diaspora public key from pkcs#1 to pkcs#8 - - logger('mod-diaspora: Fetching key for ' . $author_link ); - $key = get_diaspora_key($author_link); - - if(! $key) { - logger('mod-diaspora: Could not retrieve author key.'); - http_status_exit(400); - } - - $verify = rsa_verify($signed_data,$signature,$key); - - if(! $verify) { - logger('mod-diaspora: Message did not verify. Discarding.'); - http_status_exit(400); - } - - logger('mod-diaspora: Message verified.'); - - return array('message' => $inner_decrypted, 'author' => $author_link, 'key' => $key); - -} - - -function diaspora_request($importer,$xml) { - - $a = get_app(); - - $sender_handle = unxmlify($xml->sender_handle); - $recipient_handle = unxmlify($xml->recipient_handle); - - if(! $sender_handle || ! $recipient_handle) - return; - - $contact = diaspora_get_contact_by_handle($importer['uid'],$sender_handle); - - if($contact) { + /** + * @brief Check if posting is allowed for this contact + * + * @param array $importer Array of the importer user + * @param array $contact The contact that is checked + * @param bool $is_comment Is the check for a comment? + * + * @return bool is the contact allowed to post? + */ + private function post_allow($importer, $contact, $is_comment = false) { // perhaps we were already sharing with this person. Now they're sharing with us. // That makes us friends. - - if($contact['rel'] == CONTACT_IS_FOLLOWER && in_array($importer['page-flags'], array(PAGE_FREELOVE))) { + // Normally this should have handled by getting a request - but this could get lost + if($contact["rel"] == CONTACT_IS_FOLLOWER && in_array($importer["page-flags"], array(PAGE_FREELOVE))) { q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d", intval(CONTACT_IS_FRIEND), - intval($contact['id']), - intval($importer['uid']) + intval($contact["id"]), + intval($importer["uid"]) ); - } - // send notification - - $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", - intval($importer['uid']) - ); - - if((count($r)) && (!$r[0]['hide-friends']) && (!$contact['hidden']) && intval(get_pconfig($importer['uid'],'system','post_newfriend'))) { - require_once('include/items.php'); - - $self = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", - intval($importer['uid']) - ); - - // they are not CONTACT_IS_FOLLOWER anymore but that's what we have in the array - - if(count($self) && $contact['rel'] == CONTACT_IS_FOLLOWER) { - - $arr = array(); - $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $importer['uid']); - $arr['uid'] = $importer['uid']; - $arr['contact-id'] = $self[0]['id']; - $arr['wall'] = 1; - $arr['type'] = 'wall'; - $arr['gravity'] = 0; - $arr['origin'] = 1; - $arr['author-name'] = $arr['owner-name'] = $self[0]['name']; - $arr['author-link'] = $arr['owner-link'] = $self[0]['url']; - $arr['author-avatar'] = $arr['owner-avatar'] = $self[0]['thumb']; - $arr['verb'] = ACTIVITY_FRIEND; - $arr['object-type'] = ACTIVITY_OBJ_PERSON; - - $A = '[url=' . $self[0]['url'] . ']' . $self[0]['name'] . '[/url]'; - $B = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; - $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]'; - $arr['body'] = sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$Bphoto; - - $arr['object'] = '' . ACTIVITY_OBJ_PERSON . '' . $contact['name'] . '' - . '' . $contact['url'] . '/' . $contact['name'] . ''; - $arr['object'] .= '' . xmlify('' . "\n"); - $arr['object'] .= xmlify('' . "\n"); - $arr['object'] .= '' . "\n"; - $arr['last-child'] = 1; - - $arr['allow_cid'] = $user[0]['allow_cid']; - $arr['allow_gid'] = $user[0]['allow_gid']; - $arr['deny_cid'] = $user[0]['deny_cid']; - $arr['deny_gid'] = $user[0]['deny_gid']; - - $i = item_store($arr); - if($i) - proc_run('php',"include/notifier.php","activity","$i"); - - } - + $contact["rel"] = CONTACT_IS_FRIEND; + logger("defining user ".$contact["nick"]." as friend"); } - return; - } + if(($contact["blocked"]) || ($contact["readonly"]) || ($contact["archive"])) + return false; + if($contact["rel"] == CONTACT_IS_SHARING || $contact["rel"] == CONTACT_IS_FRIEND) + return true; + if($contact["rel"] == CONTACT_IS_FOLLOWER) + if(($importer["page-flags"] == PAGE_COMMUNITY) OR $is_comment) + return true; - $ret = find_diaspora_person_by_handle($sender_handle); - - - if((! count($ret)) || ($ret['network'] != NETWORK_DIASPORA)) { - logger('diaspora_request: Cannot resolve diaspora handle ' . $sender_handle . ' for ' . $recipient_handle); - return; - } - - $batch = (($ret['batch']) ? $ret['batch'] : implode('/', array_slice(explode('/',$ret['url']),0,3)) . '/receive/public'); - - - - $r = q("INSERT INTO `contact` (`uid`, `network`,`addr`,`created`,`url`,`nurl`,`batch`,`name`,`nick`,`photo`,`pubkey`,`notify`,`poll`,`blocked`,`priority`) - VALUES ( %d, '%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d) ", - intval($importer['uid']), - dbesc($ret['network']), - dbesc($ret['addr']), - datetime_convert(), - dbesc($ret['url']), - dbesc(normalise_link($ret['url'])), - dbesc($batch), - dbesc($ret['name']), - dbesc($ret['nick']), - dbesc($ret['photo']), - dbesc($ret['pubkey']), - dbesc($ret['notify']), - dbesc($ret['poll']), - 1, - 2 - ); - - // find the contact record we just created - - $contact_record = diaspora_get_contact_by_handle($importer['uid'],$sender_handle); - - if(! $contact_record) { - logger('diaspora_request: unable to locate newly created contact record.'); - return; - } - - $g = q("select def_gid from user where uid = %d limit 1", - intval($importer['uid']) - ); - if($g && intval($g[0]['def_gid'])) { - require_once('include/group.php'); - group_add_member($importer['uid'],'',$contact_record['id'],$g[0]['def_gid']); - } - - if($importer['page-flags'] == PAGE_NORMAL) { - - $hash = random_string() . (string) time(); // Generate a confirm_key - - $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime` ) - VALUES ( %d, %d, %d, %d, '%s', '%s', '%s' )", - intval($importer['uid']), - intval($contact_record['id']), - 0, - 0, - dbesc( t('Sharing notification from Diaspora network')), - dbesc($hash), - dbesc(datetime_convert()) - ); - } - else { - - // automatic friend approval - - require_once('include/Photo.php'); - - update_contact_avatar($contact_record['photo'],$importer['uid'],$contact_record['id']); - - // technically they are sharing with us (CONTACT_IS_SHARING), - // but if our page-type is PAGE_COMMUNITY or PAGE_SOAPBOX - // we are going to change the relationship and make them a follower. - - if($importer['page-flags'] == PAGE_FREELOVE) - $new_relation = CONTACT_IS_FRIEND; - else - $new_relation = CONTACT_IS_FOLLOWER; - - $r = q("UPDATE `contact` SET `rel` = %d, - `name-date` = '%s', - `uri-date` = '%s', - `blocked` = 0, - `pending` = 0, - `writable` = 1 - WHERE `id` = %d - ", - intval($new_relation), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($contact_record['id']) - ); - - $u = q("select * from user where uid = %d limit 1",intval($importer['uid'])); - if($u) - $ret = diaspora_share($u[0],$contact_record); - } - - return; -} - -function diaspora_post_allow($importer,$contact, $is_comment = false) { - - // perhaps we were already sharing with this person. Now they're sharing with us. - // That makes us friends. - // Normally this should have handled by getting a request - but this could get lost - if($contact['rel'] == CONTACT_IS_FOLLOWER && in_array($importer['page-flags'], array(PAGE_FREELOVE))) { - q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d", - intval(CONTACT_IS_FRIEND), - intval($contact['id']), - intval($importer['uid']) - ); - $contact['rel'] = CONTACT_IS_FRIEND; - logger('diaspora_post_allow: defining user '.$contact["nick"].' as friend'); - } - - if(($contact['blocked']) || ($contact['readonly']) || ($contact['archive'])) - return false; - if($contact['rel'] == CONTACT_IS_SHARING || $contact['rel'] == CONTACT_IS_FRIEND) - return true; - if($contact['rel'] == CONTACT_IS_FOLLOWER) - if(($importer['page-flags'] == PAGE_COMMUNITY) OR $is_comment) + // Messages for the global users are always accepted + if ($importer["uid"] == 0) return true; - // Messages for the global users are always accepted - if ($importer['uid'] == 0) - return true; - - return false; -} - -function diaspora_is_redmatrix($url) { - return(strstr($url, "/channel/")); -} - -function diaspora_plink($addr, $guid) { - $r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr)); - - // Fallback - if (!$r) - return 'https://'.substr($addr,strpos($addr,'@')+1).'/posts/'.$guid; - - // Friendica contacts are often detected as Diaspora contacts in the "fcontact" table - // So we try another way as well. - $s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"]))); - if ($s) - $r[0]["network"] = $s[0]["network"]; - - if ($r[0]["network"] == NETWORK_DFRN) - return(str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/")); - - if (diaspora_is_redmatrix($r[0]["url"])) - return $r[0]["url"]."/?f=&mid=".$guid; - - return 'https://'.substr($addr,strpos($addr,'@')+1).'/posts/'.$guid; -} - -function diaspora_repair_signature($signature, $handle = "", $level = 1) { - - if ($signature == "") - return($signature); - - if (base64_encode(base64_decode(base64_decode($signature))) == base64_decode($signature)) { - $signature = base64_decode($signature); - logger("Repaired double encoded signature from Diaspora/Hubzilla handle ".$handle." - level ".$level, LOGGER_DEBUG); - - // Do a recursive call to be able to fix even multiple levels - if ($level < 10) - $signature = diaspora_repair_signature($signature, $handle, ++$level); - } - - return($signature); -} - -function diaspora_post($importer,$xml,$msg) { - - $a = get_app(); - $guid = notags(unxmlify($xml->guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - - if($diaspora_handle != $msg['author']) { - logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.'); - return 202; - } - - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) { - logger('diaspora_post: A Contact for handle '.$diaspora_handle.' and user '.$importer['uid'].' was not found'); - return 203; - } - - if(! diaspora_post_allow($importer,$contact, false)) { - logger('diaspora_post: Ignoring this author.'); - return 202; - } - - $message_id = $diaspora_handle . ':' . $guid; - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($r)) { - logger('diaspora_post: message exists: ' . $guid); - return 208; - } - - $created = unxmlify($xml->created_at); - $private = ((unxmlify($xml->public) == 'false') ? 1 : 0); - - $body = diaspora2bb($xml->raw_message); - - $datarray = array(); - - $datarray["object"] = json_encode($xml); - - if($xml->photo->remote_photo_path AND $xml->photo->remote_photo_name) - $datarray["object-type"] = ACTIVITY_OBJ_PHOTO; - else { - $datarray['object-type'] = ACTIVITY_OBJ_NOTE; - // Add OEmbed and other information to the body - if (!diaspora_is_redmatrix($contact['url'])) - $body = add_page_info_to_body($body, false, true); - } - - $str_tags = ''; - - $cnt = preg_match_all('/@\[url=(.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - if(strlen($str_tags)) - $str_tags .= ','; - $str_tags .= '@[url=' . $mtch[1] . '[/url]'; - } - } - - $plink = diaspora_plink($diaspora_handle, $guid); - - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $contact['id']; - $datarray['wall'] = 0; - $datarray['network'] = NETWORK_DIASPORA; - $datarray['verb'] = ACTIVITY_POST; - $datarray['guid'] = $guid; - $datarray['uri'] = $datarray['parent-uri'] = $message_id; - $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created); - $datarray['private'] = $private; - $datarray['parent'] = 0; - $datarray['plink'] = $plink; - $datarray['owner-name'] = $contact['name']; - $datarray['owner-link'] = $contact['url']; - //$datarray['owner-avatar'] = $contact['thumb']; - $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']); - $datarray['author-name'] = $contact['name']; - $datarray['author-link'] = $contact['url']; - $datarray['author-avatar'] = $contact['thumb']; - $datarray['body'] = $body; - $datarray['tag'] = $str_tags; - if ($xml->provider_display_name) - $datarray["app"] = unxmlify($xml->provider_display_name); - else - $datarray['app'] = 'Diaspora'; - - // if empty content it might be a photo that hasn't arrived yet. If a photo arrives, we'll make it visible. - - $datarray['visible'] = ((strlen($body)) ? 1 : 0); - - DiasporaFetchGuid($datarray); - $message_id = item_store($datarray); - - logger("Stored item with message id ".$message_id, LOGGER_DEBUG); - - return 201; - -} - -function DiasporaFetchGuid($item) { - preg_replace_callback("&\[url=/posts/([^\[\]]*)\](.*)\[\/url\]&Usi", - function ($match) use ($item){ - return(DiasporaFetchGuidSub($match, $item)); - },$item["body"]); -} - -function DiasporaFetchGuidSub($match, $item) { - $a = get_app(); - - if (!diaspora_store_by_guid($match[1], $item["author-link"])) - diaspora_store_by_guid($match[1], $item["owner-link"]); -} - -function diaspora_store_by_guid($guid, $server, $uid = 0) { - require_once("include/Contact.php"); - - $serverparts = parse_url($server); - $server = $serverparts["scheme"]."://".$serverparts["host"]; - - logger("Trying to fetch item ".$guid." from ".$server, LOGGER_DEBUG); - - $item = diaspora_fetch_message($guid, $server); - - if (!$item) return false; + } - logger("Successfully fetched item ".$guid." from ".$server, LOGGER_DEBUG); - - $body = $item["body"]; - $str_tags = $item["tag"]; - $app = $item["app"]; - $created = $item["created"]; - $author = $item["author"]; - $guid = $item["guid"]; - $private = $item["private"]; - $object = $item["object"]; - $objecttype = $item["object-type"]; - - $message_id = $author.':'.$guid; - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($uid), - dbesc($guid) - ); - if(count($r)) - return $r[0]["id"]; - - $person = find_diaspora_person_by_handle($author); - - $contact_id = get_contact($person['url'], $uid); - - $contacts = q("SELECT * FROM `contact` WHERE `id` = %d", intval($contact_id)); - $importers = q("SELECT * FROM `user` WHERE `uid` = %d", intval($uid)); - - if ($contacts AND $importers) - if(!diaspora_post_allow($importers[0],$contacts[0], false)) { - logger('Ignoring author '.$person['url'].' for uid '.$uid); + /** + * @brief Fetches the contact id for a handle and checks if posting is allowed + * + * @param array $importer Array of the importer user + * @param string $handle The checked handle in the format user@domain.tld + * @param bool $is_comment Is the check for a comment? + * + * @return array The contact data + */ + private function allowed_contact_by_handle($importer, $handle, $is_comment = false) { + $contact = self::contact_by_handle($importer["uid"], $handle); + if (!$contact) { + logger("A Contact for handle ".$handle." and user ".$importer["uid"]." was not found"); return false; - } else - logger('Author '.$person['url'].' is allowed for uid '.$uid); + } - $datarray = array(); - $datarray['uid'] = $uid; - $datarray['contact-id'] = $contact_id; - $datarray['wall'] = 0; - $datarray['network'] = NETWORK_DIASPORA; - $datarray['guid'] = $guid; - $datarray['uri'] = $datarray['parent-uri'] = $message_id; - $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created); - $datarray['private'] = $private; - $datarray['parent'] = 0; - $datarray['plink'] = diaspora_plink($author, $guid); - $datarray['author-name'] = $person['name']; - $datarray['author-link'] = $person['url']; - $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']); - $datarray['owner-name'] = $datarray['author-name']; - $datarray['owner-link'] = $datarray['author-link']; - $datarray['owner-avatar'] = $datarray['author-avatar']; - $datarray['body'] = $body; - $datarray['tag'] = $str_tags; - $datarray['app'] = $app; - $datarray['visible'] = ((strlen($body)) ? 1 : 0); - $datarray['object'] = $object; - $datarray['object-type'] = $objecttype; + if (!self::post_allow($importer, $contact, $is_comment)) { + logger("The handle: ".$handle." is not allowed to post to user ".$importer["uid"]); + return false; + } + return $contact; + } - if ($datarray['contact-id'] == 0) - return false; + /** + * @brief Does the message already exists on the system? + * + * @param int $uid The user id + * @param string $guid The guid of the message + * + * @return int|bool message id if the message already was stored into the system - or false. + */ + private function message_exists($uid, $guid) { + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($uid), + dbesc($guid) + ); - DiasporaFetchGuid($datarray); - $message_id = item_store($datarray); + if($r) { + logger("message ".$guid." already exists for user ".$uid); + return $r[0]["id"]; + } - /// @TODO - /// Looking if there is some subscribe mechanism in Diaspora to get all comments for this post - - return $message_id; -} - -function diaspora_fetch_message($guid, $server, $level = 0) { - - if ($level > 5) - return false; - - $a = get_app(); - - // This will not work if the server is not a Diaspora server - $source_url = $server.'/p/'.$guid.'.xml'; - $x = fetch_url($source_url); - if(!$x) - return false; - - $x = str_replace(array('',''),array('',''),$x); - $source_xml = parse_xml_string($x,false); - - $item = array(); - $item["app"] = 'Diaspora'; - $item["guid"] = $guid; - $body = ""; - - if ($source_xml->post->status_message->created_at) - $item["created"] = unxmlify($source_xml->post->status_message->created_at); - - if ($source_xml->post->status_message->provider_display_name) - $item["app"] = unxmlify($source_xml->post->status_message->provider_display_name); - - if ($source_xml->post->status_message->diaspora_handle) - $item["author"] = unxmlify($source_xml->post->status_message->diaspora_handle); - - if ($source_xml->post->status_message->guid) - $item["guid"] = unxmlify($source_xml->post->status_message->guid); - - $item["private"] = (unxmlify($source_xml->post->status_message->public) == 'false'); - $item["object"] = json_encode($source_xml->post); - - if(strlen($source_xml->post->asphoto->objectId) && ($source_xml->post->asphoto->objectId != 0) && ($source_xml->post->asphoto->image_url)) { - $item["object-type"] = ACTIVITY_OBJ_PHOTO; - $body = '[url=' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '][img]' . notags(unxmlify($source_xml->post->asphoto->objectId)) . '[/img][/url]' . "\n"; - $body = scale_external_images($body,false); - } elseif($source_xml->post->asphoto->image_url) { - $item["object-type"] = ACTIVITY_OBJ_PHOTO; - $body = '[img]' . notags(unxmlify($source_xml->post->asphoto->image_url)) . '[/img]' . "\n"; - $body = scale_external_images($body); - } elseif($source_xml->post->status_message) { - $body = diaspora2bb($source_xml->post->status_message->raw_message); - - // Checking for embedded pictures - if($source_xml->post->status_message->photo->remote_photo_path AND - $source_xml->post->status_message->photo->remote_photo_name) { - - $item["object-type"] = ACTIVITY_OBJ_PHOTO; - - $remote_photo_path = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_path)); - $remote_photo_name = notags(unxmlify($source_xml->post->status_message->photo->remote_photo_name)); - - $body = '[img]'.$remote_photo_path.$remote_photo_name.'[/img]'."\n".$body; - - logger('embedded picture link found: '.$body, LOGGER_DEBUG); - } else - $item["object-type"] = ACTIVITY_OBJ_NOTE; - - $body = scale_external_images($body); - - // Add OEmbed and other information to the body - /// @TODO It could be a repeated redmatrix item - /// Then we shouldn't add further data to it - if ($item["object-type"] == ACTIVITY_OBJ_NOTE) - $body = add_page_info_to_body($body, false, true); - - } elseif($source_xml->post->reshare) { - // Reshare of a reshare - return diaspora_fetch_message($source_xml->post->reshare->root_guid, $server, ++$level); - } else { - // Maybe it is a reshare of a photo that will be delivered at a later time (testing) - logger('no content found: '.print_r($source_xml,true)); return false; } - if (trim($body) == "") - return false; - - $item["tag"] = ''; - $item["body"] = $body; - - return $item; -} - -function diaspora_reshare($importer,$xml,$msg) { - - logger('diaspora_reshare: init: ' . print_r($xml,true)); - - $a = get_app(); - $guid = notags(unxmlify($xml->guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - - - if($diaspora_handle != $msg['author']) { - logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.'); - return 202; + /** + * @brief Checks for links to posts in a message + * + * @param array $item The item array + */ + private function fetch_guid($item) { + preg_replace_callback("&\[url=/posts/([^\[\]]*)\](.*)\[\/url\]&Usi", + function ($match) use ($item){ + return(self::fetch_guid_sub($match, $item)); + },$item["body"]); } - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) - return; - - if(! diaspora_post_allow($importer,$contact, false)) { - logger('diaspora_reshare: Ignoring this author: ' . $diaspora_handle . ' ' . print_r($xml,true)); - return 202; + /** + * @brief sub function of "fetch_guid" which checks for links in messages + * + * @param array $match array containing a link that has to be checked for a message link + * @param array $item The item array + */ + private function fetch_guid_sub($match, $item) { + if (!self::store_by_guid($match[1], $item["author-link"])) + self::store_by_guid($match[1], $item["owner-link"]); } - $message_id = $diaspora_handle . ':' . $guid; - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($r)) { - logger('diaspora_reshare: message exists: ' . $guid); - return; + /** + * @brief Fetches an item with a given guid from a given server + * + * @param string $guid the message guid + * @param string $server The server address + * @param int $uid The user id of the user + * + * @return int the message id of the stored message or false + */ + private function store_by_guid($guid, $server, $uid = 0) { + $serverparts = parse_url($server); + $server = $serverparts["scheme"]."://".$serverparts["host"]; + + logger("Trying to fetch item ".$guid." from ".$server, LOGGER_DEBUG); + + $msg = self::message($guid, $server); + + if (!$msg) + return false; + + logger("Successfully fetched item ".$guid." from ".$server, LOGGER_DEBUG); + + // Now call the dispatcher + return self::dispatch_public($msg); } - $orig_author = notags(unxmlify($xml->root_diaspora_id)); - $orig_guid = notags(unxmlify($xml->root_guid)); - $orig_url = $a->get_baseurl()."/display/".$orig_guid; + /** + * @brief Fetches a message from a server + * + * @param string $guid message guid + * @param string $server The url of the server + * @param int $level Endless loop prevention + * + * @return array + * 'message' => The message XML + * 'author' => The author handle + * 'key' => The public key of the author + */ + private function message($guid, $server, $level = 0) { - $create_original_post = false; + if ($level > 5) + return false; - // Do we already have this item? - $r = q("SELECT `body`, `tag`, `app`, `created`, `plink`, `object`, `object-type`, `uri` FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", - dbesc($orig_guid), - dbesc(NETWORK_DIASPORA) - ); - if(count($r)) { - logger('reshared message '.$orig_guid." reshared by ".$guid.' already exists on system.'); + // This will work for Diaspora and newer Friendica servers + $source_url = $server."/p/".$guid.".xml"; + $x = fetch_url($source_url); + if(!$x) + return false; - // Maybe it is already a reshared item? - // Then refetch the content, since there can be many side effects with reshared posts from other networks or reshares from reshares - require_once('include/api.php'); - if (api_share_as_retweet($r[0])) - $r = array(); - else { - $body = $r[0]["body"]; - $str_tags = $r[0]["tag"]; - $app = $r[0]["app"]; - $orig_created = $r[0]["created"]; - $orig_plink = $r[0]["plink"]; - $orig_uri = $r[0]["uri"]; - $object = $r[0]["object"]; - $objecttype = $r[0]["object-type"]; + $source_xml = parse_xml_string($x, false); + + if (!is_object($source_xml)) + return false; + + if ($source_xml->post->reshare) { + // Reshare of a reshare - old Diaspora version + return self::message($source_xml->post->reshare->root_guid, $server, ++$level); + } elseif ($source_xml->getName() == "reshare") { + // Reshare of a reshare - new Diaspora version + return self::message($source_xml->root_guid, $server, ++$level); + } + + $author = ""; + + // Fetch the author - for the old and the new Diaspora version + if ($source_xml->post->status_message->diaspora_handle) + $author = (string)$source_xml->post->status_message->diaspora_handle; + elseif ($source_xml->author AND ($source_xml->getName() == "status_message")) + $author = (string)$source_xml->author; + + // If this isn't a "status_message" then quit + if (!$author) + return false; + + $msg = array("message" => $x, "author" => $author); + + $msg["key"] = self::key($msg["author"]); + + return $msg; + } + + /** + * @brief Fetches the item record of a given guid + * + * @param int $uid The user id + * @param string $guid message guid + * @param string $author The handle of the item + * @param array $contact The contact of the item owner + * + * @return array the item record + */ + private function parent_item($uid, $guid, $author, $contact) { + $r = q("SELECT `id`, `body`, `wall`, `uri`, `private`, `origin`, + `author-name`, `author-link`, `author-avatar`, + `owner-name`, `owner-link`, `owner-avatar` + FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($uid), dbesc($guid)); + + if(!$r) { + $result = self::store_by_guid($guid, $contact["url"], $uid); + + if (!$result) { + $person = self::person_by_handle($author); + $result = self::store_by_guid($guid, $person["url"], $uid); + } + + if ($result) { + logger("Fetched missing item ".$guid." - result: ".$result, LOGGER_DEBUG); + + $r = q("SELECT `id`, `body`, `wall`, `uri`, `private`, `origin`, + `author-name`, `author-link`, `author-avatar`, + `owner-name`, `owner-link`, `owner-avatar` + FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($uid), dbesc($guid)); + } + } + + if (!$r) { + logger("parent item not found: parent: ".$guid." - user: ".$uid); + return false; + } else { + logger("parent item found: parent: ".$guid." - user: ".$uid); + return $r[0]; } } - if (!count($r)) { - $body = ""; - $str_tags = ""; - $app = ""; + /** + * @brief returns contact details + * + * @param array $contact The default contact if the person isn't found + * @param array $person The record of the person + * @param int $uid The user id + * + * @return array + * 'cid' => contact id + * 'network' => network type + */ + private function author_contact_by_url($contact, $person, $uid) { - $server = 'https://'.substr($orig_author,strpos($orig_author,'@')+1); - logger('1st try: reshared message '.$orig_guid." reshared by ".$guid.' will be fetched from original server: '.$server); - $item = diaspora_fetch_message($orig_guid, $server); - - if (!$item) { - $server = 'https://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1); - logger('2nd try: reshared message '.$orig_guid." reshared by ".$guid." will be fetched from sharer's server: ".$server); - $item = diaspora_fetch_message($orig_guid, $server); - } - if (!$item) { - $server = 'http://'.substr($orig_author,strpos($orig_author,'@')+1); - logger('3rd try: reshared message '.$orig_guid." reshared by ".$guid.' will be fetched from original server: '.$server); - $item = diaspora_fetch_message($orig_guid, $server); - } - if (!$item) { - $server = 'http://'.substr($diaspora_handle,strpos($diaspora_handle,'@')+1); - logger('4th try: reshared message '.$orig_guid." reshared by ".$guid." will be fetched from sharer's server: ".$server); - $item = diaspora_fetch_message($orig_guid, $server); + $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1", + dbesc(normalise_link($person["url"])), intval($uid)); + if ($r) { + $cid = $r[0]["id"]; + $network = $r[0]["network"]; + } else { + $cid = $contact["id"]; + $network = NETWORK_DIASPORA; } - if ($item) { - $body = $item["body"]; - $str_tags = $item["tag"]; - $app = $item["app"]; - $orig_created = $item["created"]; - $orig_author = $item["author"]; - $orig_guid = $item["guid"]; - $orig_plink = diaspora_plink($orig_author, $orig_guid); - $orig_uri = $orig_author.':'.$orig_guid; - $create_original_post = ($body != ""); - $object = $item["object"]; - $objecttype = $item["object-type"]; - } + return (array("cid" => $cid, "network" => $network)); } - $plink = diaspora_plink($diaspora_handle, $guid); - - $person = find_diaspora_person_by_handle($orig_author); - - $created = unxmlify($xml->created_at); - $private = ((unxmlify($xml->public) == 'false') ? 1 : 0); - - $datarray = array(); - - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $contact['id']; - $datarray['wall'] = 0; - $datarray['network'] = NETWORK_DIASPORA; - $datarray['guid'] = $guid; - $datarray['uri'] = $datarray['parent-uri'] = $message_id; - $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created); - $datarray['private'] = $private; - $datarray['parent'] = 0; - $datarray['plink'] = $plink; - $datarray['owner-name'] = $contact['name']; - $datarray['owner-link'] = $contact['url']; - $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']); - if (!intval(get_config('system','wall-to-wall_share'))) { - $prefix = share_header($person['name'], $person['url'], ((x($person,'thumb')) ? $person['thumb'] : $person['photo']), $orig_guid, $orig_created, $orig_url); - - $datarray['author-name'] = $contact['name']; - $datarray['author-link'] = $contact['url']; - $datarray['author-avatar'] = $contact['thumb']; - $datarray['body'] = $prefix.$body."[/share]"; - } else { - // Let reshared messages look like wall-to-wall posts - $datarray['author-name'] = $person['name']; - $datarray['author-link'] = $person['url']; - $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']); - $datarray['body'] = $body; + /** + * @brief Is the profile a hubzilla profile? + * + * @param string $url The profile link + * + * @return bool is it a hubzilla server? + */ + public static function is_redmatrix($url) { + return(strstr($url, "/channel/")); } - $datarray["object"] = json_encode($xml); - $datarray['object-type'] = $objecttype; + /** + * @brief Generate a post link with a given handle and message guid + * + * @param string $addr The user handle + * @param string $guid message guid + * + * @return string the post link + */ + private function plink($addr, $guid) { + $r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr)); - $datarray['tag'] = $str_tags; - $datarray['app'] = $app; + // Fallback + if (!$r) + return "https://".substr($addr,strpos($addr,"@")+1)."/posts/".$guid; - // if empty content it might be a photo that hasn't arrived yet. If a photo arrives, we'll make it visible. (testing) - $datarray['visible'] = ((strlen($body)) ? 1 : 0); + // Friendica contacts are often detected as Diaspora contacts in the "fcontact" table + // So we try another way as well. + $s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"]))); + if ($s) + $r[0]["network"] = $s[0]["network"]; - // Store the original item of a reshare - if ($create_original_post) { - require_once("include/Contact.php"); + if ($r[0]["network"] == NETWORK_DFRN) + return(str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/")); - $datarray2 = $datarray; + if (self::is_redmatrix($r[0]["url"])) + return $r[0]["url"]."/?f=&mid=".$guid; - $datarray2['uid'] = 0; - $datarray2['contact-id'] = get_contact($person['url'], 0); - $datarray2['guid'] = $orig_guid; - $datarray2['uri'] = $datarray2['parent-uri'] = $orig_uri; - $datarray2['changed'] = $datarray2['created'] = $datarray2['edited'] = $datarray2['commented'] = $datarray2['received'] = datetime_convert('UTC','UTC',$orig_created); - $datarray2['parent'] = 0; - $datarray2['plink'] = $orig_plink; - - $datarray2['author-name'] = $person['name']; - $datarray2['author-link'] = $person['url']; - $datarray2['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']); - $datarray2['owner-name'] = $datarray2['author-name']; - $datarray2['owner-link'] = $datarray2['author-link']; - $datarray2['owner-avatar'] = $datarray2['author-avatar']; - $datarray2['body'] = $body; - $datarray2["object"] = $object; - - DiasporaFetchGuid($datarray2); - $message_id = item_store($datarray2); - - logger("Store original item ".$orig_guid." under message id ".$message_id); + return "https://".substr($addr,strpos($addr,"@")+1)."/posts/".$guid; } - DiasporaFetchGuid($datarray); - $message_id = item_store($datarray); + /** + * @brief Processes an account deletion + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool Success + */ + private function receive_account_deletion($importer, $data) { + $author = notags(unxmlify($data->author)); - return; - -} - - -function diaspora_asphoto($importer,$xml,$msg) { - logger('diaspora_asphoto called'); - - $a = get_app(); - $guid = notags(unxmlify($xml->guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - - if($diaspora_handle != $msg['author']) { - logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.'); - return 202; - } - - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) - return; - - if(! diaspora_post_allow($importer,$contact, false)) { - logger('diaspora_asphoto: Ignoring this author.'); - return 202; - } - - $message_id = $diaspora_handle . ':' . $guid; - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($r)) { - logger('diaspora_asphoto: message exists: ' . $guid); - return; - } - - $created = unxmlify($xml->created_at); - $private = ((unxmlify($xml->public) == 'false') ? 1 : 0); - - if(strlen($xml->objectId) && ($xml->objectId != 0) && ($xml->image_url)) { - $body = '[url=' . notags(unxmlify($xml->image_url)) . '][img]' . notags(unxmlify($xml->objectId)) . '[/img][/url]' . "\n"; - $body = scale_external_images($body,false); - } - elseif($xml->image_url) { - $body = '[img]' . notags(unxmlify($xml->image_url)) . '[/img]' . "\n"; - $body = scale_external_images($body); - } - else { - logger('diaspora_asphoto: no photo url found.'); - return; - } - - $plink = diaspora_plink($diaspora_handle, $guid); - - $datarray = array(); - - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $contact['id']; - $datarray['wall'] = 0; - $datarray['network'] = NETWORK_DIASPORA; - $datarray['guid'] = $guid; - $datarray['uri'] = $datarray['parent-uri'] = $message_id; - $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert('UTC','UTC',$created); - $datarray['private'] = $private; - $datarray['parent'] = 0; - $datarray['plink'] = $plink; - $datarray['owner-name'] = $contact['name']; - $datarray['owner-link'] = $contact['url']; - //$datarray['owner-avatar'] = $contact['thumb']; - $datarray['owner-avatar'] = ((x($contact,'thumb')) ? $contact['thumb'] : $contact['photo']); - $datarray['author-name'] = $contact['name']; - $datarray['author-link'] = $contact['url']; - $datarray['author-avatar'] = $contact['thumb']; - $datarray['body'] = $body; - $datarray["object"] = json_encode($xml); - $datarray['object-type'] = ACTIVITY_OBJ_PHOTO; - - $datarray['app'] = 'Diaspora/Cubbi.es'; - - DiasporaFetchGuid($datarray); - $message_id = item_store($datarray); - - //if($message_id) { - // q("update item set plink = '%s' where id = %d", - // dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id), - // intval($message_id) - // ); - //} - - return; - -} - -function diaspora_comment($importer,$xml,$msg) { - - $a = get_app(); - $guid = notags(unxmlify($xml->guid)); - $parent_guid = notags(unxmlify($xml->parent_guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - $target_type = notags(unxmlify($xml->target_type)); - $text = unxmlify($xml->text); - $author_signature = notags(unxmlify($xml->author_signature)); - - $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : ''); - - $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']); - if(! $contact) { - logger('diaspora_comment: cannot find contact: ' . $msg['author']); - return; - } - - if(! diaspora_post_allow($importer,$contact, true)) { - logger('diaspora_comment: Ignoring this author.'); - return 202; - } - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($r)) { - logger('diaspora_comment: our comment just got relayed back to us (or there was a guid collision) : ' . $guid); - return; - } - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($parent_guid) - ); - - if(!count($r)) { - $result = diaspora_store_by_guid($parent_guid, $contact['url'], $importer['uid']); - - if (!$result) { - $person = find_diaspora_person_by_handle($diaspora_handle); - $result = diaspora_store_by_guid($parent_guid, $person['url'], $importer['uid']); + $contact = self::contact_by_handle($importer["uid"], $author); + if (!$contact) { + logger("cannot find contact for author: ".$author); + return false; } - if ($result) { - logger("Fetched missing item ".$parent_guid." - result: ".$result, LOGGER_DEBUG); + // We now remove the contact + contact_remove($contact["id"]); + return true; + } - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($parent_guid) + /** + * @brief Processes an incoming comment + * + * @param array $importer Array of the importer user + * @param string $sender The sender of the message + * @param object $data The message object + * @param string $xml The original XML of the message + * + * @return int The message id of the generated comment or "false" if there was an error + */ + private function receive_comment($importer, $sender, $data, $xml) { + $guid = notags(unxmlify($data->guid)); + $parent_guid = notags(unxmlify($data->parent_guid)); + $text = unxmlify($data->text); + $author = notags(unxmlify($data->author)); + + $contact = self::allowed_contact_by_handle($importer, $sender, true); + if (!$contact) + return false; + + $message_id = self::message_exists($importer["uid"], $guid); + if ($message_id) + return $message_id; + + $parent_item = self::parent_item($importer["uid"], $parent_guid, $author, $contact); + if (!$parent_item) + return false; + + $person = self::person_by_handle($author); + if (!is_array($person)) { + logger("unable to find author details"); + return false; + } + + // Fetch the contact id - if we know this contact + $author_contact = self::author_contact_by_url($contact, $person, $importer["uid"]); + + $datarray = array(); + + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $author_contact["cid"]; + $datarray["network"] = $author_contact["network"]; + + $datarray["author-name"] = $person["name"]; + $datarray["author-link"] = $person["url"]; + $datarray["author-avatar"] = ((x($person,"thumb")) ? $person["thumb"] : $person["photo"]); + + $datarray["owner-name"] = $contact["name"]; + $datarray["owner-link"] = $contact["url"]; + $datarray["owner-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]); + + $datarray["guid"] = $guid; + $datarray["uri"] = $author.":".$guid; + + $datarray["type"] = "remote-comment"; + $datarray["verb"] = ACTIVITY_POST; + $datarray["gravity"] = GRAVITY_COMMENT; + $datarray["parent-uri"] = $parent_item["uri"]; + + $datarray["object-type"] = ACTIVITY_OBJ_COMMENT; + $datarray["object"] = $xml; + + $datarray["body"] = diaspora2bb($text); + + self::fetch_guid($datarray); + + $message_id = item_store($datarray); + + if ($message_id) + logger("Stored comment ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); + + // If we are the origin of the parent we store the original data and notify our followers + if($message_id AND $parent_item["origin"]) { + + // Formerly we stored the signed text, the signature and the author in different fields. + // We now store the raw data so that we are more flexible. + q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')", + intval($message_id), + dbesc(json_encode($data)) ); + + // notify others + proc_run("php", "include/notifier.php", "comment-import", $message_id); } + + return $message_id; } - if(! count($r)) { - logger('diaspora_comment: parent item not found: parent: ' . $parent_guid . ' item: ' . $guid); - return; - } - $parent_item = $r[0]; - - - /* How Diaspora performs comment signature checking: - - - If an item has been sent by the comment author to the top-level post owner to relay on - to the rest of the contacts on the top-level post, the top-level post owner should check - the author_signature, then create a parent_author_signature before relaying the comment on - - If an item has been relayed on by the top-level post owner, the contacts who receive it - check only the parent_author_signature. Basically, they trust that the top-level post - owner has already verified the authenticity of anything he/she sends out - - In either case, the signature that get checked is the signature created by the person - who sent the salmon - */ - - $signed_data = $guid . ';' . $parent_guid . ';' . $text . ';' . $diaspora_handle; - $key = $msg['key']; - - if($parent_author_signature) { - // If a parent_author_signature exists, then we've received the comment - // relayed from the top-level post owner. There's no need to check the - // author_signature if the parent_author_signature is valid - - $parent_author_signature = base64_decode($parent_author_signature); - - if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) { - logger('diaspora_comment: top-level owner verification failed.'); - return; - } - } - else { - // If there's no parent_author_signature, then we've received the comment - // from the comment creator. In that case, the person is commenting on - // our post, so he/she must be a contact of ours and his/her public key - // should be in $msg['key'] - - $author_signature = base64_decode($author_signature); - - if(! rsa_verify($signed_data,$author_signature,$key,'sha256')) { - logger('diaspora_comment: comment author verification failed.'); - return; - } - } - - // Phew! Everything checks out. Now create an item. - - // Find the original comment author information. - // We need this to make sure we display the comment author - // information (name and avatar) correctly. - if(strcasecmp($diaspora_handle,$msg['author']) == 0) - $person = $contact; - else { - $person = find_diaspora_person_by_handle($diaspora_handle); - - if(! is_array($person)) { - logger('diaspora_comment: unable to find author details'); - return; - } - } - - // Fetch the contact id - if we know this contact - $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1", - dbesc(normalise_link($person['url'])), intval($importer['uid'])); - if ($r) { - $cid = $r[0]['id']; - $network = $r[0]['network']; - } else { - $cid = $contact['id']; - $network = NETWORK_DIASPORA; - } - - $body = diaspora2bb($text); - $message_id = $diaspora_handle . ':' . $guid; - - $datarray = array(); - - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $cid; - $datarray['type'] = 'remote-comment'; - $datarray['wall'] = $parent_item['wall']; - $datarray['network'] = $network; - $datarray['verb'] = ACTIVITY_POST; - $datarray['gravity'] = GRAVITY_COMMENT; - $datarray['guid'] = $guid; - $datarray['uri'] = $message_id; - $datarray['parent-uri'] = $parent_item['uri']; - - // No timestamps for comments? OK, we'll the use current time. - $datarray['changed'] = $datarray['created'] = $datarray['edited'] = datetime_convert(); - $datarray['private'] = $parent_item['private']; - - $datarray['owner-name'] = $parent_item['owner-name']; - $datarray['owner-link'] = $parent_item['owner-link']; - $datarray['owner-avatar'] = $parent_item['owner-avatar']; - - $datarray['author-name'] = $person['name']; - $datarray['author-link'] = $person['url']; - $datarray['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']); - $datarray['body'] = $body; - $datarray["object"] = json_encode($xml); - $datarray["object-type"] = ACTIVITY_OBJ_COMMENT; - - // We can't be certain what the original app is if the message is relayed. - if(($parent_item['origin']) && (! $parent_author_signature)) - $datarray['app'] = 'Diaspora'; - - DiasporaFetchGuid($datarray); - $message_id = item_store($datarray); - - $datarray['id'] = $message_id; - - //if($message_id) { - //q("update item set plink = '%s' where id = %d", - // //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id), - // dbesc($a->get_baseurl().'/display/'.$datarray['guid']), - // intval($message_id) - //); - //} - - // If we are the origin of the parent we store the original signature and notify our followers - if($parent_item['origin']) { - $author_signature_base64 = base64_encode($author_signature); - $author_signature_base64 = diaspora_repair_signature($author_signature_base64, $diaspora_handle); - - q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($message_id), - dbesc($signed_data), - dbesc($author_signature_base64), - dbesc($diaspora_handle) - ); - - // notify others - proc_run('php','include/notifier.php','comment-import',$message_id); - } - - return; -} - - - - -function diaspora_conversation($importer,$xml,$msg) { - - $a = get_app(); - - $guid = notags(unxmlify($xml->guid)); - $subject = notags(unxmlify($xml->subject)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - $participant_handles = notags(unxmlify($xml->participant_handles)); - $created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at))); - - $parent_uri = $diaspora_handle . ':' . $guid; - - $messages = $xml->message; - - if(! count($messages)) { - logger('diaspora_conversation: empty conversation'); - return; - } - - $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']); - if(! $contact) { - logger('diaspora_conversation: cannot find contact: ' . $msg['author']); - return; - } - - if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) { - logger('diaspora_conversation: Ignoring this author.'); - return 202; - } - - $conversation = null; - - $c = q("select * from conv where uid = %d and guid = '%s' limit 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($c)) - $conversation = $c[0]; - else { - $r = q("insert into conv (uid,guid,creator,created,updated,subject,recips) values(%d, '%s', '%s', '%s', '%s', '%s', '%s') ", - intval($importer['uid']), - dbesc($guid), - dbesc($diaspora_handle), - dbesc(datetime_convert('UTC','UTC',$created_at)), - dbesc(datetime_convert()), - dbesc($subject), - dbesc($participant_handles) - ); - if($r) - $c = q("select * from conv where uid = %d and guid = '%s' limit 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($c)) - $conversation = $c[0]; - } - if(! $conversation) { - logger('diaspora_conversation: unable to create conversation.'); - return; - } - - foreach($messages as $mesg) { + /** + * @brief processes and stores private messages + * + * @param array $importer Array of the importer user + * @param array $contact The contact of the message + * @param object $data The message object + * @param array $msg Array of the processed message, author handle and key + * @param object $mesg The private message + * @param array $conversation The conversation record to which this message belongs + * + * @return bool "true" if it was successful + */ + private function receive_conversation_message($importer, $contact, $data, $msg, $mesg, $conversation) { + $guid = notags(unxmlify($data->guid)); + $subject = notags(unxmlify($data->subject)); + $author = notags(unxmlify($data->author)); $reply = 0; @@ -1666,1469 +1125,2102 @@ function diaspora_conversation($importer,$xml,$msg) { $msg_parent_author_signature = notags(unxmlify($mesg->parent_author_signature)); $msg_author_signature = notags(unxmlify($mesg->author_signature)); $msg_text = unxmlify($mesg->text); - $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($mesg->created_at))); - $msg_diaspora_handle = notags(unxmlify($mesg->diaspora_handle)); + $msg_created_at = datetime_convert("UTC", "UTC", notags(unxmlify($mesg->created_at))); + + // "diaspora_handle" is the element name from the old version + // "author" is the element name from the new version + if ($mesg->author) + $msg_author = notags(unxmlify($mesg->author)); + elseif ($mesg->diaspora_handle) + $msg_author = notags(unxmlify($mesg->diaspora_handle)); + else + return false; + $msg_conversation_guid = notags(unxmlify($mesg->conversation_guid)); + if($msg_conversation_guid != $guid) { - logger('diaspora_conversation: message conversation guid does not belong to the current conversation. ' . $xml); - continue; + logger("message conversation guid does not belong to the current conversation."); + return false; } $body = diaspora2bb($msg_text); - $message_id = $msg_diaspora_handle . ':' . $msg_guid; + $message_uri = $msg_author.":".$msg_guid; - $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid; + $author_signed_data = $msg_guid.";".$msg_parent_guid.";".$msg_text.";".unxmlify($mesg->created_at).";".$msg_author.";".$msg_conversation_guid; $author_signature = base64_decode($msg_author_signature); - if(strcasecmp($msg_diaspora_handle,$msg['author']) == 0) { + if(strcasecmp($msg_author,$msg["author"]) == 0) { $person = $contact; - $key = $msg['key']; - } - else { - $person = find_diaspora_person_by_handle($msg_diaspora_handle); + $key = $msg["key"]; + } else { + $person = self::person_by_handle($msg_author); - if(is_array($person) && x($person,'pubkey')) - $key = $person['pubkey']; + if (is_array($person) && x($person, "pubkey")) + $key = $person["pubkey"]; else { - logger('diaspora_conversation: unable to find author details'); - continue; + logger("unable to find author details"); + return false; } } - if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) { - logger('diaspora_conversation: verification failed.'); - continue; + if (!rsa_verify($author_signed_data, $author_signature, $key, "sha256")) { + logger("verification failed."); + return false; } if($msg_parent_author_signature) { - $owner_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($mesg->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid; + $owner_signed_data = $msg_guid.";".$msg_parent_guid.";".$msg_text.";".unxmlify($mesg->created_at).";".$msg_author.";".$msg_conversation_guid; $parent_author_signature = base64_decode($msg_parent_author_signature); - $key = $msg['key']; + $key = $msg["key"]; - if(! rsa_verify($owner_signed_data,$parent_author_signature,$key,'sha256')) { - logger('diaspora_conversation: owner verification failed.'); - continue; + if (!rsa_verify($owner_signed_data, $parent_author_signature, $key, "sha256")) { + logger("owner verification failed."); + return false; } } - $r = q("select id from mail where `uri` = '%s' limit 1", - dbesc($message_id) + $r = q("SELECT `id` FROM `mail` WHERE `uri` = '%s' LIMIT 1", + dbesc($message_uri) ); - if(count($r)) { - logger('diaspora_conversation: duplicate message already delivered.', LOGGER_DEBUG); - continue; + if($r) { + logger("duplicate message already delivered.", LOGGER_DEBUG); + return false; } - q("insert into mail ( `uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) values ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')", - intval($importer['uid']), + q("INSERT INTO `mail` (`uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) + VALUES (%d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')", + intval($importer["uid"]), dbesc($msg_guid), - intval($conversation['id']), - dbesc($person['name']), - dbesc($person['photo']), - dbesc($person['url']), - intval($contact['id']), + intval($conversation["id"]), + dbesc($person["name"]), + dbesc($person["photo"]), + dbesc($person["url"]), + intval($contact["id"]), dbesc($subject), dbesc($body), 0, 0, - dbesc($message_id), - dbesc($parent_uri), + dbesc($message_uri), + dbesc($author.":".$guid), dbesc($msg_created_at) ); - q("update conv set updated = '%s' where id = %d", + q("UPDATE `conv` SET `updated` = '%s' WHERE `id` = %d", dbesc(datetime_convert()), - intval($conversation['id']) + intval($conversation["id"]) ); notification(array( - 'type' => NOTIFY_MAIL, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' =>$importer['uid'], - 'item' => array('subject' => $subject, 'body' => $body), - 'source_name' => $person['name'], - 'source_link' => $person['url'], - 'source_photo' => $person['thumb'], - 'verb' => ACTIVITY_POST, - 'otype' => 'mail' + "type" => NOTIFY_MAIL, + "notify_flags" => $importer["notify-flags"], + "language" => $importer["language"], + "to_name" => $importer["username"], + "to_email" => $importer["email"], + "uid" =>$importer["uid"], + "item" => array("subject" => $subject, "body" => $body), + "source_name" => $person["name"], + "source_link" => $person["url"], + "source_photo" => $person["thumb"], + "verb" => ACTIVITY_POST, + "otype" => "mail" )); + return true; } - return; -} + /** + * @brief Processes new private messages (answers to private messages are processed elsewhere) + * + * @param array $importer Array of the importer user + * @param array $msg Array of the processed message, author handle and key + * @param object $data The message object + * + * @return bool Success + */ + private function receive_conversation($importer, $msg, $data) { + $guid = notags(unxmlify($data->guid)); + $subject = notags(unxmlify($data->subject)); + $created_at = datetime_convert("UTC", "UTC", notags(unxmlify($data->created_at))); + $author = notags(unxmlify($data->author)); + $participants = notags(unxmlify($data->participants)); -function diaspora_message($importer,$xml,$msg) { + $messages = $data->message; - $a = get_app(); - - $msg_guid = notags(unxmlify($xml->guid)); - $msg_parent_guid = notags(unxmlify($xml->parent_guid)); - $msg_parent_author_signature = notags(unxmlify($xml->parent_author_signature)); - $msg_author_signature = notags(unxmlify($xml->author_signature)); - $msg_text = unxmlify($xml->text); - $msg_created_at = datetime_convert('UTC','UTC',notags(unxmlify($xml->created_at))); - $msg_diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - $msg_conversation_guid = notags(unxmlify($xml->conversation_guid)); - - $parent_uri = $msg_diaspora_handle . ':' . $msg_parent_guid; - - $contact = diaspora_get_contact_by_handle($importer['uid'],$msg_diaspora_handle); - if(! $contact) { - logger('diaspora_message: cannot find contact: ' . $msg_diaspora_handle); - return; - } - - if(($contact['rel'] == CONTACT_IS_FOLLOWER) || ($contact['blocked']) || ($contact['readonly'])) { - logger('diaspora_message: Ignoring this author.'); - return 202; - } - - $conversation = null; - - $c = q("select * from conv where uid = %d and guid = '%s' limit 1", - intval($importer['uid']), - dbesc($msg_conversation_guid) - ); - if(count($c)) - $conversation = $c[0]; - else { - logger('diaspora_message: conversation not available.'); - return; - } - - $reply = 0; - - $body = diaspora2bb($msg_text); - $message_id = $msg_diaspora_handle . ':' . $msg_guid; - - $author_signed_data = $msg_guid . ';' . $msg_parent_guid . ';' . $msg_text . ';' . unxmlify($xml->created_at) . ';' . $msg_diaspora_handle . ';' . $msg_conversation_guid; - - - $author_signature = base64_decode($msg_author_signature); - - $person = find_diaspora_person_by_handle($msg_diaspora_handle); - if(is_array($person) && x($person,'pubkey')) - $key = $person['pubkey']; - else { - logger('diaspora_message: unable to find author details'); - return; - } - - if(! rsa_verify($author_signed_data,$author_signature,$key,'sha256')) { - logger('diaspora_message: verification failed.'); - return; - } - - $r = q("select id from mail where `uri` = '%s' and uid = %d limit 1", - dbesc($message_id), - intval($importer['uid']) - ); - if(count($r)) { - logger('diaspora_message: duplicate message already delivered.', LOGGER_DEBUG); - return; - } - - q("insert into mail ( `uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) values ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')", - intval($importer['uid']), - dbesc($msg_guid), - intval($conversation['id']), - dbesc($person['name']), - dbesc($person['photo']), - dbesc($person['url']), - intval($contact['id']), - dbesc($conversation['subject']), - dbesc($body), - 0, - 1, - dbesc($message_id), - dbesc($parent_uri), - dbesc($msg_created_at) - ); - - q("update conv set updated = '%s' where id = %d", - dbesc(datetime_convert()), - intval($conversation['id']) - ); - - return; -} - -function diaspora_participation($importer,$xml) { - logger("Unsupported message type 'participation' ".print_r($xml, true)); -} - -function diaspora_photo($importer,$xml,$msg,$attempt=1) { - - $a = get_app(); - - logger('diaspora_photo: init',LOGGER_DEBUG); - - $remote_photo_path = notags(unxmlify($xml->remote_photo_path)); - - $remote_photo_name = notags(unxmlify($xml->remote_photo_name)); - - $status_message_guid = notags(unxmlify($xml->status_message_guid)); - - $guid = notags(unxmlify($xml->guid)); - - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - - $public = notags(unxmlify($xml->public)); - - $created_at = notags(unxmlify($xml_created_at)); - - logger('diaspora_photo: status_message_guid: ' . $status_message_guid, LOGGER_DEBUG); - - $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']); - if(! $contact) { - logger('diaspora_photo: contact record not found: ' . $msg['author'] . ' handle: ' . $diaspora_handle); - return; - } - - if(! diaspora_post_allow($importer,$contact, false)) { - logger('diaspora_photo: Ignoring this author.'); - return 202; - } - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($status_message_guid) - ); - -/* deactivated by now since it can lead to multiplicated pictures in posts. - if(!count($r)) { - $result = diaspora_store_by_guid($status_message_guid, $contact['url'], $importer['uid']); - - if (!$result) { - $person = find_diaspora_person_by_handle($diaspora_handle); - $result = diaspora_store_by_guid($status_message_guid, $person['url'], $importer['uid']); + if (!count($messages)) { + logger("empty conversation"); + return false; } - if ($result) { - logger("Fetched missing item ".$status_message_guid." - result: ".$result, LOGGER_DEBUG); + $contact = self::allowed_contact_by_handle($importer, $msg["author"], true); + if (!$contact) + return false; - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($status_message_guid) + $conversation = null; + + $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($importer["uid"]), + dbesc($guid) + ); + if($c) + $conversation = $c[0]; + else { + $r = q("INSERT INTO `conv` (`uid`, `guid`, `creator`, `created`, `updated`, `subject`, `recips`) + VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s')", + intval($importer["uid"]), + dbesc($guid), + dbesc($author), + dbesc(datetime_convert("UTC", "UTC", $created_at)), + dbesc(datetime_convert()), + dbesc($subject), + dbesc($participants) + ); + if($r) + $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($importer["uid"]), + dbesc($guid) + ); + + if($c) + $conversation = $c[0]; + } + if (!$conversation) { + logger("unable to create conversation."); + return; + } + + foreach($messages as $mesg) + self::receive_conversation_message($importer, $contact, $data, $msg, $mesg, $conversation); + + return true; + } + + /** + * @brief Creates the body for a "like" message + * + * @param array $contact The contact that send us the "like" + * @param array $parent_item The item array of the parent item + * @param string $guid message guid + * + * @return string the body + */ + private function construct_like_body($contact, $parent_item, $guid) { + $bodyverb = t('%1$s likes %2$s\'s %3$s'); + + $ulink = "[url=".$contact["url"]."]".$contact["name"]."[/url]"; + $alink = "[url=".$parent_item["author-link"]."]".$parent_item["author-name"]."[/url]"; + $plink = "[url=".App::get_baseurl()."/display/".urlencode($guid)."]".t("status")."[/url]"; + + return sprintf($bodyverb, $ulink, $alink, $plink); + } + + /** + * @brief Creates a XML object for a "like" + * + * @param array $importer Array of the importer user + * @param array $parent_item The item array of the parent item + * + * @return string The XML + */ + private function construct_like_object($importer, $parent_item) { + $objtype = ACTIVITY_OBJ_NOTE; + $link = ''; + $parent_body = $parent_item["body"]; + + $xmldata = array("object" => array("type" => $objtype, + "local" => "1", + "id" => $parent_item["uri"], + "link" => $link, + "title" => "", + "content" => $parent_body)); + + return xml::from_array($xmldata, $xml, true); + } + + /** + * @brief Processes "like" messages + * + * @param array $importer Array of the importer user + * @param string $sender The sender of the message + * @param object $data The message object + * + * @return int The message id of the generated like or "false" if there was an error + */ + private function receive_like($importer, $sender, $data) { + $positive = notags(unxmlify($data->positive)); + $guid = notags(unxmlify($data->guid)); + $parent_type = notags(unxmlify($data->parent_type)); + $parent_guid = notags(unxmlify($data->parent_guid)); + $author = notags(unxmlify($data->author)); + + // likes on comments aren't supported by Diaspora - only on posts + // But maybe this will be supported in the future, so we will accept it. + if (!in_array($parent_type, array("Post", "Comment"))) + return false; + + $contact = self::allowed_contact_by_handle($importer, $sender, true); + if (!$contact) + return false; + + $message_id = self::message_exists($importer["uid"], $guid); + if ($message_id) + return $message_id; + + $parent_item = self::parent_item($importer["uid"], $parent_guid, $author, $contact); + if (!$parent_item) + return false; + + $person = self::person_by_handle($author); + if (!is_array($person)) { + logger("unable to find author details"); + return false; + } + + // Fetch the contact id - if we know this contact + $author_contact = self::author_contact_by_url($contact, $person, $importer["uid"]); + + // "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora + // We would accept this anyhow. + if ($positive == "true") + $verb = ACTIVITY_LIKE; + else + $verb = ACTIVITY_DISLIKE; + + $datarray = array(); + + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $author_contact["cid"]; + $datarray["network"] = $author_contact["network"]; + + $datarray["author-name"] = $person["name"]; + $datarray["author-link"] = $person["url"]; + $datarray["author-avatar"] = ((x($person,"thumb")) ? $person["thumb"] : $person["photo"]); + + $datarray["owner-name"] = $contact["name"]; + $datarray["owner-link"] = $contact["url"]; + $datarray["owner-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]); + + $datarray["guid"] = $guid; + $datarray["uri"] = $author.":".$guid; + + $datarray["type"] = "activity"; + $datarray["verb"] = $verb; + $datarray["gravity"] = GRAVITY_LIKE; + $datarray["parent-uri"] = $parent_item["uri"]; + + $datarray["object-type"] = ACTIVITY_OBJ_NOTE; + $datarray["object"] = self::construct_like_object($importer, $parent_item); + + $datarray["body"] = self::construct_like_body($contact, $parent_item, $guid); + + $message_id = item_store($datarray); + + if ($message_id) + logger("Stored like ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); + + // If we are the origin of the parent we store the original data and notify our followers + if($message_id AND $parent_item["origin"]) { + + // Formerly we stored the signed text, the signature and the author in different fields. + // We now store the raw data so that we are more flexible. + q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')", + intval($message_id), + dbesc(json_encode($data)) + ); + + // notify others + proc_run("php", "include/notifier.php", "comment-import", $message_id); + } + + return $message_id; + } + + /** + * @brief Processes private messages + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool Success? + */ + private function receive_message($importer, $data) { + $guid = notags(unxmlify($data->guid)); + $parent_guid = notags(unxmlify($data->parent_guid)); + $text = unxmlify($data->text); + $created_at = datetime_convert("UTC", "UTC", notags(unxmlify($data->created_at))); + $author = notags(unxmlify($data->author)); + $conversation_guid = notags(unxmlify($data->conversation_guid)); + + $contact = self::allowed_contact_by_handle($importer, $author, true); + if (!$contact) + return false; + + $conversation = null; + + $c = q("SELECT * FROM `conv` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", + intval($importer["uid"]), + dbesc($conversation_guid) + ); + if($c) + $conversation = $c[0]; + else { + logger("conversation not available."); + return false; + } + + $reply = 0; + + $body = diaspora2bb($text); + $message_uri = $author.":".$guid; + + $person = self::person_by_handle($author); + if (!$person) { + logger("unable to find author details"); + return false; + } + + $r = q("SELECT `id` FROM `mail` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($message_uri), + intval($importer["uid"]) + ); + if($r) { + logger("duplicate message already delivered.", LOGGER_DEBUG); + return false; + } + + q("INSERT INTO `mail` (`uid`, `guid`, `convid`, `from-name`,`from-photo`,`from-url`,`contact-id`,`title`,`body`,`seen`,`reply`,`uri`,`parent-uri`,`created`) + VALUES ( %d, '%s', %d, '%s', '%s', '%s', %d, '%s', '%s', %d, %d, '%s','%s','%s')", + intval($importer["uid"]), + dbesc($guid), + intval($conversation["id"]), + dbesc($person["name"]), + dbesc($person["photo"]), + dbesc($person["url"]), + intval($contact["id"]), + dbesc($conversation["subject"]), + dbesc($body), + 0, + 1, + dbesc($message_uri), + dbesc($author.":".$parent_guid), + dbesc($created_at) + ); + + q("UPDATE `conv` SET `updated` = '%s' WHERE `id` = %d", + dbesc(datetime_convert()), + intval($conversation["id"]) + ); + + return true; + } + + /** + * @brief Processes participations - unsupported by now + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool always true + */ + private function receive_participation($importer, $data) { + // I'm not sure if we can fully support this message type + return true; + } + + /** + * @brief Processes photos - unneeded + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool always true + */ + private function receive_photo($importer, $data) { + // There doesn't seem to be a reason for this function, since the photo data is transmitted in the status message as well + return true; + } + + /** + * @brief Processes poll participations - unssupported + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool always true + */ + private function receive_poll_participation($importer, $data) { + // We don't support polls by now + return true; + } + + /** + * @brief Processes incoming profile updates + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool Success + */ + private function receive_profile($importer, $data) { + $author = notags(unxmlify($data->author)); + + $contact = self::contact_by_handle($importer["uid"], $author); + if (!$contact) + return false; + + $name = unxmlify($data->first_name).((strlen($data->last_name)) ? " ".unxmlify($data->last_name) : ""); + $image_url = unxmlify($data->image_url); + $birthday = unxmlify($data->birthday); + $location = diaspora2bb(unxmlify($data->location)); + $about = diaspora2bb(unxmlify($data->bio)); + $gender = unxmlify($data->gender); + $searchable = (unxmlify($data->searchable) == "true"); + $nsfw = (unxmlify($data->nsfw) == "true"); + $tags = unxmlify($data->tag_string); + + $tags = explode("#", $tags); + + $keywords = array(); + foreach ($tags as $tag) { + $tag = trim(strtolower($tag)); + if ($tag != "") + $keywords[] = $tag; + } + + $keywords = implode(", ", $keywords); + + $handle_parts = explode("@", $author); + $nick = $handle_parts[0]; + + if($name === "") + $name = $handle_parts[0]; + + if( preg_match("|^https?://|", $image_url) === 0) + $image_url = "http://".$handle_parts[1].$image_url; + + update_contact_avatar($image_url, $importer["uid"], $contact["id"]); + + // Generic birthday. We don't know the timezone. The year is irrelevant. + + $birthday = str_replace("1000", "1901", $birthday); + + if ($birthday != "") + $birthday = datetime_convert("UTC", "UTC", $birthday, "Y-m-d"); + + // this is to prevent multiple birthday notifications in a single year + // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year + + if(substr($birthday,5) === substr($contact["bd"],5)) + $birthday = $contact["bd"]; + + $r = q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `name-date` = '%s', `bd` = '%s', + `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `id` = %d AND `uid` = %d", + dbesc($name), + dbesc($nick), + dbesc($author), + dbesc(datetime_convert()), + dbesc($birthday), + dbesc($location), + dbesc($about), + dbesc($keywords), + dbesc($gender), + intval($contact["id"]), + intval($importer["uid"]) + ); + + if ($searchable) { + poco_check($contact["url"], $name, NETWORK_DIASPORA, $image_url, $about, $location, $gender, $keywords, "", + datetime_convert(), 2, $contact["id"], $importer["uid"]); + } + + $gcontact = array("url" => $contact["url"], "network" => NETWORK_DIASPORA, "generation" => 2, + "photo" => $image_url, "name" => $name, "location" => $location, + "about" => $about, "birthday" => $birthday, "gender" => $gender, + "addr" => $author, "nick" => $nick, "keywords" => $keywords, + "hide" => !$searchable, "nsfw" => $nsfw); + + update_gcontact($gcontact); + + logger("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], LOGGER_DEBUG); + + return true; + } + + /** + * @brief Processes incoming friend requests + * + * @param array $importer Array of the importer user + * @param array $contact The contact that send the request + */ + private function receive_request_make_friend($importer, $contact) { + + $a = get_app(); + + if($contact["rel"] == CONTACT_IS_FOLLOWER && in_array($importer["page-flags"], array(PAGE_FREELOVE))) { + q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d", + intval(CONTACT_IS_FRIEND), + intval($contact["id"]), + intval($importer["uid"]) ); } + // send notification + + $r = q("SELECT `hide-friends` FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", + intval($importer["uid"]) + ); + + if($r && !$r[0]["hide-friends"] && !$contact["hidden"] && intval(get_pconfig($importer["uid"], "system", "post_newfriend"))) { + + $self = q("SELECT * FROM `contact` WHERE `self` AND `uid` = %d LIMIT 1", + intval($importer["uid"]) + ); + + // they are not CONTACT_IS_FOLLOWER anymore but that's what we have in the array + + if($self && $contact["rel"] == CONTACT_IS_FOLLOWER) { + + $arr = array(); + $arr["uri"] = $arr["parent-uri"] = item_new_uri($a->get_hostname(), $importer["uid"]); + $arr["uid"] = $importer["uid"]; + $arr["contact-id"] = $self[0]["id"]; + $arr["wall"] = 1; + $arr["type"] = 'wall'; + $arr["gravity"] = 0; + $arr["origin"] = 1; + $arr["author-name"] = $arr["owner-name"] = $self[0]["name"]; + $arr["author-link"] = $arr["owner-link"] = $self[0]["url"]; + $arr["author-avatar"] = $arr["owner-avatar"] = $self[0]["thumb"]; + $arr["verb"] = ACTIVITY_FRIEND; + $arr["object-type"] = ACTIVITY_OBJ_PERSON; + + $A = "[url=".$self[0]["url"]."]".$self[0]["name"]."[/url]"; + $B = "[url=".$contact["url"]."]".$contact["name"]."[/url]"; + $BPhoto = "[url=".$contact["url"]."][img]".$contact["thumb"]."[/img][/url]"; + $arr["body"] = sprintf(t("%1$s is now friends with %2$s"), $A, $B)."\n\n\n".$Bphoto; + + $arr["object"] = self::construct_new_friend_object($contact); + + $arr["last-child"] = 1; + + $arr["allow_cid"] = $user[0]["allow_cid"]; + $arr["allow_gid"] = $user[0]["allow_gid"]; + $arr["deny_cid"] = $user[0]["deny_cid"]; + $arr["deny_gid"] = $user[0]["deny_gid"]; + + $i = item_store($arr); + if($i) + proc_run("php", "include/notifier.php", "activity", $i); + } + } } + + /** + * @brief Creates a XML object for a "new friend" message + * + * @param array $contact Array of the contact + * + * @return string The XML + */ + private function construct_new_friend_object($contact) { + $objtype = ACTIVITY_OBJ_PERSON; + $link = ''."\n". + ''."\n"; + + $xmldata = array("object" => array("type" => $objtype, + "title" => $contact["name"], + "id" => $contact["url"]."/".$contact["name"], + "link" => $link)); + + return xml::from_array($xmldata, $xml, true); + } + + /** + * @brief Processes incoming sharing notification + * + * @param array $importer Array of the importer user + * @param object $data The message object + * + * @return bool Success + */ + private function receive_contact_request($importer, $data) { + $author = unxmlify($data->author); + $recipient = unxmlify($data->recipient); + + if (!$author || !$recipient) + return false; + + // the current protocol version doesn't know these fields + // That means that we will assume their existance + if (isset($data->following)) + $following = (unxmlify($data->following) == "true"); + else + $following = true; + + if (isset($data->sharing)) + $sharing = (unxmlify($data->sharing) == "true"); + else + $sharing = true; + + $contact = self::contact_by_handle($importer["uid"],$author); + + // perhaps we were already sharing with this person. Now they're sharing with us. + // That makes us friends. + if ($contact) { + if ($following AND $sharing) { + self::receive_request_make_friend($importer, $contact); + return true; + } else /// @todo Handle all possible variations of adding and retracting of permissions + return false; + } + + if (!$following AND $sharing AND in_array($importer["page-flags"], array(PAGE_SOAPBOX, PAGE_NORMAL))) { + logger("Author ".$author." wants to share with us - but doesn't want to listen. Request is ignored.", LOGGER_DEBUG); + return false; + } elseif (!$following AND !$sharing) { + logger("Author ".$author." doesn't want anything - and we don't know the author. Request is ignored.", LOGGER_DEBUG); + return false; + } + + $ret = self::person_by_handle($author); + + if (!$ret || ($ret["network"] != NETWORK_DIASPORA)) { + logger("Cannot resolve diaspora handle ".$author." for ".$recipient); + return false; + } + + $batch = (($ret["batch"]) ? $ret["batch"] : implode("/", array_slice(explode("/", $ret["url"]), 0, 3))."/receive/public"); + + $r = q("INSERT INTO `contact` (`uid`, `network`,`addr`,`created`,`url`,`nurl`,`batch`,`name`,`nick`,`photo`,`pubkey`,`notify`,`poll`,`blocked`,`priority`) + VALUES (%d, '%s', '%s', '%s', '%s','%s','%s','%s','%s','%s','%s','%s','%s',%d,%d)", + intval($importer["uid"]), + dbesc($ret["network"]), + dbesc($ret["addr"]), + datetime_convert(), + dbesc($ret["url"]), + dbesc(normalise_link($ret["url"])), + dbesc($batch), + dbesc($ret["name"]), + dbesc($ret["nick"]), + dbesc($ret["photo"]), + dbesc($ret["pubkey"]), + dbesc($ret["notify"]), + dbesc($ret["poll"]), + 1, + 2 + ); + + // find the contact record we just created + + $contact_record = self::contact_by_handle($importer["uid"],$author); + + if (!$contact_record) { + logger("unable to locate newly created contact record."); + return; + } + + $g = q("SELECT `def_gid` FROM `user` WHERE `uid` = %d LIMIT 1", + intval($importer["uid"]) + ); + + if($g && intval($g[0]["def_gid"])) + group_add_member($importer["uid"], "", $contact_record["id"], $g[0]["def_gid"]); + + if($importer["page-flags"] == PAGE_NORMAL) { + + $hash = random_string().(string)time(); // Generate a confirm_key + + $ret = q("INSERT INTO `intro` (`uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`) + VALUES (%d, %d, %d, %d, '%s', '%s', '%s')", + intval($importer["uid"]), + intval($contact_record["id"]), + 0, + 0, + dbesc(t("Sharing notification from Diaspora network")), + dbesc($hash), + dbesc(datetime_convert()) + ); + } else { + + // automatic friend approval + + update_contact_avatar($contact_record["photo"],$importer["uid"],$contact_record["id"]); + + // technically they are sharing with us (CONTACT_IS_SHARING), + // but if our page-type is PAGE_COMMUNITY or PAGE_SOAPBOX + // we are going to change the relationship and make them a follower. + + if (($importer["page-flags"] == PAGE_FREELOVE) AND $sharing AND $following) + $new_relation = CONTACT_IS_FRIEND; + elseif (($importer["page-flags"] == PAGE_FREELOVE) AND $sharing) + $new_relation = CONTACT_IS_SHARING; + else + $new_relation = CONTACT_IS_FOLLOWER; + + $r = q("UPDATE `contact` SET `rel` = %d, + `name-date` = '%s', + `uri-date` = '%s', + `blocked` = 0, + `pending` = 0, + `writable` = 1 + WHERE `id` = %d + ", + intval($new_relation), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($contact_record["id"]) + ); + + $u = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($importer["uid"])); + if($u) + $ret = self::send_share($u[0], $contact_record); + } + + return true; + } + + /** + * @brief Fetches a message with a given guid + * + * @param string $guid message guid + * @param string $orig_author handle of the original post + * @param string $author handle of the sharer + * + * @return array The fetched item + */ + private function original_item($guid, $orig_author, $author) { + + // Do we already have this item? + $r = q("SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, + `author-name`, `author-link`, `author-avatar` + FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", + dbesc($guid)); + + if($r) { + logger("reshared message ".$guid." already exists on system."); + + // Maybe it is already a reshared item? + // Then refetch the content, if it is a reshare from a reshare. + // If it is a reshared post from another network then reformat to avoid display problems with two share elements + if (self::is_reshare($r[0]["body"], true)) + $r = array(); + elseif (self::is_reshare($r[0]["body"], false)) { + $r[0]["body"] = diaspora2bb(bb2diaspora($r[0]["body"])); + + // Add OEmbed and other information to the body + $r[0]["body"] = add_page_info_to_body($r[0]["body"], false, true); + + return $r[0]; + } else + return $r[0]; + } + + if (!$r) { + $server = "https://".substr($orig_author, strpos($orig_author, "@") + 1); + logger("1st try: reshared message ".$guid." will be fetched from original server: ".$server); + $item_id = self::store_by_guid($guid, $server); + + if (!$item_id) { + $server = "http://".substr($orig_author, strpos($orig_author, "@") + 1); + logger("2nd try: reshared message ".$guid." will be fetched from original server: ".$server); + $item_id = self::store_by_guid($guid, $server); + } + + // Deactivated by now since there is a risk that someone could manipulate postings through this method +/* if (!$item_id) { + $server = "https://".substr($author, strpos($author, "@") + 1); + logger("3rd try: reshared message ".$guid." will be fetched from sharer's server: ".$server); + $item_id = self::store_by_guid($guid, $server); + } + if (!$item_id) { + $server = "http://".substr($author, strpos($author, "@") + 1); + logger("4th try: reshared message ".$guid." will be fetched from sharer's server: ".$server); + $item_id = self::store_by_guid($guid, $server); + } */ - if(!count($r)) { - if($attempt <= 3) { - q("INSERT INTO dsprphotoq (uid, msg, attempt) VALUES (%d, '%s', %d)", - intval($importer['uid']), - dbesc(serialize($msg)), - intval($attempt + 1) - ); - } + if ($item_id) { + $r = q("SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, + `author-name`, `author-link`, `author-avatar` + FROM `item` WHERE `id` = %d AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", + intval($item_id)); - logger('diaspora_photo: attempt = ' . $attempt . '; status message not found: ' . $status_message_guid . ' for photo: ' . $guid); - return; - } + if ($r) + return $r[0]; - $parent_item = $r[0]; - - $link_text = '[img]' . $remote_photo_path . $remote_photo_name . '[/img]' . "\n"; - - $link_text = scale_external_images($link_text, true, - array($remote_photo_name, 'scaled_full_' . $remote_photo_name)); - - if(strpos($parent_item['body'],$link_text) === false) { - - $parent_item['body'] = $link_text . $parent_item['body']; - - $r = q("UPDATE `item` SET `body` = '%s', `visible` = 1 WHERE `id` = %d AND `uid` = %d", - dbesc($parent_item['body']), - intval($parent_item['id']), - intval($parent_item['uid']) - ); - put_item_in_cache($parent_item, true); - update_thread($parent_item['id']); - } - - return; -} - - - - -function diaspora_like($importer,$xml,$msg) { - - $a = get_app(); - $guid = notags(unxmlify($xml->guid)); - $parent_guid = notags(unxmlify($xml->parent_guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - $target_type = notags(unxmlify($xml->target_type)); - $positive = notags(unxmlify($xml->positive)); - $author_signature = notags(unxmlify($xml->author_signature)); - - $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : ''); - - // likes on comments not supported here and likes on photos not supported by Diaspora - -// if($target_type !== 'Post') -// return; - - $contact = diaspora_get_contact_by_handle($importer['uid'],$msg['author']); - if(! $contact) { - logger('diaspora_like: cannot find contact: ' . $msg['author']); - return; - } - - if(! diaspora_post_allow($importer,$contact, false)) { - logger('diaspora_like: Ignoring this author.'); - return 202; - } - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($parent_guid) - ); - - if(!count($r)) { - $result = diaspora_store_by_guid($parent_guid, $contact['url'], $importer['uid']); - - if (!$result) { - $person = find_diaspora_person_by_handle($diaspora_handle); - $result = diaspora_store_by_guid($parent_guid, $person['url'], $importer['uid']); - } - - if ($result) { - logger("Fetched missing item ".$parent_guid." - result: ".$result, LOGGER_DEBUG); - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($parent_guid) - ); - } - } - - if(! count($r)) { - logger('diaspora_like: parent item not found: ' . $guid); - return; - } - - $parent_item = $r[0]; - - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($importer['uid']), - dbesc($guid) - ); - if(count($r)) { - if($positive === 'true') { - logger('diaspora_like: duplicate like: ' . $guid); - return; - } - // Note: I don't think "Like" objects with positive = "false" are ever actually used - // It looks like "RelayableRetractions" are used for "unlike" instead - if($positive === 'false') { - logger('diaspora_like: received a like with positive set to "false"...ignoring'); -/* q("UPDATE `item` SET `deleted` = 1 WHERE `id` = %d AND `uid` = %d", - intval($r[0]['id']), - intval($importer['uid']) - );*/ - // FIXME--actually don't unless it turns out that Diaspora does indeed send out "false" likes - // send notification via proc_run() - return; - } - } - // Note: I don't think "Like" objects with positive = "false" are ever actually used - // It looks like "RelayableRetractions" are used for "unlike" instead - if($positive === 'false') { - logger('diaspora_like: received a like with positive set to "false"'); - logger('diaspora_like: unlike received with no corresponding like...ignoring'); - return; - } - - - /* How Diaspora performs "like" signature checking: - - - If an item has been sent by the like author to the top-level post owner to relay on - to the rest of the contacts on the top-level post, the top-level post owner should check - the author_signature, then create a parent_author_signature before relaying the like on - - If an item has been relayed on by the top-level post owner, the contacts who receive it - check only the parent_author_signature. Basically, they trust that the top-level post - owner has already verified the authenticity of anything he/she sends out - - In either case, the signature that get checked is the signature created by the person - who sent the salmon - */ - - // Diaspora has changed the way they are signing the likes. - // Just to make sure that we don't miss any likes we will check the old and the current way. - $old_signed_data = $guid . ';' . $target_type . ';' . $parent_guid . ';' . $positive . ';' . $diaspora_handle; - - $signed_data = $positive . ';' . $guid . ';' . $target_type . ';' . $parent_guid . ';' . $diaspora_handle; - - $key = $msg['key']; - - if ($parent_author_signature) { - // If a parent_author_signature exists, then we've received the like - // relayed from the top-level post owner. There's no need to check the - // author_signature if the parent_author_signature is valid - - $parent_author_signature = base64_decode($parent_author_signature); - - if (!rsa_verify($signed_data,$parent_author_signature,$key,'sha256') AND - !rsa_verify($old_signed_data,$parent_author_signature,$key,'sha256')) { - - logger('diaspora_like: top-level owner verification failed.'); - return; - } - } else { - // If there's no parent_author_signature, then we've received the like - // from the like creator. In that case, the person is "like"ing - // our post, so he/she must be a contact of ours and his/her public key - // should be in $msg['key'] - - $author_signature = base64_decode($author_signature); - - if (!rsa_verify($signed_data,$author_signature,$key,'sha256') AND - !rsa_verify($old_signed_data,$author_signature,$key,'sha256')) { - - logger('diaspora_like: like creator verification failed.'); - return; - } - } - - // Phew! Everything checks out. Now create an item. - - // Find the original comment author information. - // We need this to make sure we display the comment author - // information (name and avatar) correctly. - if(strcasecmp($diaspora_handle,$msg['author']) == 0) - $person = $contact; - else { - $person = find_diaspora_person_by_handle($diaspora_handle); - - if(! is_array($person)) { - logger('diaspora_like: unable to find author details'); - return; - } - } - - $uri = $diaspora_handle . ':' . $guid; - - $activity = ACTIVITY_LIKE; - $post_type = (($parent_item['resource-id']) ? t('photo') : t('status')); - $objtype = (($parent_item['resource-id']) ? ACTIVITY_OBJ_PHOTO : ACTIVITY_OBJ_NOTE ); - $link = xmlify('' . "\n") ; - $body = $parent_item['body']; - - $obj = <<< EOT - - - $objtype - 1 - {$parent_item['uri']} - $link - - $body - -EOT; - $bodyverb = t('%1$s likes %2$s\'s %3$s'); - - // Fetch the contact id - if we know this contact - $r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1", - dbesc(normalise_link($person['url'])), intval($importer['uid'])); - if ($r) { - $cid = $r[0]['id']; - $network = $r[0]['network']; - } else { - $cid = $contact['id']; - $network = NETWORK_DIASPORA; - } - - $arr = array(); - - $arr['uri'] = $uri; - $arr['uid'] = $importer['uid']; - $arr['guid'] = $guid; - $arr['network'] = $network; - $arr['contact-id'] = $cid; - $arr['type'] = 'activity'; - $arr['wall'] = $parent_item['wall']; - $arr['gravity'] = GRAVITY_LIKE; - $arr['parent'] = $parent_item['id']; - $arr['parent-uri'] = $parent_item['uri']; - - $arr['owner-name'] = $parent_item['name']; - $arr['owner-link'] = $parent_item['url']; - //$arr['owner-avatar'] = $parent_item['thumb']; - $arr['owner-avatar'] = ((x($parent_item,'thumb')) ? $parent_item['thumb'] : $parent_item['photo']); - - $arr['author-name'] = $person['name']; - $arr['author-link'] = $person['url']; - $arr['author-avatar'] = ((x($person,'thumb')) ? $person['thumb'] : $person['photo']); - - $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; - $alink = '[url=' . $parent_item['author-link'] . ']' . $parent_item['author-name'] . '[/url]'; - //$plink = '[url=' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . ']' . $post_type . '[/url]'; - $plink = '[url='.$a->get_baseurl().'/display/'.urlencode($guid).']'.$post_type.'[/url]'; - $arr['body'] = sprintf( $bodyverb, $ulink, $alink, $plink ); - - $arr['app'] = 'Diaspora'; - - $arr['private'] = $parent_item['private']; - $arr['verb'] = $activity; - $arr['object-type'] = $objtype; - $arr['object'] = $obj; - $arr['visible'] = 1; - $arr['unseen'] = 1; - $arr['last-child'] = 0; - - $message_id = item_store($arr); - - - //if($message_id) { - // q("update item set plink = '%s' where id = %d", - // //dbesc($a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $message_id), - // dbesc($a->get_baseurl().'/display/'.$guid), - // intval($message_id) - // ); - //} - - // If we are the origin of the parent we store the original signature and notify our followers - if($parent_item['origin']) { - $author_signature_base64 = base64_encode($author_signature); - $author_signature_base64 = diaspora_repair_signature($author_signature_base64, $diaspora_handle); - - q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($message_id), - dbesc($signed_data), - dbesc($author_signature_base64), - dbesc($diaspora_handle) - ); - - // notify others - proc_run('php','include/notifier.php','comment-import',$message_id); - } - - return; -} - -function diaspora_retraction($importer,$xml) { - - - $guid = notags(unxmlify($xml->guid)); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - $type = notags(unxmlify($xml->type)); - - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) - return; - - if($type === 'Person') { - require_once('include/Contact.php'); - contact_remove($contact['id']); - } elseif($type === 'StatusMessage') { - $guid = notags(unxmlify($xml->post_guid)); - - $r = q("SELECT * FROM `item` WHERE `guid` = '%s' AND `uid` = %d AND NOT `file` LIKE '%%[%%' LIMIT 1", - dbesc($guid), - intval($importer['uid']) - ); - if(count($r)) { - if(link_compare($r[0]['author-link'],$contact['url'])) { - q("UPDATE `item` SET `deleted` = 1, `changed` = '%s' WHERE `id` = %d", - dbesc(datetime_convert()), - intval($r[0]['id']) - ); - delete_thread($r[0]['id'], $r[0]['parent-uri']); } } - } elseif($type === 'Post') { - $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1", - dbesc('guid'), - intval($importer['uid']) + return false; + } + + /** + * @brief Processes a reshare message + * + * @param array $importer Array of the importer user + * @param object $data The message object + * @param string $xml The original XML of the message + * + * @return int the message id + */ + private function receive_reshare($importer, $data, $xml) { + $root_author = notags(unxmlify($data->root_author)); + $root_guid = notags(unxmlify($data->root_guid)); + $guid = notags(unxmlify($data->guid)); + $author = notags(unxmlify($data->author)); + $public = notags(unxmlify($data->public)); + $created_at = notags(unxmlify($data->created_at)); + + $contact = self::allowed_contact_by_handle($importer, $author, false); + if (!$contact) + return false; + + $message_id = self::message_exists($importer["uid"], $guid); + if ($message_id) + return $message_id; + + $original_item = self::original_item($root_guid, $root_author, $author); + if (!$original_item) + return false; + + $orig_url = App::get_baseurl()."/display/".$original_item["guid"]; + + $datarray = array(); + + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $contact["id"]; + $datarray["network"] = NETWORK_DIASPORA; + + $datarray["author-name"] = $contact["name"]; + $datarray["author-link"] = $contact["url"]; + $datarray["author-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]); + + $datarray["owner-name"] = $datarray["author-name"]; + $datarray["owner-link"] = $datarray["author-link"]; + $datarray["owner-avatar"] = $datarray["author-avatar"]; + + $datarray["guid"] = $guid; + $datarray["uri"] = $datarray["parent-uri"] = $author.":".$guid; + + $datarray["verb"] = ACTIVITY_POST; + $datarray["gravity"] = GRAVITY_PARENT; + + $datarray["object"] = $xml; + + $prefix = share_header($original_item["author-name"], $original_item["author-link"], $original_item["author-avatar"], + $original_item["guid"], $original_item["created"], $orig_url); + $datarray["body"] = $prefix.$original_item["body"]."[/share]"; + + $datarray["tag"] = $original_item["tag"]; + $datarray["app"] = $original_item["app"]; + + $datarray["plink"] = self::plink($author, $guid); + $datarray["private"] = (($public == "false") ? 1 : 0); + $datarray["changed"] = $datarray["created"] = $datarray["edited"] = datetime_convert("UTC", "UTC", $created_at); + + $datarray["object-type"] = $original_item["object-type"]; + + self::fetch_guid($datarray); + $message_id = item_store($datarray); + + if ($message_id) + logger("Stored reshare ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); + + return $message_id; + } + + /** + * @brief Processes retractions + * + * @param array $importer Array of the importer user + * @param array $contact The contact of the item owner + * @param object $data The message object + * + * @return bool success + */ + private function item_retraction($importer, $contact, $data) { + $target_type = notags(unxmlify($data->target_type)); + $target_guid = notags(unxmlify($data->target_guid)); + $author = notags(unxmlify($data->author)); + + $person = self::person_by_handle($author); + if (!is_array($person)) { + logger("unable to find author detail for ".$author); + return false; + } + + $r = q("SELECT `id`, `parent`, `parent-uri`, `author-link` FROM `item` WHERE `guid` = '%s' AND `uid` = %d AND NOT `file` LIKE '%%[%%' LIMIT 1", + dbesc($target_guid), + intval($importer["uid"]) ); - if(count($r)) { - if(link_compare($r[0]['author-link'],$contact['url'])) { - q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d", - dbesc(datetime_convert()), - intval($r[0]['id']) - ); - delete_thread($r[0]['id'], $r[0]['parent-uri']); + if (!$r) + return false; + + // Only delete it if the author really fits + if (!link_compare($r[0]["author-link"], $person["url"])) { + logger("Item author ".$r[0]["author-link"]." doesn't fit to expected contact ".$person["url"], LOGGER_DEBUG); + return false; + } + + // Check if the sender is the thread owner + $p = q("SELECT `id`, `author-link`, `origin` FROM `item` WHERE `id` = %d", + intval($r[0]["parent"])); + + // Only delete it if the parent author really fits + if (!link_compare($p[0]["author-link"], $contact["url"]) AND !link_compare($r[0]["author-link"], $contact["url"])) { + logger("Thread author ".$p[0]["author-link"]." and item author ".$r[0]["author-link"]." don't fit to expected contact ".$contact["url"], LOGGER_DEBUG); + return false; + } + + // Currently we don't have a central deletion function that we could use in this case. The function "item_drop" doesn't work for that case + q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' WHERE `id` = %d", + dbesc(datetime_convert()), + dbesc(datetime_convert()), + intval($r[0]["id"]) + ); + delete_thread($r[0]["id"], $r[0]["parent-uri"]); + + logger("Deleted target ".$target_guid." (".$r[0]["id"].") from user ".$importer["uid"]." parent: ".$p[0]["id"], LOGGER_DEBUG); + + // Now check if the retraction needs to be relayed by us + if($p[0]["origin"]) { + // notify others + proc_run("php", "include/notifier.php", "drop", $r[0]["id"]); + } + + return true; + } + + /** + * @brief Receives retraction messages + * + * @param array $importer Array of the importer user + * @param string $sender The sender of the message + * @param object $data The message object + * + * @return bool Success + */ + private function receive_retraction($importer, $sender, $data) { + $target_type = notags(unxmlify($data->target_type)); + + $contact = self::contact_by_handle($importer["uid"], $sender); + if (!$contact) { + logger("cannot find contact for sender: ".$sender." and user ".$importer["uid"]); + return false; + } + + logger("Got retraction for ".$target_type.", sender ".$sender." and user ".$importer["uid"], LOGGER_DEBUG); + + switch ($target_type) { + case "Comment": + case "Like": + case "Post": // "Post" will be supported in a future version + case "Reshare": + case "StatusMessage": + return self::item_retraction($importer, $contact, $data);; + + case "Person": + /// @todo What should we do with an "unshare"? + // Removing the contact isn't correct since we still can read the public items + //contact_remove($contact["id"]); + return true; + + default: + logger("Unknown target type ".$target_type); + return false; + } + return true; + } + + /** + * @brief Receives status messages + * + * @param array $importer Array of the importer user + * @param object $data The message object + * @param string $xml The original XML of the message + * + * @return int The message id of the newly created item + */ + private function receive_status_message($importer, $data, $xml) { + + $raw_message = unxmlify($data->raw_message); + $guid = notags(unxmlify($data->guid)); + $author = notags(unxmlify($data->author)); + $public = notags(unxmlify($data->public)); + $created_at = notags(unxmlify($data->created_at)); + $provider_display_name = notags(unxmlify($data->provider_display_name)); + + /// @todo enable support for polls + //if ($data->poll) { + // foreach ($data->poll AS $poll) + // print_r($poll); + // die("poll!\n"); + //} + $contact = self::allowed_contact_by_handle($importer, $author, false); + if (!$contact) + return false; + + $message_id = self::message_exists($importer["uid"], $guid); + if ($message_id) + return $message_id; + + $address = array(); + if ($data->location) + foreach ($data->location->children() AS $fieldname => $data) + $address[$fieldname] = notags(unxmlify($data)); + + $body = diaspora2bb($raw_message); + + $datarray = array(); + + // Attach embedded pictures to the body + if ($data->photo) { + foreach ($data->photo AS $photo) + $body = "[img]".unxmlify($photo->remote_photo_path). + unxmlify($photo->remote_photo_name)."[/img]\n".$body; + + $datarray["object-type"] = ACTIVITY_OBJ_PHOTO; + } else { + $datarray["object-type"] = ACTIVITY_OBJ_NOTE; + + // Add OEmbed and other information to the body + if (!self::is_redmatrix($contact["url"])) + $body = add_page_info_to_body($body, false, true); + } + + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $contact["id"]; + $datarray["network"] = NETWORK_DIASPORA; + + $datarray["author-name"] = $contact["name"]; + $datarray["author-link"] = $contact["url"]; + $datarray["author-avatar"] = ((x($contact,"thumb")) ? $contact["thumb"] : $contact["photo"]); + + $datarray["owner-name"] = $datarray["author-name"]; + $datarray["owner-link"] = $datarray["author-link"]; + $datarray["owner-avatar"] = $datarray["author-avatar"]; + + $datarray["guid"] = $guid; + $datarray["uri"] = $datarray["parent-uri"] = $author.":".$guid; + + $datarray["verb"] = ACTIVITY_POST; + $datarray["gravity"] = GRAVITY_PARENT; + + $datarray["object"] = $xml; + + $datarray["body"] = $body; + + if ($provider_display_name != "") + $datarray["app"] = $provider_display_name; + + $datarray["plink"] = self::plink($author, $guid); + $datarray["private"] = (($public == "false") ? 1 : 0); + $datarray["changed"] = $datarray["created"] = $datarray["edited"] = datetime_convert("UTC", "UTC", $created_at); + + if (isset($address["address"])) + $datarray["location"] = $address["address"]; + + if (isset($address["lat"]) AND isset($address["lng"])) + $datarray["coord"] = $address["lat"]." ".$address["lng"]; + + self::fetch_guid($datarray); + $message_id = item_store($datarray); + + if ($message_id) + logger("Stored item ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); + + return $message_id; + } + + /* ************************************************************************************** * + * Here are all the functions that are needed to transmit data with the Diaspora protocol * + * ************************************************************************************** */ + + /** + * @brief returnes the handle of a contact + * + * @param array $me contact array + * + * @return string the handle in the format user@domain.tld + */ + private function my_handle($contact) { + if ($contact["addr"] != "") + return $contact["addr"]; + + // Normally we should have a filled "addr" field - but in the past this wasn't the case + // So - just in case - we build the the address here. + if ($contact["nickname"] != "") + $nick = $contact["nickname"]; + else + $nick = $contact["nick"]; + + return $nick."@".substr(App::get_baseurl(), strpos(App::get_baseurl(),"://") + 3); + } + + /** + * @brief Creates the envelope for a public message + * + * @param string $msg The message that is to be transmitted + * @param array $user The record of the sender + * @param array $contact Target of the communication + * @param string $prvkey The private key of the sender + * @param string $pubkey The public key of the receiver + * + * @return string The envelope + */ + private function build_public_message($msg, $user, $contact, $prvkey, $pubkey) { + + logger("Message: ".$msg, LOGGER_DATA); + + $handle = self::my_handle($user); + + $b64url_data = base64url_encode($msg); + + $data = str_replace(array("\n", "\r", " ", "\t"), array("", "", "", ""), $b64url_data); + + $type = "application/xml"; + $encoding = "base64url"; + $alg = "RSA-SHA256"; + + $signable_data = $data.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg); + + $signature = rsa_sign($signable_data,$prvkey); + $sig = base64url_encode($signature); + + $xmldata = array("diaspora" => array("header" => array("author_id" => $handle), + "me:env" => array("me:encoding" => "base64url", + "me:alg" => "RSA-SHA256", + "me:data" => $data, + "@attributes" => array("type" => "application/xml"), + "me:sig" => $sig))); + + $namespaces = array("" => "https://joindiaspora.com/protocol", + "me" => "http://salmon-protocol.org/ns/magic-env"); + + $magic_env = xml::from_array($xmldata, $xml, false, $namespaces); + + logger("magic_env: ".$magic_env, LOGGER_DATA); + return $magic_env; + } + + /** + * @brief Creates the envelope for a private message + * + * @param string $msg The message that is to be transmitted + * @param array $user The record of the sender + * @param array $contact Target of the communication + * @param string $prvkey The private key of the sender + * @param string $pubkey The public key of the receiver + * + * @return string The envelope + */ + private function build_private_message($msg, $user, $contact, $prvkey, $pubkey) { + + logger("Message: ".$msg, LOGGER_DATA); + + // without a public key nothing will work + + if (!$pubkey) { + logger("pubkey missing: contact id: ".$contact["id"]); + return false; + } + + $inner_aes_key = random_string(32); + $b_inner_aes_key = base64_encode($inner_aes_key); + $inner_iv = random_string(16); + $b_inner_iv = base64_encode($inner_iv); + + $outer_aes_key = random_string(32); + $b_outer_aes_key = base64_encode($outer_aes_key); + $outer_iv = random_string(16); + $b_outer_iv = base64_encode($outer_iv); + + $handle = self::my_handle($user); + + $padded_data = pkcs5_pad($msg,16); + $inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv); + + $b64_data = base64_encode($inner_encrypted); + + + $b64url_data = base64url_encode($b64_data); + $data = str_replace(array("\n", "\r", " ", "\t"), array("", "", "", ""), $b64url_data); + + $type = "application/xml"; + $encoding = "base64url"; + $alg = "RSA-SHA256"; + + $signable_data = $data.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg); + + $signature = rsa_sign($signable_data,$prvkey); + $sig = base64url_encode($signature); + + $xmldata = array("decrypted_header" => array("iv" => $b_inner_iv, + "aes_key" => $b_inner_aes_key, + "author_id" => $handle)); + + $decrypted_header = xml::from_array($xmldata, $xml, true); + $decrypted_header = pkcs5_pad($decrypted_header,16); + + $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv); + + $outer_json = json_encode(array("iv" => $b_outer_iv, "key" => $b_outer_aes_key)); + + $encrypted_outer_key_bundle = ""; + openssl_public_encrypt($outer_json, $encrypted_outer_key_bundle, $pubkey); + + $b64_encrypted_outer_key_bundle = base64_encode($encrypted_outer_key_bundle); + + logger("outer_bundle: ".$b64_encrypted_outer_key_bundle." key: ".$pubkey, LOGGER_DATA); + + $encrypted_header_json_object = json_encode(array("aes_key" => base64_encode($encrypted_outer_key_bundle), + "ciphertext" => base64_encode($ciphertext))); + $cipher_json = base64_encode($encrypted_header_json_object); + + $xmldata = array("diaspora" => array("encrypted_header" => $cipher_json, + "me:env" => array("me:encoding" => "base64url", + "me:alg" => "RSA-SHA256", + "me:data" => $data, + "@attributes" => array("type" => "application/xml"), + "me:sig" => $sig))); + + $namespaces = array("" => "https://joindiaspora.com/protocol", + "me" => "http://salmon-protocol.org/ns/magic-env"); + + $magic_env = xml::from_array($xmldata, $xml, false, $namespaces); + + logger("magic_env: ".$magic_env, LOGGER_DATA); + return $magic_env; + } + + /** + * @brief Create the envelope for a message + * + * @param string $msg The message that is to be transmitted + * @param array $user The record of the sender + * @param array $contact Target of the communication + * @param string $prvkey The private key of the sender + * @param string $pubkey The public key of the receiver + * @param bool $public Is the message public? + * + * @return string The message that will be transmitted to other servers + */ + private function build_message($msg, $user, $contact, $prvkey, $pubkey, $public = false) { + + if ($public) + $magic_env = self::build_public_message($msg,$user,$contact,$prvkey,$pubkey); + else + $magic_env = self::build_private_message($msg,$user,$contact,$prvkey,$pubkey); + + // The data that will be transmitted is double encoded via "urlencode", strange ... + $slap = "xml=".urlencode(urlencode($magic_env)); + return $slap; + } + + /** + * @brief Creates a signature for a message + * + * @param array $owner the array of the owner of the message + * @param array $message The message that is to be signed + * + * @return string The signature + */ + private function signature($owner, $message) { + $sigmsg = $message; + unset($sigmsg["author_signature"]); + unset($sigmsg["parent_author_signature"]); + + $signed_text = implode(";", $sigmsg); + + return base64_encode(rsa_sign($signed_text, $owner["uprvkey"], "sha256")); + } + + /** + * @brief Transmit a message to a target server + * + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param string $slap The message that is to be transmitted + * @param bool $public_batch Is it a public post? + * @param bool $queue_run Is the transmission called from the queue? + * @param string $guid message guid + * + * @return int Result of the transmission + */ + public static function transmit($owner, $contact, $slap, $public_batch, $queue_run=false, $guid = "") { + + $a = get_app(); + + $enabled = intval(get_config("system", "diaspora_enabled")); + if(!$enabled) + return 200; + + $logid = random_string(4); + $dest_url = (($public_batch) ? $contact["batch"] : $contact["notify"]); + if (!$dest_url) { + logger("no url for contact: ".$contact["id"]." batch mode =".$public_batch); + return 0; + } + + logger("transmit: ".$logid."-".$guid." ".$dest_url); + + if (!$queue_run && was_recently_delayed($contact["id"])) { + $return_code = 0; + } else { + if (!intval(get_config("system", "diaspora_test"))) { + post_url($dest_url."/", $slap); + $return_code = $a->get_curl_code(); + } else { + logger("test_mode"); + return 200; } } - } - return 202; - // NOTREACHED -} + logger("transmit: ".$logid."-".$guid." returns: ".$return_code); -function diaspora_signed_retraction($importer,$xml,$msg) { + if(!$return_code || (($return_code == 503) && (stristr($a->get_curl_headers(), "retry-after")))) { + logger("queue message"); - - $guid = notags(unxmlify($xml->target_guid)); - $diaspora_handle = notags(unxmlify($xml->sender_handle)); - $type = notags(unxmlify($xml->target_type)); - $sig = notags(unxmlify($xml->target_author_signature)); - - $parent_author_signature = (($xml->parent_author_signature) ? notags(unxmlify($xml->parent_author_signature)) : ''); - - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) { - logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['uid']); - return; - } - - - $signed_data = $guid . ';' . $type ; - $key = $msg['key']; - - /* How Diaspora performs relayable_retraction signature checking: - - - If an item has been sent by the item author to the top-level post owner to relay on - to the rest of the contacts on the top-level post, the top-level post owner checks - the author_signature, then creates a parent_author_signature before relaying the item on - - If an item has been relayed on by the top-level post owner, the contacts who receive it - check only the parent_author_signature. Basically, they trust that the top-level post - owner has already verified the authenticity of anything he/she sends out - - In either case, the signature that get checked is the signature created by the person - who sent the salmon - */ - - if($parent_author_signature) { - - $parent_author_signature = base64_decode($parent_author_signature); - - if(! rsa_verify($signed_data,$parent_author_signature,$key,'sha256')) { - logger('diaspora_signed_retraction: top-level post owner verification failed'); - return; + $r = q("SELECT `id` FROM `queue` WHERE `cid` = %d AND `network` = '%s' AND `content` = '%s' AND `batch` = %d LIMIT 1", + intval($contact["id"]), + dbesc(NETWORK_DIASPORA), + dbesc($slap), + intval($public_batch) + ); + if($r) { + logger("add_to_queue ignored - identical item already in queue"); + } else { + // queue message for redelivery + add_to_queue($contact["id"], NETWORK_DIASPORA, $slap, $public_batch); + } } - } else { - - $sig_decode = base64_decode($sig); - - if(! rsa_verify($signed_data,$sig_decode,$key,'sha256')) { - logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg,true)); - return; - } + return(($return_code) ? $return_code : (-1)); } - if($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') { - $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1", - dbesc($guid), - intval($importer['uid']) + + /** + * @brief Builds and transmit messages + * + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param string $type The message type + * @param array $message The message data + * @param bool $public_batch Is it a public post? + * @param string $guid message guid + * @param bool $spool Should the transmission be spooled or transmitted? + * + * @return int Result of the transmission + */ + private function build_and_transmit($owner, $contact, $type, $message, $public_batch = false, $guid = "", $spool = false) { + + $data = array("XML" => array("post" => array($type => $message))); + + $msg = xml::from_array($data, $xml); + + logger('message: '.$msg, LOGGER_DATA); + logger('send guid '.$guid, LOGGER_DEBUG); + + $slap = self::build_message($msg, $owner, $contact, $owner['uprvkey'], $contact['pubkey'], $public_batch); + + if ($spool) { + add_to_queue($contact['id'], NETWORK_DIASPORA, $slap, $public_batch); + return true; + } else + $return_code = self::transmit($owner, $contact, $slap, $public_batch, false, $guid); + + logger("guid: ".$item["guid"]." result ".$return_code, LOGGER_DEBUG); + + return $return_code; + } + + /** + * @brief Sends a "share" message + * + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * + * @return int The result of the transmission + */ + public static function send_share($owner,$contact) { + + $message = array("sender_handle" => self::my_handle($owner), + "recipient_handle" => $contact["addr"]); + + return self::build_and_transmit($owner, $contact, "request", $message); + } + + /** + * @brief sends an "unshare" + * + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * + * @return int The result of the transmission + */ + public static function send_unshare($owner,$contact) { + + $message = array("post_guid" => $owner["guid"], + "diaspora_handle" => self::my_handle($owner), + "type" => "Person"); + + return self::build_and_transmit($owner, $contact, "retraction", $message); + } + + /** + * @brief Checks a message body if it is a reshare + * + * @param string $body The message body that is to be check + * @param bool $complete Should it be a complete check or a simple check? + * + * @return array|bool Reshare details or "false" if no reshare + */ + public static function is_reshare($body, $complete = true) { + $body = trim($body); + + // Skip if it isn't a pure repeated messages + // Does it start with a share? + if (strpos($body, "[share") > 0) + return(false); + + // Does it end with a share? + if (strlen($body) > (strrpos($body, "[/share]") + 8)) + return(false); + + $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); + // Skip if there is no shared message in there + if ($body == $attributes) + return(false); + + // If we don't do the complete check we quit here + if (!$complete) + return true; + + $guid = ""; + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $guid = $matches[1]; + + preg_match('/guid="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $guid = $matches[1]; + + if ($guid != "") { + $r = q("SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1", + dbesc($guid), NETWORK_DFRN, NETWORK_DIASPORA); + if ($r) { + $ret= array(); + $ret["root_handle"] = self::handle_from_contact($r[0]["contact-id"]); + $ret["root_guid"] = $guid; + return($ret); + } + } + + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + preg_match('/profile="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + $ret= array(); + + $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile); + if (($ret["root_handle"] == $profile) OR ($ret["root_handle"] == "")) + return(false); + + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + preg_match('/link="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link); + if (($ret["root_guid"] == $link) OR (trim($ret["root_guid"]) == "")) + return(false); + + return($ret); + } + + /** + * @brief Sends a post + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param bool $public_batch Is it a public post? + * + * @return int The result of the transmission + */ + public static function send_status($item, $owner, $contact, $public_batch = false) { + + $myaddr = self::my_handle($owner); + + $public = (($item["private"]) ? "false" : "true"); + + $created = datetime_convert("UTC", "UTC", $item["created"], 'Y-m-d H:i:s \U\T\C'); + + // Detect a share element and do a reshare + if (!$item['private'] AND ($ret = self::is_reshare($item["body"]))) { + $message = array("root_diaspora_id" => $ret["root_handle"], + "root_guid" => $ret["root_guid"], + "guid" => $item["guid"], + "diaspora_handle" => $myaddr, + "public" => $public, + "created_at" => $created, + "provider_display_name" => $item["app"]); + + $type = "reshare"; + } else { + $title = $item["title"]; + $body = $item["body"]; + + // convert to markdown + $body = html_entity_decode(bb2diaspora($body)); + + // Adding the title + if(strlen($title)) + $body = "## ".html_entity_decode($title)."\n\n".$body; + + if ($item["attach"]) { + $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism', $item["attach"], $matches, PREG_SET_ORDER); + if(cnt) { + $body .= "\n".t("Attachments:")."\n"; + foreach($matches as $mtch) + $body .= "[".$mtch[3]."](".$mtch[1].")\n"; + } + } + + $location = array(); + + if ($item["location"] != "") + $location["address"] = $item["location"]; + + if ($item["coord"] != "") { + $coord = explode(" ", $item["coord"]); + $location["lat"] = $coord[0]; + $location["lng"] = $coord[1]; + } + + $message = array("raw_message" => $body, + "location" => $location, + "guid" => $item["guid"], + "diaspora_handle" => $myaddr, + "public" => $public, + "created_at" => $created, + "provider_display_name" => $item["app"]); + + if (count($location) == 0) + unset($message["location"]); + + $type = "status_message"; + } + + return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]); + } + + /** + * @brief Creates a "like" object + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * + * @return array The data for a "like" + */ + private function construct_like($item, $owner) { + + $p = q("SELECT `guid`, `uri`, `parent-uri` FROM `item` WHERE `uri` = '%s' LIMIT 1", + dbesc($item["thr-parent"])); + if(!$p) + return false; + + $parent = $p[0]; + + $target_type = ($parent["uri"] === $parent["parent-uri"] ? "Post" : "Comment"); + $positive = "true"; + + return(array("positive" => $positive, + "guid" => $item["guid"], + "target_type" => $target_type, + "parent_guid" => $parent["guid"], + "author_signature" => "", + "diaspora_handle" => self::my_handle($owner))); + } + + /** + * @brief Creates the object for a comment + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * + * @return array The data for a comment + */ + private function construct_comment($item, $owner) { + + $p = q("SELECT `guid` FROM `item` WHERE `parent` = %d AND `id` = %d LIMIT 1", + intval($item["parent"]), + intval($item["parent"]) ); - if(count($r)) { - if(link_compare($r[0]['author-link'],$contact['url'])) { - q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d", - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($r[0]['id']) - ); - delete_thread($r[0]['id'], $r[0]['parent-uri']); - // Now check if the retraction needs to be relayed by us - // - // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always - // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent. - // The only item with `parent` and `id` as the parent id is the parent item. - $p = q("SELECT `origin` FROM `item` WHERE `parent` = %d AND `id` = %d LIMIT 1", - intval($r[0]['parent']), - intval($r[0]['parent']) - ); - if(count($p)) { - if($p[0]['origin']) { - q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - $r[0]['id'], - dbesc($signed_data), - dbesc($sig), - dbesc($diaspora_handle) - ); + if (!$p) + return false; - // the existence of parent_author_signature would have meant the parent_author or owner - // is already relaying. - logger('diaspora_signed_retraction: relaying relayable_retraction'); + $parent = $p[0]; - proc_run('php','include/notifier.php','drop',$r[0]['id']); + $text = html_entity_decode(bb2diaspora($item["body"])); + + return(array("guid" => $item["guid"], + "parent_guid" => $parent["guid"], + "author_signature" => "", + "text" => $text, + "diaspora_handle" => self::my_handle($owner))); + } + + /** + * @brief Send a like or a comment + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param bool $public_batch Is it a public post? + * + * @return int The result of the transmission + */ + public static function send_followup($item,$owner,$contact,$public_batch = false) { + + if($item['verb'] === ACTIVITY_LIKE) { + $message = self::construct_like($item, $owner); + $type = "like"; + } else { + $message = self::construct_comment($item, $owner); + $type = "comment"; + } + + if (!$message) + return false; + + $message["author_signature"] = self::signature($owner, $message); + + return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]); + } + + /** + * @brief Creates a message from a signature record entry + * + * @param array $item The item that will be exported + * @param array $signature The entry of the "sign" record + * + * @return string The message + */ + private function message_from_signature($item, $signature) { + + // Split the signed text + $signed_parts = explode(";", $signature['signed_text']); + + if ($item["deleted"]) + $message = array("parent_author_signature" => "", + "target_guid" => $signed_parts[0], + "target_type" => $signed_parts[1], + "sender_handle" => $signature['signer'], + "target_author_signature" => $signature['signature']); + elseif ($item['verb'] === ACTIVITY_LIKE) + $message = array("positive" => $signed_parts[0], + "guid" => $signed_parts[1], + "target_type" => $signed_parts[2], + "parent_guid" => $signed_parts[3], + "parent_author_signature" => "", + "author_signature" => $signature['signature'], + "diaspora_handle" => $signed_parts[4]); + else { + // Remove the comment guid + $guid = array_shift($signed_parts); + + // Remove the parent guid + $parent_guid = array_shift($signed_parts); + + // Remove the handle + $handle = array_pop($signed_parts); + + // Glue the parts together + $text = implode(";", $signed_parts); + + $message = array("guid" => $guid, + "parent_guid" => $parent_guid, + "parent_author_signature" => "", + "author_signature" => $signature['signature'], + "text" => implode(";", $signed_parts), + "diaspora_handle" => $handle); + } + return $message; + } + + /** + * @brief Relays messages (like, comment, retraction) to other servers if we are the thread owner + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param bool $public_batch Is it a public post? + * + * @return int The result of the transmission + */ + public static function send_relay($item, $owner, $contact, $public_batch = false) { + + if ($item["deleted"]) + return self::send_retraction($item, $owner, $contact, $public_batch, true); + elseif ($item['verb'] === ACTIVITY_LIKE) + $type = "like"; + else + $type = "comment"; + + logger("Got relayable data ".$type." for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG); + + // fetch the original signature + + $r = q("SELECT `signed_text`, `signature`, `signer` FROM `sign` WHERE `iid` = %d LIMIT 1", + intval($item["id"])); + + if (!$r) { + logger("Couldn't fetch signatur for item ".$item["guid"]." (".$item["id"].")", LOGGER_DEBUG); + return false; + } + + $signature = $r[0]; + + // Old way - is used by the internal Friendica functions + /// @todo Change all signatur storing functions to the new format + if ($signature['signed_text'] AND $signature['signature'] AND $signature['signer']) + $message = self::message_from_signature($item, $signature); + else {// New way + $msg = json_decode($signature['signed_text'], true); + + $message = array(); + if (is_array($msg)) { + foreach ($msg AS $field => $data) { + if (!$item["deleted"]) { + if ($field == "author") + $field = "diaspora_handle"; + if ($field == "parent_type") + $field = "target_type"; + } + + $message[$field] = $data; + } + } else + logger("Signature text for item ".$item["guid"]." (".$item["id"].") couldn't be extracted: ".$signature['signed_text'], LOGGER_DEBUG); + } + + $message["parent_author_signature"] = self::signature($owner, $message); + + logger("Relayed data ".print_r($message, true), LOGGER_DEBUG); + + return self::build_and_transmit($owner, $contact, $type, $message, $public_batch, $item["guid"]); + } + + /** + * @brief Sends a retraction (deletion) of a message, like or comment + * + * @param array $item The item that will be exported + * @param array $owner the array of the item owner + * @param array $contact Target of the communication + * @param bool $public_batch Is it a public post? + * @param bool $relay Is the retraction transmitted from a relay? + * + * @return int The result of the transmission + */ + public static function send_retraction($item, $owner, $contact, $public_batch = false, $relay = false) { + + $itemaddr = self::handle_from_contact($item["contact-id"], $item["gcontact-id"]); + + // Check whether the retraction is for a top-level post or whether it's a relayable + if ($item["uri"] !== $item["parent-uri"]) { + $msg_type = "relayable_retraction"; + $target_type = (($item["verb"] === ACTIVITY_LIKE) ? "Like" : "Comment"); + } else { + $msg_type = "signed_retraction"; + $target_type = "StatusMessage"; + } + + if ($relay AND ($item["uri"] !== $item["parent-uri"])) + $signature = "parent_author_signature"; + else + $signature = "target_author_signature"; + + $signed_text = $item["guid"].";".$target_type; + + $message = array("target_guid" => $item['guid'], + "target_type" => $target_type, + "sender_handle" => $itemaddr, + $signature => base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'))); + + logger("Got message ".print_r($message, true), LOGGER_DEBUG); + + return self::build_and_transmit($owner, $contact, $msg_type, $message, $public_batch, $item["guid"]); + } + + /** + * @brief Sends a mail + * + * @param array $item The item that will be exported + * @param array $owner The owner + * @param array $contact Target of the communication + * + * @return int The result of the transmission + */ + public static function send_mail($item, $owner, $contact) { + + $myaddr = self::my_handle($owner); + + $r = q("SELECT * FROM `conv` WHERE `id` = %d AND `uid` = %d LIMIT 1", + intval($item["convid"]), + intval($item["uid"]) + ); + + if (!$r) { + logger("conversation not found."); + return; + } + $cnv = $r[0]; + + $conv = array( + "guid" => $cnv["guid"], + "subject" => $cnv["subject"], + "created_at" => datetime_convert("UTC", "UTC", $cnv['created'], 'Y-m-d H:i:s \U\T\C'), + "diaspora_handle" => $cnv["creator"], + "participant_handles" => $cnv["recips"] + ); + + $body = bb2diaspora($item["body"]); + $created = datetime_convert("UTC", "UTC", $item["created"], 'Y-m-d H:i:s \U\T\C'); + + $signed_text = $item["guid"].";".$cnv["guid"].";".$body.";".$created.";".$myaddr.";".$cnv['guid']; + $sig = base64_encode(rsa_sign($signed_text, $owner["uprvkey"], "sha256")); + + $msg = array( + "guid" => $item["guid"], + "parent_guid" => $cnv["guid"], + "parent_author_signature" => $sig, + "author_signature" => $sig, + "text" => $body, + "created_at" => $created, + "diaspora_handle" => $myaddr, + "conversation_guid" => $cnv["guid"] + ); + + if ($item["reply"]) { + $message = $msg; + $type = "message"; + } else { + $message = array("guid" => $cnv["guid"], + "subject" => $cnv["subject"], + "created_at" => datetime_convert("UTC", "UTC", $cnv['created'], 'Y-m-d H:i:s \U\T\C'), + "message" => $msg, + "diaspora_handle" => $cnv["creator"], + "participant_handles" => $cnv["recips"]); + + $type = "conversation"; + } + + return self::build_and_transmit($owner, $contact, $type, $message, false, $item["guid"]); + } + + /** + * @brief Sends profile data + * + * @param int $uid The user id + */ + public static function send_profile($uid) { + + if (!$uid) + return; + + $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s' + AND `uid` = %d AND `rel` != %d", + dbesc(NETWORK_DIASPORA), + intval($uid), + intval(CONTACT_IS_SHARING) + ); + if (!$recips) + return; + + $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.*, `user`.`prvkey` AS `uprvkey`, `contact`.`addr` + FROM `profile` + INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` + INNER JOIN `contact` ON `profile`.`uid` = `contact`.`uid` + WHERE `user`.`uid` = %d AND `profile`.`is-default` AND `contact`.`self` LIMIT 1", + intval($uid) + ); + + if (!$r) + return; + + $profile = $r[0]; + + $handle = $profile["addr"]; + $first = ((strpos($profile['name'],' ') + ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name'])); + $last = (($first === $profile['name']) ? '' : trim(substr($profile['name'], strlen($first)))); + $large = App::get_baseurl().'/photo/custom/300/'.$profile['uid'].'.jpg'; + $medium = App::get_baseurl().'/photo/custom/100/'.$profile['uid'].'.jpg'; + $small = App::get_baseurl().'/photo/custom/50/' .$profile['uid'].'.jpg'; + $searchable = (($profile['publish'] && $profile['net-publish']) ? 'true' : 'false'); + + if ($searchable === 'true') { + $dob = '1000-00-00'; + + if (($profile['dob']) && ($profile['dob'] != '0000-00-00')) + $dob = ((intval($profile['dob'])) ? intval($profile['dob']) : '1000') .'-'. datetime_convert('UTC','UTC',$profile['dob'],'m-d'); + + $about = $profile['about']; + $about = strip_tags(bbcode($about)); + + $location = formatted_location($profile); + $tags = ''; + if ($profile['pub_keywords']) { + $kw = str_replace(',',' ',$profile['pub_keywords']); + $kw = str_replace(' ',' ',$kw); + $arr = explode(' ',$profile['pub_keywords']); + if (count($arr)) { + for($x = 0; $x < 5; $x ++) { + if (trim($arr[$x])) + $tags .= '#'. trim($arr[$x]) .' '; } } } + $tags = trim($tags); } - } - else - logger('diaspora_signed_retraction: unknown type: ' . $type); - return 202; - // NOTREACHED -} + $message = array("diaspora_handle" => $handle, + "first_name" => $first, + "last_name" => $last, + "image_url" => $large, + "image_url_medium" => $medium, + "image_url_small" => $small, + "birthday" => $dob, + "gender" => $profile['gender'], + "bio" => $about, + "location" => $location, + "searchable" => $searchable, + "tag_string" => $tags); -function diaspora_profile($importer,$xml,$msg) { - - $a = get_app(); - $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); - - - if($diaspora_handle != $msg['author']) { - logger('diaspora_post: Potential forgery. Message handle is not the same as envelope sender.'); - return 202; + foreach($recips as $recip) + self::build_and_transmit($profile, $recip, "profile", $message, false, "", true); } - $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); - if(! $contact) - return; + /** + * @brief Stores the signature for likes that are created on our system + * + * @param array $contact The contact array of the "like" + * @param int $post_id The post id of the "like" + * + * @return bool Success + */ + public static function store_like_signature($contact, $post_id) { - //if($contact['blocked']) { - // logger('diaspora_post: Ignoring this author.'); - // return 202; - //} - - $name = unxmlify($xml->first_name) . ((strlen($xml->last_name)) ? ' ' . unxmlify($xml->last_name) : ''); - $image_url = unxmlify($xml->image_url); - $birthday = unxmlify($xml->birthday); - $location = diaspora2bb(unxmlify($xml->location)); - $about = diaspora2bb(unxmlify($xml->bio)); - $gender = unxmlify($xml->gender); - $searchable = (unxmlify($xml->searchable) == "true"); - $nsfw = (unxmlify($xml->nsfw) == "true"); - $tags = unxmlify($xml->tag_string); - - $tags = explode("#", $tags); - - $keywords = array(); - foreach ($tags as $tag) { - $tag = trim(strtolower($tag)); - if ($tag != "") - $keywords[] = $tag; - } - - $keywords = implode(", ", $keywords); - - $handle_parts = explode("@", $diaspora_handle); - $nick = $handle_parts[0]; - - if($name === '') { - $name = $handle_parts[0]; - } - - if( preg_match("|^https?://|", $image_url) === 0) { - $image_url = "http://" . $handle_parts[1] . $image_url; - } - -/* $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ", - intval($importer['uid']), - intval($contact['id']) - ); - $oldphotos = ((count($r)) ? $r : null);*/ - - require_once('include/Photo.php'); - - update_contact_avatar($image_url,$importer['uid'],$contact['id']); - - // Generic birthday. We don't know the timezone. The year is irrelevant. - - $birthday = str_replace('1000','1901',$birthday); - - if ($birthday != "") - $birthday = datetime_convert('UTC','UTC',$birthday,'Y-m-d'); - - // this is to prevent multiple birthday notifications in a single year - // if we already have a stored birthday and the 'm-d' part hasn't changed, preserve the entry, which will preserve the notify year - - if(substr($birthday,5) === substr($contact['bd'],5)) - $birthday = $contact['bd']; - - /// @TODO Update name on item['author-name'] if the name changed. See consume_feed() - /// (Not doing this currently because D* protocol is scheduled for revision soon). - - $r = q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s', `name-date` = '%s', `bd` = '%s', - `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s' WHERE `id` = %d AND `uid` = %d", - dbesc($name), - dbesc($nick), - dbesc($diaspora_handle), - dbesc(datetime_convert()), - dbesc($birthday), - dbesc($location), - dbesc($about), - dbesc($keywords), - dbesc($gender), - intval($contact['id']), - intval($importer['uid']) - ); - - if ($searchable) { - require_once('include/socgraph.php'); - poco_check($contact['url'], $name, NETWORK_DIASPORA, $image_url, $about, $location, $gender, $keywords, "", - datetime_convert(), 2, $contact['id'], $importer['uid']); - } - - update_gcontact(array("url" => $contact['url'], "network" => NETWORK_DIASPORA, "generation" => 2, - "photo" => $image_url, "name" => $name, "location" => $location, - "about" => $about, "birthday" => $birthday, "gender" => $gender, - "addr" => $diaspora_handle, "nick" => $nick, "keywords" => $keywords, - "hide" => !$searchable, "nsfw" => $nsfw)); - -/* if($r) { - if($oldphotos) { - foreach($oldphotos as $ph) { - q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ", - intval($importer['uid']), - intval($contact['id']), - dbesc($ph['resource-id']) - ); - } + $enabled = intval(get_config('system','diaspora_enabled')); + if (!$enabled) { + logger('Diaspora support disabled, not storing like signature', LOGGER_DEBUG); + return false; } - } */ - return; - -} - -function diaspora_share($me,$contact) { - $a = get_app(); - $myaddr = $me['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - $theiraddr = $contact['addr']; - - $tpl = get_markup_template('diaspora_share.tpl'); - $msg = replace_macros($tpl, array( - '$sender' => $myaddr, - '$recipient' => $theiraddr - )); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey']))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey'])); - - return(diaspora_transmit($owner,$contact,$slap, false)); -} - -function diaspora_unshare($me,$contact) { - - $a = get_app(); - $myaddr = $me['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - - $tpl = get_markup_template('diaspora_retract.tpl'); - $msg = replace_macros($tpl, array( - '$guid' => $me['guid'], - '$type' => 'Person', - '$handle' => $myaddr - )); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey']))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$me,$contact,$me['prvkey'],$contact['pubkey'])); - - return(diaspora_transmit($owner,$contact,$slap, false)); - -} - - -function diaspora_send_status($item,$owner,$contact,$public_batch = false) { - - $a = get_app(); - $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - $theiraddr = $contact['addr']; - - $images = array(); - - $title = $item['title']; - $body = $item['body']; - -/* - // We're trying to match Diaspora's split message/photo protocol but - // all the photos are displayed on D* as links and not img's - even - // though we're sending pretty much precisely what they send us when - // doing the same operation. - // Commented out for now, we'll use bb2diaspora to convert photos to markdown - // which seems to get through intact. - - $cnt = preg_match_all('|\[img\](.*?)\[\/img\]|',$body,$matches,PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $detail = array(); - $detail['str'] = $mtch[0]; - $detail['path'] = dirname($mtch[1]) . '/'; - $detail['file'] = basename($mtch[1]); - $detail['guid'] = $item['guid']; - $detail['handle'] = $myaddr; - $images[] = $detail; - $body = str_replace($detail['str'],$mtch[1],$body); + // Is the contact the owner? Then fetch the private key + if (!$contact['self'] OR ($contact['uid'] == 0)) { + logger("No owner post, so not storing signature", LOGGER_DEBUG); + return false; } - } -*/ - //if(strlen($title)) - // $body = "[b]".html_entity_decode($title)."[/b]\n\n".$body; + $r = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", intval($contact['uid'])); + if(!$r) + return false; - // convert to markdown - $body = xmlify(html_entity_decode(bb2diaspora($body))); - //$body = bb2diaspora($body); + $contact["uprvkey"] = $r[0]['prvkey']; - // Adding the title - if(strlen($title)) - $body = "## ".html_entity_decode($title)."\n\n".$body; + $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($post_id)); + if (!$r) + return false; - if($item['attach']) { - $cnt = preg_match_all('/href=\"(.*?)\"(.*?)title=\"(.*?)\"/ism',$item['attach'],$matches,PREG_SET_ORDER); - if(cnt) { - $body .= "\n" . t('Attachments:') . "\n"; - foreach($matches as $mtch) { - $body .= '[' . $mtch[3] . '](' . $mtch[1] . ')' . "\n"; - } - } - } + if (!in_array($r[0]["verb"], array(ACTIVITY_LIKE, ACTIVITY_DISLIKE))) + return false; + $message = self::construct_like($r[0], $contact); + $message["author_signature"] = self::signature($contact, $message); - $public = (($item['private']) ? 'false' : 'true'); + // In the future we will store the signature more flexible to support new fields. + // Right now we cannot change this since old Friendica versions (prior to 3.5) can only handle this format. + // (We are transmitting this data here via DFRN) - require_once('include/datetime.php'); - $created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C'); + $signed_text = $message["positive"].";".$message["guid"].";".$message["target_type"].";". + $message["parent_guid"].";".$message["diaspora_handle"]; - // Detect a share element and do a reshare - // see: https://github.com/Raven24/diaspora-federation/blob/master/lib/diaspora-federation/entities/reshare.rb - if (!$item['private'] AND ($ret = diaspora_is_reshare($item["body"]))) { - $tpl = get_markup_template('diaspora_reshare.tpl'); - $msg = replace_macros($tpl, array( - '$root_handle' => xmlify($ret['root_handle']), - '$root_guid' => $ret['root_guid'], - '$guid' => $item['guid'], - '$handle' => xmlify($myaddr), - '$public' => $public, - '$created' => $created, - '$provider' => $item["app"] - )); - } else { - $tpl = get_markup_template('diaspora_post.tpl'); - $msg = replace_macros($tpl, array( - '$body' => $body, - '$guid' => $item['guid'], - '$handle' => xmlify($myaddr), - '$public' => $public, - '$created' => $created, - '$provider' => $item["app"] - )); - } - - logger('diaspora_send_status: '.$owner['username'].' -> '.$contact['name'].' base message: '.$msg, LOGGER_DATA); - logger('send guid '.$item['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)); - - $return_code = diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid']); - - logger('diaspora_send_status: guid: '.$item['guid'].' result '.$return_code, LOGGER_DEBUG); - - if(count($images)) { - diaspora_send_images($item,$owner,$contact,$images,$public_batch); - } - - return $return_code; -} - -function diaspora_is_reshare($body) { - $body = trim($body); - - // Skip if it isn't a pure repeated messages - // Does it start with a share? - if (strpos($body, "[share") > 0) - return(false); - - // Does it end with a share? - if (strlen($body) > (strrpos($body, "[/share]") + 8)) - return(false); - - $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); - // Skip if there is no shared message in there - if ($body == $attributes) - return(false); - - $guid = ""; - preg_match("/guid='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $guid = $matches[1]; - - preg_match('/guid="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $guid = $matches[1]; - - if ($guid != "") { - $r = q("SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1", - dbesc($guid), NETWORK_DFRN, NETWORK_DIASPORA); - if ($r) { - $ret= array(); - $ret["root_handle"] = diaspora_handle_from_contact($r[0]["contact-id"]); - $ret["root_guid"] = $guid; - return($ret); - } - } - - $profile = ""; - preg_match("/profile='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; - - preg_match('/profile="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $profile = $matches[1]; - - $ret= array(); - - $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile); - if (($ret["root_handle"] == $profile) OR ($ret["root_handle"] == "")) - return(false); - - $link = ""; - preg_match("/link='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $link = $matches[1]; - - preg_match('/link="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $link = $matches[1]; - - $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link); - if (($ret["root_guid"] == $link) OR ($ret["root_guid"] == "")) - return(false); - - return($ret); -} - -function diaspora_send_images($item,$owner,$contact,$images,$public_batch = false) { - $a = get_app(); - if(! count($images)) - return; - $mysite = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://') + 3) . '/photo'; - - $tpl = get_markup_template('diaspora_photo.tpl'); - foreach($images as $image) { - if(! stristr($image['path'],$mysite)) - continue; - $resource = str_replace('.jpg','',$image['file']); - $resource = substr($resource,0,strpos($resource,'-')); - - $r = q("select * from photo where `resource-id` = '%s' and `uid` = %d limit 1", - dbesc($resource), - intval($owner['uid']) + q("INSERT INTO `sign` (`iid`,`signed_text`,`signature`,`signer`) VALUES (%d,'%s','%s','%s')", + intval($post_id), + dbesc($signed_text), + dbesc($message["author_signature"]), + dbesc($message["diaspora_handle"]) ); - if(! count($r)) - continue; - $public = (($r[0]['allow_cid'] || $r[0]['allow_gid'] || $r[0]['deny_cid'] || $r[0]['deny_gid']) ? 'false' : 'true' ); - $msg = replace_macros($tpl,array( - '$path' => xmlify($image['path']), - '$filename' => xmlify($image['file']), - '$msg_guid' => xmlify($image['guid']), - '$guid' => xmlify($r[0]['guid']), - '$handle' => xmlify($image['handle']), - '$public' => xmlify($public), - '$created_at' => xmlify(datetime_convert('UTC','UTC',$r[0]['created'],'Y-m-d H:i:s \U\T\C')) - )); + // This here will replace the lines above, once Diaspora changed its protocol + //q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')", + // intval($message_id), + // dbesc(json_encode($message)) + //); - logger('diaspora_send_photo: base message: ' . $msg, LOGGER_DATA); - logger('send guid '.$r[0]['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)); - - diaspora_transmit($owner,$contact,$slap,$public_batch,false,$r[0]['guid']); + logger('Stored diaspora like signature'); + return true; } -} + /** + * @brief Stores the signature for comments that are created on our system + * + * @param array $item The item array of the comment + * @param array $contact The contact array of the item owner + * @param string $uprvkey The private key of the sender + * @param int $message_id The message id of the comment + * + * @return bool Success + */ + public static function store_comment_signature($item, $contact, $uprvkey, $message_id) { -function diaspora_send_followup($item,$owner,$contact,$public_batch = false) { + if ($uprvkey == "") { + logger('No private key, so not storing comment signature', LOGGER_DEBUG); + return false; + } - $a = get_app(); - $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); -// $theiraddr = $contact['addr']; + $enabled = intval(get_config('system','diaspora_enabled')); + if (!$enabled) { + logger('Diaspora support disabled, not storing comment signature', LOGGER_DEBUG); + return false; + } - // Diaspora doesn't support threaded comments, but some - // versions of Diaspora (i.e. Diaspora-pistos) support - // likes on comments - if($item['verb'] === ACTIVITY_LIKE && $item['thr-parent']) { - $p = q("select guid, type, uri, `parent-uri` from item where uri = '%s' limit 1", - dbesc($item['thr-parent']) - ); - } - else { - // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always - // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent. - // The only item with `parent` and `id` as the parent id is the parent item. - $p = q("select guid, type, uri, `parent-uri` from item where parent = %d and id = %d limit 1", - intval($item['parent']), - intval($item['parent']) + $contact["uprvkey"] = $uprvkey; + + $message = self::construct_comment($item, $contact); + $message["author_signature"] = self::signature($contact, $message); + + // In the future we will store the signature more flexible to support new fields. + // Right now we cannot change this since old Friendica versions (prior to 3.5) can only handle this format. + // (We are transmitting this data here via DFRN) + $signed_text = $message["guid"].";".$message["parent_guid"].";". + $message["text"].";".$message["diaspora_handle"]; + + q("INSERT INTO `sign` (`iid`,`signed_text`,`signature`,`signer`) VALUES (%d,'%s','%s','%s')", + intval($message_id), + dbesc($signed_text), + dbesc($message["author_signature"]), + dbesc($message["diaspora_handle"]) ); + + // This here will replace the lines above, once Diaspora changed its protocol + //q("INSERT INTO `sign` (`iid`,`signed_text`) VALUES (%d,'%s')", + // intval($message_id), + // dbesc(json_encode($message)) + //); + + logger('Stored diaspora comment signature'); + return true; } - if(count($p)) - $parent = $p[0]; - else - return; - - if($item['verb'] === ACTIVITY_LIKE) { - $tpl = get_markup_template('diaspora_like.tpl'); - $like = true; - $target_type = ( $parent['uri'] === $parent['parent-uri'] ? 'Post' : 'Comment'); -// $target_type = (strpos($parent['type'], 'comment') ? 'Comment' : 'Post'); -// $positive = (($item['deleted']) ? 'false' : 'true'); - $positive = 'true'; - - if(($item['deleted'])) - logger('diaspora_send_followup: received deleted "like". Those should go to diaspora_send_retraction'); - } - else { - $tpl = get_markup_template('diaspora_comment.tpl'); - $like = false; - } - - $text = html_entity_decode(bb2diaspora($item['body'])); - - // sign it - - if($like) - $signed_text = $positive . ';' . $item['guid'] . ';' . $target_type . ';' . $parent['guid'] . ';' . $myaddr; - else - $signed_text = $item['guid'] . ';' . $parent['guid'] . ';' . $text . ';' . $myaddr; - - $authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); - - $msg = replace_macros($tpl,array( - '$guid' => xmlify($item['guid']), - '$parent_guid' => xmlify($parent['guid']), - '$target_type' =>xmlify($target_type), - '$authorsig' => xmlify($authorsig), - '$body' => xmlify($text), - '$positive' => xmlify($positive), - '$handle' => xmlify($myaddr) - )); - - logger('diaspora_followup: base message: ' . $msg, LOGGER_DATA); - logger('send guid '.$item['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)); - - return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid'])); -} - - -function diaspora_send_relay($item,$owner,$contact,$public_batch = false) { - - - $a = get_app(); - $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); -// $theiraddr = $contact['addr']; - - // Diaspora doesn't support threaded comments, but some - // versions of Diaspora (i.e. Diaspora-pistos) support - // likes on comments - if($item['verb'] === ACTIVITY_LIKE && $item['thr-parent']) { - $p = q("select guid, type, uri, `parent-uri` from item where uri = '%s' limit 1", - dbesc($item['thr-parent']) - ); - } - else { - // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always - // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent. - // The only item with `parent` and `id` as the parent id is the parent item. - $p = q("select guid, type, uri, `parent-uri` from item where parent = %d and id = %d limit 1", - intval($item['parent']), - intval($item['parent']) - ); - } - if(count($p)) - $parent = $p[0]; - else - return; - - $like = false; - $relay_retract = false; - $sql_sign_id = 'iid'; - if( $item['deleted']) { - $relay_retract = true; - - $target_type = ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment'); - - $sql_sign_id = 'retract_iid'; - $tpl = get_markup_template('diaspora_relayable_retraction.tpl'); - } - elseif($item['verb'] === ACTIVITY_LIKE) { - $like = true; - - $target_type = ( $parent['uri'] === $parent['parent-uri'] ? 'Post' : 'Comment'); -// $positive = (($item['deleted']) ? 'false' : 'true'); - $positive = 'true'; - - $tpl = get_markup_template('diaspora_like_relay.tpl'); - } - else { // item is a comment - $tpl = get_markup_template('diaspora_comment_relay.tpl'); - } - - - // fetch the original signature if the relayable was created by a Diaspora - // or DFRN user. Relayables for other networks are not supported. - - $r = q("SELECT `signed_text`, `signature`, `signer` FROM `sign` WHERE " . $sql_sign_id . " = %d LIMIT 1", - intval($item['id']) - ); - if(count($r)) { - $orig_sign = $r[0]; - $signed_text = $orig_sign['signed_text']; - $authorsig = $orig_sign['signature']; - $handle = $orig_sign['signer']; - - // Split the signed text - $signed_parts = explode(";", $signed_text); - - // Remove the parent guid - array_shift($signed_parts); - - // Remove the comment guid - array_shift($signed_parts); - - // Remove the handle - array_pop($signed_parts); - - // Glue the parts together - $text = implode(";", $signed_parts); - } - else { - // This part is meant for cases where we don't have the signatur. (Which shouldn't happen with posts from Diaspora and Friendica) - // This means that the comment won't be accepted by newer Diaspora servers - - $body = $item['body']; - $text = html_entity_decode(bb2diaspora($body)); - - $handle = diaspora_handle_from_contact($item['contact-id']); - if(! $handle) - return; - - if($relay_retract) - $signed_text = $item['guid'] . ';' . $target_type; - elseif($like) - $signed_text = $item['guid'] . ';' . $target_type . ';' . $parent['guid'] . ';' . $positive . ';' . $handle; - else - $signed_text = $item['guid'] . ';' . $parent['guid'] . ';' . $text . ';' . $handle; - - $authorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); - } - - // Sign the relayable with the top-level owner's signature - $parentauthorsig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); - - $msg = replace_macros($tpl,array( - '$guid' => xmlify($item['guid']), - '$parent_guid' => xmlify($parent['guid']), - '$target_type' =>xmlify($target_type), - '$authorsig' => xmlify($authorsig), - '$parentsig' => xmlify($parentauthorsig), - '$body' => xmlify($text), - '$positive' => xmlify($positive), - '$handle' => xmlify($handle) - )); - - logger('diaspora_send_relay: base message: ' . $msg, LOGGER_DATA); - logger('send guid '.$item['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)); - - return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid'])); - -} - - - -function diaspora_send_retraction($item,$owner,$contact,$public_batch = false) { - - $a = get_app(); - $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - - // Check whether the retraction is for a top-level post or whether it's a relayable - if( $item['uri'] !== $item['parent-uri'] ) { - - $tpl = get_markup_template('diaspora_relay_retraction.tpl'); - $target_type = (($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment'); - } - else { - - $tpl = get_markup_template('diaspora_signed_retract.tpl'); - $target_type = 'StatusMessage'; - } - - $signed_text = $item['guid'] . ';' . $target_type; - - $msg = replace_macros($tpl, array( - '$guid' => xmlify($item['guid']), - '$type' => xmlify($target_type), - '$handle' => xmlify($myaddr), - '$signature' => xmlify(base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256'))) - )); - - logger('send guid '.$item['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($msg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],$public_batch)); - - return(diaspora_transmit($owner,$contact,$slap,$public_batch,false,$item['guid'])); -} - -function diaspora_send_mail($item,$owner,$contact) { - - $a = get_app(); - $myaddr = $owner['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); - - $r = q("select * from conv where id = %d and uid = %d limit 1", - intval($item['convid']), - intval($item['uid']) - ); - - if(! count($r)) { - logger('diaspora_send_mail: conversation not found.'); - return; - } - $cnv = $r[0]; - - $conv = array( - 'guid' => xmlify($cnv['guid']), - 'subject' => xmlify($cnv['subject']), - 'created_at' => xmlify(datetime_convert('UTC','UTC',$cnv['created'],'Y-m-d H:i:s \U\T\C')), - 'diaspora_handle' => xmlify($cnv['creator']), - 'participant_handles' => xmlify($cnv['recips']) - ); - - $body = bb2diaspora($item['body']); - $created = datetime_convert('UTC','UTC',$item['created'],'Y-m-d H:i:s \U\T\C'); - - $signed_text = $item['guid'] . ';' . $cnv['guid'] . ';' . $body . ';' - . $created . ';' . $myaddr . ';' . $cnv['guid']; - - $sig = base64_encode(rsa_sign($signed_text,$owner['uprvkey'],'sha256')); - - $msg = array( - 'guid' => xmlify($item['guid']), - 'parent_guid' => xmlify($cnv['guid']), - 'parent_author_signature' => xmlify($sig), - 'author_signature' => xmlify($sig), - 'text' => xmlify($body), - 'created_at' => xmlify($created), - 'diaspora_handle' => xmlify($myaddr), - 'conversation_guid' => xmlify($cnv['guid']) - ); - - if($item['reply']) { - $tpl = get_markup_template('diaspora_message.tpl'); - $xmsg = replace_macros($tpl, array('$msg' => $msg)); - } - else { - $conv['messages'] = array($msg); - $tpl = get_markup_template('diaspora_conversation.tpl'); - $xmsg = replace_macros($tpl, array('$conv' => $conv)); - } - - logger('diaspora_conversation: ' . print_r($xmsg,true), LOGGER_DATA); - logger('send guid '.$item['guid'], LOGGER_DEBUG); - - $slap = 'xml=' . urlencode(urlencode(diaspora_msg_build($xmsg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],false))); - //$slap = 'xml=' . urlencode(diaspora_msg_build($xmsg,$owner,$contact,$owner['uprvkey'],$contact['pubkey'],false)); - - return(diaspora_transmit($owner,$contact,$slap,false,false,$item['guid'])); - - -} - -function diaspora_transmit($owner,$contact,$slap,$public_batch,$queue_run=false,$guid = "") { - - $enabled = intval(get_config('system','diaspora_enabled')); - if(! $enabled) { - return 200; - } - - $a = get_app(); - $logid = random_string(4); - $dest_url = (($public_batch) ? $contact['batch'] : $contact['notify']); - if(! $dest_url) { - logger('diaspora_transmit: no url for contact: ' . $contact['id'] . ' batch mode =' . $public_batch); - return 0; - } - - logger('diaspora_transmit: '.$logid.'-'.$guid.' '.$dest_url); - - if( (! $queue_run) && (was_recently_delayed($contact['id'])) ) { - $return_code = 0; - } - else { - if (!intval(get_config('system','diaspora_test'))) { - post_url($dest_url . '/', $slap); - $return_code = $a->get_curl_code(); - } else { - logger('diaspora_transmit: test_mode'); - return 200; - } - } - - logger('diaspora_transmit: '.$logid.'-'.$guid.' returns: '.$return_code); - - if((! $return_code) || (($return_code == 503) && (stristr($a->get_curl_headers(),'retry-after')))) { - logger('diaspora_transmit: queue message'); - - $r = q("SELECT id from queue where cid = %d and network = '%s' and content = '%s' and batch = %d limit 1", - intval($contact['id']), - dbesc(NETWORK_DIASPORA), - dbesc($slap), - intval($public_batch) - ); - if(count($r)) { - logger('diaspora_transmit: add_to_queue ignored - identical item already in queue'); - } - else { - // queue message for redelivery - add_to_queue($contact['id'],NETWORK_DIASPORA,$slap,$public_batch); - } - } - - - return(($return_code) ? $return_code : (-1)); -} - -function diaspora_fetch_relay() { - - $serverdata = get_config("system", "relay_server"); - if ($serverdata == "") - return array(); - - $relay = array(); - - $servers = explode(",", $serverdata); - - foreach($servers AS $server) { - $server = trim($server); - $batch = $server."/receive/public"; - - $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch)); - - if (!$relais) { - $addr = "relay@".str_replace("http://", "", normalise_link($server)); - - $r = q("INSERT INTO `contact` (`uid`, `created`, `name`, `nick`, `addr`, `url`, `nurl`, `batch`, `network`, `rel`, `blocked`, `pending`, `writable`, `name-date`, `uri-date`, `avatar-date`) - VALUES (0, '%s', '%s', 'relay', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, '%s', '%s', '%s')", - datetime_convert(), - dbesc($addr), - dbesc($addr), - dbesc($server), - dbesc(normalise_link($server)), - dbesc($batch), - dbesc(NETWORK_DIASPORA), - intval(CONTACT_IS_FOLLOWER), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()) - ); - - $relais = q("SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' LIMIT 1", dbesc($batch)); - if ($relais) - $relay[] = $relais[0]; - } else - $relay[] = $relais[0]; - } - - return $relay; } +?> diff --git a/include/discover_poco.php b/include/discover_poco.php index a8f670334b..0b468faea1 100644 --- a/include/discover_poco.php +++ b/include/discover_poco.php @@ -20,22 +20,14 @@ function discover_poco_run(&$argv, &$argc){ require_once('include/session.php'); require_once('include/datetime.php'); - require_once('include/pidfile.php'); load_config('config'); load_config('system'); - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - - $load = current_load(); - if($load) { - if(intval($load) > $maxsysload) { - logger('system: load ' . $load . ' too high. discover_poco deferred to next scheduled run.'); + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::maxload_reached()) return; - } - } if(($argc > 2) && ($argv[1] == "dirsearch")) { $search = urldecode($argv[2]); @@ -50,21 +42,10 @@ function discover_poco_run(&$argv, &$argc){ } else die("Unknown or missing parameter ".$argv[1]."\n"); - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'discover_poco'.$mode.urlencode($search)); - if($pidfile->is_already_running()) { - logger("discover_poco: Already running"); - if ($pidfile->running_time() > 19*60) { - $pidfile->kill(); - logger("discover_poco: killed stale process"); - // Calling a new instance - if ($mode == 0) - proc_run('php','include/discover_poco.php'); - } - exit; - } - } + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::is_already_running('discover_poco'.$mode.urlencode($search), 'include/discover_poco.php', 1140)) + return; $a->set_baseurl(get_config('system','url')); diff --git a/include/dsprphotoq.php b/include/dsprphotoq.php deleted file mode 100644 index 0d8088d4bd..0000000000 --- a/include/dsprphotoq.php +++ /dev/null @@ -1,55 +0,0 @@ - 0, "page-flags" => PAGE_FREELOVE); - else - $r = q("SELECT * FROM user WHERE uid = %d", - intval($dphoto['uid'])); - - if(!$r) { - logger("diaspora photo queue: user " . $dphoto['uid'] . " not found"); - return; - } - - $ret = diaspora_dispatch($r[0],unserialize($dphoto['msg']),$dphoto['attempt']); - q("DELETE FROM dsprphotoq WHERE id = %d", - intval($dphoto['id']) - ); - } -} - - -if (array_search(__file__,get_included_files())===0){ - dsprphotoq_run($_SERVER["argv"],$_SERVER["argc"]); - killme(); -} diff --git a/include/event.php b/include/event.php index 13c414c9e3..a9f054fc2e 100644 --- a/include/event.php +++ b/include/event.php @@ -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 = '' . $h . ''; diff --git a/include/feed.php b/include/feed.php index eb91f7efd4..293de3cc96 100644 --- a/include/feed.php +++ b/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,29 @@ 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; - //} + $author["author-id"] = $xpath->evaluate('/atom:feed/atom:author/atom:uri/text()')->item(0)->nodeValue; + + if ($author["author-link"] == "") + $author["author-link"] = $author["author-id"]; + + 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 +80,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 +97,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 +115,22 @@ 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"]); + unset($author["author-id"]); + } $header = array(); $header["uid"] = $importer["uid"]; @@ -120,6 +153,8 @@ function feed_import($xml,$importer,&$contact, &$hub) { if (!is_object($entries)) return; + $items = array(); + $entrylist = array(); foreach ($entries AS $entry) @@ -201,13 +236,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 +307,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); } ?> diff --git a/include/follow.php b/include/follow.php index 22ff079b63..d0411a466a 100644 --- a/include/follow.php +++ b/include/follow.php @@ -1,5 +1,6 @@ user,$contact); - logger('mod_follow: diaspora_share returns: ' . $ret); + $ret = diaspora::send_share($a->user,$contact); + logger('share returns: '.$ret); } } diff --git a/include/forums.php b/include/forums.php deleted file mode 100644 index 952d09a494..0000000000 --- a/include/forums.php +++ /dev/null @@ -1,185 +0,0 @@ - forum url - * 'name' => forum name - * 'id' => number of the key from the array - * 'micro' => contact photo in format micro - */ -function get_forumlist($uid, $showhidden = true, $lastitem, $showprivate = false) { - - $forumlist = array(); - - $order = (($showhidden) ? '' : ' AND NOT `hidden` '); - $order .= (($lastitem) ? ' ORDER BY `last-item` DESC ' : ' ORDER BY `name` ASC '); - $select = '`forum` '; - if ($showprivate) { - $select = '(`forum` OR `prv`)'; - } - - $contacts = q("SELECT `contact`.`id`, `contact`.`url`, `contact`.`name`, `contact`.`micro` FROM `contact` - WHERE `network`= 'dfrn' AND $select AND `uid` = %d - AND NOT `blocked` AND NOT `hidden` AND NOT `pending` AND NOT `archive` - AND `success_update` > `failure_update` - $order ", - intval($uid) - ); - - if (!$contacts) - return($forumlist); - - foreach($contacts as $contact) { - $forumlist[] = array( - 'url' => $contact['url'], - 'name' => $contact['name'], - 'id' => $contact['id'], - 'micro' => $contact['micro'], - ); - } - return($forumlist); -} - - -/** - * @brief Forumlist widget - * - * Sidebar widget to show subcribed friendica forums. If activated - * in the settings, it appears at the notwork page sidebar - * - * @param int $uid - * @param int $cid - * The contact id which is used to mark a forum as "selected" - * @return string - */ -function widget_forumlist($uid,$cid = 0) { - - if(! intval(feature_enabled(local_user(),'forumlist_widget'))) - return; - - $o = ''; - - //sort by last updated item - $lastitem = true; - - $contacts = get_forumlist($uid,true,$lastitem, true); - $total = count($contacts); - $visible_forums = 10; - - if(count($contacts)) { - - $id = 0; - - foreach($contacts as $contact) { - - $selected = (($cid == $contact['id']) ? ' forum-selected' : ''); - - $entry = array( - 'url' => z_root() . '/network?f=&cid=' . $contact['id'], - 'external_url' => z_root() . '/redir/' . $contact['id'], - 'name' => $contact['name'], - 'cid' => $contact['id'], - 'selected' => $selected, - 'micro' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO), - 'id' => ++$id, - ); - $entries[] = $entry; - } - - $tpl = get_markup_template('widget_forumlist.tpl'); - - $o .= replace_macros($tpl,array( - '$title' => t('Forums'), - '$forums' => $entries, - '$link_desc' => t('External link to forum'), - '$total' => $total, - '$visible_forums' => $visible_forums, - '$showmore' => t('show more'), - )); - } - - return $o; -} - -/** - * @brief Format forumlist as contact block - * - * This function is used to show the forumlist in - * the advanced profile. - * - * @param int $uid - * @return string - * - */ -function forumlist_profile_advanced($uid) { - - $profile = intval(feature_enabled($uid,'forumlist_profile')); - if(! $profile) - return; - - $o = ''; - - // place holder in case somebody wants configurability - $show_total = 9999; - - //don't sort by last updated item - $lastitem = false; - - $contacts = get_forumlist($uid,false,$lastitem,false); - - $total_shown = 0; - - foreach($contacts as $contact) { - $forumlist .= micropro($contact,false,'forumlist-profile-advanced'); - $total_shown ++; - if($total_shown == $show_total) - break; - } - - if(count($contacts) > 0) - $o .= $forumlist; - return $o; -} - -/** - * @brief count unread forum items - * - * Count unread items of connected forums and private groups - * - * @return array - * 'id' => contact id - * 'name' => contact/forum name - * 'count' => counted unseen forum items - * - */ - -function forums_count_unseen() { - $r = q("SELECT `contact`.`id`, `contact`.`name`, COUNT(*) AS `count` FROM `item` - INNER JOIN `contact` ON `item`.`contact-id` = `contact`.`id` - WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted` AND `item`.`unseen` - AND `contact`.`network`= 'dfrn' AND (`contact`.`forum` OR `contact`.`prv`) - AND NOT `contact`.`blocked` AND NOT `contact`.`hidden` - AND NOT `contact`.`pending` AND NOT `contact`.`archive` - AND `contact`.`success_update` > `failure_update` - GROUP BY `contact`.`id` ", - intval(local_user()) - ); - - return $r; -} diff --git a/include/friendica_smarty.php b/include/friendica_smarty.php index 99dc12bf8d..9ba2d2a744 100644 --- a/include/friendica_smarty.php +++ b/include/friendica_smarty.php @@ -60,6 +60,8 @@ class FriendicaSmartyEngine implements ITemplateEngine { $template = $s; $s = new FriendicaSmarty(); } + + $r['$APP'] = get_app(); // "middleware": inject variables into templates $arr = array( diff --git a/include/group.php b/include/group.php index a1375e00df..00b66ad586 100644 --- a/include/group.php +++ b/include/group.php @@ -188,7 +188,7 @@ function group_public_members($gid) { } -function mini_group_select($uid,$gid = 0) { +function mini_group_select($uid,$gid = 0, $label = "") { $grps = array(); $o = ''; @@ -205,8 +205,11 @@ function mini_group_select($uid,$gid = 0) { } logger('groups: ' . print_r($grps,true)); + if ($label == "") + $label = t('Default privacy group for new contacts'); + $o = replace_macros(get_markup_template('group_selection.tpl'), array( - '$label' => t('Default privacy group for new contacts'), + '$label' => $label, '$groups' => $grps )); return $o; @@ -215,7 +218,7 @@ function mini_group_select($uid,$gid = 0) { /** * @brief Create group sidebar widget - * + * * @param string $every * @param string $each * @param string $editmode @@ -234,7 +237,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro return ''; $groups = array(); - + $groups[] = array( 'text' => t('Everybody'), 'id' => 0, @@ -255,7 +258,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 +267,7 @@ function group_side($every="contacts",$each="group",$editmode = "standard", $gro } else { $groupedit = null; } - + $groups[] = array( 'id' => $rr['id'], 'cid' => $cid, @@ -362,17 +365,41 @@ 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()) ); return $r; } + +/** + * @brief Returns the default group for a given user and network + * + * @param int $uid User id + * @param string $network network name + * + * @return int group id + */ +function get_default_group($uid, $network = "") { + + $default_group = 0; + + if ($network == NETWORK_OSTATUS) + $default_group = get_pconfig($uid, "ostatus", "default_group"); + + if ($default_group != 0) + return $default_group; + + $g = q("SELECT `def_gid` FROM `user` WHERE `uid` = %d LIMIT 1", intval($uid)); + if($g && intval($g[0]["def_gid"])) + $default_group = $g[0]["def_gid"]; + + return $default_group; +} diff --git a/include/identity.php b/include/identity.php index fdd28f81ce..888a09ee6f 100644 --- a/include/identity.php +++ b/include/identity.php @@ -3,7 +3,7 @@ * @file include/identity.php */ -require_once('include/forums.php'); +require_once('include/ForumManager.php'); require_once('include/bbcode.php'); require_once("mod/proxy.php"); @@ -237,6 +237,7 @@ function profile_sidebar($profile, $block = 0) { if ($connect AND ($profile['network'] != NETWORK_DFRN) AND !isset($profile['remoteconnect'])) $connect = false; + $remoteconnect = NULL; if (isset($profile['remoteconnect'])) $remoteconnect = $profile['remoteconnect']; @@ -292,9 +293,9 @@ function profile_sidebar($profile, $block = 0) { // check if profile is a forum if((intval($profile['page-flags']) == PAGE_COMMUNITY) || (intval($profile['page-flags']) == PAGE_PRVGROUP) - || (intval($profile['forum'])) - || (intval($profile['prv'])) - || (intval($profile['community']))) + || (isset($profile['forum']) && intval($profile['forum'])) + || (isset($profile['prv']) && intval($profile['prv'])) + || (isset($profile['community']) && intval($profile['community']))) $account_type = t('Forum'); else $account_type = ""; @@ -332,9 +333,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){ @@ -655,7 +656,7 @@ function advanced_profile(&$a) { //show subcribed forum if it is enabled in the usersettings if (feature_enabled($uid,'forumlist_profile')) { - $profile['forumlist'] = array( t('Forums:'), forumlist_profile_advanced($uid)); + $profile['forumlist'] = array( t('Forums:'), ForumManager::profile_advanced($uid)); } if ($a->profile['uid'] == local_user()) diff --git a/include/items.php b/include/items.php index 21a0c414dc..4627b10ca2 100644 --- a/include/items.php +++ b/include/items.php @@ -17,6 +17,7 @@ require_once('include/feed.php'); require_once('include/Contact.php'); require_once('mod/share.php'); require_once('include/enotify.php'); +require_once('include/dfrn.php'); require_once('library/defuse/php-encryption-1.2.1/Crypto.php'); @@ -144,413 +145,6 @@ function title_is_body($title, $body) { return($title == $body); } -function get_atom_elements($feed, $item, $contact = array()) { - - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); - - $best_photo = array(); - - $res = array(); - - $author = $item->get_author(); - if($author) { - $res['author-name'] = unxmlify($author->get_name()); - $res['author-link'] = unxmlify($author->get_link()); - } - else { - $res['author-name'] = unxmlify($feed->get_title()); - $res['author-link'] = unxmlify($feed->get_permalink()); - } - $res['uri'] = unxmlify($item->get_id()); - $res['title'] = unxmlify($item->get_title()); - $res['body'] = unxmlify($item->get_content()); - $res['plink'] = unxmlify($item->get_link(0)); - - if($res['plink']) - $base_url = implode('/', array_slice(explode('/',$res['plink']),0,3)); - else - $base_url = ''; - - // look for a photo. We should check media size and find the best one, - // but for now let's just find any author photo - // Additionally we look for an alternate author link. On OStatus this one is the one we want. - - $authorlinks = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"]["http://www.w3.org/2005/Atom"]["link"]; - if (is_array($authorlinks)) { - foreach ($authorlinks as $link) { - $linkdata = array_shift($link["attribs"]); - - if ($linkdata["rel"] == "alternate") - $res["author-link"] = $linkdata["href"]; - }; - } - - $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate') - $res['author-link'] = unxmlify($link['attribs']['']['href']); - - if(!x($res, 'author-avatar') || !$res['author-avatar']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $res['author-avatar'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); - - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) { - $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - if($base && count($base)) { - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link'])) - $res['author-link'] = unxmlify($link['attribs']['']['href']); - if(!x($res, 'author-avatar') || !$res['author-avatar']) { - if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') - $res['author-avatar'] = unxmlify($link['attribs']['']['href']); - } - } - } - } - - // No photo/profile-link on the item - look at the feed level - - if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) { - $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link'])) - $res['author-link'] = unxmlify($link['attribs']['']['href']); - if(! $res['author-avatar']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $res['author-avatar'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); - - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) { - $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - - if($base && count($base)) { - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link'])) - $res['author-link'] = unxmlify($link['attribs']['']['href']); - if(! (x($res,'author-avatar'))) { - if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') - $res['author-avatar'] = unxmlify($link['attribs']['']['href']); - } - } - } - } - } - - $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info'); - if($apps && $apps[0]['attribs']['']['source']) { - $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); - if($res['app'] === 'web') - $res['app'] = 'OStatus'; - } - - // base64 encoded json structure representing Diaspora signature - - $dsig = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_signature'); - if($dsig) { - $res['dsprsig'] = unxmlify($dsig[0]['data']); - } - - $dguid = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_guid'); - if($dguid) - $res['guid'] = unxmlify($dguid[0]['data']); - - $bm = $item->get_item_tags(NAMESPACE_DFRN,'bookmark'); - if($bm) - $res['bookmark'] = ((unxmlify($bm[0]['data']) === 'true') ? 1 : 0); - - - /** - * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it. - */ - - $have_real_body = false; - - $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env'); - if($rawenv) { - $have_real_body = true; - $res['body'] = $rawenv[0]['data']; - $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']); - // make sure nobody is trying to sneak some html tags by us - $res['body'] = notags(base64url_decode($res['body'])); - } - - - $res['body'] = limit_body_size($res['body']); - - // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust - // the content type. Our own network only emits text normally, though it might have been converted to - // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will - // have to assume it is all html and needs to be purified. - - // It doesn't matter all that much security wise - because before this content is used anywhere, we are - // going to escape any tags we find regardless, but this lets us import a limited subset of html from - // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining - // html. - - if((strpos($res['body'],'<') !== false) && (strpos($res['body'],'>') !== false)) { - - $res['body'] = reltoabs($res['body'],$base_url); - - $res['body'] = html2bb_video($res['body']); - - $res['body'] = oembed_html2bbcode($res['body']); - - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - - // we shouldn't need a whitelist, because the bbcode converter - // will strip out any unsupported tags. - - $purifier = new HTMLPurifier($config); - $res['body'] = $purifier->purify($res['body']); - - $res['body'] = @html2bbcode($res['body']); - - - } - elseif(! $have_real_body) { - - // it's not one of our messages and it has no tags - // so it's probably just text. We'll escape it just to be safe. - - $res['body'] = escape_tags($res['body']); - } - - - // this tag is obsolete but we keep it for really old sites - - $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow'); - if($allow && $allow[0]['data'] == 1) - $res['last-child'] = 1; - else - $res['last-child'] = 0; - - $private = $item->get_item_tags(NAMESPACE_DFRN,'private'); - if($private && intval($private[0]['data']) > 0) - $res['private'] = intval($private[0]['data']); - else - $res['private'] = 0; - - $extid = $item->get_item_tags(NAMESPACE_DFRN,'extid'); - if($extid && $extid[0]['data']) - $res['extid'] = $extid[0]['data']; - - $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location'); - if($rawlocation) - $res['location'] = unxmlify($rawlocation[0]['data']); - - - $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published'); - if($rawcreated) - $res['created'] = unxmlify($rawcreated[0]['data']); - - - $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated'); - if($rawedited) - $res['edited'] = unxmlify($rawedited[0]['data']); - - if((x($res,'edited')) && (! (x($res,'created')))) - $res['created'] = $res['edited']; - - if(! $res['created']) - $res['created'] = $item->get_date('c'); - - if(! $res['edited']) - $res['edited'] = $item->get_date('c'); - - - // Disallow time travelling posts - - $d1 = strtotime($res['created']); - $d2 = strtotime($res['edited']); - $d3 = strtotime('now'); - - if($d1 > $d3) - $res['created'] = datetime_convert(); - if($d2 > $d3) - $res['edited'] = datetime_convert(); - - $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']) - $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']); - elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']) - $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']); - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']) - $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); - elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']) - $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']); - - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - - foreach($base as $link) { - if(!x($res, 'owner-avatar') || !$res['owner-avatar']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $res['owner-avatar'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point'); - if($rawgeo) - $res['coord'] = unxmlify($rawgeo[0]['data']); - - if ($contact["network"] == NETWORK_FEED) { - $res['verb'] = ACTIVITY_POST; - $res['object-type'] = ACTIVITY_OBJ_NOTE; - } - - $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb'); - - // select between supported verbs - - if($rawverb) { - $res['verb'] = unxmlify($rawverb[0]['data']); - } - - // translate OStatus unfollow to activity streams if it happened to get selected - - if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) - $res['verb'] = ACTIVITY_UNFOLLOW; - - $cats = $item->get_categories(); - if($cats) { - $tag_arr = array(); - foreach($cats as $cat) { - $term = $cat->get_term(); - if(! $term) - $term = $cat->get_label(); - $scheme = $cat->get_scheme(); - if($scheme && $term && stristr($scheme,'X-DFRN:')) - $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]'; - elseif($term) - $tag_arr[] = notags(trim($term)); - } - $res['tag'] = implode(',', $tag_arr); - } - - $attach = $item->get_enclosures(); - if($attach) { - $att_arr = array(); - foreach($attach as $att) { - $len = intval($att->get_length()); - $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link())))); - $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title())))); - $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type())))); - if(strpos($type,';')) - $type = substr($type,0,strpos($type,';')); - if((! $link) || (strpos($link,'http') !== 0)) - continue; - - if(! $title) - $title = ' '; - if(! $type) - $type = 'application/octet-stream'; - - $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; - } - $res['attach'] = implode(',', $att_arr); - } - - $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); - - if($rawobj) { - $res['object'] = '' . "\n"; - $child = $rawobj[0]['child']; - if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { - $res['object-type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; - $res['object'] .= '' . $child[NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '' . "\n"; - } - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) - $res['object'] .= '' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) - $res['object'] .= '' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) - $res['object'] .= '' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; - if(! $body) - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; - // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events - $res['object'] .= '' . xmlify($body) . '' . "\n"; - if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - - $body = html2bb_video($body); - - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - - $purifier = new HTMLPurifier($config); - $body = $purifier->purify($body); - $body = html2bbcode($body); - } - - $res['object'] .= '' . $body . '' . "\n"; - } - - $res['object'] .= '' . "\n"; - } - - $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target'); - - if($rawobj) { - $res['target'] = '' . "\n"; - $child = $rawobj[0]['child']; - if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { - $res['target'] .= '' . $child[NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '' . "\n"; - } - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) - $res['target'] .= '' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) - $res['target'] .= '' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) - $res['target'] .= '' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '' . "\n"; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; - if(! $body) - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; - // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events - $res['target'] .= '' . xmlify($body) . '' . "\n"; - if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - - $body = html2bb_video($body); - - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - - $purifier = new HTMLPurifier($config); - $body = $purifier->purify($body); - $body = html2bbcode($body); - } - - $res['target'] .= '' . $body . '' . "\n"; - } - - $res['target'] .= '' . "\n"; - } - - $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); - - call_hooks('parse_atom', $arr); - - return $res; -} - function add_page_info_data($data) { call_hooks('page_info_data', $data); @@ -697,37 +291,6 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) { return $body; } -function encode_rel_links($links) { - $o = ''; - if(! ((is_array($links)) && (count($links)))) - return $o; - foreach($links as $link) { - $o .= '') !== false)) @@ -884,6 +444,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa if ($notify) $guid_prefix = ""; + elseif ((trim($arr['guid']) == "") AND (trim($arr['plink']) != "")) + $arr['guid'] = uri_to_guid($arr['plink']); + elseif ((trim($arr['guid']) == "") AND (trim($arr['uri']) != "")) + $arr['guid'] = uri_to_guid($arr['uri']); else { $parsed = parse_url($arr["author-link"]); $guid_prefix = hash("crc32", $parsed["host"]); @@ -935,6 +499,10 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa $arr['inform'] = ((x($arr,'inform')) ? trim($arr['inform']) : ''); $arr['file'] = ((x($arr,'file')) ? trim($arr['file']) : ''); + + 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(); $arr['plink'] = $a->get_baseurl().'/display/'.urlencode($arr['guid']); @@ -1139,9 +707,9 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa if ($arr["uid"] == 0) { $arr["global"] = true; - q("UPDATE `item` SET `global` = 1 WHERE `guid` = '%s'", dbesc($arr["guid"])); + q("UPDATE `item` SET `global` = 1 WHERE `uri` = '%s'", dbesc($arr["uri"])); } else { - $isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `guid` = '%s'", dbesc($arr["guid"])); + $isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `uri` = '%s'", dbesc($arr["uri"])); $arr["global"] = (count($isglobal) > 0); } @@ -1194,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); @@ -1317,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); @@ -1681,7 +1243,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) //$tempfile = tempnam(get_temppath(), "ostatus2"); //file_put_contents($tempfile, $xml); logger("Consume OStatus messages ", LOGGER_DEBUG); - ostatus_import($xml,$importer,$contact, $hub); + ostatus::import($xml,$importer,$contact, $hub); } return; } @@ -1694,639 +1256,25 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) return; } - require_once('library/simplepie/simplepie.inc'); - require_once('include/contact_selectors.php'); - - if(! strlen($xml)) { - logger('consume_feed: empty input'); - return; - } - - $feed = new SimplePie(); - $feed->set_raw_data($xml); - if($datedir) - $feed->enable_order_by_date(true); - else - $feed->enable_order_by_date(false); - $feed->init(); - - if($feed->error()) - logger('consume_feed: Error parsing XML: ' . $feed->error()); - - $permalink = $feed->get_permalink(); - - // Check at the feed level for updated contact name and/or photo - - $name_updated = ''; - $new_name = ''; - $photo_timestamp = ''; - $photo_url = ''; - $birthday = ''; - $contact_updated = ''; - - $hubs = $feed->get_links('hub'); - logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA); - - if(count($hubs)) - $hub = implode(',', $hubs); - - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner'); - if(! $rawtags) - $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) { - $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; - $new_name = $elems['name'][0]['data']; - - // Manually checking for changed contact names - if (($new_name != $contact['name']) AND ($new_name != "") AND ($name_updated <= $contact['name-date'])) { - $name_updated = date("c"); - $photo_timestamp = date("c"); - } - } - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { - if ($photo_timestamp == "") - $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']); - $photo_url = $elems['link'][0]['attribs']['']['href']; - } - - if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) { - $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']); - } - } - - if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) { - logger('consume_feed: Updating photo for '.$contact['name'].' from '.$photo_url.' uid: '.$contact['uid']); - - $contact_updated = $photo_timestamp; - - require_once("include/Photo.php"); - $photos = import_profile_photo($photo_url,$contact['uid'],$contact['id']); - - q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s' - WHERE `uid` = %d AND `id` = %d AND NOT `self`", - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - intval($contact['uid']), - intval($contact['id']) - ); - } - - if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) { - if ($name_updated > $contact_updated) - $contact_updated = $name_updated; - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval($contact['uid']), - intval($contact['id']) - ); - - $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d AND `name` != '%s' AND NOT `self`", - dbesc(notags(trim($new_name))), - dbesc(datetime_convert()), - intval($contact['uid']), - intval($contact['id']), - dbesc(notags(trim($new_name))) - ); - - // do our best to update the name on content items - - if(count($r) AND (notags(trim($new_name)) != $r[0]['name'])) { - q("UPDATE `item` SET `author-name` = '%s' WHERE `author-name` = '%s' AND `author-link` = '%s' AND `uid` = %d AND `author-name` != '%s'", - dbesc(notags(trim($new_name))), - dbesc($r[0]['name']), - dbesc($r[0]['url']), - intval($contact['uid']), - dbesc(notags(trim($new_name))) - ); - } - } - - if ($contact_updated AND $new_name AND $photo_url) - poco_check($contact['url'], $new_name, NETWORK_DFRN, $photo_url, "", "", "", "", "", $contact_updated, 2, $contact['id'], $contact['uid']); - - if(strlen($birthday)) { - if(substr($birthday,0,4) != $contact['bdyear']) { - logger('consume_feed: updating birthday: ' . $birthday); - - /** - * - * Add new birthday event for this person - * - * $bdtext is just a readable placeholder in case the event is shared - * with others. We will replace it during presentation to our $importer - * to contain a sparkle link and perhaps a photo. - * - */ - - $bdtext = sprintf( t('%s\'s birthday'), $contact['name']); - $bdtext2 = sprintf( t('Happy Birthday %s'), ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ) ; - - - $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($contact['uid']), - intval($contact['id']), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert('UTC','UTC', $birthday)), - dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')), - dbesc($bdtext), - dbesc($bdtext2), - dbesc('birthday') - ); - - - // update bdyear - - q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d", - dbesc(substr($birthday,0,4)), - intval($contact['uid']), - intval($contact['id']) - ); - - // This function is called twice without reloading the contact - // Make sure we only create one event. This is why &$contact - // is a reference var in this function - - $contact['bdyear'] = substr($birthday,0,4); - } - } - - $community_page = 0; - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community'); - if($rawtags) { - $community_page = intval($rawtags[0]['data']); - } - if(is_array($contact) && intval($contact['forum']) != $community_page) { - q("update contact set forum = %d where id = %d", - intval($community_page), - intval($contact['id']) - ); - $contact['forum'] = (string) $community_page; - } - - - // process any deleted entries - - $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); - if(is_array($del_entries) && count($del_entries) && $pass != 2) { - foreach($del_entries as $dentry) { - $deleted = false; - if(isset($dentry['attribs']['']['ref'])) { - $uri = $dentry['attribs']['']['ref']; - $deleted = true; - if(isset($dentry['attribs']['']['when'])) { - $when = $dentry['attribs']['']['when']; - $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); - } - else - $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - } - if($deleted && is_array($contact)) { - $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN `contact` on `item`.`contact-id` = `contact`.`id` - WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", - dbesc($uri), - intval($importer['uid']), - intval($contact['id']) - ); - if(count($r)) { - $item = $r[0]; - - if(! $item['deleted']) - logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG); - - if($item['object-type'] === ACTIVITY_OBJ_EVENT) { - logger("Deleting event ".$item['event-id'], LOGGER_DEBUG); - event_delete($item['event-id']); - } - - if(($item['verb'] === ACTIVITY_TAG) && ($item['object-type'] === ACTIVITY_OBJ_TAGTERM)) { - $xo = parse_xml_string($item['object'],false); - $xt = parse_xml_string($item['target'],false); - if($xt->type === ACTIVITY_OBJ_NOTE) { - $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(count($i)) { - - // For tags, the owner cannot remove the tag on the author's copy of the post. - - $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false); - $author_remove = (($item['origin'] && $item['self']) ? true : false); - $author_copy = (($item['origin']) ? true : false); - - if($owner_remove && $author_copy) - continue; - if($author_remove || $owner_remove) { - $tags = explode(',',$i[0]['tag']); - $newtags = array(); - if(count($tags)) { - foreach($tags as $tag) - if(trim($tag) !== trim($xo->body)) - $newtags[] = trim($tag); - } - q("update item set tag = '%s' where id = %d", - dbesc(implode(',',$newtags)), - intval($i[0]['id']) - ); - create_tags_from_item($i[0]['id']); - } - } - } - } - - if($item['uri'] == $item['parent-uri']) { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `parent-uri` = '%s' AND `uid` = %d", - dbesc($when), - dbesc(datetime_convert()), - dbesc($item['uri']), - intval($importer['uid']) - ); - create_tags_from_itemuri($item['uri'], $importer['uid']); - create_files_from_itemuri($item['uri'], $importer['uid']); - update_thread_uri($item['uri'], $importer['uid']); - } - else { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `uri` = '%s' AND `uid` = %d", - dbesc($when), - dbesc(datetime_convert()), - dbesc($uri), - intval($importer['uid']) - ); - create_tags_from_itemuri($uri, $importer['uid']); - create_files_from_itemuri($uri, $importer['uid']); - if($item['last-child']) { - // ensure that last-child is set in case the comment that had it just got wiped. - q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", - dbesc(datetime_convert()), - dbesc($item['parent-uri']), - intval($item['uid']) - ); - // who is the last child now? - $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `moderated` = 0 AND `uid` = %d - ORDER BY `created` DESC LIMIT 1", - dbesc($item['parent-uri']), - intval($importer['uid']) - ); - if(count($r)) { - q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d", - intval($r[0]['id']) - ); - } - } - } - } - } - } - } - - // Now process the feed - - if($feed->get_item_quantity()) { - - logger('consume_feed: feed item count = ' . $feed->get_item_quantity()); - - // in inverse date order - if ($datedir) - $items = array_reverse($feed->get_items()); - else - $items = $feed->get_items(); - - - foreach($items as $item) { - - $is_reply = false; - $item_id = $item->get_id(); - $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); - if(isset($rawthread[0]['attribs']['']['ref'])) { - $is_reply = true; - $parent_uri = $rawthread[0]['attribs']['']['ref']; - } - - if(($is_reply) && is_array($contact)) { - - if($pass == 1) - continue; - - // not allowed to post - - if($contact['rel'] == CONTACT_IS_FOLLOWER) - continue; - - - // Have we seen it? If not, import it. - - $item_id = $item->get_id(); - $datarray = get_atom_elements($feed, $item, $contact); - - if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-name'] = $contact['name']; - if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-link'] = $contact['url']; - if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-avatar'] = $contact['thumb']; - - if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) { - logger('consume_feed: no author information! ' . print_r($datarray,true)); - continue; - } - - $force_parent = false; - if($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'],'twitter.com')) { - if($contact['network'] === NETWORK_OSTATUS) - $force_parent = true; - if(strlen($datarray['title'])) - unset($datarray['title']); - $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", - dbesc(datetime_convert()), - dbesc($parent_uri), - intval($importer['uid']) - ); - $datarray['last-child'] = 1; - update_thread_uri($parent_uri, $importer['uid']); - } - - - $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if (edited_timestamp_is_newer($r[0], $datarray)) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc($datarray['tag']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['uid']) - ); - create_tags_from_itemuri($item_id, $importer['uid']); - update_thread_uri($item_id, $importer['uid']); - } - - // update last-child if it changes - - $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); - if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) { - $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", - dbesc(datetime_convert()), - dbesc($parent_uri), - intval($importer['uid']) - ); - $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - intval($allow[0]['data']), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['uid']) - ); - update_thread_uri($item_id, $importer['uid']); - } - continue; - } - - - if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) { - // one way feed - no remote comment ability - $datarray['last-child'] = 0; - } - $datarray['parent-uri'] = $parent_uri; - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $contact['id']; - if(($datarray['verb'] === ACTIVITY_LIKE) - || ($datarray['verb'] === ACTIVITY_DISLIKE) - || ($datarray['verb'] === ACTIVITY_ATTEND) - || ($datarray['verb'] === ACTIVITY_ATTENDNO) - || ($datarray['verb'] === ACTIVITY_ATTENDMAYBE)) { - $datarray['type'] = 'activity'; - $datarray['gravity'] = GRAVITY_LIKE; - // only one like or dislike per person - // splitted into two queries for performance issues - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - ); - if($r && count($r)) - continue; - - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - ); - if($r && count($r)) - continue; - } - - if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) { - $xo = parse_xml_string($datarray['object'],false); - $xt = parse_xml_string($datarray['target'],false); - - if($xt->type == ACTIVITY_OBJ_NOTE) { - $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(! count($r)) - continue; - - // extract tag, if not duplicate, add to parent item - if($xo->id && $xo->content) { - $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]'; - if(! (stristr($r[0]['tag'],$newtag))) { - q("UPDATE item SET tag = '%s' WHERE id = %d", - dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag), - intval($r[0]['id']) - ); - create_tags_from_item($r[0]['id']); - } - } - } - } - - $r = item_store($datarray,$force_parent); - continue; - } - - else { - - // Head post of a conversation. Have we seen it? If not, import it. - - $item_id = $item->get_id(); - - $datarray = get_atom_elements($feed, $item, $contact); - - if(is_array($contact)) { - if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-name'] = $contact['name']; - if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-link'] = $contact['url']; - if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN)) - $datarray['author-avatar'] = $contact['thumb']; - } - - if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) { - logger('consume_feed: no author information! ' . print_r($datarray,true)); - continue; - } - - // special handling for events - - if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) { - $ev = bbtoevent($datarray['body']); - if((x($ev,'desc') || x($ev,'summary')) && x($ev,'start')) { - $ev['uid'] = $importer['uid']; - $ev['uri'] = $item_id; - $ev['edited'] = $datarray['edited']; - $ev['private'] = $datarray['private']; - $ev['guid'] = $datarray['guid']; - - if(is_array($contact)) - $ev['cid'] = $contact['id']; - $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['uid']) - ); - if(count($r)) - $ev['id'] = $r[0]['id']; - $xyz = event_store($ev); - continue; - } - } - - if($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'],'twitter.com')) { - if(strlen($datarray['title'])) - unset($datarray['title']); - $datarray['last-child'] = 1; - } - - - $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if (edited_timestamp_is_newer($r[0], $datarray)) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc($datarray['tag']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['uid']) - ); - create_tags_from_itemuri($item_id, $importer['uid']); - update_thread_uri($item_id, $importer['uid']); - } - - // update last-child if it changes - - $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); - if($allow && $allow[0]['data'] != $r[0]['last-child']) { - $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - intval($allow[0]['data']), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['uid']) - ); - update_thread_uri($item_id, $importer['uid']); - } - continue; - } - - if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) { - logger('consume-feed: New follower'); - new_follower($importer,$contact,$datarray,$item); - return; - } - if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW)) { - lose_follower($importer,$contact,$datarray,$item); - return; - } - - if(activity_match($datarray['verb'],ACTIVITY_REQ_FRIEND)) { - logger('consume-feed: New friend request'); - new_follower($importer,$contact,$datarray,$item,true); - return; - } - if(activity_match($datarray['verb'],ACTIVITY_UNFRIEND)) { - lose_sharer($importer,$contact,$datarray,$item); - return; - } - - - if(! is_array($contact)) - return; - - - if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) { - // one way feed - no remote comment ability - $datarray['last-child'] = 0; - } - if($contact['network'] === NETWORK_FEED) - $datarray['private'] = 2; - - $datarray['parent-uri'] = $item_id; - $datarray['uid'] = $importer['uid']; - $datarray['contact-id'] = $contact['id']; - - if(! link_compare($datarray['owner-link'],$contact['url'])) { - // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, - // but otherwise there's a possible data mixup on the sender's system. - // the tgroup delivery code called from item_store will correct it if it's a forum, - // but we're going to unconditionally correct it here so that the post will always be owned by our contact. - logger('consume_feed: Correcting item owner.', LOGGER_DEBUG); - $datarray['owner-name'] = $contact['name']; - $datarray['owner-link'] = $contact['url']; - $datarray['owner-avatar'] = $contact['thumb']; - } - - // We've allowed "followers" to reach this point so we can decide if they are - // posting an @-tag delivery, which followers are allowed to do for certain - // page types. Now that we've parsed the post, let's check if it is legit. Otherwise ignore it. - - if(($contact['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['uid'],$datarray))) - continue; - - // This is my contact on another system, but it's really me. - // Turn this into a wall post. - $notify = item_is_remote_self($contact, $datarray); - - $r = item_store($datarray, false, $notify); - logger('Stored - Contact '.$contact['url'].' Notify '.$notify.' return '.$r.' Item '.print_r($datarray, true), LOGGER_DEBUG); - continue; - - } + if ($contact['network'] === NETWORK_DFRN) { + logger("Consume DFRN messages", LOGGER_DEBUG); + + $r = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`, + `contact`.`pubkey` AS `cpubkey`, + `contact`.`prvkey` AS `cprvkey`, + `contact`.`thumb` AS `thumb`, + `contact`.`url` as `url`, + `contact`.`name` as `senderName`, + `user`.* + FROM `contact` + LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` + WHERE `contact`.`id` = %d AND `user`.`uid` = %d", + dbesc($contact["id"]), dbesc($importer["uid"]) + ); + if ($r) { + logger("Now import the DFRN feed"); + dfrn::import($xml,$r[0], true); + return; } } } @@ -2391,1068 +1339,6 @@ function item_is_remote_self($contact, &$datarray) { return true; } -function local_delivery($importer,$data) { - - require_once('library/simplepie/simplepie.inc'); - - $a = get_app(); - - logger(__function__, LOGGER_TRACE); - - if($importer['readonly']) { - // We aren't receiving stuff from this person. But we will quietly ignore them - // rather than a blatant "go away" message. - logger('local_delivery: ignoring'); - return 0; - //NOTREACHED - } - - // Consume notification feed. This may differ from consuming a public feed in several ways - // - might contain email or friend suggestions - // - might contain remote followup to our message - // - in which case we need to accept it and then notify other conversants - // - we may need to send various email notifications - - $feed = new SimplePie(); - $feed->set_raw_data($data); - $feed->enable_order_by_date(false); - $feed->init(); - - - if($feed->error()) - logger('local_delivery: Error parsing XML: ' . $feed->error()); - - - // Check at the feed level for updated contact name and/or photo - - $name_updated = ''; - $new_name = ''; - $photo_timestamp = ''; - $photo_url = ''; - $contact_updated = ''; - - - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner'); - -// Fallback should not be needed here. If it isn't DFRN it won't have DFRN updated tags -// if(! $rawtags) -// $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) { - $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; - $new_name = $elems['name'][0]['data']; - - // Manually checking for changed contact names - if (($new_name != $importer['name']) AND ($new_name != "") AND ($name_updated <= $importer['name-date'])) { - $name_updated = date("c"); - $photo_timestamp = date("c"); - } - } - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { - if ($photo_timestamp == "") - $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']); - $photo_url = $elems['link'][0]['attribs']['']['href']; - } - } - - if(($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $importer['avatar-date'])) { - - $contact_updated = $photo_timestamp; - - logger('local_delivery: Updating photo for ' . $importer['name']); - require_once("include/Photo.php"); - - $photos = import_profile_photo($photo_url,$importer['importer_uid'],$importer['id']); - - q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s' - WHERE `uid` = %d AND `id` = %d AND NOT `self`", - dbesc(datetime_convert()), - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - intval($importer['importer_uid']), - intval($importer['id']) - ); - } - - if(($name_updated) && (strlen($new_name)) && ($name_updated > $importer['name-date'])) { - if ($name_updated > $contact_updated) - $contact_updated = $name_updated; - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval($importer['importer_uid']), - intval($importer['id']) - ); - - $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d AND `name` != '%s' AND NOT `self`", - dbesc(notags(trim($new_name))), - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($importer['id']), - dbesc(notags(trim($new_name))) - ); - - // do our best to update the name on content items - - if(count($r) AND (notags(trim($new_name)) != $r[0]['name'])) { - q("UPDATE `item` SET `author-name` = '%s' WHERE `author-name` = '%s' AND `author-link` = '%s' AND `uid` = %d AND `author-name` != '%s'", - dbesc(notags(trim($new_name))), - dbesc($r[0]['name']), - dbesc($r[0]['url']), - intval($importer['importer_uid']), - dbesc(notags(trim($new_name))) - ); - } - } - - if ($contact_updated AND $new_name AND $photo_url) - poco_check($importer['url'], $new_name, NETWORK_DFRN, $photo_url, "", "", "", "", "", $contact_updated, 2, $importer['id'], $importer['importer_uid']); - - // Currently unsupported - needs a lot of work - $reloc = $feed->get_feed_tags( NAMESPACE_DFRN, 'relocate' ); - if(isset($reloc[0]['child'][NAMESPACE_DFRN])) { - $base = $reloc[0]['child'][NAMESPACE_DFRN]; - $newloc = array(); - $newloc['uid'] = $importer['importer_uid']; - $newloc['cid'] = $importer['id']; - $newloc['name'] = notags(unxmlify($base['name'][0]['data'])); - $newloc['photo'] = notags(unxmlify($base['photo'][0]['data'])); - $newloc['thumb'] = notags(unxmlify($base['thumb'][0]['data'])); - $newloc['micro'] = notags(unxmlify($base['micro'][0]['data'])); - $newloc['url'] = notags(unxmlify($base['url'][0]['data'])); - $newloc['request'] = notags(unxmlify($base['request'][0]['data'])); - $newloc['confirm'] = notags(unxmlify($base['confirm'][0]['data'])); - $newloc['notify'] = notags(unxmlify($base['notify'][0]['data'])); - $newloc['poll'] = notags(unxmlify($base['poll'][0]['data'])); - $newloc['sitepubkey'] = notags(unxmlify($base['sitepubkey'][0]['data'])); - /** relocated user must have original key pair */ - /*$newloc['pubkey'] = notags(unxmlify($base['pubkey'][0]['data'])); - $newloc['prvkey'] = notags(unxmlify($base['prvkey'][0]['data']));*/ - - logger("items:relocate contact ".print_r($newloc, true).print_r($importer, true), LOGGER_DEBUG); - - // update contact - $r = q("SELECT photo, url FROM contact WHERE id=%d AND uid=%d;", - intval($importer['id']), - intval($importer['importer_uid'])); - if ($r === false) - return 1; - $old = $r[0]; - - $x = q("UPDATE contact SET - name = '%s', - photo = '%s', - thumb = '%s', - micro = '%s', - url = '%s', - nurl = '%s', - request = '%s', - confirm = '%s', - notify = '%s', - poll = '%s', - `site-pubkey` = '%s' - WHERE id=%d AND uid=%d;", - dbesc($newloc['name']), - dbesc($newloc['photo']), - dbesc($newloc['thumb']), - dbesc($newloc['micro']), - dbesc($newloc['url']), - dbesc(normalise_link($newloc['url'])), - dbesc($newloc['request']), - dbesc($newloc['confirm']), - dbesc($newloc['notify']), - dbesc($newloc['poll']), - dbesc($newloc['sitepubkey']), - intval($importer['id']), - intval($importer['importer_uid'])); - - if ($x === false) - return 1; - // update items - $fields = array( - 'owner-link' => array($old['url'], $newloc['url']), - 'author-link' => array($old['url'], $newloc['url']), - 'owner-avatar' => array($old['photo'], $newloc['photo']), - 'author-avatar' => array($old['photo'], $newloc['photo']), - ); - foreach ($fields as $n=>$f){ - $x = q("UPDATE `item` SET `%s`='%s' WHERE `%s`='%s' AND uid=%d", - $n, dbesc($f[1]), - $n, dbesc($f[0]), - intval($importer['importer_uid'])); - if ($x === false) - return 1; - } - - /// @TODO - /// merge with current record, current contents have priority - /// update record, set url-updated - /// update profile photos - /// schedule a scan? - return 0; - } - - - // handle friend suggestion notification - - $sugg = $feed->get_feed_tags( NAMESPACE_DFRN, 'suggest' ); - if(isset($sugg[0]['child'][NAMESPACE_DFRN])) { - $base = $sugg[0]['child'][NAMESPACE_DFRN]; - $fsugg = array(); - $fsugg['uid'] = $importer['importer_uid']; - $fsugg['cid'] = $importer['id']; - $fsugg['name'] = notags(unxmlify($base['name'][0]['data'])); - $fsugg['photo'] = notags(unxmlify($base['photo'][0]['data'])); - $fsugg['url'] = notags(unxmlify($base['url'][0]['data'])); - $fsugg['request'] = notags(unxmlify($base['request'][0]['data'])); - $fsugg['body'] = escape_tags(unxmlify($base['note'][0]['data'])); - - // Does our member already have a friend matching this description? - - $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `nurl` = '%s' AND `uid` = %d LIMIT 1", - dbesc($fsugg['name']), - dbesc(normalise_link($fsugg['url'])), - intval($fsugg['uid']) - ); - if(count($r)) - return 0; - - // Do we already have an fcontact record for this person? - - $fid = 0; - $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", - dbesc($fsugg['url']), - dbesc($fsugg['name']), - dbesc($fsugg['request']) - ); - if(count($r)) { - $fid = $r[0]['id']; - - // OK, we do. Do we already have an introduction for this person ? - $r = q("select id from intro where uid = %d and fid = %d limit 1", - intval($fsugg['uid']), - intval($fid) - ); - if(count($r)) - return 0; - } - if(! $fid) - $r = q("INSERT INTO `fcontact` ( `name`,`url`,`photo`,`request` ) VALUES ( '%s', '%s', '%s', '%s' ) ", - dbesc($fsugg['name']), - dbesc($fsugg['url']), - dbesc($fsugg['photo']), - dbesc($fsugg['request']) - ); - $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", - dbesc($fsugg['url']), - dbesc($fsugg['name']), - dbesc($fsugg['request']) - ); - if(count($r)) { - $fid = $r[0]['id']; - } - // database record did not get created. Quietly give up. - else - return 0; - - - $hash = random_string(); - - $r = q("INSERT INTO `intro` ( `uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked` ) - VALUES( %d, %d, %d, '%s', '%s', '%s', %d )", - intval($fsugg['uid']), - intval($fid), - intval($fsugg['cid']), - dbesc($fsugg['body']), - dbesc($hash), - dbesc(datetime_convert()), - intval(0) - ); - - notification(array( - 'type' => NOTIFY_SUGGEST, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $fsugg, - 'link' => $a->get_baseurl() . '/notifications/intros', - 'source_name' => $importer['name'], - 'source_link' => $importer['url'], - 'source_photo' => $importer['photo'], - 'verb' => ACTIVITY_REQ_FRIEND, - 'otype' => 'intro' - )); - - return 0; - } - - $ismail = false; - - $rawmail = $feed->get_feed_tags( NAMESPACE_DFRN, 'mail' ); - if(isset($rawmail[0]['child'][NAMESPACE_DFRN])) { - - logger('local_delivery: private message received'); - - $ismail = true; - $base = $rawmail[0]['child'][NAMESPACE_DFRN]; - - $msg = array(); - $msg['uid'] = $importer['importer_uid']; - $msg['from-name'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['name'][0]['data'])); - $msg['from-photo'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['avatar'][0]['data'])); - $msg['from-url'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])); - $msg['contact-id'] = $importer['id']; - $msg['title'] = notags(unxmlify($base['subject'][0]['data'])); - $msg['body'] = escape_tags(unxmlify($base['content'][0]['data'])); - $msg['seen'] = 0; - $msg['replied'] = 0; - $msg['uri'] = notags(unxmlify($base['id'][0]['data'])); - $msg['parent-uri'] = notags(unxmlify($base['in-reply-to'][0]['data'])); - $msg['created'] = datetime_convert(notags(unxmlify('UTC','UTC',$base['sentdate'][0]['data']))); - - dbesc_array($msg); - - $r = dbq("INSERT INTO `mail` (`" . implode("`, `", array_keys($msg)) - . "`) VALUES ('" . implode("', '", array_values($msg)) . "')" ); - - // send notifications. - - require_once('include/enotify.php'); - - $notif_params = array( - 'type' => NOTIFY_MAIL, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $msg, - 'source_name' => $msg['from-name'], - 'source_link' => $importer['url'], - 'source_photo' => $importer['thumb'], - 'verb' => ACTIVITY_POST, - 'otype' => 'mail' - ); - - notification($notif_params); - return 0; - - // NOTREACHED - } - - $community_page = 0; - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community'); - if($rawtags) { - $community_page = intval($rawtags[0]['data']); - } - if(intval($importer['forum']) != $community_page) { - q("update contact set forum = %d where id = %d", - intval($community_page), - intval($importer['id']) - ); - $importer['forum'] = (string) $community_page; - } - - logger('local_delivery: feed item count = ' . $feed->get_item_quantity()); - - // process any deleted entries - - $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); - if(is_array($del_entries) && count($del_entries)) { - foreach($del_entries as $dentry) { - $deleted = false; - if(isset($dentry['attribs']['']['ref'])) { - $uri = $dentry['attribs']['']['ref']; - $deleted = true; - if(isset($dentry['attribs']['']['when'])) { - $when = $dentry['attribs']['']['when']; - $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); - } - else - $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - } - if($deleted) { - - // check for relayed deletes to our conversation - - $is_reply = false; - $r = q("select * from item where uri = '%s' and uid = %d limit 1", - dbesc($uri), - intval($importer['importer_uid']) - ); - if(count($r)) { - $parent_uri = $r[0]['parent-uri']; - if($r[0]['id'] != $r[0]['parent']) - $is_reply = true; - } - - if($is_reply) { - $community = false; - - if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) { - $sql_extra = ''; - $community = true; - logger('local_delivery: possible community delete'); - } - else - $sql_extra = " and contact.self = 1 and item.wall = 1 "; - - // was the top-level post for this reply written by somebody on this site? - // Specifically, the recipient? - - $is_a_remote_delete = false; - - // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used? - $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, - `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` - INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' or `item`.`thr-parent` = '%s') - AND `item`.`uid` = %d - $sql_extra - LIMIT 1", - dbesc($parent_uri), - dbesc($parent_uri), - dbesc($parent_uri), - intval($importer['importer_uid']) - ); - if($r && count($r)) - $is_a_remote_delete = true; - - // Does this have the characteristics of a community or private group comment? - // If it's a reply to a wall post on a community/prvgroup page it's a - // valid community comment. Also forum_mode makes it valid for sure. - // If neither, it's not. - - if($is_a_remote_delete && $community) { - if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) { - $is_a_remote_delete = false; - logger('local_delivery: not a community delete'); - } - } - - if($is_a_remote_delete) { - logger('local_delivery: received remote delete'); - } - } - - $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN contact on `item`.`contact-id` = `contact`.`id` - WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", - dbesc($uri), - intval($importer['importer_uid']), - intval($importer['id']) - ); - - if(count($r)) { - $item = $r[0]; - - if($item['deleted']) - continue; - - logger('local_delivery: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG); - - if($item['object-type'] === ACTIVITY_OBJ_EVENT) { - logger("Deleting event ".$item['event-id'], LOGGER_DEBUG); - event_delete($item['event-id']); - } - - if(($item['verb'] === ACTIVITY_TAG) && ($item['object-type'] === ACTIVITY_OBJ_TAGTERM)) { - $xo = parse_xml_string($item['object'],false); - $xt = parse_xml_string($item['target'],false); - - if($xt->type === ACTIVITY_OBJ_NOTE) { - $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(count($i)) { - - // For tags, the owner cannot remove the tag on the author's copy of the post. - - $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false); - $author_remove = (($item['origin'] && $item['self']) ? true : false); - $author_copy = (($item['origin']) ? true : false); - - if($owner_remove && $author_copy) - continue; - if($author_remove || $owner_remove) { - $tags = explode(',',$i[0]['tag']); - $newtags = array(); - if(count($tags)) { - foreach($tags as $tag) - if(trim($tag) !== trim($xo->body)) - $newtags[] = trim($tag); - } - q("update item set tag = '%s' where id = %d", - dbesc(implode(',',$newtags)), - intval($i[0]['id']) - ); - create_tags_from_item($i[0]['id']); - } - } - } - } - - if($item['uri'] == $item['parent-uri']) { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `parent-uri` = '%s' AND `uid` = %d", - dbesc($when), - dbesc(datetime_convert()), - dbesc($item['uri']), - intval($importer['importer_uid']) - ); - create_tags_from_itemuri($item['uri'], $importer['importer_uid']); - create_files_from_itemuri($item['uri'], $importer['importer_uid']); - update_thread_uri($item['uri'], $importer['importer_uid']); - } - else { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `uri` = '%s' AND `uid` = %d", - dbesc($when), - dbesc(datetime_convert()), - dbesc($uri), - intval($importer['importer_uid']) - ); - create_tags_from_itemuri($uri, $importer['importer_uid']); - create_files_from_itemuri($uri, $importer['importer_uid']); - update_thread_uri($uri, $importer['importer_uid']); - if($item['last-child']) { - // ensure that last-child is set in case the comment that had it just got wiped. - q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", - dbesc(datetime_convert()), - dbesc($item['parent-uri']), - intval($item['uid']) - ); - // who is the last child now? - $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d - ORDER BY `created` DESC LIMIT 1", - dbesc($item['parent-uri']), - intval($importer['importer_uid']) - ); - if(count($r)) { - q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d", - intval($r[0]['id']) - ); - } - } - // if this is a relayed delete, propagate it to other recipients - - if($is_a_remote_delete) - proc_run('php',"include/notifier.php","drop",$item['id']); - } - } - } - } - } - - - foreach($feed->get_items() as $item) { - - $is_reply = false; - $item_id = $item->get_id(); - $rawthread = $item->get_item_tags( NAMESPACE_THREAD, 'in-reply-to'); - if(isset($rawthread[0]['attribs']['']['ref'])) { - $is_reply = true; - $parent_uri = $rawthread[0]['attribs']['']['ref']; - } - - if($is_reply) { - $community = false; - - if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) { - $sql_extra = ''; - $community = true; - logger('local_delivery: possible community reply'); - } - else - $sql_extra = " and contact.self = 1 and item.wall = 1 "; - - // was the top-level post for this reply written by somebody on this site? - // Specifically, the recipient? - - $is_a_remote_comment = false; - $top_uri = $parent_uri; - - $r = q("select `item`.`parent-uri` from `item` - WHERE `item`.`uri` = '%s' - LIMIT 1", - dbesc($parent_uri) - ); - if($r && count($r)) { - $top_uri = $r[0]['parent-uri']; - - // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used? - $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, - `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` - INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' or `item`.`thr-parent` = '%s') - AND `item`.`uid` = %d - $sql_extra - LIMIT 1", - dbesc($top_uri), - dbesc($top_uri), - dbesc($top_uri), - intval($importer['importer_uid']) - ); - if($r && count($r)) - $is_a_remote_comment = true; - } - - // Does this have the characteristics of a community or private group comment? - // If it's a reply to a wall post on a community/prvgroup page it's a - // valid community comment. Also forum_mode makes it valid for sure. - // If neither, it's not. - - if($is_a_remote_comment && $community) { - if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) { - $is_a_remote_comment = false; - logger('local_delivery: not a community reply'); - } - } - - if($is_a_remote_comment) { - logger('local_delivery: received remote comment'); - $is_like = false; - // remote reply to our post. Import and then notify everybody else. - - $datarray = get_atom_elements($feed, $item); - - $r = q("SELECT `id`, `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - $iid = $r[0]['id']; - if (edited_timestamp_is_newer($r[0], $datarray)) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - logger('received updated comment' , LOGGER_DEBUG); - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc($datarray['tag']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['importer_uid']) - ); - create_tags_from_itemuri($item_id, $importer['importer_uid']); - - proc_run('php',"include/notifier.php","comment-import",$iid); - - } - - continue; - } - - - - $own = q("select name,url,thumb from contact where uid = %d and self = 1 limit 1", - intval($importer['importer_uid']) - ); - - - $datarray['type'] = 'remote-comment'; - $datarray['wall'] = 1; - $datarray['parent-uri'] = $parent_uri; - $datarray['uid'] = $importer['importer_uid']; - $datarray['owner-name'] = $own[0]['name']; - $datarray['owner-link'] = $own[0]['url']; - $datarray['owner-avatar'] = $own[0]['thumb']; - $datarray['contact-id'] = $importer['id']; - - if(($datarray['verb'] === ACTIVITY_LIKE) - || ($datarray['verb'] === ACTIVITY_DISLIKE) - || ($datarray['verb'] === ACTIVITY_ATTEND) - || ($datarray['verb'] === ACTIVITY_ATTENDNO) - || ($datarray['verb'] === ACTIVITY_ATTENDMAYBE)) { - $is_like = true; - $datarray['type'] = 'activity'; - $datarray['gravity'] = GRAVITY_LIKE; - $datarray['last-child'] = 0; - // only one like or dislike per person - // splitted into two queries for performance issues - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - ); - if($r && count($r)) - continue; - - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - - ); - if($r && count($r)) - continue; - } - - if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) { - - $xo = parse_xml_string($datarray['object'],false); - $xt = parse_xml_string($datarray['target'],false); - - if(($xt->type == ACTIVITY_OBJ_NOTE) && ($xt->id)) { - - // fetch the parent item - - $tagp = q("select * from item where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(! count($tagp)) - continue; - - // extract tag, if not duplicate, and this user allows tags, add to parent item - - if($xo->id && $xo->content) { - $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]'; - if(! (stristr($tagp[0]['tag'],$newtag))) { - $i = q("SELECT `blocktags` FROM `user` where `uid` = %d LIMIT 1", - intval($importer['importer_uid']) - ); - if(count($i) && ! intval($i[0]['blocktags'])) { - q("UPDATE item SET tag = '%s', `edited` = '%s', `changed` = '%s' WHERE id = %d", - dbesc($tagp[0]['tag'] . (strlen($tagp[0]['tag']) ? ',' : '') . $newtag), - intval($tagp[0]['id']), - dbesc(datetime_convert()), - dbesc(datetime_convert()) - ); - create_tags_from_item($tagp[0]['id']); - } - } - } - } - } - - - $posted_id = item_store($datarray); - $parent = 0; - - if($posted_id) { - - $datarray["id"] = $posted_id; - - $r = q("SELECT `parent`, `parent-uri` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($posted_id), - intval($importer['importer_uid']) - ); - if(count($r)) { - $parent = $r[0]['parent']; - $parent_uri = $r[0]['parent-uri']; - } - - if(! $is_like) { - $r1 = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `uid` = %d AND `parent` = %d", - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($r[0]['parent']) - ); - - $r2 = q("UPDATE `item` SET `last-child` = 1, `changed` = '%s' WHERE `uid` = %d AND `id` = %d", - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($posted_id) - ); - } - - if($posted_id && $parent) { - proc_run('php',"include/notifier.php","comment-import","$posted_id"); - } - - return 0; - // NOTREACHED - } - } - else { - - // regular comment that is part of this total conversation. Have we seen it? If not, import it. - - $item_id = $item->get_id(); - $datarray = get_atom_elements($feed,$item); - - if($importer['rel'] == CONTACT_IS_FOLLOWER) - continue; - - $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if (edited_timestamp_is_newer($r[0], $datarray)) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc($datarray['tag']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['importer_uid']) - ); - create_tags_from_itemuri($item_id, $importer['importer_uid']); - } - - // update last-child if it changes - - $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); - if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) { - $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", - dbesc(datetime_convert()), - dbesc($parent_uri), - intval($importer['importer_uid']) - ); - $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - intval($allow[0]['data']), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['importer_uid']) - ); - } - continue; - } - - $datarray['parent-uri'] = $parent_uri; - $datarray['uid'] = $importer['importer_uid']; - $datarray['contact-id'] = $importer['id']; - if(($datarray['verb'] === ACTIVITY_LIKE) - || ($datarray['verb'] === ACTIVITY_DISLIKE) - || ($datarray['verb'] === ACTIVITY_ATTEND) - || ($datarray['verb'] === ACTIVITY_ATTENDNO) - || ($datarray['verb'] === ACTIVITY_ATTENDMAYBE)) { - $datarray['type'] = 'activity'; - $datarray['gravity'] = GRAVITY_LIKE; - // only one like or dislike per person - // splitted into two queries for performance issues - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `parent-uri` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - ); - if($r && count($r)) - continue; - - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `author-link` = '%s' AND `verb` = '%s' AND `thr-parent` = '%s' AND NOT `deleted` LIMIT 1", - intval($datarray['uid']), - dbesc($datarray['author-link']), - dbesc($datarray['verb']), - dbesc($datarray['parent-uri']) - ); - if($r && count($r)) - continue; - - } - - if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) { - - $xo = parse_xml_string($datarray['object'],false); - $xt = parse_xml_string($datarray['target'],false); - - if($xt->type == ACTIVITY_OBJ_NOTE) { - $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(! count($r)) - continue; - - // extract tag, if not duplicate, add to parent item - if($xo->content) { - if(! (stristr($r[0]['tag'],trim($xo->content)))) { - q("UPDATE item SET tag = '%s' WHERE id = %d", - dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'), - intval($r[0]['id']) - ); - create_tags_from_item($r[0]['id']); - } - } - } - } - - $posted_id = item_store($datarray); - - continue; - } - } - - else { - - // Head post of a conversation. Have we seen it? If not, import it. - - - $item_id = $item->get_id(); - $datarray = get_atom_elements($feed,$item); - - if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) { - $ev = bbtoevent($datarray['body']); - if((x($ev,'desc') || x($ev,'summary')) && x($ev,'start')) { - $ev['cid'] = $importer['id']; - $ev['uid'] = $importer['uid']; - $ev['uri'] = $item_id; - $ev['edited'] = $datarray['edited']; - $ev['private'] = $datarray['private']; - $ev['guid'] = $datarray['guid']; - - $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['uid']) - ); - if(count($r)) - $ev['id'] = $r[0]['id']; - $xyz = event_store($ev); - continue; - } - } - - $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if (edited_timestamp_is_newer($r[0], $datarray)) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s', `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc($datarray['tag']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['importer_uid']) - ); - create_tags_from_itemuri($item_id, $importer['importer_uid']); - update_thread_uri($item_id, $importer['importer_uid']); - } - - // update last-child if it changes - - $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); - if($allow && $allow[0]['data'] != $r[0]['last-child']) { - $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d", - intval($allow[0]['data']), - dbesc(datetime_convert()), - dbesc($item_id), - intval($importer['importer_uid']) - ); - } - continue; - } - - $datarray['parent-uri'] = $item_id; - $datarray['uid'] = $importer['importer_uid']; - $datarray['contact-id'] = $importer['id']; - - - if(! link_compare($datarray['owner-link'],$importer['url'])) { - // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, - // but otherwise there's a possible data mixup on the sender's system. - // the tgroup delivery code called from item_store will correct it if it's a forum, - // but we're going to unconditionally correct it here so that the post will always be owned by our contact. - logger('local_delivery: Correcting item owner.', LOGGER_DEBUG); - $datarray['owner-name'] = $importer['senderName']; - $datarray['owner-link'] = $importer['url']; - $datarray['owner-avatar'] = $importer['thumb']; - } - - if(($importer['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['importer_uid'],$datarray))) - continue; - - // This is my contact on another system, but it's really me. - // Turn this into a wall post. - $notify = item_is_remote_self($importer, $datarray); - - $posted_id = item_store($datarray, false, $notify); - - if(stristr($datarray['verb'],ACTIVITY_POKE)) { - $verb = urldecode(substr($datarray['verb'],strpos($datarray['verb'],'#')+1)); - if(! $verb) - continue; - $xo = parse_xml_string($datarray['object'],false); - - if(($xo->type == ACTIVITY_OBJ_PERSON) && ($xo->id)) { - - // somebody was poked/prodded. Was it me? - - $links = parse_xml_string("".unxmlify($xo->link)."",false); - - foreach($links->link as $l) { - $atts = $l->attributes(); - switch($atts['rel']) { - case "alternate": - $Blink = $atts['href']; - break; - default: - break; - } - } - if($Blink && link_compare($Blink,$a->get_baseurl() . '/profile/' . $importer['nickname'])) { - - // send a notification - require_once('include/enotify.php'); - - notification(array( - 'type' => NOTIFY_POKE, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $datarray, - 'link' => $a->get_baseurl().'/display/'.urlencode(get_item_guid($posted_id)), - 'source_name' => stripslashes($datarray['author-name']), - 'source_link' => $datarray['author-link'], - 'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) - ? $importer['thumb'] : $datarray['author-avatar']), - 'verb' => $datarray['verb'], - 'otype' => 'person', - 'activity' => $verb, - 'parent' => $datarray['parent'] - )); - } - } - } - - continue; - } - } - - return 0; - // NOTREACHED - -} - - function new_follower($importer,$contact,$datarray,$item,$sharing = false) { $url = notags(trim($datarray['author-link'])); $name = notags(trim($datarray['author-name'])); @@ -3563,7 +1449,7 @@ function new_follower($importer,$contact,$datarray,$item,$sharing = false) { } } -function lose_follower($importer,$contact,$datarray,$item) { +function lose_follower($importer,$contact,$datarray = array(),$item = "") { if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_SHARING)) { q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d", @@ -3576,7 +1462,7 @@ function lose_follower($importer,$contact,$datarray,$item) { } } -function lose_sharer($importer,$contact,$datarray,$item) { +function lose_sharer($importer,$contact,$datarray = array(),$item = "") { if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_FOLLOWER)) { q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d", @@ -4094,9 +1980,6 @@ function drop_item($id,$interactive = true) { intval($r[0]['id']) ); } - - // Add a relayable_retraction signature for Diaspora. - store_diaspora_retract_sig($item, $a->user, $a->get_baseurl()); } $drop_id = intval($item['id']); @@ -4229,51 +2112,3 @@ function posted_date_widget($url,$uid,$wall) { )); return $o; } - -function store_diaspora_retract_sig($item, $user, $baseurl) { - // Note that we can't add a target_author_signature - // if the comment was deleted by a remote user. That should be ok, because if a remote user is deleting - // the comment, that means we're the home of the post, and Diaspora will only - // check the parent_author_signature of retractions that it doesn't have to relay further - // - // I don't think this function gets called for an "unlike," but I'll check anyway - - $enabled = intval(get_config('system','diaspora_enabled')); - if(! $enabled) { - logger('drop_item: diaspora support disabled, not storing retraction signature', LOGGER_DEBUG); - return; - } - - logger('drop_item: storing diaspora retraction signature'); - - $signed_text = $item['guid'] . ';' . ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment'); - - if(local_user() == $item['uid']) { - - $handle = $user['nickname'] . '@' . substr($baseurl, strpos($baseurl,'://') + 3); - $authorsig = base64_encode(rsa_sign($signed_text,$user['prvkey'],'sha256')); - } - else { - $r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1", - $item['contact-id'] // If this function gets called, drop_item() has already checked remote_user() == $item['contact-id'] - ); - if(count($r)) { - // The below handle only works for NETWORK_DFRN. I think that's ok, because this function - // only handles DFRN deletes - $handle_baseurl_start = strpos($r['url'],'://') + 3; - $handle_baseurl_length = strpos($r['url'],'/profile') - $handle_baseurl_start; - $handle = $r['nick'] . '@' . substr($r['url'], $handle_baseurl_start, $handle_baseurl_length); - $authorsig = ''; - } - } - - if(isset($handle)) - q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($item['id']), - dbesc($signed_text), - dbesc($authorsig), - dbesc($handle) - ); - - return; -} diff --git a/include/like.php b/include/like.php index 646e0727be..15633fc767 100644 --- a/include/like.php +++ b/include/like.php @@ -1,4 +1,5 @@ 0)) { - $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1", - intval($contact['uid']) - ); - - if($r) - $authorsig = base64_encode(rsa_sign($signed_text,$r[0]['prvkey'],'sha256')); - } - - if(! isset($authorsig)) - $authorsig = ''; - - q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($like_item['id']), - dbesc($signed_text), - dbesc($authorsig), - dbesc($diaspora_handle) - ); - } - - return; -} - -function store_diaspora_like_sig($activity, $post_type, $contact, $post_id) { - // Note that we can only create a signature for a user of the local server. We don't have - // a key for remote users. That is ok, because if a remote user is "unlike"ing a post, it - // means we are the relay, and for relayable_retractions, Diaspora - // only checks the parent_author_signature if it doesn't have to relay further - - $enabled = intval(get_config('system','diaspora_enabled')); - if(! $enabled) { - logger('mod_like: diaspora support disabled, not storing like signature', LOGGER_DEBUG); - return; - } - - logger('mod_like: storing diaspora like signature'); - - if(($activity === ACTIVITY_LIKE) && ($post_type === t('status'))) { - // Only works for NETWORK_DFRN - $contact_baseurl_start = strpos($contact['url'],'://') + 3; - $contact_baseurl_length = strpos($contact['url'],'/profile') - $contact_baseurl_start; - $contact_baseurl = substr($contact['url'], $contact_baseurl_start, $contact_baseurl_length); - $diaspora_handle = $contact['nick'] . '@' . $contact_baseurl; - - - // This code could never had worked (the return values form the queries were used in a wrong way. - // Additionally it is needlessly complicated. Either the contact is owner or not. And we have this data already. -/* - // Get contact's private key if he's a user of the local Friendica server - $r = q("SELECT `contact`.`uid` FROM `contact` WHERE `url` = '%s' AND `self` = 1 LIMIT 1", - dbesc($contact['url']) - ); - - if( $r) { - $contact_uid = $r['uid']; - $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1", - intval($contact_uid) - ); - - if( $r) - $contact_uprvkey = $r['prvkey']; - } -*/ - - // Is the contact the owner? Then fetch the private key - if ($contact['self'] AND ($contact['uid'] > 0)) { - $r = q("SELECT prvkey FROM user WHERE uid = %d LIMIT 1", - intval($contact['uid']) - ); - - if($r) - $contact_uprvkey = $r[0]['prvkey']; - } - - $r = q("SELECT guid, parent FROM `item` WHERE id = %d LIMIT 1", - intval($post_id) - ); - if( $r) { - $p = q("SELECT guid FROM `item` WHERE id = %d AND parent = %d LIMIT 1", - intval($r[0]['parent']), - intval($r[0]['parent']) - ); - if( $p) { - $signed_text = 'true;'.$r[0]['guid'].';Post;'.$p[0]['guid'].';'.$diaspora_handle; - - if(isset($contact_uprvkey)) - $authorsig = base64_encode(rsa_sign($signed_text,$contact_uprvkey,'sha256')); - else - $authorsig = ''; - - q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($post_id), - dbesc($signed_text), - dbesc($authorsig), - dbesc($diaspora_handle) - ); - } - } - } - - return; -} diff --git a/include/nav.php b/include/nav.php index a90e850fe0..8428b6b0fd 100644 --- a/include/nav.php +++ b/include/nav.php @@ -85,7 +85,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'], ); @@ -110,7 +110,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')); diff --git a/include/network.php b/include/network.php index 84f1297e03..5fd73bfef9 100644 --- a/include/network.php +++ b/include/network.php @@ -862,64 +862,6 @@ function parse_xml_string($s,$strict = true) { return $x; }} -function add_fcontact($arr,$update = false) { - - if($update) { - $r = q("UPDATE `fcontact` SET - `name` = '%s', - `photo` = '%s', - `request` = '%s', - `nick` = '%s', - `addr` = '%s', - `batch` = '%s', - `notify` = '%s', - `poll` = '%s', - `confirm` = '%s', - `alias` = '%s', - `pubkey` = '%s', - `updated` = '%s' - WHERE `url` = '%s' AND `network` = '%s'", - dbesc($arr['name']), - dbesc($arr['photo']), - dbesc($arr['request']), - dbesc($arr['nick']), - dbesc($arr['addr']), - dbesc($arr['batch']), - dbesc($arr['notify']), - dbesc($arr['poll']), - dbesc($arr['confirm']), - dbesc($arr['alias']), - dbesc($arr['pubkey']), - dbesc(datetime_convert()), - dbesc($arr['url']), - dbesc($arr['network']) - ); - } - else { - $r = q("insert into fcontact ( `url`,`name`,`photo`,`request`,`nick`,`addr`, - `batch`, `notify`,`poll`,`confirm`,`network`,`alias`,`pubkey`,`updated` ) - values('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", - dbesc($arr['url']), - dbesc($arr['name']), - dbesc($arr['photo']), - dbesc($arr['request']), - dbesc($arr['nick']), - dbesc($arr['addr']), - dbesc($arr['batch']), - dbesc($arr['notify']), - dbesc($arr['poll']), - dbesc($arr['confirm']), - dbesc($arr['network']), - dbesc($arr['alias']), - dbesc($arr['pubkey']), - dbesc(datetime_convert()) - ); - } - - return $r; -} - - function scale_external_images($srctext, $include_link = true, $scale_replace = false) { // Suppress "view full size" diff --git a/include/notifier.php b/include/notifier.php index 6c42f19c6a..ffbb22e7bf 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -223,13 +223,13 @@ function notifier_run(&$argv, &$argc){ if(! ($mail || $fsuggest || $relocate)) { - $slap = ostatus_salmon($target_item,$owner); + $slap = ostatus::salmon($target_item,$owner); require_once('include/group.php'); $parent = $items[0]; - $thr_parent = q("SELECT `network` FROM `item` WHERE `uri` = '%s' AND `uid` = %d", + $thr_parent = q("SELECT `network`, `author-link`, `owner-link` FROM `item` WHERE `uri` = '%s' AND `uid` = %d", dbesc($target_item["thr-parent"]), intval($target_item["uid"])); logger('Parent is '.$parent['network'].'. Thread parent is '.$thr_parent[0]['network'], LOGGER_DEBUG); @@ -390,6 +390,20 @@ function notifier_run(&$argv, &$argc){ logger('Some parent is OStatus for '.$target_item["guid"], LOGGER_DEBUG); + // Send a salmon to the parent author + $probed_contact = probe_url($thr_parent[0]['author-link']); + if ($probed_contact["notify"] != "") { + logger('Notify parent author '.$probed_contact["url"].': '.$probed_contact["notify"]); + $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; + } + + // Send a salmon to the parent owner + $probed_contact = probe_url($thr_parent[0]['owner-link']); + if ($probed_contact["notify"] != "") { + logger('Notify parent owner '.$probed_contact["url"].': '.$probed_contact["notify"]); + $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; + } + // Send a salmon notification to every person we mentioned in the post $arr = explode(',',$target_item['tag']); foreach($arr as $x) { @@ -536,7 +550,7 @@ function notifier_run(&$argv, &$argc){ if($public_message) { if (!$followup AND $top_level) - $r0 = diaspora_fetch_relay(); + $r0 = diaspora::relay_list(); else $r0 = array(); @@ -628,13 +642,6 @@ function notifier_run(&$argv, &$argc){ proc_run('php','include/pubsubpublish.php'); } - // If the item was deleted, clean up the `sign` table - if($target_item['deleted']) { - $r = q("DELETE FROM sign where `retract_iid` = %d", - intval($target_item['id']) - ); - } - logger('notifier: calling hooks', LOGGER_DEBUG); if($normal_mode) diff --git a/include/onepoll.php b/include/onepoll.php index 6fb191f73d..eb1045de14 100644 --- a/include/onepoll.php +++ b/include/onepoll.php @@ -31,7 +31,6 @@ function onepoll_run(&$argv, &$argc){ require_once('include/Contact.php'); require_once('include/email.php'); require_once('include/socgraph.php'); - require_once('include/pidfile.php'); require_once('include/queue_fn.php'); load_config('config'); @@ -60,18 +59,10 @@ function onepoll_run(&$argv, &$argc){ return; } - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'onepoll'.$contact_id); - if ($pidfile->is_already_running()) { - logger("onepoll: Already running for contact ".$contact_id); - if ($pidfile->running_time() > 9*60) { - $pidfile->kill(); - logger("killed stale process"); - } - exit; - } - } + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::is_already_running('onepoll'.$contact_id, '', 540)) + return; $d = datetime_convert(); diff --git a/include/ostatus.php b/include/ostatus.php index 37b308db70..b798a605f9 100644 --- a/include/ostatus.php +++ b/include/ostatus.php @@ -1,4 +1,8 @@ evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; + $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue; - if (!$r) - return; + $aliaslink = $author["author-link"]; - foreach ($r AS $contact) { - ostatus_follow_friends($contact["uid"], $contact["v"]); - set_pconfig($contact["uid"], "system", "ostatus_legacy_contact", ""); - } -} + $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes; + if (is_object($alternate)) + foreach($alternate AS $attributes) + if ($attributes->name == "href") + $author["author-link"] = $attributes->textContent; -// This function doesn't work reliable by now. -function ostatus_follow_friends($uid, $url) { - $contact = probe_url($url); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'", + intval($importer["uid"]), dbesc(normalise_link($author["author-link"])), + dbesc(normalise_link($aliaslink)), dbesc(NETWORK_STATUSNET)); + if ($r) { + $contact = $r[0]; + $author["contact-id"] = $r[0]["id"]; + } else + $author["contact-id"] = $contact["id"]; - if (!$contact) - return; - - $api = $contact["baseurl"]."/api/"; - - // Fetching friends - $data = z_fetch_url($api."statuses/friends.json?screen_name=".$contact["nick"]); - - if (!$data["success"]) - return; - - $friends = json_decode($data["body"]); - - foreach ($friends AS $friend) { - $url = $friend->statusnet_profile_url; - $r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND - (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND - `network` != '%s' LIMIT 1", - intval($uid), dbesc(normalise_link($url)), - dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET)); - if (!$r) { - $data = probe_url($friend->statusnet_profile_url); - if ($data["network"] == NETWORK_OSTATUS) { - $result = new_contact($uid,$friend->statusnet_profile_url); - if ($result["success"]) - logger($friend->name." ".$url." - success", LOGGER_DEBUG); - else - logger($friend->name." ".$url." - failed", LOGGER_DEBUG); - } else - logger($friend->name." ".$url." - not OStatus", LOGGER_DEBUG); + $avatarlist = array(); + $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context); + foreach($avatars AS $avatar) { + $href = ""; + $width = 0; + foreach($avatar->attributes AS $attributes) { + if ($attributes->name == "href") + $href = $attributes->textContent; + if ($attributes->name == "width") + $width = $attributes->textContent; + } + if (($width > 0) AND ($href != "")) + $avatarlist[$width] = $href; } - } -} - -function ostatus_fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch) { - - $author = array(); - $author["author-link"] = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; - $author["author-name"] = $xpath->evaluate('atom:author/atom:name/text()', $context)->item(0)->nodeValue; - - // Preserve the value - $authorlink = $author["author-link"]; - - $alternate = $xpath->query("atom:author/atom:link[@rel='alternate']", $context)->item(0)->attributes; - if (is_object($alternate)) - foreach($alternate AS $attributes) - if ($attributes->name == "href") - $author["author-link"] = $attributes->textContent; - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` IN ('%s', '%s') AND `network` != '%s'", - intval($importer["uid"]), dbesc(normalise_link($author["author-link"])), - dbesc(normalise_link($authorlink)), dbesc(NETWORK_STATUSNET)); - if ($r) { - $contact = $r[0]; - $author["contact-id"] = $r[0]["id"]; - } else - $author["contact-id"] = $contact["id"]; - - $avatarlist = array(); - $avatars = $xpath->query("atom:author/atom:link[@rel='avatar']", $context); - foreach($avatars AS $avatar) { - $href = ""; - $width = 0; - foreach($avatar->attributes AS $attributes) { - if ($attributes->name == "href") - $href = $attributes->textContent; - if ($attributes->name == "width") - $width = $attributes->textContent; - } - if (($width > 0) AND ($href != "")) - $avatarlist[$width] = $href; - } - if (count($avatarlist) > 0) { - krsort($avatarlist); - $author["author-avatar"] = current($avatarlist); - } - - $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; - if ($displayname != "") - $author["author-name"] = $displayname; - - $author["owner-name"] = $author["author-name"]; - $author["owner-link"] = $author["author-link"]; - $author["owner-avatar"] = $author["author-avatar"]; - - // Only update the contacts if it is an OStatus contact - if ($r AND !$onlyfetch AND ($contact["network"] == NETWORK_OSTATUS)) { - // Update contact data - - $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue; - if ($value != "") - $contact["notify"] = $value; - - $value = $xpath->evaluate('atom:author/uri/text()', $context)->item(0)->nodeValue; - if ($value != "") - $contact["alias"] = $value; - - $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; - if ($value != "") - $contact["name"] = $value; - - $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; - if ($value != "") - $contact["nick"] = $value; - - $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue; - if ($value != "") - $contact["about"] = html2bbcode($value); - - $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue; - if ($value != "") - $contact["location"] = $value; - - if (($contact["name"] != $r[0]["name"]) OR ($contact["nick"] != $r[0]["nick"]) OR ($contact["about"] != $r[0]["about"]) OR ($contact["location"] != $r[0]["location"])) { - - logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG); - - q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d", - dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]), - dbesc(datetime_convert()), intval($contact["id"])); - - poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"], - "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]); + if (count($avatarlist) > 0) { + krsort($avatarlist); + $author["author-avatar"] = current($avatarlist); } - if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) { - logger("Update profile picture for contact ".$contact["id"], LOGGER_DEBUG); + $displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; + if ($displayname != "") + $author["author-name"] = $displayname; - update_contact_avatar($author["author-avatar"], $importer["uid"], $contact["id"]); - } + $author["owner-name"] = $author["author-name"]; + $author["owner-link"] = $author["author-link"]; + $author["owner-avatar"] = $author["author-avatar"]; + // Only update the contacts if it is an OStatus contact + if ($r AND !$onlyfetch AND ($contact["network"] == NETWORK_OSTATUS)) { - /// @todo Add the "addr" field - $contact["generation"] = 2; - $contact["photo"] = $author["author-avatar"]; - update_gcontact($contact); - } + // Update contact data - return($author); -} + // This query doesn't seem to work + // $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue; + // if ($value != "") + // $contact["notify"] = $value; -function ostatus_salmon_author($xml, $importer) { - $a = get_app(); + // This query doesn't seem to work as well - I hate these queries + // $value = $xpath->query("atom:link[@rel='self' and @type='application/atom+xml']", $context)->item(0)->nodeValue; + // if ($value != "") + // $contact["poll"] = $value; - if ($xml == "") - return; + $value = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["alias"] = $value; - $doc = new DOMDocument(); - @$doc->loadXML($xml); + $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["name"] = $value; - $xpath = new DomXPath($doc); - $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); - $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0"); - $xpath->registerNamespace('georss', "http://www.georss.org/georss"); - $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/"); - $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia"); - $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0"); - $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0"); - $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/"); + $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["nick"] = $value; - $entries = $xpath->query('/atom:entry'); + $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["about"] = html2bbcode($value); - foreach ($entries AS $entry) { - // fetch the author - $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, true); - return $author; - } -} + $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["location"] = $value; -function ostatus_import($xml,$importer,&$contact, &$hub) { + if (($contact["name"] != $r[0]["name"]) OR ($contact["nick"] != $r[0]["nick"]) OR ($contact["about"] != $r[0]["about"]) OR + ($contact["alias"] != $r[0]["alias"]) OR ($contact["location"] != $r[0]["location"])) { - $a = get_app(); + logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG); - logger("Import OStatus message", LOGGER_DEBUG); + q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `alias` = '%s', `about` = '%s', `location` = '%s', `name-date` = '%s' WHERE `id` = %d", + dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["alias"]), + dbesc($contact["about"]), dbesc($contact["location"]), + dbesc(datetime_convert()), intval($contact["id"])); - if ($xml == "") - return; - - $doc = new DOMDocument(); - @$doc->loadXML($xml); - - $xpath = new DomXPath($doc); - $xpath->registerNamespace('atom', "http://www.w3.org/2005/Atom"); - $xpath->registerNamespace('thr', "http://purl.org/syndication/thread/1.0"); - $xpath->registerNamespace('georss', "http://www.georss.org/georss"); - $xpath->registerNamespace('activity', "http://activitystrea.ms/spec/1.0/"); - $xpath->registerNamespace('media', "http://purl.org/syndication/atommedia"); - $xpath->registerNamespace('poco', "http://portablecontacts.net/spec/1.0"); - $xpath->registerNamespace('ostatus', "http://ostatus.org/schema/1.0"); - $xpath->registerNamespace('statusnet', "http://status.net/schema/api/1/"); - - $gub = ""; - $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes; - if (is_object($hub_attributes)) - foreach($hub_attributes AS $hub_attribute) - if ($hub_attribute->name == "href") { - $hub = $hub_attribute->textContent; - logger("Found hub ".$hub, LOGGER_DEBUG); + poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"], + "", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]); } - $header = array(); - $header["uid"] = $importer["uid"]; - $header["network"] = NETWORK_OSTATUS; - $header["type"] = "remote"; - $header["wall"] = 0; - $header["origin"] = 0; - $header["gravity"] = GRAVITY_PARENT; + if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) { + logger("Update profile picture for contact ".$contact["id"], LOGGER_DEBUG); - // it could either be a received post or a post we fetched by ourselves - // depending on that, the first node is different - $first_child = $doc->firstChild->tagName; + update_contact_avatar($author["author-avatar"], $importer["uid"], $contact["id"]); + } + + // Ensure that we are having this contact (with uid=0) + $cid = get_contact($author["author-link"], 0); + + if ($cid) { + // Update it with the current values + q("UPDATE `contact` SET `url` = '%s', `name` = '%s', `nick` = '%s', `alias` = '%s', + `about` = '%s', `location` = '%s', + `success_update` = '%s', `last-update` = '%s' + WHERE `id` = %d", + dbesc($author["author-link"]), dbesc($contact["name"]), dbesc($contact["nick"]), + dbesc($contact["alias"]), dbesc($contact["about"]), dbesc($contact["location"]), + dbesc(datetime_convert()), dbesc(datetime_convert()), intval($cid)); + + // Update the avatar + update_contact_avatar($author["author-avatar"], 0, $cid); + } + + $contact["generation"] = 2; + $contact["photo"] = $author["author-avatar"]; + update_gcontact($contact); + } + + return($author); + } + + /** + * @brief Fetches author data from a given XML string + * + * @param string $xml The XML + * @param array $importer user record of the importing user + * + * @return array Array of author related entries for the item + */ + public static function salmon_author($xml, $importer) { + + if ($xml == "") + return; + + $doc = new DOMDocument(); + @$doc->loadXML($xml); + + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', NAMESPACE_ATOM1); + $xpath->registerNamespace('thr', NAMESPACE_THREAD); + $xpath->registerNamespace('georss', NAMESPACE_GEORSS); + $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY); + $xpath->registerNamespace('media', NAMESPACE_MEDIA); + $xpath->registerNamespace('poco', NAMESPACE_POCO); + $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); + $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET); - if ($first_child == "feed") - $entries = $xpath->query('/atom:feed/atom:entry'); - else $entries = $xpath->query('/atom:entry'); - $conversation = ""; - $conversationlist = array(); - $item_id = 0; - - // Reverse the order of the entries - $entrylist = array(); - - foreach ($entries AS $entry) - $entrylist[] = $entry; - - foreach (array_reverse($entrylist) AS $entry) { - - $mention = false; - - // fetch the author - if ($first_child == "feed") - $author = ostatus_fetchauthor($xpath, $doc->firstChild, $importer, $contact, false); - else - $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, false); - - $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; - if ($value != "") - $nickname = $value; - else - $nickname = $author["author-name"]; - - $item = array_merge($header, $author); - - // Now get the item - $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue; - - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", - intval($importer["uid"]), dbesc($item["uri"])); - if ($r) { - logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG); - continue; + foreach ($entries AS $entry) { + // fetch the author + $author = self::fetchauthor($xpath, $entry, $importer, $contact, true); + return $author; } + } - $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue)); - $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue; + /** + * @brief Imports an XML string containing OStatus elements + * + * @param string $xml The XML + * @param array $importer user record of the importing user + * @param $contact + * @param array $hub Called by reference, returns the fetched hub data + */ + public static function import($xml,$importer,&$contact, &$hub) { + /// @todo this function is too long. It has to be split in many parts - if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) OR ($item["object-type"] == ACTIVITY_OBJ_EVENT)) { - $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; - $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue; - } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION) - $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; + logger("Import OStatus message", LOGGER_DEBUG); - $item["object"] = $xml; - $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue; + if ($xml == "") + return; - /// @TODO - /// Delete a message - if ($item["verb"] == "qvitter-delete-notice") { - // ignore "Delete" messages (by now) - logger("Ignore delete message ".print_r($item, true)); - continue; - } + //$tempfile = tempnam(get_temppath(), "import"); + //file_put_contents($tempfile, $xml); - if ($item["verb"] == ACTIVITY_JOIN) { - // ignore "Join" messages - logger("Ignore join message ".print_r($item, true)); - continue; - } + $doc = new DOMDocument(); + @$doc->loadXML($xml); - if ($item["verb"] == ACTIVITY_FOLLOW) { - new_follower($importer, $contact, $item, $nickname); - continue; - } + $xpath = new DomXPath($doc); + $xpath->registerNamespace('atom', NAMESPACE_ATOM1); + $xpath->registerNamespace('thr', NAMESPACE_THREAD); + $xpath->registerNamespace('georss', NAMESPACE_GEORSS); + $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY); + $xpath->registerNamespace('media', NAMESPACE_MEDIA); + $xpath->registerNamespace('poco', NAMESPACE_POCO); + $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); + $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET); - if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") { - lose_follower($importer, $contact, $item, $dummy); - continue; - } - - if ($item["verb"] == ACTIVITY_FAVORITE) { - $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue; - logger("Favorite ".$orig_uri." ".print_r($item, true)); - - $item["verb"] = ACTIVITY_LIKE; - $item["parent-uri"] = $orig_uri; - $item["gravity"] = GRAVITY_LIKE; - } - - if ($item["verb"] == NAMESPACE_OSTATUS."/unfavorite") { - // Ignore "Unfavorite" message - logger("Ignore unfavorite message ".print_r($item, true)); - continue; - } - - // http://activitystrea.ms/schema/1.0/rsvp-yes - if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE))) - logger("Unhandled verb ".$item["verb"]." ".print_r($item, true)); - - $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue; - $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue; - $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue; - - $related = ""; - - $inreplyto = $xpath->query('thr:in-reply-to', $entry); - if (is_object($inreplyto->item(0))) { - foreach($inreplyto->item(0)->attributes AS $attributes) { - if ($attributes->name == "ref") - $item["parent-uri"] = $attributes->textContent; - if ($attributes->name == "href") - $related = $attributes->textContent; - } - } - - $georsspoint = $xpath->query('georss:point', $entry); - if ($georsspoint) - $item["coord"] = $georsspoint->item(0)->nodeValue; - - /// @TODO - /// $item["location"] = - - $categories = $xpath->query('atom:category', $entry); - if ($categories) { - foreach ($categories AS $category) { - foreach($category->attributes AS $attributes) - if ($attributes->name == "term") { - $term = $attributes->textContent; - if(strlen($item["tag"])) - $item["tag"] .= ','; - $item["tag"] .= "#[url=".$a->get_baseurl()."/search?tag=".$term."]".$term."[/url]"; - } - } - } - - $self = ""; - $enclosure = ""; - - $links = $xpath->query('atom:link', $entry); - if ($links) { - $rel = ""; - $href = ""; - $type = ""; - $length = "0"; - $title = ""; - foreach ($links AS $link) { - foreach($link->attributes AS $attributes) { - if ($attributes->name == "href") - $href = $attributes->textContent; - if ($attributes->name == "rel") - $rel = $attributes->textContent; - if ($attributes->name == "type") - $type = $attributes->textContent; - if ($attributes->name == "length") - $length = $attributes->textContent; - if ($attributes->name == "title") - $title = $attributes->textContent; + $gub = ""; + $hub_attributes = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0)->attributes; + if (is_object($hub_attributes)) + foreach($hub_attributes AS $hub_attribute) + if ($hub_attribute->name == "href") { + $hub = $hub_attribute->textContent; + logger("Found hub ".$hub, LOGGER_DEBUG); } - if (($rel != "") AND ($href != "")) - switch($rel) { - case "alternate": - $item["plink"] = $href; - if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) OR - ($item["object-type"] == ACTIVITY_OBJ_EVENT)) - $item["body"] .= add_page_info($href); - break; - case "ostatus:conversation": - $conversation = $href; - break; - case "enclosure": - $enclosure = $href; - if(strlen($item["attach"])) - $item["attach"] .= ','; - $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; - break; - case "related": - if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) { - if (!isset($item["parent-uri"])) - $item["parent-uri"] = $href; + $header = array(); + $header["uid"] = $importer["uid"]; + $header["network"] = NETWORK_OSTATUS; + $header["type"] = "remote"; + $header["wall"] = 0; + $header["origin"] = 0; + $header["gravity"] = GRAVITY_PARENT; - if ($related == "") - $related = $href; - } else - $item["body"] .= add_page_info($href); - break; - case "self": - $self = $href; - break; - case "mentioned": - // Notification check - if ($importer["nurl"] == normalise_link($href)) - $mention = true; - break; - } + // it could either be a received post or a post we fetched by ourselves + // depending on that, the first node is different + $first_child = $doc->firstChild->tagName; + + if ($first_child == "feed") + $entries = $xpath->query('/atom:feed/atom:entry'); + else + $entries = $xpath->query('/atom:entry'); + + $conversation = ""; + $conversationlist = array(); + $item_id = 0; + + // Reverse the order of the entries + $entrylist = array(); + + foreach ($entries AS $entry) + $entrylist[] = $entry; + + foreach (array_reverse($entrylist) AS $entry) { + + $mention = false; + + // fetch the author + if ($first_child == "feed") + $author = self::fetchauthor($xpath, $doc->firstChild, $importer, $contact, false); + else + $author = self::fetchauthor($xpath, $entry, $importer, $contact, false); + + $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; + if ($value != "") + $nickname = $value; + else + $nickname = $author["author-name"]; + + $item = array_merge($header, $author); + + // Now get the item + $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue; + + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["uri"])); + if ($r) { + logger("Item with uri ".$item["uri"]." for user ".$importer["uid"]." already existed under id ".$r[0]["id"], LOGGER_DEBUG); + continue; } - } - $local_id = ""; - $repeat_of = ""; + $item["body"] = add_page_info_to_body(html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue)); + $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue; - $notice_info = $xpath->query('statusnet:notice_info', $entry); - if ($notice_info AND ($notice_info->length > 0)) { - foreach($notice_info->item(0)->attributes AS $attributes) { - if ($attributes->name == "source") - $item["app"] = strip_tags($attributes->textContent); - if ($attributes->name == "local_id") - $local_id = $attributes->textContent; - if ($attributes->name == "repeat_of") - $repeat_of = $attributes->textContent; + if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) OR ($item["object-type"] == ACTIVITY_OBJ_EVENT)) { + $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; + $item["body"] = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue; + } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION) + $item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue; + + $item["object"] = $xml; + $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue; + + /// @TODO + /// Delete a message + if ($item["verb"] == "qvitter-delete-notice") { + // ignore "Delete" messages (by now) + logger("Ignore delete message ".print_r($item, true)); + continue; } - } - // Is it a repeated post? - if ($repeat_of != "") { - $activityobjects = $xpath->query('activity:object', $entry)->item(0); + if ($item["verb"] == ACTIVITY_JOIN) { + // ignore "Join" messages + logger("Ignore join message ".print_r($item, true)); + continue; + } - if (is_object($activityobjects)) { + if ($item["verb"] == ACTIVITY_FOLLOW) { + new_follower($importer, $contact, $item, $nickname); + continue; + } - $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue; - if (!isset($orig_uri)) - $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue; + if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") { + lose_follower($importer, $contact, $item, $dummy); + continue; + } - $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects); - if ($orig_links AND ($orig_links->length > 0)) - foreach($orig_links->item(0)->attributes AS $attributes) + if ($item["verb"] == ACTIVITY_FAVORITE) { + $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue; + logger("Favorite ".$orig_uri." ".print_r($item, true)); + + $item["verb"] = ACTIVITY_LIKE; + $item["parent-uri"] = $orig_uri; + $item["gravity"] = GRAVITY_LIKE; + } + + if ($item["verb"] == NAMESPACE_OSTATUS."/unfavorite") { + // Ignore "Unfavorite" message + logger("Ignore unfavorite message ".print_r($item, true)); + continue; + } + + // http://activitystrea.ms/schema/1.0/rsvp-yes + if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE))) + logger("Unhandled verb ".$item["verb"]." ".print_r($item, true)); + + $item["created"] = $xpath->query('atom:published/text()', $entry)->item(0)->nodeValue; + $item["edited"] = $xpath->query('atom:updated/text()', $entry)->item(0)->nodeValue; + $conversation = $xpath->query('ostatus:conversation/text()', $entry)->item(0)->nodeValue; + + $related = ""; + + $inreplyto = $xpath->query('thr:in-reply-to', $entry); + if (is_object($inreplyto->item(0))) { + foreach($inreplyto->item(0)->attributes AS $attributes) { + if ($attributes->name == "ref") + $item["parent-uri"] = $attributes->textContent; + if ($attributes->name == "href") + $related = $attributes->textContent; + } + } + + $georsspoint = $xpath->query('georss:point', $entry); + if ($georsspoint) + $item["coord"] = $georsspoint->item(0)->nodeValue; + + $categories = $xpath->query('atom:category', $entry); + if ($categories) { + foreach ($categories AS $category) { + foreach($category->attributes AS $attributes) + if ($attributes->name == "term") { + $term = $attributes->textContent; + if(strlen($item["tag"])) + $item["tag"] .= ','; + $item["tag"] .= "#[url=".App::get_baseurl()."/search?tag=".$term."]".$term."[/url]"; + } + } + } + + $self = ""; + $enclosure = ""; + + $links = $xpath->query('atom:link', $entry); + if ($links) { + $rel = ""; + $href = ""; + $type = ""; + $length = "0"; + $title = ""; + foreach ($links AS $link) { + foreach($link->attributes AS $attributes) { if ($attributes->name == "href") - $orig_link = $attributes->textContent; + $href = $attributes->textContent; + if ($attributes->name == "rel") + $rel = $attributes->textContent; + if ($attributes->name == "type") + $type = $attributes->textContent; + if ($attributes->name == "length") + $length = $attributes->textContent; + if ($attributes->name == "title") + $title = $attributes->textContent; + } + if (($rel != "") AND ($href != "")) + switch($rel) { + case "alternate": + $item["plink"] = $href; + if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) OR + ($item["object-type"] == ACTIVITY_OBJ_EVENT)) + $item["body"] .= add_page_info($href); + break; + case "ostatus:conversation": + $conversation = $href; + break; + case "enclosure": + $enclosure = $href; + if(strlen($item["attach"])) + $item["attach"] .= ','; - if (!isset($orig_link)) - $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue; + $item["attach"] .= '[attach]href="'.$href.'" length="'.$length.'" type="'.$type.'" title="'.$title.'"[/attach]'; + break; + case "related": + if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) { + if (!isset($item["parent-uri"])) + $item["parent-uri"] = $href; - if (!isset($orig_link)) - $orig_link = ostatus_convert_href($orig_uri); + if ($related == "") + $related = $href; + } else + $item["body"] .= add_page_info($href); + break; + case "self": + $self = $href; + break; + case "mentioned": + // Notification check + if ($importer["nurl"] == normalise_link($href)) + $mention = true; + break; + } + } + } - $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue; - if (!isset($orig_body)) - $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue; + $local_id = ""; + $repeat_of = ""; - $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue; + $notice_info = $xpath->query('statusnet:notice_info', $entry); + if ($notice_info AND ($notice_info->length > 0)) { + foreach($notice_info->item(0)->attributes AS $attributes) { + if ($attributes->name == "source") + $item["app"] = strip_tags($attributes->textContent); + if ($attributes->name == "local_id") + $local_id = $attributes->textContent; + if ($attributes->name == "repeat_of") + $repeat_of = $attributes->textContent; + } + } - $orig_contact = $contact; - $orig_author = ostatus_fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false); + // Is it a repeated post? + if ($repeat_of != "") { + $activityobjects = $xpath->query('activity:object', $entry)->item(0); + + if (is_object($activityobjects)) { + + $orig_uri = $xpath->query("activity:object/atom:id", $activityobjects)->item(0)->nodeValue; + if (!isset($orig_uri)) + $orig_uri = $xpath->query('atom:id/text()', $activityobjects)->item(0)->nodeValue; + + $orig_links = $xpath->query("activity:object/atom:link[@rel='alternate']", $activityobjects); + if ($orig_links AND ($orig_links->length > 0)) + foreach($orig_links->item(0)->attributes AS $attributes) + if ($attributes->name == "href") + $orig_link = $attributes->textContent; + + if (!isset($orig_link)) + $orig_link = $xpath->query("atom:link[@rel='alternate']", $activityobjects)->item(0)->nodeValue; + + if (!isset($orig_link)) + $orig_link = self::convert_href($orig_uri); + + $orig_body = $xpath->query('activity:object/atom:content/text()', $activityobjects)->item(0)->nodeValue; + if (!isset($orig_body)) + $orig_body = $xpath->query('atom:content/text()', $activityobjects)->item(0)->nodeValue; + + $orig_created = $xpath->query('atom:published/text()', $activityobjects)->item(0)->nodeValue; + + $orig_contact = $contact; + $orig_author = self::fetchauthor($xpath, $activityobjects, $importer, $orig_contact, false); - //if (!intval(get_config('system','wall-to-wall_share'))) { - // $prefix = share_header($orig_author['author-name'], $orig_author['author-link'], $orig_author['author-avatar'], "", $orig_created, $orig_link); - // $item["body"] = $prefix.add_page_info_to_body(html2bbcode($orig_body))."[/share]"; - //} else { $item["author-name"] = $orig_author["author-name"]; $item["author-link"] = $orig_author["author-link"]; $item["author-avatar"] = $orig_author["author-avatar"]; @@ -509,1086 +504,1511 @@ function ostatus_import($xml,$importer,&$contact, &$hub) { $item["uri"] = $orig_uri; $item["plink"] = $orig_link; - //} - $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue; + $item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue; - $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue; - if (!isset($item["object-type"])) - $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue; - } - } - - //if ($enclosure != "") - // $item["body"] .= add_page_info($enclosure); - - if (isset($item["parent-uri"])) { - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", - intval($importer["uid"]), dbesc($item["parent-uri"])); - - if (!$r AND ($related != "")) { - $reply_path = str_replace("/notice/", "/api/statuses/show/", $related).".atom"; - - if ($reply_path != $related) { - logger("Fetching related items for user ".$importer["uid"]." from ".$reply_path, LOGGER_DEBUG); - $reply_xml = fetch_url($reply_path); - - $reply_contact = $contact; - ostatus_import($reply_xml,$importer,$reply_contact, $reply_hub); - - // After the import try to fetch the parent item again - $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", - intval($importer["uid"]), dbesc($item["parent-uri"])); + $item["object-type"] = $xpath->query('activity:object/activity:object-type/text()', $activityobjects)->item(0)->nodeValue; + if (!isset($item["object-type"])) + $item["object-type"] = $xpath->query('activity:object-type/text()', $activityobjects)->item(0)->nodeValue; } } - if ($r) { - $item["type"] = 'remote-comment'; - $item["gravity"] = GRAVITY_COMMENT; - } - } else - $item["parent-uri"] = $item["uri"]; - $item_id = ostatus_completion($conversation, $importer["uid"], $item); + //if ($enclosure != "") + // $item["body"] .= add_page_info($enclosure); - if (!$item_id) { - logger("Error storing item", LOGGER_DEBUG); - continue; - } + if (isset($item["parent-uri"])) { + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["parent-uri"])); - logger("Item was stored with id ".$item_id, LOGGER_DEBUG); - } -} + if (!$r AND ($related != "")) { + $reply_path = str_replace("/notice/", "/api/statuses/show/", $related).".atom"; -function ostatus_convert_href($href) { - $elements = explode(":",$href); + if ($reply_path != $related) { + logger("Fetching related items for user ".$importer["uid"]." from ".$reply_path, LOGGER_DEBUG); + $reply_xml = fetch_url($reply_path); - if ((count($elements) <= 2) OR ($elements[0] != "tag")) - return $href; + $reply_contact = $contact; + self::import($reply_xml,$importer,$reply_contact, $reply_hub); - $server = explode(",", $elements[1]); - $conversation = explode("=", $elements[2]); - - if ((count($elements) == 4) AND ($elements[2] == "post")) - return "http://".$server[0]."/notice/".$elements[3]; - - if ((count($conversation) != 2) OR ($conversation[1] =="")) - return $href; - - if ($elements[3] == "objectType=thread") - return "http://".$server[0]."/conversation/".$conversation[1]; - else - return "http://".$server[0]."/notice/".$conversation[1]; - - return $href; -} - -function check_conversations($mentions = false, $override = false) { - $last = get_config('system','ostatus_last_poll'); - - $poll_interval = intval(get_config('system','ostatus_poll_interval')); - if(! $poll_interval) - $poll_interval = OSTATUS_DEFAULT_POLL_INTERVAL; - - // Don't poll if the interval is set negative - if (($poll_interval < 0) AND !$override) - return; - - if (!$mentions) { - $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); - if (!$poll_timeframe) - $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME; - } else { - $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); - if (!$poll_timeframe) - $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS; - } - - - if ($last AND !$override) { - $next = $last + ($poll_interval * 60); - if ($next > time()) { - logger('poll interval not reached'); - return; - } - } - - logger('cron_start'); - - $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60)); - - if ($mentions) - $conversations = q("SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term` - STRAIGHT_JOIN `thread` ON `thread`.`iid` = `term`.`oid` AND `thread`.`uid` = `term`.`uid` - WHERE `term`.`type` = 7 AND `term`.`term` > '%s' AND `thread`.`mention` - GROUP BY `term`.`url`, `term`.`uid` ORDER BY `term`.`term` DESC", dbesc($start)); - else - $conversations = q("SELECT `oid`, `url`, `uid` FROM `term` - WHERE `type` = 7 AND `term` > '%s' - GROUP BY `url`, `uid` ORDER BY `term` DESC", dbesc($start)); - - foreach ($conversations AS $conversation) { - ostatus_completion($conversation['url'], $conversation['uid']); - } - - logger('cron_end'); - - set_config('system','ostatus_last_poll', time()); -} - -function ostatus_completion($conversation_url, $uid, $item = array()) { - - $a = get_app(); - - $item_stored = -1; - - $conversation_url = ostatus_convert_href($conversation_url); - - // If the thread shouldn't be completed then store the item and go away - if ((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) { - //$arr["app"] .= " (OStatus-NoCompletion)"; - $item_stored = item_store($item, true); - return($item_stored); - } - - // Get the parent - $parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN - (SELECT `parent` FROM `item` WHERE `id` IN - (SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))", - intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url)); - - if ($parents) - $parent = $parents[0]; - elseif (count($item) > 0) { - $parent = $item; - $parent["type"] = "remote"; - $parent["verb"] = ACTIVITY_POST; - $parent["visible"] = 1; - } else { - // Preset the parent - $r = q("SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d", $uid); - if (!$r) - return(-2); - - $parent = array(); - $parent["id"] = 0; - $parent["parent"] = 0; - $parent["uri"] = ""; - $parent["contact-id"] = $r[0]["id"]; - $parent["type"] = "remote"; - $parent["verb"] = ACTIVITY_POST; - $parent["visible"] = 1; - } - - $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as"; - $pageno = 1; - $items = array(); - - logger('fetching conversation url '.$conv.' for user '.$uid); - - do { - $conv_arr = z_fetch_url($conv."?page=".$pageno); - - // If it is a non-ssl site and there is an error, then try ssl or vice versa - if (!$conv_arr["success"] AND (substr($conv, 0, 7) == "http://")) { - $conv = str_replace("http://", "https://", $conv); - $conv_as = fetch_url($conv."?page=".$pageno); - } elseif (!$conv_arr["success"] AND (substr($conv, 0, 8) == "https://")) { - $conv = str_replace("https://", "http://", $conv); - $conv_as = fetch_url($conv."?page=".$pageno); - } else - $conv_as = $conv_arr["body"]; - - $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as); - $conv_as = json_decode($conv_as); - - $no_of_items = sizeof($items); - - if (@is_array($conv_as->items)) - foreach ($conv_as->items AS $single_item) - $items[$single_item->id] = $single_item; - - if ($no_of_items == sizeof($items)) - break; - - $pageno++; - - } while (true); - - logger('fetching conversation done. Found '.count($items).' items'); - - if (!sizeof($items)) { - if (count($item) > 0) { - //$arr["app"] .= " (OStatus-NoConvFetched)"; - $item_stored = item_store($item, true); - - if ($item_stored) { - logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG); - ostatus_store_conversation($item_id, $conversation_url); - } - - return($item_stored); - } else - return(-3); - } - - $items = array_reverse($items); - - $r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid)); - $importer = $r[0]; - - foreach ($items as $single_conv) { - - // Test - remove before flight - //$tempfile = tempnam(get_temppath(), "conversation"); - //file_put_contents($tempfile, json_encode($single_conv)); - - $mention = false; - - if (isset($single_conv->object->id)) - $single_conv->id = $single_conv->object->id; - - $plink = ostatus_convert_href($single_conv->id); - if (isset($single_conv->object->url)) - $plink = ostatus_convert_href($single_conv->object->url); - - if (@!$single_conv->id) - continue; - - logger("Got id ".$single_conv->id, LOGGER_DEBUG); - - if ($first_id == "") { - $first_id = $single_conv->id; - - // The first post of the conversation isn't our first post. There are three options: - // 1. Our conversation hasn't the "real" thread starter - // 2. This first post is a post inside our thread - // 3. This first post is a post inside another thread - if (($first_id != $parent["uri"]) AND ($parent["uri"] != "")) { - $new_parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN - (SELECT `parent` FROM `item` - WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s')) LIMIT 1", - intval($uid), dbesc($first_id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); - if ($new_parents) { - if ($new_parents[0]["parent"] == $parent["parent"]) { - // Option 2: This post is already present inside our thread - but not as thread starter - logger("Option 2: uri present in our thread: ".$first_id, LOGGER_DEBUG); - $first_id = $parent["uri"]; - } else { - // Option 3: Not so good. We have mixed parents. We have to see how to clean this up. - // For now just take the new parent. - $parent = $new_parents[0]; - $first_id = $parent["uri"]; - logger("Option 3: mixed parents for uri ".$first_id, LOGGER_DEBUG); + // After the import try to fetch the parent item again + $r = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($importer["uid"]), dbesc($item["parent-uri"])); } - } else { - // Option 1: We hadn't got the real thread starter - // We have to clean up our existing messages. - $parent["id"] = 0; - $parent["uri"] = $first_id; - logger("Option 1: we have a new parent: ".$first_id, LOGGER_DEBUG); } - } elseif ($parent["uri"] == "") { - $parent["id"] = 0; - $parent["uri"] = $first_id; - } - } - - $parent_uri = $parent["uri"]; - - // "context" only seems to exist on older servers - if (isset($single_conv->context->inReplyTo->id)) { - $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", - intval($uid), dbesc($single_conv->context->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); - if ($parent_exists) - $parent_uri = $single_conv->context->inReplyTo->id; - } - - // This is the current way - if (isset($single_conv->object->inReplyTo->id)) { - $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", - intval($uid), dbesc($single_conv->object->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); - if ($parent_exists) - $parent_uri = $single_conv->object->inReplyTo->id; - } - - $message_exists = q("SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", - intval($uid), dbesc($single_conv->id), - dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); - if ($message_exists) { - logger("Message ".$single_conv->id." already existed on the system", LOGGER_DEBUG); - - if ($parent["id"] != 0) { - $existing_message = $message_exists[0]; - - // We improved the way we fetch OStatus messages, this shouldn't happen very often now - /// @TODO We have to change the shadow copies as well. This way here is really ugly. - if ($existing_message["parent"] != $parent["id"]) { - logger('updating id '.$existing_message["id"].' with parent '.$existing_message["parent"].' to parent '.$parent["id"].' uri '.$parent["uri"].' thread '.$parent_uri, LOGGER_DEBUG); - - // Update the parent id of the selected item - $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d", - intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["id"])); - - // Update the parent uri in the thread - but only if it points to itself - $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent`", - dbesc($parent_uri), intval($existing_message["id"])); - - // try to change all items of the same parent - $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d", - intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["parent"])); - - // Update the parent uri in the thread - but only if it points to itself - $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`)", - dbesc($parent["uri"]), intval($existing_message["parent"])); - - // Now delete the thread - delete_thread($existing_message["parent"]); + if ($r) { + $item["type"] = 'remote-comment'; + $item["gravity"] = GRAVITY_COMMENT; } + } else + $item["parent-uri"] = $item["uri"]; + + $item_id = self::completion($conversation, $importer["uid"], $item, $self); + + if (!$item_id) { + logger("Error storing item", LOGGER_DEBUG); + continue; } - // The item we are having on the system is the one that we wanted to store via the item array - if (isset($item["uri"]) AND ($item["uri"] == $existing_message["uri"])) { - $item = array(); - $item_stored = 0; - } - - continue; + logger("Item was stored with id ".$item_id, LOGGER_DEBUG); } + } - if (is_array($single_conv->to)) - foreach($single_conv->to AS $to) - if ($importer["nurl"] == normalise_link($to->id)) - $mention = true; + /** + * @brief Create an url out of an uri + * + * @param string $href URI in the format "parameter1:parameter1:..." + * + * @return string URL in the format http(s)://.... + */ + public static function convert_href($href) { + $elements = explode(":",$href); - $actor = $single_conv->actor->id; - if (isset($single_conv->actor->url)) - $actor = $single_conv->actor->url; + if ((count($elements) <= 2) OR ($elements[0] != "tag")) + return $href; - $contact = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'", - $uid, normalise_link($actor), NETWORK_STATUSNET); + $server = explode(",", $elements[1]); + $conversation = explode("=", $elements[2]); - if (count($contact)) { - logger("Found contact for url ".$actor, LOGGER_DEBUG); - $contact_id = $contact[0]["id"]; + if ((count($elements) == 4) AND ($elements[2] == "post")) + return "http://".$server[0]."/notice/".$elements[3]; + + if ((count($conversation) != 2) OR ($conversation[1] =="")) + return $href; + + if ($elements[3] == "objectType=thread") + return "http://".$server[0]."/conversation/".$conversation[1]; + else + return "http://".$server[0]."/notice/".$conversation[1]; + + return $href; + } + + /** + * @brief Checks if there are entries in conversations that aren't present on our side + * + * @param bool $mentions Fetch conversations where we are mentioned + * @param bool $override Override the interval setting + */ + public static function check_conversations($mentions = false, $override = false) { + $last = get_config('system','ostatus_last_poll'); + + $poll_interval = intval(get_config('system','ostatus_poll_interval')); + if(! $poll_interval) + $poll_interval = OSTATUS_DEFAULT_POLL_INTERVAL; + + // Don't poll if the interval is set negative + if (($poll_interval < 0) AND !$override) + return; + + if (!$mentions) { + $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); + if (!$poll_timeframe) + $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME; } else { - logger("No contact found for url ".$actor, LOGGER_DEBUG); + $poll_timeframe = intval(get_config('system','ostatus_poll_timeframe')); + if (!$poll_timeframe) + $poll_timeframe = OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS; + } + + + if ($last AND !$override) { + $next = $last + ($poll_interval * 60); + if ($next > time()) { + logger('poll interval not reached'); + return; + } + } + + logger('cron_start'); + + $start = date("Y-m-d H:i:s", time() - ($poll_timeframe * 60)); + + if ($mentions) + $conversations = q("SELECT `term`.`oid`, `term`.`url`, `term`.`uid` FROM `term` + STRAIGHT_JOIN `thread` ON `thread`.`iid` = `term`.`oid` AND `thread`.`uid` = `term`.`uid` + WHERE `term`.`type` = 7 AND `term`.`term` > '%s' AND `thread`.`mention` + GROUP BY `term`.`url`, `term`.`uid` ORDER BY `term`.`term` DESC", dbesc($start)); + else + $conversations = q("SELECT `oid`, `url`, `uid` FROM `term` + WHERE `type` = 7 AND `term` > '%s' + GROUP BY `url`, `uid` ORDER BY `term` DESC", dbesc($start)); + + foreach ($conversations AS $conversation) { + self::completion($conversation['url'], $conversation['uid']); + } + + logger('cron_end'); + + 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 + */ + private function 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); + } + + /** + * @brief Fetches the conversation url for a given item link or conversation id + * + * @param string $self The link to the posting + * @param string $conversation_id The conversation id + * + * @return string The conversation url + */ + private function fetch_conversation($self, $conversation_id = "") { + + if ($conversation_id != "") { + $elements = explode(":", $conversation_id); + + if ((count($elements) <= 2) OR ($elements[0] != "tag")) + return $conversation_id; + } + + if ($self == "") + return ""; + + $json = str_replace(".atom", ".json", $self); + + $raw = fetch_url($json); + if ($raw == "") + return ""; + + $data = json_decode($raw); + if (!is_object($data)) + return ""; + + $conversation_id = $data->statusnet_conversation_id; + + $pos = strpos($self, "/api/statuses/show/"); + $base_url = substr($self, 0, $pos); + + return $base_url."/conversation/".$conversation_id; + } + + /** + * @brief Fetches actor details of a given actor and user id + * + * @param string $actor The actor url + * @param int $uid The user id + * @param int $contact_id The default contact-id + * + * @return array Array with actor details + */ + private function get_actor_details($actor, $uid, $contact_id) { + + $details = array(); + + $contact = q("SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'", + $uid, normalise_link($actor), NETWORK_STATUSNET); + + if (!$contact) + $contact = q("SELECT `id`, `rel`, `network` FROM `contact` WHERE `uid` = %d AND `alias` IN ('%s', '%s') AND `network` != '%s'", + $uid, $actor, normalise_link($actor), NETWORK_STATUSNET); + + if ($contact) { + logger("Found contact for url ".$actor, LOGGER_DEBUG); + $details["contact_id"] = $contact[0]["id"]; + $details["network"] = $contact[0]["network"]; + + $details["not_following"] = !in_array($contact[0]["rel"], array(CONTACT_IS_SHARING, CONTACT_IS_FRIEND)); + } else { + logger("No contact found for user ".$uid." and url ".$actor, LOGGER_DEBUG); // Adding a global contact /// @TODO Use this data for the post - $global_contact_id = get_contact($actor, 0); + $details["global_contact_id"] = get_contact($actor, 0); logger("Global contact ".$global_contact_id." found for url ".$actor, LOGGER_DEBUG); - $contact_id = $parent["contact-id"]; + $details["contact_id"] = $contact_id; + $details["network"] = NETWORK_OSTATUS; + + $details["not_following"] = true; } - $arr = array(); - $arr["network"] = NETWORK_OSTATUS; - $arr["uri"] = $single_conv->id; - $arr["plink"] = $plink; - $arr["uid"] = $uid; - $arr["contact-id"] = $contact_id; - $arr["parent-uri"] = $parent_uri; - $arr["created"] = $single_conv->published; - $arr["edited"] = $single_conv->published; - $arr["owner-name"] = $single_conv->actor->displayName; - if ($arr["owner-name"] == '') - $arr["owner-name"] = $single_conv->actor->contact->displayName; - if ($arr["owner-name"] == '') - $arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName; + return $details; + } - $arr["owner-link"] = $actor; - $arr["owner-avatar"] = $single_conv->actor->image->url; - $arr["author-name"] = $arr["owner-name"]; - $arr["author-link"] = $actor; - $arr["author-avatar"] = $single_conv->actor->image->url; - $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content)); + /** + * @brief Stores an item and completes the thread + * + * @param string $conversation_url The URI of the conversation + * @param integer $uid The user id + * @param array $item Data of the item that is to be posted + * + * @return integer The item id of the posted item array + */ + private function completion($conversation_url, $uid, $item = array(), $self = "") { - if (isset($single_conv->status_net->notice_info->source)) - $arr["app"] = strip_tags($single_conv->status_net->notice_info->source); - elseif (isset($single_conv->statusnet->notice_info->source)) - $arr["app"] = strip_tags($single_conv->statusnet->notice_info->source); - elseif (isset($single_conv->statusnet_notice_info->source)) - $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source); - elseif (isset($single_conv->provider->displayName)) - $arr["app"] = $single_conv->provider->displayName; - else - $arr["app"] = "OStatus"; + /// @todo This function is totally ugly and has to be rewritten totally - //$arr["app"] .= " (Conversation)"; + $item_stored = -1; - $arr["object"] = json_encode($single_conv); - $arr["verb"] = $parent["verb"]; - $arr["visible"] = $parent["visible"]; - $arr["location"] = $single_conv->location->displayName; - $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon); + $conversation_url = self::fetch_conversation($self, $conversation_url); - // Is it a reshared item? - if (isset($single_conv->verb) AND ($single_conv->verb == "share") AND isset($single_conv->object)) { - if (is_array($single_conv->object)) - $single_conv->object = $single_conv->object[0]; + // If the thread shouldn't be completed then store the item and go away + // Don't do a completion on liked content + if (((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) OR + ($item["verb"] == ACTIVITY_LIKE) OR ($conversation_url == "")) { + $item_stored = item_store($item, true); + return($item_stored); + } - logger("Found reshared item ".$single_conv->object->id); + // Get the parent + $parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN + (SELECT `parent` FROM `item` WHERE `id` IN + (SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))", + intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url)); - // $single_conv->object->context->conversation; + if ($parents) + $parent = $parents[0]; + elseif (count($item) > 0) { + $parent = $item; + $parent["type"] = "remote"; + $parent["verb"] = ACTIVITY_POST; + $parent["visible"] = 1; + } else { + // Preset the parent + $r = q("SELECT `id` FROM `contact` WHERE `self` AND `uid`=%d", $uid); + if (!$r) + return(-2); - if (isset($single_conv->object->object->id)) - $arr["uri"] = $single_conv->object->object->id; - else - $arr["uri"] = $single_conv->object->id; + $parent = array(); + $parent["id"] = 0; + $parent["parent"] = 0; + $parent["uri"] = ""; + $parent["contact-id"] = $r[0]["id"]; + $parent["type"] = "remote"; + $parent["verb"] = ACTIVITY_POST; + $parent["visible"] = 1; + } - if (isset($single_conv->object->object->url)) - $plink = ostatus_convert_href($single_conv->object->object->url); - else - $plink = ostatus_convert_href($single_conv->object->url); + $conv = str_replace("/conversation/", "/api/statusnet/conversation/", $conversation_url).".as"; + $pageno = 1; + $items = array(); - if (isset($single_conv->object->object->content)) - $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->object->content)); - else - $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->content)); + logger('fetching conversation url '.$conv.' (Self: '.$self.') for user '.$uid); + do { + $conv_arr = z_fetch_url($conv."?page=".$pageno); + + // If it is a non-ssl site and there is an error, then try ssl or vice versa + if (!$conv_arr["success"] AND (substr($conv, 0, 7) == "http://")) { + $conv = str_replace("http://", "https://", $conv); + $conv_as = fetch_url($conv."?page=".$pageno); + } elseif (!$conv_arr["success"] AND (substr($conv, 0, 8) == "https://")) { + $conv = str_replace("https://", "http://", $conv); + $conv_as = fetch_url($conv."?page=".$pageno); + } else + $conv_as = $conv_arr["body"]; + + $conv_as = str_replace(',"statusnet:notice_info":', ',"statusnet_notice_info":', $conv_as); + $conv_as = json_decode($conv_as); + + $no_of_items = sizeof($items); + + if (@is_array($conv_as->items)) + foreach ($conv_as->items AS $single_item) + $items[$single_item->id] = $single_item; + + if ($no_of_items == sizeof($items)) + break; + + $pageno++; + + } while (true); + + logger('fetching conversation done. Found '.count($items).' items'); + + if (!sizeof($items)) { + if (count($item) > 0) { + $item_stored = item_store($item, true); + + if ($item_stored) { + logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG); + self::store_conversation($item_id, $conversation_url); + } + + return($item_stored); + } else + return(-3); + } + + $items = array_reverse($items); + + $r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid)); + $importer = $r[0]; + + $new_parent = true; + + foreach ($items as $single_conv) { + + // Update the gcontact table + self::conv_fetch_actor($single_conv->actor); + + // Test - remove before flight + //$tempfile = tempnam(get_temppath(), "conversation"); + //file_put_contents($tempfile, json_encode($single_conv)); + + $mention = false; + + if (isset($single_conv->object->id)) + $single_conv->id = $single_conv->object->id; + + $plink = self::convert_href($single_conv->id); + if (isset($single_conv->object->url)) + $plink = self::convert_href($single_conv->object->url); + + if (@!$single_conv->id) + continue; + + logger("Got id ".$single_conv->id, LOGGER_DEBUG); + + if ($first_id == "") { + $first_id = $single_conv->id; + + // The first post of the conversation isn't our first post. There are three options: + // 1. Our conversation hasn't the "real" thread starter + // 2. This first post is a post inside our thread + // 3. This first post is a post inside another thread + if (($first_id != $parent["uri"]) AND ($parent["uri"] != "")) { + + $new_parent = true; + + $new_parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN + (SELECT `parent` FROM `item` + WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s')) LIMIT 1", + intval($uid), dbesc($first_id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($new_parents) { + if ($new_parents[0]["parent"] == $parent["parent"]) { + // Option 2: This post is already present inside our thread - but not as thread starter + logger("Option 2: uri present in our thread: ".$first_id, LOGGER_DEBUG); + $first_id = $parent["uri"]; + } else { + // Option 3: Not so good. We have mixed parents. We have to see how to clean this up. + // For now just take the new parent. + $parent = $new_parents[0]; + $first_id = $parent["uri"]; + logger("Option 3: mixed parents for uri ".$first_id, LOGGER_DEBUG); + } + } else { + // Option 1: We hadn't got the real thread starter + // We have to clean up our existing messages. + $parent["id"] = 0; + $parent["uri"] = $first_id; + logger("Option 1: we have a new parent: ".$first_id, LOGGER_DEBUG); + } + } elseif ($parent["uri"] == "") { + $parent["id"] = 0; + $parent["uri"] = $first_id; + } + } + + $parent_uri = $parent["uri"]; + + // "context" only seems to exist on older servers + if (isset($single_conv->context->inReplyTo->id)) { + $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->context->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($parent_exists) + $parent_uri = $single_conv->context->inReplyTo->id; + } + + // This is the current way + if (isset($single_conv->object->inReplyTo->id)) { + $parent_exists = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->object->inReplyTo->id), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($parent_exists) + $parent_uri = $single_conv->object->inReplyTo->id; + } + + $message_exists = q("SELECT `id`, `parent`, `uri` FROM `item` WHERE `uid` = %d AND `uri` = '%s' AND `network` IN ('%s','%s') LIMIT 1", + intval($uid), dbesc($single_conv->id), + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN)); + if ($message_exists) { + logger("Message ".$single_conv->id." already existed on the system", LOGGER_DEBUG); + + if ($parent["id"] != 0) { + $existing_message = $message_exists[0]; + + // We improved the way we fetch OStatus messages, this shouldn't happen very often now + /// @TODO We have to change the shadow copies as well. This way here is really ugly. + if ($existing_message["parent"] != $parent["id"]) { + logger('updating id '.$existing_message["id"].' with parent '.$existing_message["parent"].' to parent '.$parent["id"].' uri '.$parent["uri"].' thread '.$parent_uri, LOGGER_DEBUG); + + // Update the parent id of the selected item + $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `id` = %d", + intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["id"])); + + // Update the parent uri in the thread - but only if it points to itself + $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE `id` = %d AND `uri` = `thr-parent`", + dbesc($parent_uri), intval($existing_message["id"])); + + // try to change all items of the same parent + $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s' WHERE `parent` = %d", + intval($parent["id"]), dbesc($parent["uri"]), intval($existing_message["parent"])); + + // Update the parent uri in the thread - but only if it points to itself + $r = q("UPDATE `item` SET `thr-parent` = '%s' WHERE (`parent` = %d) AND (`uri` = `thr-parent`)", + dbesc($parent["uri"]), intval($existing_message["parent"])); + + // Now delete the thread + delete_thread($existing_message["parent"]); + } + } + + // The item we are having on the system is the one that we wanted to store via the item array + if (isset($item["uri"]) AND ($item["uri"] == $existing_message["uri"])) { + $item = array(); + $item_stored = 0; + } + + continue; + } + + if (is_array($single_conv->to)) + foreach($single_conv->to AS $to) + if ($importer["nurl"] == normalise_link($to->id)) + $mention = true; + + $actor = $single_conv->actor->id; + if (isset($single_conv->actor->url)) + $actor = $single_conv->actor->url; + + $details = self::get_actor_details($actor, $uid, $parent["contact-id"]); + + // Do we only want to import threads that were started by our contacts? + if ($details["not_following"] AND $new_parent AND get_config('system','ostatus_full_threads')) { + logger("Don't import uri ".$first_id." because user ".$uid." doesn't follow the person ".$actor, LOGGER_DEBUG); + continue; + } + + $arr = array(); + $arr["network"] = $details["network"]; + $arr["uri"] = $single_conv->id; $arr["plink"] = $plink; - - $arr["created"] = $single_conv->object->published; - $arr["edited"] = $single_conv->object->published; - - $arr["author-name"] = $single_conv->object->actor->displayName; + $arr["uid"] = $uid; + $arr["contact-id"] = $details["contact_id"]; + $arr["parent-uri"] = $parent_uri; + $arr["created"] = $single_conv->published; + $arr["edited"] = $single_conv->published; + $arr["owner-name"] = $single_conv->actor->displayName; if ($arr["owner-name"] == '') - $arr["author-name"] = $single_conv->object->actor->contact->displayName; + $arr["owner-name"] = $single_conv->actor->contact->displayName; + if ($arr["owner-name"] == '') + $arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName; - $arr["author-link"] = $single_conv->object->actor->url; - $arr["author-avatar"] = $single_conv->object->actor->image->url; + $arr["owner-link"] = $actor; + $arr["owner-avatar"] = $single_conv->actor->image->url; + $arr["author-name"] = $arr["owner-name"]; + $arr["author-link"] = $actor; + $arr["author-avatar"] = $single_conv->actor->image->url; + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content)); - $arr["app"] = $single_conv->object->provider->displayName."#"; - //$arr["verb"] = $single_conv->object->verb; + if (isset($single_conv->status_net->notice_info->source)) + $arr["app"] = strip_tags($single_conv->status_net->notice_info->source); + elseif (isset($single_conv->statusnet->notice_info->source)) + $arr["app"] = strip_tags($single_conv->statusnet->notice_info->source); + elseif (isset($single_conv->statusnet_notice_info->source)) + $arr["app"] = strip_tags($single_conv->statusnet_notice_info->source); + elseif (isset($single_conv->provider->displayName)) + $arr["app"] = $single_conv->provider->displayName; + else + $arr["app"] = "OStatus"; - $arr["location"] = $single_conv->object->location->displayName; - $arr["coord"] = trim($single_conv->object->location->lat." ".$single_conv->object->location->lon); + + $arr["object"] = json_encode($single_conv); + $arr["verb"] = $parent["verb"]; + $arr["visible"] = $parent["visible"]; + $arr["location"] = $single_conv->location->displayName; + $arr["coord"] = trim($single_conv->location->lat." ".$single_conv->location->lon); + + // Is it a reshared item? + if (isset($single_conv->verb) AND ($single_conv->verb == "share") AND isset($single_conv->object)) { + if (is_array($single_conv->object)) + $single_conv->object = $single_conv->object[0]; + + logger("Found reshared item ".$single_conv->object->id); + + // $single_conv->object->context->conversation; + + if (isset($single_conv->object->object->id)) + $arr["uri"] = $single_conv->object->object->id; + else + $arr["uri"] = $single_conv->object->id; + + if (isset($single_conv->object->object->url)) + $plink = self::convert_href($single_conv->object->object->url); + else + $plink = self::convert_href($single_conv->object->url); + + if (isset($single_conv->object->object->content)) + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->object->content)); + else + $arr["body"] = add_page_info_to_body(html2bbcode($single_conv->object->content)); + + $arr["plink"] = $plink; + + $arr["created"] = $single_conv->object->published; + $arr["edited"] = $single_conv->object->published; + + $arr["author-name"] = $single_conv->object->actor->displayName; + if ($arr["owner-name"] == '') + $arr["author-name"] = $single_conv->object->actor->contact->displayName; + + $arr["author-link"] = $single_conv->object->actor->url; + $arr["author-avatar"] = $single_conv->object->actor->image->url; + + $arr["app"] = $single_conv->object->provider->displayName."#"; + //$arr["verb"] = $single_conv->object->verb; + + $arr["location"] = $single_conv->object->location->displayName; + $arr["coord"] = trim($single_conv->object->location->lat." ".$single_conv->object->location->lon); + } + + if ($arr["location"] == "") + unset($arr["location"]); + + if ($arr["coord"] == "") + unset($arr["coord"]); + + // Copy fields from given item array + if (isset($item["uri"]) AND (($item["uri"] == $arr["uri"]) OR ($item["uri"] == $single_conv->id))) { + $copy_fields = array("owner-name", "owner-link", "owner-avatar", "author-name", "author-link", "author-avatar", + "gravity", "body", "object-type", "object", "verb", "created", "edited", "coord", "tag", + "title", "attach", "app", "type", "location", "contact-id", "uri"); + foreach ($copy_fields AS $field) + if (isset($item[$field])) + $arr[$field] = $item[$field]; + + } + + $newitem = item_store($arr); + if (!$newitem) { + logger("Item wasn't stored ".print_r($arr, true), LOGGER_DEBUG); + continue; + } + + if (isset($item["uri"]) AND ($item["uri"] == $arr["uri"])) { + $item = array(); + $item_stored = $newitem; + } + + logger('Stored new item '.$plink.' for parent '.$arr["parent-uri"].' under id '.$newitem, LOGGER_DEBUG); + + // Add the conversation entry (but don't fetch the whole conversation) + self::store_conversation($newitem, $conversation_url); + + // If the newly created item is the top item then change the parent settings of the thread + // This shouldn't happen anymore. This is supposed to be absolote. + if ($arr["uri"] == $first_id) { + logger('setting new parent to id '.$newitem); + $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", + intval($uid), intval($newitem)); + if ($new_parents) + $parent = $new_parents[0]; + } } - if ($arr["location"] == "") - unset($arr["location"]); + if (($item_stored < 0) AND (count($item) > 0)) { - if ($arr["coord"] == "") - unset($arr["coord"]); + if (get_config('system','ostatus_full_threads')) { + $details = self::get_actor_details($item["owner-link"], $uid, $item["contact-id"]); + if ($details["not_following"]) { + logger("Don't import uri ".$item["uri"]." because user ".$uid." doesn't follow the person ".$item["owner-link"], LOGGER_DEBUG); + return false; + } + } - // Copy fields from given item array - if (isset($item["uri"]) AND (($item["uri"] == $arr["uri"]) OR ($item["uri"] == $single_conv->id))) { - $copy_fields = array("owner-name", "owner-link", "owner-avatar", "author-name", "author-link", "author-avatar", - "gravity", "body", "object-type", "object", "verb", "created", "edited", "coord", "tag", - "title", "attach", "app", "type", "location", "contact-id", "uri"); - foreach ($copy_fields AS $field) - if (isset($item[$field])) - $arr[$field] = $item[$field]; - - //$arr["app"] .= " (OStatus)"; + $item_stored = item_store($item, true); + if ($item_stored) { + logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG); + self::store_conversation($item_stored, $conversation_url); + } } - $newitem = item_store($arr); - if (!$newitem) { - logger("Item wasn't stored ".print_r($arr, true), LOGGER_DEBUG); - continue; - } + return($item_stored); + } - if (isset($item["uri"]) AND ($item["uri"] == $arr["uri"])) { - $item = array(); - $item_stored = $newitem; - } + /** + * @brief Stores conversation data into the database + * + * @param integer $itemid The id of the item + * @param string $conversation_url The uri of the conversation + */ + private function store_conversation($itemid, $conversation_url) { - logger('Stored new item '.$plink.' for parent '.$arr["parent-uri"].' under id '.$newitem, LOGGER_DEBUG); + $conversation_url = self::convert_href($conversation_url); - // Add the conversation entry (but don't fetch the whole conversation) - ostatus_store_conversation($newitem, $conversation_url); + $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid)); + if (!$messages) + return; + $message = $messages[0]; - // If the newly created item is the top item then change the parent settings of the thread - // This shouldn't happen anymore. This is supposed to be absolote. - if ($arr["uri"] == $first_id) { - logger('setting new parent to id '.$newitem); - $new_parents = q("SELECT `id`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `uid` = %d AND `id` = %d LIMIT 1", - intval($uid), intval($newitem)); - if ($new_parents) - $parent = $new_parents[0]; + // Store conversation url if not done before + $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d", + intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION)); + + if (!$conversation) { + $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", + intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), + dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"])); + logger('Storing conversation url '.$conversation_url.' for id '.$itemid); } } - if (($item_stored < 0) AND (count($item) > 0)) { - //$arr["app"] .= " (OStatus-NoConvFound)"; - $item_stored = item_store($item, true); - if ($item_stored) { - logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG); - ostatus_store_conversation($item_stored, $conversation_url); + /** + * @brief Checks if the current post is a reshare + * + * @param array $item The item array of thw post + * + * @return string The guid if the post is a reshare + */ + private function get_reshared_guid($item) { + $body = trim($item["body"]); + + // Skip if it isn't a pure repeated messages + // Does it start with a share? + if (strpos($body, "[share") > 0) + return(""); + + // Does it end with a share? + if (strlen($body) > (strrpos($body, "[/share]") + 8)) + return(""); + + $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); + // Skip if there is no shared message in there + if ($body == $attributes) + return(false); + + $guid = ""; + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $guid = $matches[1]; + + preg_match('/guid="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $guid = $matches[1]; + + return $guid; + } + + /** + * @brief Cleans the body of a post if it contains picture links + * + * @param string $body The body + * + * @return string The cleaned body + */ + private function format_picture_post($body) { + $siteinfo = get_attached_data($body); + + if (($siteinfo["type"] == "photo")) { + if (isset($siteinfo["preview"])) + $preview = $siteinfo["preview"]; + else + $preview = $siteinfo["image"]; + + // Is it a remote picture? Then make a smaller preview here + $preview = proxy_url($preview, false, PROXY_SIZE_SMALL); + + // Is it a local picture? Then make it smaller here + $preview = str_replace(array("-0.jpg", "-0.png"), array("-2.jpg", "-2.png"), $preview); + $preview = str_replace(array("-1.jpg", "-1.png"), array("-2.jpg", "-2.png"), $preview); + + if (isset($siteinfo["url"])) + $url = $siteinfo["url"]; + else + $url = $siteinfo["image"]; + + $body = trim($siteinfo["text"])." [url]".$url."[/url]\n[img]".$preview."[/img]"; } + + return $body; } - return($item_stored); -} + /** + * @brief Adds the header elements to the XML document + * + * @param object $doc XML document + * @param array $owner Contact data of the poster + * + * @return object header root element + */ + private function add_header($doc, $owner) { -function ostatus_store_conversation($itemid, $conversation_url) { - global $a; + $a = get_app(); - $conversation_url = ostatus_convert_href($conversation_url); + $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed'); + $doc->appendChild($root); - $messages = q("SELECT `uid`, `parent`, `created`, `received`, `guid` FROM `item` WHERE `id` = %d LIMIT 1", intval($itemid)); - if (!$messages) - return; - $message = $messages[0]; + $root->setAttribute("xmlns:thr", NAMESPACE_THREAD); + $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS); + $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); + $root->setAttribute("xmlns:media", NAMESPACE_MEDIA); + $root->setAttribute("xmlns:poco", NAMESPACE_POCO); + $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); + $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - // Store conversation url if not done before - $conversation = q("SELECT `url` FROM `term` WHERE `uid` = %d AND `oid` = %d AND `otype` = %d AND `type` = %d", - intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION)); + $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); + xml::add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); + xml::add_element($doc, $root, "id", App::get_baseurl()."/profile/".$owner["nick"]); + xml::add_element($doc, $root, "title", sprintf("%s timeline", $owner["name"])); + xml::add_element($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], $a->config["sitename"])); + xml::add_element($doc, $root, "logo", $owner["photo"]); + xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); - if (!$conversation) { - $r = q("INSERT INTO `term` (`uid`, `oid`, `otype`, `type`, `term`, `url`, `created`, `received`, `guid`) VALUES (%d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s')", - intval($message["uid"]), intval($itemid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), - dbesc($message["created"]), dbesc($conversation_url), dbesc($message["created"]), dbesc($message["received"]), dbesc($message["guid"])); - logger('Storing conversation url '.$conversation_url.' for id '.$itemid); - } -} + $author = self::add_author($doc, $owner); + $root->appendChild($author); -function get_reshared_guid($item) { - $body = trim($item["body"]); + $attributes = array("href" => $owner["url"], "rel" => "alternate", "type" => "text/html"); + xml::add_element($doc, $root, "link", "", $attributes); - // Skip if it isn't a pure repeated messages - // Does it start with a share? - if (strpos($body, "[share") > 0) - return(""); + /// @TODO We have to find out what this is + /// $attributes = array("href" => App::get_baseurl()."/sup", + /// "rel" => "http://api.friendfeed.com/2008/03#sup", + /// "type" => "application/json"); + /// xml::add_element($doc, $root, "link", "", $attributes); - // Does it end with a share? - if (strlen($body) > (strrpos($body, "[/share]") + 8)) - return(""); + self::hublinks($doc, $root); - $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body); - // Skip if there is no shared message in there - if ($body == $attributes) - return(false); + $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "salmon"); + xml::add_element($doc, $root, "link", "", $attributes); - $guid = ""; - preg_match("/guid='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $guid = $matches[1]; + $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies"); + xml::add_element($doc, $root, "link", "", $attributes); - preg_match('/guid="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") - $guid = $matches[1]; + $attributes = array("href" => App::get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention"); + xml::add_element($doc, $root, "link", "", $attributes); - return $guid; -} + $attributes = array("href" => App::get_baseurl()."/api/statuses/user_timeline/".$owner["nick"].".atom", + "rel" => "self", "type" => "application/atom+xml"); + xml::add_element($doc, $root, "link", "", $attributes); -function xml_create_element($doc, $element, $value = "", $attributes = array()) { - $element = $doc->createElement($element, xmlify($value)); - - foreach ($attributes AS $key => $value) { - $attribute = $doc->createAttribute($key); - $attribute->value = xmlify($value); - $element->appendChild($attribute); - } - return $element; -} - -function xml_add_element($doc, $parent, $element, $value = "", $attributes = array()) { - $element = xml_create_element($doc, $element, $value, $attributes); - $parent->appendChild($element); -} - -function ostatus_format_picture_post($body) { - $siteinfo = get_attached_data($body); - - if (($siteinfo["type"] == "photo")) { - if (isset($siteinfo["preview"])) - $preview = $siteinfo["preview"]; - else - $preview = $siteinfo["image"]; - - // Is it a remote picture? Then make a smaller preview here - $preview = proxy_url($preview, false, PROXY_SIZE_SMALL); - - // Is it a local picture? Then make it smaller here - $preview = str_replace(array("-0.jpg", "-0.png"), array("-2.jpg", "-2.png"), $preview); - $preview = str_replace(array("-1.jpg", "-1.png"), array("-2.jpg", "-2.png"), $preview); - - if (isset($siteinfo["url"])) - $url = $siteinfo["url"]; - else - $url = $siteinfo["image"]; - - $body = trim($siteinfo["text"])." [url]".$url."[/url]\n[img]".$preview."[/img]"; + return $root; } - return $body; -} + /** + * @brief Add the link to the push hubs to the XML document + * + * @param object $doc XML document + * @param object $root XML root element where the hub links are added + */ + public static function hublinks($doc, $root) { + $hub = get_config('system','huburl'); -function ostatus_add_header($doc, $owner) { - $a = get_app(); - - $root = $doc->createElementNS(NS_ATOM, 'feed'); - $doc->appendChild($root); - - $root->setAttribute("xmlns:thr", NS_THR); - $root->setAttribute("xmlns:georss", NS_GEORSS); - $root->setAttribute("xmlns:activity", NS_ACTIVITY); - $root->setAttribute("xmlns:media", NS_MEDIA); - $root->setAttribute("xmlns:poco", NS_POCO); - $root->setAttribute("xmlns:ostatus", NS_OSTATUS); - $root->setAttribute("xmlns:statusnet", NS_STATUSNET); - - $attributes = array("uri" => "https://friendi.ca", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION); - xml_add_element($doc, $root, "generator", FRIENDICA_PLATFORM, $attributes); - xml_add_element($doc, $root, "id", $a->get_baseurl()."/profile/".$owner["nick"]); - xml_add_element($doc, $root, "title", sprintf("%s timeline", $owner["name"])); - xml_add_element($doc, $root, "subtitle", sprintf("Updates from %s on %s", $owner["name"], $a->config["sitename"])); - xml_add_element($doc, $root, "logo", $owner["photo"]); - xml_add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); - - $author = ostatus_add_author($doc, $owner); - $root->appendChild($author); - - $attributes = array("href" => $owner["url"], "rel" => "alternate", "type" => "text/html"); - xml_add_element($doc, $root, "link", "", $attributes); - - /// @TODO We have to find out what this is - /// $attributes = array("href" => $a->get_baseurl()."/sup", - /// "rel" => "http://api.friendfeed.com/2008/03#sup", - /// "type" => "application/json"); - /// xml_add_element($doc, $root, "link", "", $attributes); - - ostatus_hublinks($doc, $root); - - $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "salmon"); - xml_add_element($doc, $root, "link", "", $attributes); - - $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-replies"); - xml_add_element($doc, $root, "link", "", $attributes); - - $attributes = array("href" => $a->get_baseurl()."/salmon/".$owner["nick"], "rel" => "http://salmon-protocol.org/ns/salmon-mention"); - xml_add_element($doc, $root, "link", "", $attributes); - - $attributes = array("href" => $a->get_baseurl()."/api/statuses/user_timeline/".$owner["nick"].".atom", - "rel" => "self", "type" => "application/atom+xml"); - xml_add_element($doc, $root, "link", "", $attributes); - - return $root; -} - -function ostatus_hublinks($doc, $root) { - $a = get_app(); - $hub = get_config('system','huburl'); - - $hubxml = ''; - if(strlen($hub)) { - $hubs = explode(',', $hub); - if(count($hubs)) { - foreach($hubs as $h) { - $h = trim($h); - if(! strlen($h)) - continue; - if ($h === '[internal]') - $h = $a->get_baseurl() . '/pubsubhubbub'; - xml_add_element($doc, $root, "link", "", array("href" => $h, "rel" => "hub")); + $hubxml = ''; + if(strlen($hub)) { + $hubs = explode(',', $hub); + if(count($hubs)) { + foreach($hubs as $h) { + $h = trim($h); + if(! strlen($h)) + continue; + if ($h === '[internal]') + $h = App::get_baseurl() . '/pubsubhubbub'; + xml::add_element($doc, $root, "link", "", array("href" => $h, "rel" => "hub")); + } } } } -} -function ostatus_get_attachment($doc, $root, $item) { - $o = ""; - $siteinfo = get_attached_data($item["body"]); + /** + * @brief Adds attachement data to the XML document + * + * @param object $doc XML document + * @param object $root XML root element where the hub links are added + * @param array $item Data of the item that is to be posted + */ + private function get_attachment($doc, $root, $item) { + $o = ""; + $siteinfo = get_attached_data($item["body"]); - switch($siteinfo["type"]) { - case 'link': - $attributes = array("rel" => "enclosure", - "href" => $siteinfo["url"], - "type" => "text/html; charset=UTF-8", - "length" => "", - "title" => $siteinfo["title"]); - xml_add_element($doc, $root, "link", "", $attributes); - break; - case 'photo': - $imgdata = get_photo_info($siteinfo["image"]); - $attributes = array("rel" => "enclosure", - "href" => $siteinfo["image"], - "type" => $imgdata["mime"], - "length" => intval($imgdata["size"])); - xml_add_element($doc, $root, "link", "", $attributes); - break; - case 'video': - $attributes = array("rel" => "enclosure", - "href" => $siteinfo["url"], - "type" => "text/html; charset=UTF-8", - "length" => "", - "title" => $siteinfo["title"]); - xml_add_element($doc, $root, "link", "", $attributes); - break; - default: - break; - } - - if (($siteinfo["type"] != "photo") AND isset($siteinfo["image"])) { - $photodata = get_photo_info($siteinfo["image"]); - - $attributes = array("rel" => "preview", "href" => $siteinfo["image"], "media:width" => $photodata[0], "media:height" => $photodata[1]); - xml_add_element($doc, $root, "link", "", $attributes); - } - - - $arr = explode('[/attach],',$item['attach']); - if(count($arr)) { - foreach($arr as $r) { - $matches = false; - $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches); - if($cnt) { + switch($siteinfo["type"]) { + case 'link': $attributes = array("rel" => "enclosure", - "href" => $matches[1], - "type" => $matches[3]); + "href" => $siteinfo["url"], + "type" => "text/html; charset=UTF-8", + "length" => "", + "title" => $siteinfo["title"]); + xml::add_element($doc, $root, "link", "", $attributes); + break; + case 'photo': + $imgdata = get_photo_info($siteinfo["image"]); + $attributes = array("rel" => "enclosure", + "href" => $siteinfo["image"], + "type" => $imgdata["mime"], + "length" => intval($imgdata["size"])); + xml::add_element($doc, $root, "link", "", $attributes); + break; + case 'video': + $attributes = array("rel" => "enclosure", + "href" => $siteinfo["url"], + "type" => "text/html; charset=UTF-8", + "length" => "", + "title" => $siteinfo["title"]); + xml::add_element($doc, $root, "link", "", $attributes); + break; + default: + break; + } - if(intval($matches[2])) - $attributes["length"] = intval($matches[2]); + if (($siteinfo["type"] != "photo") AND isset($siteinfo["image"])) { + $photodata = get_photo_info($siteinfo["image"]); - if(trim($matches[4]) != "") - $attributes["title"] = trim($matches[4]); + $attributes = array("rel" => "preview", "href" => $siteinfo["image"], "media:width" => $photodata[0], "media:height" => $photodata[1]); + xml::add_element($doc, $root, "link", "", $attributes); + } - xml_add_element($doc, $root, "link", "", $attributes); + + $arr = explode('[/attach],',$item['attach']); + if(count($arr)) { + foreach($arr as $r) { + $matches = false; + $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches); + if($cnt) { + $attributes = array("rel" => "enclosure", + "href" => $matches[1], + "type" => $matches[3]); + + if(intval($matches[2])) + $attributes["length"] = intval($matches[2]); + + if(trim($matches[4]) != "") + $attributes["title"] = trim($matches[4]); + + xml::add_element($doc, $root, "link", "", $attributes); + } } } } -} -function ostatus_add_author($doc, $owner) { - $a = get_app(); + /** + * @brief Adds the author element to the XML document + * + * @param object $doc XML document + * @param array $owner Contact data of the poster + * + * @return object author element + */ + private function add_author($doc, $owner) { - $r = q("SELECT `homepage` FROM `profile` WHERE `uid` = %d AND `is-default` LIMIT 1", intval($owner["uid"])); - if ($r) - $profile = $r[0]; + $r = q("SELECT `homepage` FROM `profile` WHERE `uid` = %d AND `is-default` LIMIT 1", intval($owner["uid"])); + if ($r) + $profile = $r[0]; - $author = $doc->createElement("author"); - xml_add_element($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON); - xml_add_element($doc, $author, "uri", $owner["url"]); - xml_add_element($doc, $author, "name", $owner["name"]); + $author = $doc->createElement("author"); + xml::add_element($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON); + xml::add_element($doc, $author, "uri", $owner["url"]); + xml::add_element($doc, $author, "name", $owner["name"]); + xml::add_element($doc, $author, "summary", bbcode($owner["about"], false, false, 7)); - $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $owner["url"]); - xml_add_element($doc, $author, "link", "", $attributes); + $attributes = array("rel" => "alternate", "type" => "text/html", "href" => $owner["url"]); + xml::add_element($doc, $author, "link", "", $attributes); - $attributes = array( - "rel" => "avatar", - "type" => "image/jpeg", // To-Do? - "media:width" => 175, - "media:height" => 175, - "href" => $owner["photo"]); - xml_add_element($doc, $author, "link", "", $attributes); - - if (isset($owner["thumb"])) { $attributes = array( "rel" => "avatar", "type" => "image/jpeg", // To-Do? - "media:width" => 80, - "media:height" => 80, - "href" => $owner["thumb"]); - xml_add_element($doc, $author, "link", "", $attributes); + "media:width" => 175, + "media:height" => 175, + "href" => $owner["photo"]); + xml::add_element($doc, $author, "link", "", $attributes); + + if (isset($owner["thumb"])) { + $attributes = array( + "rel" => "avatar", + "type" => "image/jpeg", // To-Do? + "media:width" => 80, + "media:height" => 80, + "href" => $owner["thumb"]); + xml::add_element($doc, $author, "link", "", $attributes); + } + + xml::add_element($doc, $author, "poco:preferredUsername", $owner["nick"]); + xml::add_element($doc, $author, "poco:displayName", $owner["name"]); + xml::add_element($doc, $author, "poco:note", bbcode($owner["about"], false, false, 7)); + + if (trim($owner["location"]) != "") { + $element = $doc->createElement("poco:address"); + xml::add_element($doc, $element, "poco:formatted", $owner["location"]); + $author->appendChild($element); + } + + if (trim($profile["homepage"]) != "") { + $urls = $doc->createElement("poco:urls"); + xml::add_element($doc, $urls, "poco:type", "homepage"); + xml::add_element($doc, $urls, "poco:value", $profile["homepage"]); + xml::add_element($doc, $urls, "poco:primary", "true"); + $author->appendChild($urls); + } + + if (count($profile)) { + xml::add_element($doc, $author, "followers", "", array("url" => App::get_baseurl()."/viewcontacts/".$owner["nick"])); + xml::add_element($doc, $author, "statusnet:profile_info", "", array("local_id" => $owner["uid"])); + } + + return $author; } - xml_add_element($doc, $author, "poco:preferredUsername", $owner["nick"]); - xml_add_element($doc, $author, "poco:displayName", $owner["name"]); - xml_add_element($doc, $author, "poco:note", $owner["about"]); + /** + * @TODO Picture attachments should look like this: + * https://status.pirati.ca/attachment/572819 + * + */ - if (trim($owner["location"]) != "") { - $element = $doc->createElement("poco:address"); - xml_add_element($doc, $element, "poco:formatted", $owner["location"]); - $author->appendChild($element); + /** + * @brief Returns the given activity if present - otherwise returns the "post" activity + * + * @param array $item Data of the item that is to be posted + * + * @return string activity + */ + function construct_verb($item) { + if ($item['verb']) + return $item['verb']; + return ACTIVITY_POST; } - if (trim($profile["homepage"]) != "") { - $urls = $doc->createElement("poco:urls"); - xml_add_element($doc, $urls, "poco:type", "homepage"); - xml_add_element($doc, $urls, "poco:value", $profile["homepage"]); - xml_add_element($doc, $urls, "poco:primary", "true"); - $author->appendChild($urls); + /** + * @brief Returns the given object type if present - otherwise returns the "note" object type + * + * @param array $item Data of the item that is to be posted + * + * @return string Object type + */ + function construct_objecttype($item) { + if (in_array($item['object-type'], array(ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT))) + return $item['object-type']; + return ACTIVITY_OBJ_NOTE; } - if (count($profile)) { - xml_add_element($doc, $author, "followers", "", array("url" => $a->get_baseurl()."/viewcontacts/".$owner["nick"])); - xml_add_element($doc, $author, "statusnet:profile_info", "", array("local_id" => $owner["uid"])); + /** + * @brief Adds an entry element to the XML document + * + * @param object $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $toplevel + * + * @return object Entry element + */ + private function entry($doc, $item, $owner, $toplevel = false) { + $repeated_guid = self::get_reshared_guid($item); + if ($repeated_guid != "") + $xml = self::reshare_entry($doc, $item, $owner, $repeated_guid, $toplevel); + + if ($xml) + return $xml; + + if ($item["verb"] == ACTIVITY_LIKE) + return self::like_entry($doc, $item, $owner, $toplevel); + else + return self::note_entry($doc, $item, $owner, $toplevel); } - return $author; -} + /** + * @brief Adds a source entry to the XML document + * + * @param object $doc XML document + * @param array $contact Array of the contact that is added + * + * @return object Source element + */ + private function source_entry($doc, $contact) { + $source = $doc->createElement("source"); + xml::add_element($doc, $source, "id", $contact["poll"]); + xml::add_element($doc, $source, "title", $contact["name"]); + xml::add_element($doc, $source, "link", "", array("rel" => "alternate", + "type" => "text/html", + "href" => $contact["alias"])); + xml::add_element($doc, $source, "link", "", array("rel" => "self", + "type" => "application/atom+xml", + "href" => $contact["poll"])); + xml::add_element($doc, $source, "icon", $contact["photo"]); + xml::add_element($doc, $source, "updated", datetime_convert("UTC","UTC",$contact["success_update"]."+00:00",ATOM_TIME)); -/** - * @TODO Picture attachments should look like this: - * https://status.pirati.ca/attachment/572819 - * -*/ + return $source; + } -function ostatus_entry($doc, $item, $owner, $toplevel = false, $repeat = false) { - $a = get_app(); + /** + * @brief Fetches contact data from the contact or the gcontact table + * + * @param string $url URL of the contact + * @param array $owner Contact data of the poster + * + * @return array Contact array + */ + private function contact_entry($url, $owner) { - $is_repeat = false; + $r = q("SELECT * FROM `contact` WHERE `nurl` = '%s' AND `uid` IN (0, %d) ORDER BY `uid` DESC LIMIT 1", + dbesc(normalise_link($url)), intval($owner["uid"])); + if ($r) { + $contact = $r[0]; + $contact["uid"] = -1; + } -/* if (!$repeat) { - $repeated_guid = get_reshared_guid($item); - - if ($repeated_guid != "") { - $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' LIMIT 1", - intval($owner["uid"]), dbesc($repeated_guid)); + if (!$r) { + $r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1", + dbesc(normalise_link($url))); if ($r) { - $repeated_item = $r[0]; - $is_repeat = true; + $contact = $r[0]; + $contact["uid"] = -1; + $contact["success_update"] = $contact["updated"]; } } - } -*/ - if (!$toplevel AND !$repeat) { - $entry = $doc->createElement("entry"); - $title = sprintf("New note by %s", $owner["nick"]); - } elseif (!$toplevel AND $repeat) { - $entry = $doc->createElement("activity:object"); - $title = sprintf("New note by %s", $owner["nick"]); - } else { - $entry = $doc->createElementNS(NS_ATOM, "entry"); - $entry->setAttribute("xmlns:thr", NS_THR); - $entry->setAttribute("xmlns:georss", NS_GEORSS); - $entry->setAttribute("xmlns:activity", NS_ACTIVITY); - $entry->setAttribute("xmlns:media", NS_MEDIA); - $entry->setAttribute("xmlns:poco", NS_POCO); - $entry->setAttribute("xmlns:ostatus", NS_OSTATUS); - $entry->setAttribute("xmlns:statusnet", NS_STATUSNET); + if (!$r) + $contact = owner; - $author = ostatus_add_author($doc, $owner); - $entry->appendChild($author); + if (!isset($contact["poll"])) { + $data = probe_url($url); + $contact["poll"] = $data["poll"]; - $title = sprintf("New comment by %s", $owner["nick"]); - } - - // To use the object-type "bookmark" we have to implement these elements: - // - // http://activitystrea.ms/schema/1.0/bookmark - // Historic Rocket Landing - // Nur ein Testbeitrag. - // - // - // - // But: it seems as if it doesn't federate well between the GS servers - // So we just set it to "note" to be sure that it reaches their target systems - - if (!$repeat) - xml_add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); - else - xml_add_element($doc, $entry, "activity:object-type", NAMESPACE_ACTIVITY_SCHEMA.'activity'); - - xml_add_element($doc, $entry, "id", $item["uri"]); - xml_add_element($doc, $entry, "title", $title); - - if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) - $body = fix_private_photos($item['body'],$owner['uid'],$item, 0); - else - $body = $item['body']; - - $body = ostatus_format_picture_post($body); - - if ($item['title'] != "") - $body = "[b]".$item['title']."[/b]\n\n".$body; - - //$body = bb_remove_share_information($body); - $body = bbcode($body, false, false, 7); - - xml_add_element($doc, $entry, "content", $body, array("type" => "html")); - - xml_add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", - "href" => $a->get_baseurl()."/display/".$item["guid"])); - - xml_add_element($doc, $entry, "status_net", "", array("notice_id" => $item["id"])); - - if (!$is_repeat) - xml_add_element($doc, $entry, "activity:verb", construct_verb($item)); - else - xml_add_element($doc, $entry, "activity:verb", ACTIVITY_SHARE); - - xml_add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); - xml_add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); - - if ($is_repeat) { - $repeated_owner = array(); - $repeated_owner["name"] = $repeated_item["author-name"]; - $repeated_owner["url"] = $repeated_item["author-link"]; - $repeated_owner["photo"] = $repeated_item["author-avatar"]; - $repeated_owner["nick"] = $repeated_owner["name"]; - $repeated_owner["location"] = ""; - $repeated_owner["about"] = ""; - $repeated_owner["uid"] = 0; - - // Fetch the missing data from the global contacts - $r =q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", normalise_link($repeated_item["author-link"])); - if ($r) { - if ($r[0]["nick"] != "") - $repeated_owner["nick"] = $r[0]["nick"]; - - $repeated_owner["location"] = $r[0]["location"]; - $repeated_owner["about"] = $r[0]["about"]; + if (!$contact["alias"]) + $contact["alias"] = $data["alias"]; } - $entry_repeat = ostatus_entry($doc, $repeated_item, $repeated_owner, false, true); - $entry->appendChild($entry_repeat); - } elseif ($repeat) { - $author = ostatus_add_author($doc, $owner); - $entry->appendChild($author); + if (!isset($contact["alias"])) + $contact["alias"] = $contact["url"]; + + return $contact; } - $mentioned = array(); + /** + * @brief Adds an entry element with reshared content + * + * @param object $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param $repeated_guid + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return object Entry element + */ + private function reshare_entry($doc, $item, $owner, $repeated_guid, $toplevel) { + + if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) { + logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); + } + + $title = self::entry_header($doc, $entry, $owner, $toplevel); + + $r = q("SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1", + intval($owner["uid"]), dbesc($repeated_guid), + dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS)); + if ($r) + $repeated_item = $r[0]; + else + return false; + + $contact = self::contact_entry($repeated_item['author-link'], $owner); - if (($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) { - $parent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `id` = %d", intval($item["parent"])); $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); - $attributes = array( - "ref" => $parent_item, - "type" => "text/html", - "href" => $a->get_baseurl()."/display/".$parent[0]["guid"]); - xml_add_element($doc, $entry, "thr:in-reply-to", "", $attributes); + $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; - $attributes = array( - "rel" => "related", - "href" => $a->get_baseurl()."/display/".$parent[0]["guid"]); - xml_add_element($doc, $entry, "link", "", $attributes); + self::entry_content($doc, $entry, $item, $owner, $title, ACTIVITY_SHARE, false); - $mentioned[$parent[0]["author-link"]] = $parent[0]["author-link"]; - $mentioned[$parent[0]["owner-link"]] = $parent[0]["owner-link"]; + $as_object = $doc->createElement("activity:object"); - $thrparent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + xml::add_element($doc, $as_object, "activity:object-type", NAMESPACE_ACTIVITY_SCHEMA."activity"); + + self::entry_content($doc, $as_object, $repeated_item, $owner, "", "", false); + + $author = self::add_author($doc, $contact); + $as_object->appendChild($author); + + $as_object2 = $doc->createElement("activity:object"); + + xml::add_element($doc, $as_object2, "activity:object-type", self::construct_objecttype($repeated_item)); + + $title = sprintf("New comment by %s", $contact["nick"]); + + self::entry_content($doc, $as_object2, $repeated_item, $owner, $title); + + $as_object->appendChild($as_object2); + + self::entry_footer($doc, $as_object, $item, $owner, false); + + $source = self::source_entry($doc, $contact); + + $as_object->appendChild($source); + + $entry->appendChild($as_object); + + self::entry_footer($doc, $entry, $item, $owner); + + return $entry; + } + + /** + * @brief Adds an entry element with a "like" + * + * @param object $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return object Entry element with "like" + */ + private function like_entry($doc, $item, $owner, $toplevel) { + + if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) { + logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); + } + + $title = self::entry_header($doc, $entry, $owner, $toplevel); + + $verb = NAMESPACE_ACTIVITY_SCHEMA."favorite"; + self::entry_content($doc, $entry, $item, $owner, "Favorite", $verb, false); + + $as_object = $doc->createElement("activity:object"); + + $parent = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d", + dbesc($item["thr-parent"]), intval($item["uid"])); + $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); + + xml::add_element($doc, $as_object, "activity:object-type", self::construct_objecttype($parent[0])); + + self::entry_content($doc, $as_object, $parent[0], $owner, "New entry"); + + $entry->appendChild($as_object); + + self::entry_footer($doc, $entry, $item, $owner); + + return $entry; + } + + /** + * @brief Adds a regular entry element + * + * @param object $doc XML document + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return object Entry element + */ + private function note_entry($doc, $item, $owner, $toplevel) { + + if (($item["id"] != $item["parent"]) AND (normalise_link($item["author-link"]) != normalise_link($owner["url"]))) { + logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); + } + + $title = self::entry_header($doc, $entry, $owner, $toplevel); + + xml::add_element($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); + + self::entry_content($doc, $entry, $item, $owner, $title); + + self::entry_footer($doc, $entry, $item, $owner); + + return $entry; + } + + /** + * @brief Adds a header element to the XML document + * + * @param object $doc XML document + * @param object $entry The entry element where the elements are added + * @param array $owner Contact data of the poster + * @param bool $toplevel Is it for en entry element (false) or a feed entry (true)? + * + * @return string The title for the element + */ + private function entry_header($doc, &$entry, $owner, $toplevel) { + /// @todo Check if this title stuff is really needed (I guess not) + if (!$toplevel) { + $entry = $doc->createElement("entry"); + $title = sprintf("New note by %s", $owner["nick"]); + } else { + $entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry"); + + $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD); + $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS); + $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); + $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA); + $entry->setAttribute("xmlns:poco", NAMESPACE_POCO); + $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); + $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); + + $author = self::add_author($doc, $owner); + $entry->appendChild($author); + + $title = sprintf("New comment by %s", $owner["nick"]); + } + return $title; + } + + /** + * @brief Adds elements to the XML document + * + * @param object $doc XML document + * @param object $entry Entry element where the content is added + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param string $title Title for the post + * @param string $verb The activity verb + * @param bool $complete Add the "status_net" element? + */ + private function entry_content($doc, $entry, $item, $owner, $title, $verb = "", $complete = true) { + + if ($verb == "") + $verb = self::construct_verb($item); + + xml::add_element($doc, $entry, "id", $item["uri"]); + xml::add_element($doc, $entry, "title", $title); + + $body = self::format_picture_post($item['body']); + + if ($item['title'] != "") + $body = "[b]".$item['title']."[/b]\n\n".$body; + + $body = bbcode($body, false, false, 7); + + xml::add_element($doc, $entry, "content", $body, array("type" => "html")); + + xml::add_element($doc, $entry, "link", "", array("rel" => "alternate", "type" => "text/html", + "href" => App::get_baseurl()."/display/".$item["guid"])); + + if ($complete) + xml::add_element($doc, $entry, "status_net", "", array("notice_id" => $item["id"])); + + xml::add_element($doc, $entry, "activity:verb", $verb); + + xml::add_element($doc, $entry, "published", datetime_convert("UTC","UTC",$item["created"]."+00:00",ATOM_TIME)); + xml::add_element($doc, $entry, "updated", datetime_convert("UTC","UTC",$item["edited"]."+00:00",ATOM_TIME)); + } + + /** + * @brief Adds the elements at the foot of an entry to the XML document + * + * @param object $doc XML document + * @param object $entry The entry element where the elements are added + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * @param $complete + */ + private function entry_footer($doc, $entry, $item, $owner, $complete = true) { + + $mentioned = array(); + + if (($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) { + $parent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `id` = %d", intval($item["parent"])); + $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']); + + $attributes = array( + "ref" => $parent_item, + "type" => "text/html", + "href" => App::get_baseurl()."/display/".$parent[0]["guid"]); + xml::add_element($doc, $entry, "thr:in-reply-to", "", $attributes); + + $attributes = array( + "rel" => "related", + "href" => App::get_baseurl()."/display/".$parent[0]["guid"]); + xml::add_element($doc, $entry, "link", "", $attributes); + + $mentioned[$parent[0]["author-link"]] = $parent[0]["author-link"]; + $mentioned[$parent[0]["owner-link"]] = $parent[0]["owner-link"]; + + $thrparent = q("SELECT `guid`, `author-link`, `owner-link` FROM `item` WHERE `uid` = %d AND `uri` = '%s'", + intval($owner["uid"]), + dbesc($parent_item)); + if ($thrparent) { + $mentioned[$thrparent[0]["author-link"]] = $thrparent[0]["author-link"]; + $mentioned[$thrparent[0]["owner-link"]] = $thrparent[0]["owner-link"]; + } + } + + xml::add_element($doc, $entry, "link", "", array("rel" => "ostatus:conversation", + "href" => App::get_baseurl()."/display/".$owner["nick"]."/".$item["parent"])); + xml::add_element($doc, $entry, "ostatus:conversation", App::get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]); + + $tags = item_getfeedtags($item); + + if(count($tags)) + foreach($tags as $t) + if ($t[0] == "@") + $mentioned[$t[1]] = $t[1]; + + // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) + $newmentions = array(); + foreach ($mentioned AS $mention) { + $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention); + $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention); + } + $mentioned = $newmentions; + + foreach ($mentioned AS $mention) { + $r = q("SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'", intval($owner["uid"]), - dbesc($parent_item)); - if ($thrparent) { - $mentioned[$thrparent[0]["author-link"]] = $thrparent[0]["author-link"]; - $mentioned[$thrparent[0]["owner-link"]] = $thrparent[0]["owner-link"]; + dbesc(normalise_link($mention))); + if ($r[0]["forum"] OR $r[0]["prv"]) + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", + "ostatus:object-type" => ACTIVITY_OBJ_GROUP, + "href" => $mention)); + else + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", + "ostatus:object-type" => ACTIVITY_OBJ_PERSON, + "href" => $mention)); + } + + if (!$item["private"]) { + xml::add_element($doc, $entry, "link", "", array("rel" => "ostatus:attention", + "href" => "http://activityschema.org/collection/public")); + xml::add_element($doc, $entry, "link", "", array("rel" => "mentioned", + "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection", + "href" => "http://activityschema.org/collection/public")); + } + + if(count($tags)) + foreach($tags as $t) + if ($t[0] != "@") + xml::add_element($doc, $entry, "category", "", array("term" => $t[2])); + + self::get_attachment($doc, $entry, $item); + + if ($complete) { + $app = $item["app"]; + if ($app == "") + $app = "web"; + + $attributes = array("local_id" => $item["id"], "source" => $app); + + if (isset($parent["id"])) + $attributes["repeat_of"] = $parent["id"]; + + if ($item["coord"] != "") + xml::add_element($doc, $entry, "georss:point", $item["coord"]); + + xml::add_element($doc, $entry, "statusnet:notice_info", "", $attributes); } } - xml_add_element($doc, $entry, "link", "", array("rel" => "ostatus:conversation", - "href" => $a->get_baseurl()."/display/".$owner["nick"]."/".$item["parent"])); - xml_add_element($doc, $entry, "ostatus:conversation", $a->get_baseurl()."/display/".$owner["nick"]."/".$item["parent"]); + /** + * @brief Creates the XML feed for a given nickname + * + * @param app $a The application class + * @param string $owner_nick Nickname of the feed owner + * @param string $last_update Date of the last update + * + * @return string XML feed + */ + public static function feed(&$a, $owner_nick, $last_update) { - $tags = item_getfeedtags($item); + $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` + FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` + WHERE `contact`.`self` AND `user`.`nickname` = '%s' LIMIT 1", + dbesc($owner_nick)); + if (!$r) + return; - if(count($tags)) - foreach($tags as $t) - if ($t[0] == "@") - $mentioned[$t[1]] = $t[1]; + $owner = $r[0]; - // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) - $newmentions = array(); - foreach ($mentioned AS $mention) { - $newmentions[str_replace("http://", "https://", $mention)] = str_replace("http://", "https://", $mention); - $newmentions[str_replace("https://", "http://", $mention)] = str_replace("https://", "http://", $mention); - } - $mentioned = $newmentions; + if(!strlen($last_update)) + $last_update = 'now -30 days'; - foreach ($mentioned AS $mention) { - $r = q("SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'", - intval($owner["uid"]), - dbesc(normalise_link($mention))); - if ($r[0]["forum"] OR $r[0]["prv"]) - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_GROUP, - "href" => $mention)); - else - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_PERSON, - "href" => $mention)); + $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s'); + + $items = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id` FROM `item` + INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` + LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid` + WHERE `item`.`uid` = %d AND `item`.`received` > '%s' AND NOT `item`.`private` AND NOT `item`.`deleted` + AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' + AND ((`item`.`wall` AND (`item`.`parent` = `item`.`id`)) + OR (`item`.`network` = '%s' AND ((`thread`.`network` IN ('%s', '%s')) OR (`thritem`.`network` IN ('%s', '%s')))) AND `thread`.`mention`) + AND ((`item`.`owner-link` IN ('%s', '%s') AND (`item`.`parent` = `item`.`id`)) + OR (`item`.`author-link` IN ('%s', '%s'))) + ORDER BY `item`.`received` DESC + LIMIT 0, 300", + intval($owner["uid"]), dbesc($check_date), dbesc(NETWORK_DFRN), + //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS), + //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS), + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN), + dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN), + dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])), + dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])) + ); + + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; + + $root = self::add_header($doc, $owner); + + foreach ($items AS $item) { + $entry = self::entry($doc, $item, $owner); + $root->appendChild($entry); + } + + return(trim($doc->saveXML())); } - if (!$item["private"]) - xml_add_element($doc, $entry, "link", "", array("rel" => "mentioned", - "ostatus:object-type" => "http://activitystrea.ms/schema/1.0/collection", - "href" => "http://activityschema.org/collection/public")); + /** + * @brief Creates the XML for a salmon message + * + * @param array $item Data of the item that is to be posted + * @param array $owner Contact data of the poster + * + * @return string XML for the salmon + */ + public static function salmon($item,$owner) { - if(count($tags)) - foreach($tags as $t) - if ($t[0] != "@") - xml_add_element($doc, $entry, "category", "", array("term" => $t[2])); + $doc = new DOMDocument('1.0', 'utf-8'); + $doc->formatOutput = true; - ostatus_get_attachment($doc, $entry, $item); + $entry = self::entry($doc, $item, $owner, true); - /// @TODO - /// The API call has yet to be implemented - //$attributes = array("href" => $a->get_baseurl()."/api/statuses/show/".$item["id"].".atom", - // "rel" => "self", "type" => "application/atom+xml"); - //xml_add_element($doc, $entry, "link", "", $attributes); + $doc->appendChild($entry); - //$attributes = array("href" => $a->get_baseurl()."/api/statuses/show/".$item["id"].".atom", - // "rel" => "edit", "type" => "application/atom+xml"); - //xml_add_element($doc, $entry, "link", "", $attributes); - - $app = $item["app"]; - if ($app == "") - $app = "web"; - - - $attributes = array("local_id" => $item["id"], "source" => $app); - if ($is_repeat) - $attributes["repeat_of"] = $repeated_item["id"]; - - xml_add_element($doc, $entry, "statusnet:notice_info", "", $attributes); - - return $entry; -} - -function ostatus_feed(&$a, $owner_nick, $last_update) { - - $r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` - FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` - WHERE `contact`.`self` AND `user`.`nickname` = '%s' LIMIT 1", - dbesc($owner_nick)); - if (!$r) - return; - - $owner = $r[0]; - - if(!strlen($last_update)) - $last_update = 'now -30 days'; - - $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s'); - - $items = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id` FROM `item` - INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` - LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid` - WHERE `item`.`uid` = %d AND `item`.`received` > '%s' AND NOT `item`.`private` AND NOT `item`.`deleted` - AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND ((`item`.`wall` AND (`item`.`parent` = `item`.`id`)) - OR (`item`.`network` = '%s' AND ((`thread`.`network` IN ('%s', '%s')) OR (`thritem`.`network` IN ('%s', '%s')))) AND `thread`.`mention`) - AND ((`item`.`owner-link` IN ('%s', '%s') AND (`item`.`parent` = `item`.`id`)) - OR (`item`.`author-link` IN ('%s', '%s'))) - ORDER BY `item`.`received` DESC - LIMIT 0, 300", - intval($owner["uid"]), dbesc($check_date), dbesc(NETWORK_DFRN), - //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS), - //dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS), - dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN), - dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN), - dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])), - dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])) - ); - - $doc = new DOMDocument('1.0', 'utf-8'); - $doc->formatOutput = true; - - $root = ostatus_add_header($doc, $owner); - - foreach ($items AS $item) { - $entry = ostatus_entry($doc, $item, $owner); - $root->appendChild($entry); + return(trim($doc->saveXML())); } - - return(trim($doc->saveXML())); -} - -function ostatus_salmon($item,$owner) { - - $doc = new DOMDocument('1.0', 'utf-8'); - $doc->formatOutput = true; - - $entry = ostatus_entry($doc, $item, $owner, true); - - $doc->appendChild($entry); - - return(trim($doc->saveXML())); } ?> diff --git a/include/plaintext.php b/include/plaintext.php index 05431bee2d..a2b2c56522 100644 --- a/include/plaintext.php +++ b/include/plaintext.php @@ -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')); diff --git a/include/poller.php b/include/poller.php index 190f3fb1ad..7ffd47aa68 100644 --- a/include/poller.php +++ b/include/poller.php @@ -26,17 +26,11 @@ function poller_run(&$argv, &$argc){ unset($db_host, $db_user, $db_pass, $db_data); }; - $load = current_load(); - if($load) { - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; + if (poller_max_connections_reached()) + return; - if(intval($load) > $maxsysload) { - logger('system: load ' . $load . ' too high. poller deferred to next scheduled run.'); - return; - } - } + if (App::maxload_reached()) + return; // Checking the number of workers if (poller_too_much_workers(1)) { @@ -65,6 +59,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; @@ -117,12 +115,93 @@ function poller_run(&$argv, &$argc){ } +/** + * @brief Checks if the number of database connections has reached a critical limit. + * + * @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; + + $max = intval($r[0]["Value"]); + if ($max == 0) + return false; + + $r = q("SHOW STATUS WHERE `variable_name` = 'Threads_connected'"); + if (!$r) + return false; + + $used = intval($r[0]["Value"]); + if ($used == 0) + return false; + + logger("Connection usage (system values): ".$used."/".$max, LOGGER_DEBUG); + + $level = $used / $max; + + if ($level < (3/4)) + return false; + + logger("Maximum level (3/4) of system connections reached: ".$used."/".$max); + return true; +} + /** * @brief fix the queue entry if the worker process died * */ function poller_kill_stale_workers() { $r = q("SELECT `pid`, `executed` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'"); + + if (!is_array($r) || count($r) == 0) { + // No processing here needed + return; + } + foreach($r AS $pid) if (!posix_kill($pid["pid"], 0)) q("UPDATE `workerqueue` SET `executed` = '0000-00-00 00:00:00', `pid` = 0 WHERE `pid` = %d", diff --git a/include/post_update.php b/include/post_update.php new file mode 100644 index 0000000000..2bdfe1f6fd --- /dev/null +++ b/include/post_update.php @@ -0,0 +1,141 @@ += 1192) + return true; + + // Check if the first step is done (Setting "gcontact-id" in the item table) + $r = q("SELECT `author-link`, `author-name`, `author-avatar`, `uid`, `network` FROM `item` WHERE `gcontact-id` = 0 LIMIT 1000"); + if (!$r) { + // Are there unfinished entries in the thread table? + $r = q("SELECT COUNT(*) AS `total` FROM `thread` + INNER JOIN `item` ON `item`.`id` =`thread`.`iid` + WHERE `thread`.`gcontact-id` = 0 AND + (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)"); + + if ($r AND ($r[0]["total"] == 0)) { + set_config("system", "post_update_version", 1192); + return true; + } + + // Update the thread table from the item table + q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid` + SET `thread`.`gcontact-id` = `item`.`gcontact-id` + WHERE `thread`.`gcontact-id` = 0 AND + (`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)"); + + return false; + } + + $item_arr = array(); + foreach ($r AS $item) { + $index = $item["author-link"]."-".$item["uid"]; + $item_arr[$index] = array("author-link" => $item["author-link"], + "uid" => $item["uid"], + "network" => $item["network"]); + } + + // Set the "gcontact-id" in the item table and add a new gcontact entry if needed + foreach($item_arr AS $item) { + $gcontact_id = get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'], + "photo" => $item['author-avatar'], "name" => $item['author-name'])); + q("UPDATE `item` SET `gcontact-id` = %d WHERE `uid` = %d AND `author-link` = '%s' AND `gcontact-id` = 0", + intval($gcontact_id), intval($item["uid"]), dbesc($item["author-link"])); + } + return false; +} + +/** + * @brief Updates the "global" field in the item table + * + * @return bool "true" when the job is done + */ +function post_update_1194() { + + // Was the script completed? + if (get_config("system", "post_update_version") >= 1194) + return true; + + logger("Start", LOGGER_DEBUG); + + $end_id = get_config("system", "post_update_1194_end"); + if (!$end_id) { + $r = q("SELECT `id` FROM `item` WHERE `uid` != 0 ORDER BY `id` DESC LIMIT 1"); + if ($r) { + set_config("system", "post_update_1194_end", $r[0]["id"]); + $end_id = get_config("system", "post_update_1194_end"); + } + } + + logger("End ID: ".$end_id, LOGGER_DEBUG); + + $start_id = get_config("system", "post_update_1194_start"); + + $query1 = "SELECT `item`.`id` FROM `item` "; + + $query2 = "INNER JOIN `item` AS `shadow` ON `item`.`uri` = `shadow`.`uri` AND `shadow`.`uid` = 0 "; + + $query3 = "WHERE `item`.`uid` != 0 AND `item`.`id` >= %d AND `item`.`id` <= %d + AND `item`.`visible` AND NOT `item`.`private` + AND NOT `item`.`deleted` AND NOT `item`.`moderated` + AND `item`.`network` IN ('%s', '%s', '%s', '') + AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' + AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' + AND NOT `item`.`global`"; + + $r = q($query1.$query2.$query3." ORDER BY `item`.`id` LIMIT 1", + intval($start_id), intval($end_id), + dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS)); + if (!$r) { + set_config("system", "post_update_version", 1194); + logger("Update is done", LOGGER_DEBUG); + return true; + } else { + set_config("system", "post_update_1194_start", $r[0]["id"]); + $start_id = get_config("system", "post_update_1194_start"); + } + + logger("Start ID: ".$start_id, LOGGER_DEBUG); + + $r = q($query1.$query2.$query3." ORDER BY `item`.`id` LIMIT 1000,1", + intval($start_id), intval($end_id), + dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS)); + if ($r) + $pos_id = $r[0]["id"]; + else + $pos_id = $end_id; + + logger("Progress: Start: ".$start_id." position: ".$pos_id." end: ".$end_id, LOGGER_DEBUG); + + $r = q("UPDATE `item` ".$query2." SET `item`.`global` = 1 ".$query3, + intval($start_id), intval($pos_id), + dbesc(NETWORK_DFRN), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_OSTATUS)); + + logger("Done", LOGGER_DEBUG); +} +?> diff --git a/include/profile_update.php b/include/profile_update.php index 7cc72cc866..399150f21c 100644 --- a/include/profile_update.php +++ b/include/profile_update.php @@ -1,96 +1,6 @@ get_baseurl() . '/profile/' . $a->user['nickname']; -// if($url && strlen(get_config('system','directory'))) -// proc_run('php',"include/directory.php","$url"); - - $recips = q("SELECT `id`,`name`,`network`,`pubkey`,`notify` FROM `contact` WHERE `network` = '%s' - AND `uid` = %d AND `rel` != %d ", - dbesc(NETWORK_DIASPORA), - intval(local_user()), - intval(CONTACT_IS_SHARING) - ); - if(! count($recips)) - return; - - $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `user`.* FROM `profile` - INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`uid` = %d AND `profile`.`is-default` = 1 LIMIT 1", - intval(local_user()) - ); - - if(! count($r)) - return; - $profile = $r[0]; - - $handle = xmlify($a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3)); - $first = xmlify(((strpos($profile['name'],' ')) - ? trim(substr($profile['name'],0,strpos($profile['name'],' '))) : $profile['name'])); - $last = xmlify((($first === $profile['name']) ? '' : trim(substr($profile['name'],strlen($first))))); - $large = xmlify($a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg'); - $medium = xmlify($a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg'); - $small = xmlify($a->get_baseurl() . '/photo/custom/50/' . $profile['uid'] . '.jpg'); - $searchable = xmlify((($profile['publish'] && $profile['net-publish']) ? 'true' : 'false' )); -// $searchable = 'true'; - - if($searchable === 'true') { - $dob = '1000-00-00'; - - if(($profile['dob']) && ($profile['dob'] != '0000-00-00')) - $dob = ((intval($profile['dob'])) ? intval($profile['dob']) : '1000') . '-' . datetime_convert('UTC','UTC',$profile['dob'],'m-d'); - $gender = xmlify($profile['gender']); - $about = xmlify($profile['about']); - require_once('include/bbcode.php'); - $about = xmlify(strip_tags(bbcode($about))); - $location = formatted_location($profile); - $location = xmlify($location); - $tags = ''; - if($profile['pub_keywords']) { - $kw = str_replace(',',' ',$profile['pub_keywords']); - $kw = str_replace(' ',' ',$kw); - $arr = explode(' ',$profile['pub_keywords']); - if(count($arr)) { - for($x = 0; $x < 5; $x ++) { - if(trim($arr[$x])) - $tags .= '#' . trim($arr[$x]) . ' '; - } - } - } - $tags = xmlify(trim($tags)); - } - - $tpl = get_markup_template('diaspora_profile.tpl'); - - $msg = replace_macros($tpl,array( - '$handle' => $handle, - '$first' => $first, - '$last' => $last, - '$large' => $large, - '$medium' => $medium, - '$small' => $small, - '$dob' => $dob, - '$gender' => $gender, - '$about' => $about, - '$location' => $location, - '$searchable' => $searchable, - '$tags' => $tags - )); - logger('profile_change: ' . $msg, LOGGER_ALL); - - foreach($recips as $recip) { - $msgtosend = 'xml=' . urlencode(urlencode(diaspora_msg_build($msg,$a->user,$recip,$a->user['prvkey'],$recip['pubkey'],false))); - add_to_queue($recip['id'],NETWORK_DIASPORA,$msgtosend,false); - } + diaspora::send_profile(local_user()); } diff --git a/include/pubsubpublish.php b/include/pubsubpublish.php index 0ac50aaaa7..625eefc261 100644 --- a/include/pubsubpublish.php +++ b/include/pubsubpublish.php @@ -16,7 +16,7 @@ function handle_pubsubhubbub() { logger("Generate feed for user ".$rr['nickname']." - last updated ".$rr['last_update'], LOGGER_DEBUG); - $params = ostatus_feed($a, $rr['nickname'], $rr['last_update']); + $params = ostatus::feed($a, $rr['nickname'], $rr['last_update']); $hmac_sig = hash_hmac("sha1", $params, $rr['secret']); $headers = array("Content-type: application/atom+xml", @@ -74,25 +74,14 @@ function pubsubpublish_run(&$argv, &$argc){ }; require_once('include/items.php'); - require_once('include/pidfile.php'); load_config('config'); load_config('system'); - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'pubsubpublish'); - if($pidfile->is_already_running()) { - logger("Already running"); - if ($pidfile->running_time() > 9*60) { - $pidfile->kill(); - logger("killed stale process"); - // Calling a new instance - proc_run('php',"include/pubsubpublish.php"); - } + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::is_already_running("pubsubpublish", "include/pubsubpublish.php", 540)) return; - } - } $a->set_baseurl(get_config('system','url')); diff --git a/include/queue.php b/include/queue.php index 1525ca3abf..878c149731 100644 --- a/include/queue.php +++ b/include/queue.php @@ -22,26 +22,15 @@ function queue_run(&$argv, &$argc){ require_once("include/datetime.php"); require_once('include/items.php'); require_once('include/bbcode.php'); - require_once('include/pidfile.php'); require_once('include/socgraph.php'); load_config('config'); load_config('system'); - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'queue'); - if($pidfile->is_already_running()) { - logger("queue: Already running"); - if ($pidfile->running_time() > 9*60) { - $pidfile->kill(); - logger("queue: killed stale process"); - // Calling a new instance - proc_run('php',"include/queue.php"); - } + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::is_already_running('queue', 'include/queue.php', 540)) return; - } - } $a->set_baseurl(get_config('system','url')); @@ -204,7 +193,7 @@ function queue_run(&$argv, &$argc){ case NETWORK_DIASPORA: if($contact['notify']) { logger('queue: diaspora_delivery: item '.$q_item['id'].' for '.$contact['name'].' <'.$contact['url'].'>'); - $deliver_status = diaspora_transmit($owner,$contact,$data,$public,true); + $deliver_status = diaspora::transmit($owner,$contact,$data,$public,true); if($deliver_status == (-1)) { update_queue_time($q_item['id']); diff --git a/include/session.php b/include/session.php index 11641d6cea..8f9d64606c 100644 --- a/include/session.php +++ b/include/session.php @@ -69,7 +69,6 @@ function ref_session_destroy ($id) { if(! function_exists('ref_session_gc')) { function ref_session_gc($expire) { q("DELETE FROM `session` WHERE `expire` < %d", dbesc(time())); - q("OPTIMIZE TABLE `sess_data`"); return true; }} diff --git a/include/socgraph.php b/include/socgraph.php index c545343393..33d62dc5b9 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -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); } /** diff --git a/include/text.php b/include/text.php index e14930b102..956344d63d 100644 --- a/include/text.php +++ b/include/text.php @@ -23,7 +23,7 @@ function replace_macros($s,$r) { $a = get_app(); // pass $baseurl to all templates - $r['$baseurl'] = z_root(); + $r['$baseurl'] = $a->get_baseurl(); $t = $a->template_engine(); @@ -286,7 +286,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="") { @@ -924,7 +924,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; @@ -965,13 +965,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'), @@ -1152,7 +1152,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']); } } @@ -1268,7 +1268,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]; @@ -1443,7 +1443,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 ); @@ -1461,7 +1461,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 ); @@ -1486,15 +1486,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'); } diff --git a/include/update_gcontact.php b/include/update_gcontact.php index b5ea30a0a4..88e1817f0b 100644 --- a/include/update_gcontact.php +++ b/include/update_gcontact.php @@ -16,7 +16,6 @@ function update_gcontact_run(&$argv, &$argc){ unset($db_host, $db_user, $db_pass, $db_data); }; - require_once('include/pidfile.php'); require_once('include/Scrape.php'); require_once("include/socgraph.php"); @@ -37,18 +36,10 @@ function update_gcontact_run(&$argv, &$argc){ return; } - $lockpath = get_lockpath(); - if ($lockpath != '') { - $pidfile = new pidfile($lockpath, 'update_gcontact'.$contact_id); - if ($pidfile->is_already_running()) { - logger("update_gcontact: Already running for contact ".$contact_id); - if ($pidfile->running_time() > 9*60) { - $pidfile->kill(); - logger("killed stale process"); - } - exit; - } - } + // Don't check this stuff if the function is called by the poller + if (App::callstack() != "poller_run") + if (App::is_already_running('update_gcontact'.$contact_id, '', 540)) + return; $r = q("SELECT * FROM `gcontact` WHERE `id` = %d", intval($contact_id)); diff --git a/include/xml.php b/include/xml.php new file mode 100644 index 0000000000..76ad88cf48 --- /dev/null +++ b/include/xml.php @@ -0,0 +1,131 @@ + $value) { + foreach ($namespaces AS $nskey => $nsvalue) + $key .= " xmlns".($nskey == "" ? "":":").$nskey.'="'.$nsvalue.'"'; + + $root = new SimpleXMLElement("<".$key."/>"); + self::from_array($value, $root, $remove_header, $namespaces, false); + + $dom = dom_import_simplexml($root)->ownerDocument; + $dom->formatOutput = true; + $xml = $dom; + + $xml_text = $dom->saveXML(); + + if ($remove_header) + $xml_text = trim(substr($xml_text, 21)); + + return $xml_text; + } + } + + foreach($array as $key => $value) { + if ($key == "@attributes") { + if (!isset($element) OR !is_array($value)) + continue; + + foreach ($value as $attr_key => $attr_value) { + $element_parts = explode(":", $attr_key); + if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]])) + $namespace = $namespaces[$element_parts[0]]; + else + $namespace = NULL; + + $element->addAttribute ($attr_key, $attr_value, $namespace); + } + + continue; + } + + $element_parts = explode(":", $key); + if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]])) + $namespace = $namespaces[$element_parts[0]]; + else + $namespace = NULL; + + if (!is_array($value)) + $element = $xml->addChild($key, xmlify($value), $namespace); + elseif (is_array($value)) { + $element = $xml->addChild($key, NULL, $namespace); + self::from_array($value, $element, $remove_header, $namespaces, false); + } + } + } + + /** + * @brief Copies an XML object + * + * @param object $source The XML source + * @param object $target The XML target + * @param string $elementname Name of the XML element of the target + */ + public static function copy(&$source, &$target, $elementname) { + if (count($source->children()) == 0) + $target->addChild($elementname, xmlify($source)); + else { + $child = $target->addChild($elementname); + foreach ($source->children() AS $childfield => $childentry) + self::copy($childentry, $child, $childfield); + } + } + + /** + * @brief Create an XML element + * + * @param object $doc XML root + * @param string $element XML element name + * @param string $value XML value + * @param array $attributes array containing the attributes + * + * @return object XML element object + */ + public static function create_element($doc, $element, $value = "", $attributes = array()) { + $element = $doc->createElement($element, xmlify($value)); + + foreach ($attributes AS $key => $value) { + $attribute = $doc->createAttribute($key); + $attribute->value = xmlify($value); + $element->appendChild($attribute); + } + return $element; + } + + /** + * @brief Create an XML and append it to the parent object + * + * @param object $doc XML root + * @param object $parent parent object + * @param string $element XML element name + * @param string $value XML value + * @param array $attributes array containing the attributes + */ + public static function add_element($doc, $parent, $element, $value = "", $attributes = array()) { + $element = self::create_element($doc, $element, $value, $attributes); + $parent->appendChild($element); + } +} +?> diff --git a/index.php b/index.php index bf926d1fe7..625c2d82dc 100644 --- a/index.php +++ b/index.php @@ -72,7 +72,8 @@ if(!$install) { (intval(get_config('system','ssl_policy')) == SSL_POLICY_FULL) AND (substr($a->get_baseurl(), 0, 8) == "https://")) { header("HTTP/1.1 302 Moved Temporarily"); - header("location: ".$a->get_baseurl()."/".$a->query_string); + header("Location: ".$a->get_baseurl()."/".$a->query_string); + exit(); } require_once("include/session.php"); @@ -371,7 +372,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'] .= ''; @@ -407,15 +408,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'] .= '
'; - - /** * * Add the navigation (menu) template @@ -432,10 +424,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, diff --git a/js/acl.js b/js/acl.js index 487ffafc77..3c96d13469 100644 --- a/js/acl.js +++ b/js/acl.js @@ -1,49 +1,56 @@ -function ACL(backend_url, preset, automention){ - that = this; +function ACL(backend_url, preset, automention, is_mobile){ - that.url = backend_url; - that.automention = automention; + this.url = backend_url; + this.automention = automention; + this.is_mobile = is_mobile; - that.kp_timer = null; + + this.kp_timer = null; if (preset==undefined) preset = []; - that.allow_cid = (preset[0] || []); - that.allow_gid = (preset[1] || []); - that.deny_cid = (preset[2] || []); - that.deny_gid = (preset[3] || []); - that.group_uids = []; - that.nw = 4; //items per row. should be calulated from #acl-list.width - - that.list_content = $("#acl-list-content"); - that.item_tpl = unescape($(".acl-list-item[rel=acl-template]").html()); - that.showall = $("#acl-showall"); + this.allow_cid = (preset[0] || []); + this.allow_gid = (preset[1] || []); + this.deny_cid = (preset[2] || []); + this.deny_gid = (preset[3] || []); + this.group_uids = []; - if (preset.length==0) that.showall.addClass("selected"); + if (this.is_mobile) { + this.nw = 1; + } else { + this.nw = 4; + } + + + this.list_content = $("#acl-list-content"); + this.item_tpl = unescape($(".acl-list-item[rel=acl-template]").html()); + this.showall = $("#acl-showall"); + + if (preset.length==0) this.showall.addClass("selected"); /*events*/ - that.showall.click(that.on_showall); - $(document).on("click", ".acl-button-show", that.on_button_show); - $(document).on("click", ".acl-button-hide", that.on_button_hide); - $("#acl-search").keypress(that.on_search); - $("#acl-wrapper").parents("form").submit(that.on_submit); + this.showall.click(this.on_showall.bind(this)); + $(document).on("click", ".acl-button-show", this.on_button_show.bind(this)); + $(document).on("click", ".acl-button-hide", this.on_button_hide.bind(this)); + $("#acl-search").keypress(this.on_search.bind(this)); + $("#acl-wrapper").parents("form").submit(this.on_submit.bind(this)); /* add/remove mentions */ - that.element = $("#profile-jot-text"); - that.htmlelm = that.element.get()[0]; + this.element = $("#profile-jot-text"); + this.htmlelm = this.element.get()[0]; /* startup! */ - that.get(0,100); + this.get(0,100); } ACL.prototype.remove_mention = function(id) { - if (!that.automention) return; - var nick = that.data[id].nick; + if (!this.automention) return; + var nick = this.data[id].nick; var searchText = "@"+nick+"+"+id+" "; if (tinyMCE.activeEditor===null) { - start = that.element.val().indexOf(searchText); + start = this.element.val().indexOf(searchText); if ( start<0) return; end = start+searchText.length; - that.element.setSelection(start,end).replaceSelectedText('').collapseSelection(false); + this.element.setSelection(start,end).replaceSelectedText('').collapseSelection(false); } else { start = tinyMCE.activeEditor.getContent({format : 'raw'}).search( searchText ); if ( start<0 ) return; @@ -54,12 +61,12 @@ ACL.prototype.remove_mention = function(id) { } ACL.prototype.add_mention = function(id) { - if (!that.automention) return; - var nick = that.data[id].nick; + if (!this.automention) return; + var nick = this.data[id].nick; var searchText = "@"+nick+"+"+id+" "; if (tinyMCE.activeEditor===null) { - if ( that.element.val().indexOf( searchText) >= 0 ) return; - that.element.val( searchText + that.element.val() ); + if ( this.element.val().indexOf( searchText) >= 0 ) return; + this.element.val( searchText + this.element.val() ); } else { if ( tinyMCE.activeEditor.getContent({format : 'raw'}).search(searchText) >= 0 ) return; tinyMCE.activeEditor.dom.add(tinyMCE.activeEditor.getBody(), 'dummy', {}, searchText); @@ -68,46 +75,46 @@ ACL.prototype.add_mention = function(id) { ACL.prototype.on_submit = function(){ aclfileds = $("#acl-fields").html(""); - $(that.allow_gid).each(function(i,v){ + $(this.allow_gid).each(function(i,v){ aclfileds.append(""); }); - $(that.allow_cid).each(function(i,v){ + $(this.allow_cid).each(function(i,v){ aclfileds.append(""); }); - $(that.deny_gid).each(function(i,v){ + $(this.deny_gid).each(function(i,v){ aclfileds.append(""); }); - $(that.deny_cid).each(function(i,v){ + $(this.deny_cid).each(function(i,v){ aclfileds.append(""); }); } ACL.prototype.search = function(){ var srcstr = $("#acl-search").val(); - that.list_content.html(""); - that.get(0,100, srcstr); + this.list_content.html(""); + this.get(0,100, srcstr); } ACL.prototype.on_search = function(event){ - if (that.kp_timer) clearTimeout(that.kp_timer); - that.kp_timer = setTimeout( that.search, 1000); + if (this.kp_timer) clearTimeout(this.kp_timer); + this.kp_timer = setTimeout( this.search.bind(this), 1000); } ACL.prototype.on_showall = function(event){ event.preventDefault() event.stopPropagation(); - if (that.showall.hasClass("selected")){ + if (this.showall.hasClass("selected")){ return false; } - that.showall.addClass("selected"); + this.showall.addClass("selected"); - that.allow_cid = []; - that.allow_gid = []; - that.deny_cid = []; - that.deny_gid = []; + this.allow_cid = []; + this.allow_gid = []; + this.deny_cid = []; + this.deny_gid = []; - that.update_view(); + this.update_view(); return false; } @@ -116,12 +123,8 @@ ACL.prototype.on_button_show = function(event){ event.preventDefault() event.stopImmediatePropagation() event.stopPropagation(); - - /*that.showall.removeClass("selected"); - $(this).siblings(".acl-button-hide").removeClass("selected"); - $(this).toggleClass("selected");*/ - - that.set_allow($(this).parent().attr('id')); + + this.set_allow($(event.target).parent().attr('id')); return false; } @@ -130,11 +133,7 @@ ACL.prototype.on_button_hide = function(event){ event.stopImmediatePropagation() event.stopPropagation(); - /*that.showall.removeClass("selected"); - $(this).siblings(".acl-button-show").removeClass("selected"); - $(this).toggleClass("selected");*/ - - that.set_deny($(this).parent().attr('id')); + this.set_deny($(event.target).parent().attr('id')); return false; } @@ -145,25 +144,25 @@ ACL.prototype.set_allow = function(itemid){ switch(type){ case "g": - if (that.allow_gid.indexOf(id)<0){ - that.allow_gid.push(id) + if (this.allow_gid.indexOf(id)<0){ + this.allow_gid.push(id) }else { - that.allow_gid.remove(id); + this.allow_gid.remove(id); } - if (that.deny_gid.indexOf(id)>=0) that.deny_gid.remove(id); + if (this.deny_gid.indexOf(id)>=0) this.deny_gid.remove(id); break; case "c": - if (that.allow_cid.indexOf(id)<0){ - that.allow_cid.push(id) - if (that.data[id].forum=="1") that.add_mention(id); + if (this.allow_cid.indexOf(id)<0){ + this.allow_cid.push(id) + if (this.data[id].forum=="1") this.add_mention(id); } else { - that.allow_cid.remove(id); - if (that.data[id].forum=="1") that.remove_mention(id); + this.allow_cid.remove(id); + if (this.data[id].forum=="1") this.remove_mention(id); } - if (that.deny_cid.indexOf(id)>=0) that.deny_cid.remove(id); + if (this.deny_cid.indexOf(id)>=0) this.deny_cid.remove(id); break; } - that.update_view(); + this.update_view(); } ACL.prototype.set_deny = function(itemid){ @@ -172,34 +171,34 @@ ACL.prototype.set_deny = function(itemid){ switch(type){ case "g": - if (that.deny_gid.indexOf(id)<0){ - that.deny_gid.push(id) + if (this.deny_gid.indexOf(id)<0){ + this.deny_gid.push(id) } else { - that.deny_gid.remove(id); + this.deny_gid.remove(id); } - if (that.allow_gid.indexOf(id)>=0) that.allow_gid.remove(id); + if (this.allow_gid.indexOf(id)>=0) this.allow_gid.remove(id); break; case "c": - if (that.data[id].forum=="1") that.remove_mention(id); - if (that.deny_cid.indexOf(id)<0){ - that.deny_cid.push(id) + if (this.data[id].forum=="1") this.remove_mention(id); + if (this.deny_cid.indexOf(id)<0){ + this.deny_cid.push(id) } else { - that.deny_cid.remove(id); + this.deny_cid.remove(id); } - if (that.allow_cid.indexOf(id)>=0) that.allow_cid.remove(id); + if (this.allow_cid.indexOf(id)>=0) this.allow_cid.remove(id); break; } - that.update_view(); + this.update_view(); } ACL.prototype.is_show_all = function() { - return (that.allow_gid.length==0 && that.allow_cid.length==0 && - that.deny_gid.length==0 && that.deny_cid.length==0); + return (this.allow_gid.length==0 && this.allow_cid.length==0 && + this.deny_gid.length==0 && this.deny_cid.length==0); } ACL.prototype.update_view = function(){ if (this.is_show_all()){ - that.showall.addClass("selected"); + this.showall.addClass("selected"); /* jot acl */ $('#jot-perms-icon').removeClass('lock').addClass('unlock'); $('#jot-public').show(); @@ -209,7 +208,7 @@ ACL.prototype.update_view = function(){ } } else { - that.showall.removeClass("selected"); + this.showall.removeClass("selected"); /* jot acl */ $('#jot-perms-icon').removeClass('unlock').addClass('lock'); $('#jot-public').hide(); @@ -220,29 +219,29 @@ ACL.prototype.update_view = function(){ $(this).removeClass("groupshow grouphide"); }); - $("#acl-list-content .acl-list-item").each(function(){ - itemid = $(this).attr('id'); + $("#acl-list-content .acl-list-item").each(function(index, element){ + itemid = $(element).attr('id'); type = itemid[0]; id = parseInt(itemid.substr(1)); - btshow = $(this).children(".acl-button-show").removeClass("selected"); - bthide = $(this).children(".acl-button-hide").removeClass("selected"); + btshow = $(element).children(".acl-button-show").removeClass("selected"); + bthide = $(element).children(".acl-button-hide").removeClass("selected"); switch(type){ case "g": var uclass = ""; - if (that.allow_gid.indexOf(id)>=0){ + if (this.allow_gid.indexOf(id)>=0){ btshow.addClass("selected"); bthide.removeClass("selected"); uclass="groupshow"; } - if (that.deny_gid.indexOf(id)>=0){ + if (this.deny_gid.indexOf(id)>=0){ btshow.removeClass("selected"); bthide.addClass("selected"); uclass="grouphide"; } - $(that.group_uids[id]).each(function(i,v) { + $(this.group_uids[id]).each(function(i,v) { if(uclass == "grouphide") $("#c"+v).removeClass("groupshow"); if(uclass != "") { @@ -257,17 +256,17 @@ ACL.prototype.update_view = function(){ break; case "c": - if (that.allow_cid.indexOf(id)>=0){ + if (this.allow_cid.indexOf(id)>=0){ btshow.addClass("selected"); bthide.removeClass("selected"); } - if (that.deny_cid.indexOf(id)>=0){ + if (this.deny_cid.indexOf(id)>=0){ btshow.removeClass("selected"); bthide.addClass("selected"); } } - }); + }.bind(this)); } @@ -281,30 +280,30 @@ ACL.prototype.get = function(start,count, search){ $.ajax({ type:'POST', - url: that.url, + url: this.url, data: postdata, dataType: 'json', - success:that.populate + success:this.populate.bind(this) }); } ACL.prototype.populate = function(data){ - var height = Math.ceil(data.tot / that.nw) * 42; - that.list_content.height(height); - that.data = {}; - $(data.items).each(function(){ - html = "
"+that.item_tpl+"
"; - html = html.format(this.photo, this.name, this.type, this.id, (this.forum=='1'?'forum':''), this.network, this.link); - if (this.uids!=undefined) that.group_uids[this.id] = this.uids; - //console.log(html); - that.list_content.append(html); - that.data[this.id] = this; - }); - $(".acl-list-item img[data-src]", that.list_content).each(function(i, el){ + var height = Math.ceil(data.tot / this.nw) * 42; + this.list_content.height(height); + this.data = {}; + $(data.items).each(function(index, item){ + html = "
"+this.item_tpl+"
"; + html = html.format(item.photo, item.name, item.type, item.id, (item.forum=='1'?'forum':''), item.network, item.link); + if (item.uids!=undefined) this.group_uids[item.id] = item.uids; + + this.list_content.append(html); + this.data[item.id] = item; + }.bind(this)); + $(".acl-list-item img[data-src]", this.list_content).each(function(i, el){ // Add src attribute for images with a data-src attribute $(el).attr('src', $(el).data("src")); }); - that.update_view(); + this.update_view(); } diff --git a/js/main.js b/js/main.js index 01dcb98f97..1ed0c4b6af 100644 --- a/js/main.js +++ b/js/main.js @@ -9,7 +9,7 @@ if (h==ch) { return; } - console.log("_resizeIframe", obj, desth, ch); + //console.log("_resizeIframe", obj, desth, ch); if (desth!=ch) { setTimeout(_resizeIframe, 500, obj, ch); } else { @@ -170,9 +170,8 @@ var notifications_mark = unescape($('
').append( $("#nav-notifications-mark-all").clone() ).html()); //outerHtml hack var notifications_empty = unescape($("#nav-notifications-menu").html()); - /* enable perfect-scrollbars for nav-notivications-menu */ - $('#nav-notifications-menu').perfectScrollbar(); - $('aside').perfectScrollbar(); + /* enable perfect-scrollbars for different elements */ + $('#nav-notifications-menu, aside').perfectScrollbar(); /* nav update event */ $('nav').bind('nav-update', function(e,data){ diff --git a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php deleted file mode 100644 index 292c040d4b..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php +++ /dev/null @@ -1,21 +0,0 @@ - 1.0) $result = '1'; - return $result; - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php deleted file mode 100644 index 6599c5b2dd..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php +++ /dev/null @@ -1,28 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/library/HTMLPurifier/AttrDef/CSS/FontFamily.php deleted file mode 100644 index 42c2054c2a..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ /dev/null @@ -1,72 +0,0 @@ - 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 diff --git a/library/HTMLPurifier/AttrDef/CSS/Length.php b/library/HTMLPurifier/AttrDef/CSS/Length.php deleted file mode 100644 index a07ec58135..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/Length.php +++ /dev/null @@ -1,47 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/library/HTMLPurifier/AttrDef/CSS/ListStyle.php deleted file mode 100644 index 4406868c08..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/ListStyle.php +++ /dev/null @@ -1,78 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/library/HTMLPurifier/AttrDef/CSS/Percentage.php deleted file mode 100644 index c34b8fc3c3..0000000000 --- a/library/HTMLPurifier/AttrDef/CSS/Percentage.php +++ /dev/null @@ -1,40 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/HTML/Bool.php b/library/HTMLPurifier/AttrDef/HTML/Bool.php deleted file mode 100644 index e06987eb8d..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/Bool.php +++ /dev/null @@ -1,28 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/HTML/Color.php b/library/HTMLPurifier/AttrDef/HTML/Color.php deleted file mode 100644 index d01e20454e..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/Color.php +++ /dev/null @@ -1,32 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php deleted file mode 100644 index ae6ea7c01d..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php +++ /dev/null @@ -1,21 +0,0 @@ -valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets'); - return parent::validate($string, $config, $context); - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/ID.php b/library/HTMLPurifier/AttrDef/HTML/ID.php deleted file mode 100644 index 81d03762de..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/ID.php +++ /dev/null @@ -1,70 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/HTML/Length.php b/library/HTMLPurifier/AttrDef/HTML/Length.php deleted file mode 100644 index a242f9c238..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/Length.php +++ /dev/null @@ -1,41 +0,0 @@ - 100) return '100%'; - - return ((string) $points) . '%'; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/library/HTMLPurifier/AttrDef/HTML/MultiLength.php deleted file mode 100644 index c72fc76e4d..0000000000 --- a/library/HTMLPurifier/AttrDef/HTML/MultiLength.php +++ /dev/null @@ -1,41 +0,0 @@ -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 - // 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 diff --git a/library/HTMLPurifier/AttrDef/Text.php b/library/HTMLPurifier/AttrDef/Text.php deleted file mode 100644 index c6216cc531..0000000000 --- a/library/HTMLPurifier/AttrDef/Text.php +++ /dev/null @@ -1,15 +0,0 @@ -parseCDATA($string); - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI/Host.php b/library/HTMLPurifier/AttrDef/URI/Host.php deleted file mode 100644 index 2156c10c66..0000000000 --- a/library/HTMLPurifier/AttrDef/URI/Host.php +++ /dev/null @@ -1,62 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrDef/URI/IPv6.php b/library/HTMLPurifier/AttrDef/URI/IPv6.php deleted file mode 100644 index 9454e9be50..0000000000 --- a/library/HTMLPurifier/AttrDef/URI/IPv6.php +++ /dev/null @@ -1,99 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/BoolToCSS.php b/library/HTMLPurifier/AttrTransform/BoolToCSS.php deleted file mode 100644 index 51159b6715..0000000000 --- a/library/HTMLPurifier/AttrTransform/BoolToCSS.php +++ /dev/null @@ -1,36 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/EnumToCSS.php b/library/HTMLPurifier/AttrTransform/EnumToCSS.php deleted file mode 100644 index 2a5b4514ab..0000000000 --- a/library/HTMLPurifier/AttrTransform/EnumToCSS.php +++ /dev/null @@ -1,58 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/Length.php b/library/HTMLPurifier/AttrTransform/Length.php deleted file mode 100644 index ea2f30473d..0000000000 --- a/library/HTMLPurifier/AttrTransform/Length.php +++ /dev/null @@ -1,27 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/Name.php b/library/HTMLPurifier/AttrTransform/Name.php deleted file mode 100644 index 15315bc735..0000000000 --- a/library/HTMLPurifier/AttrTransform/Name.php +++ /dev/null @@ -1,21 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/NameSync.php b/library/HTMLPurifier/AttrTransform/NameSync.php deleted file mode 100644 index a95638c140..0000000000 --- a/library/HTMLPurifier/AttrTransform/NameSync.php +++ /dev/null @@ -1,27 +0,0 @@ -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 diff --git a/library/HTMLPurifier/AttrTransform/SafeObject.php b/library/HTMLPurifier/AttrTransform/SafeObject.php deleted file mode 100644 index 1ed74898ba..0000000000 --- a/library/HTMLPurifier/AttrTransform/SafeObject.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ -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 diff --git a/library/HTMLPurifier/Bootstrap.php b/library/HTMLPurifier/Bootstrap.php deleted file mode 100644 index 559f61a232..0000000000 --- a/library/HTMLPurifier/Bootstrap.php +++ /dev/null @@ -1,98 +0,0 @@ - -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 diff --git a/library/HTMLPurifier/CSSDefinition.php b/library/HTMLPurifier/CSSDefinition.php deleted file mode 100644 index 6a2e6f56d9..0000000000 --- a/library/HTMLPurifier/CSSDefinition.php +++ /dev/null @@ -1,292 +0,0 @@ -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 diff --git a/library/HTMLPurifier/ChildDef/Optional.php b/library/HTMLPurifier/ChildDef/Optional.php deleted file mode 100644 index 32bcb9898e..0000000000 --- a/library/HTMLPurifier/ChildDef/Optional.php +++ /dev/null @@ -1,26 +0,0 @@ -whitespace) return $tokens_of_children; - else return array(); - } - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/ChildDef/Required.php b/library/HTMLPurifier/ChildDef/Required.php deleted file mode 100644 index 4889f249b8..0000000000 --- a/library/HTMLPurifier/ChildDef/Required.php +++ /dev/null @@ -1,117 +0,0 @@ - $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 diff --git a/library/HTMLPurifier/ChildDef/StrictBlockquote.php b/library/HTMLPurifier/ChildDef/StrictBlockquote.php deleted file mode 100644 index dfae8a6e5e..0000000000 --- a/library/HTMLPurifier/ChildDef/StrictBlockquote.php +++ /dev/null @@ -1,88 +0,0 @@ -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 diff --git a/library/HTMLPurifier/ChildDef/Table.php b/library/HTMLPurifier/ChildDef/Table.php deleted file mode 100644 index 34f0227dd2..0000000000 --- a/library/HTMLPurifier/ChildDef/Table.php +++ /dev/null @@ -1,142 +0,0 @@ - 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 diff --git a/library/HTMLPurifier/Config.php b/library/HTMLPurifier/Config.php deleted file mode 100644 index 2a334b0d83..0000000000 --- a/library/HTMLPurifier/Config.php +++ /dev/null @@ -1,580 +0,0 @@ -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 diff --git a/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php deleted file mode 100644 index b95aea18cc..0000000000 --- a/library/HTMLPurifier/ConfigSchema/ValidatorAtom.php +++ /dev/null @@ -1,66 +0,0 @@ -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 diff --git a/library/HTMLPurifier/ConfigSchema/schema.ser b/library/HTMLPurifier/ConfigSchema/schema.ser deleted file mode 100644 index 22b8d54a59..0000000000 Binary files a/library/HTMLPurifier/ConfigSchema/schema.ser and /dev/null differ diff --git a/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt deleted file mode 100644 index 888d558196..0000000000 --- a/library/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt +++ /dev/null @@ -1,18 +0,0 @@ -HTML.AllowedElements -TYPE: lookup/null -VERSION: 1.3.0 -DEFAULT: NULL ---DESCRIPTION-- -

- 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. -

-

- Warning: If another directive conflicts with the - elements here, that directive will win and override. -

---# vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Context.php b/library/HTMLPurifier/Context.php deleted file mode 100644 index 9ddf0c5476..0000000000 --- a/library/HTMLPurifier/Context.php +++ /dev/null @@ -1,82 +0,0 @@ -_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 diff --git a/library/HTMLPurifier/Definition.php b/library/HTMLPurifier/Definition.php deleted file mode 100644 index a7408c9749..0000000000 --- a/library/HTMLPurifier/Definition.php +++ /dev/null @@ -1,39 +0,0 @@ -setup) return; - $this->setup = true; - $this->doSetup($config); - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/DefinitionCache/Decorator.php b/library/HTMLPurifier/DefinitionCache/Decorator.php deleted file mode 100644 index b0fb6d0cd6..0000000000 --- a/library/HTMLPurifier/DefinitionCache/Decorator.php +++ /dev/null @@ -1,62 +0,0 @@ -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 diff --git a/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php deleted file mode 100644 index d4cc35c4bc..0000000000 --- a/library/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php +++ /dev/null @@ -1,43 +0,0 @@ -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 diff --git a/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in deleted file mode 100644 index 21a8fcfda2..0000000000 --- a/library/HTMLPurifier/DefinitionCache/Decorator/Template.php.in +++ /dev/null @@ -1,47 +0,0 @@ -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 diff --git a/library/HTMLPurifier/EntityLookup/entities.ser b/library/HTMLPurifier/EntityLookup/entities.ser deleted file mode 100644 index f2b8b8f2db..0000000000 --- a/library/HTMLPurifier/EntityLookup/entities.ser +++ /dev/null @@ -1 +0,0 @@ -a:246:{s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";} \ No newline at end of file diff --git a/library/HTMLPurifier/Filter/ExtractStyleBlocks.php b/library/HTMLPurifier/Filter/ExtractStyleBlocks.php deleted file mode 100644 index bbf78a6630..0000000000 --- a/library/HTMLPurifier/Filter/ExtractStyleBlocks.php +++ /dev/null @@ -1,135 +0,0 @@ - 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 #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 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 diff --git a/library/HTMLPurifier/Filter/YouTube.php b/library/HTMLPurifier/Filter/YouTube.php deleted file mode 100644 index 23df221eaa..0000000000 --- a/library/HTMLPurifier/Filter/YouTube.php +++ /dev/null @@ -1,39 +0,0 @@ -]+>.+?'. - 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; - $pre_replace = '\1'; - return preg_replace($pre_regex, $pre_replace, $html); - } - - public function postFilter($html, $config, $context) { - $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; - 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 ''. - ''. - ''. - ''; - - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/HTMLModule/Forms.php b/library/HTMLPurifier/HTMLModule/Forms.php deleted file mode 100644 index 44c22f6f8b..0000000000 --- a/library/HTMLPurifier/HTMLModule/Forms.php +++ /dev/null @@ -1,118 +0,0 @@ - '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 . This one's a little complex - // because it maps to multiple elements. - - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/HTMLModule/Tidy/Strict.php b/library/HTMLPurifier/HTMLModule/Tidy/Strict.php deleted file mode 100644 index c73dc3c4d1..0000000000 --- a/library/HTMLPurifier/HTMLModule/Tidy/Strict.php +++ /dev/null @@ -1,21 +0,0 @@ -content_model_type != 'strictblockquote') return parent::getChildDef($def); - return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model); - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Injector/RemoveEmpty.php b/library/HTMLPurifier/Injector/RemoveEmpty.php deleted file mode 100644 index 638bfca03b..0000000000 --- a/library/HTMLPurifier/Injector/RemoveEmpty.php +++ /dev/null @@ -1,51 +0,0 @@ -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 diff --git a/library/HTMLPurifier/Language/messages/en.php b/library/HTMLPurifier/Language/messages/en.php deleted file mode 100644 index 8d7b5736bb..0000000000 --- a/library/HTMLPurifier/Language/messages/en.php +++ /dev/null @@ -1,63 +0,0 @@ - '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 diff --git a/library/HTMLPurifier/Lexer/PEARSax3.php b/library/HTMLPurifier/Lexer/PEARSax3.php deleted file mode 100644 index 1d358c7b6b..0000000000 --- a/library/HTMLPurifier/Lexer/PEARSax3.php +++ /dev/null @@ -1,139 +0,0 @@ -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 diff --git a/library/HTMLPurifier/Lexer/PH5P.php b/library/HTMLPurifier/Lexer/PH5P.php deleted file mode 100644 index fa1bf973e0..0000000000 --- a/library/HTMLPurifier/Lexer/PH5P.php +++ /dev/null @@ -1,3906 +0,0 @@ -normalize($html, $config, $context); - $new_html = $this->wrapHTML($new_html, $config, $context); - try { - $parser = new HTML5($new_html); - $doc = $parser->save(); - } catch (DOMException $e) { - // Uh oh, it failed. Punt to DirectLex. - $lexer = new HTMLPurifier_Lexer_DirectLex(); - $context->register('PH5PError', $e); // save the error, so we can detect it - return $lexer->tokenizeHTML($html, $config, $context); // use original HTML - } - $tokens = array(); - $this->tokenizeDOM( - $doc->getElementsByTagName('html')->item(0)-> // - getElementsByTagName('body')->item(0)-> // - getElementsByTagName('div')->item(0) //
- , $tokens); - return $tokens; - } - -} - -/* - -Copyright 2007 Jeroen van der Meer - -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, and/or 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. - -*/ - -class HTML5 { - private $data; - private $char; - private $EOF; - private $state; - private $tree; - private $token; - private $content_model; - private $escape = false; - private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute', - 'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;', - 'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;', - 'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;', - 'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;', - 'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;', - 'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;', - 'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;', - 'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;', - 'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN', - 'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;', - 'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;', - 'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig', - 'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;', - 'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;', - 'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil', - 'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;', - 'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;', - 'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;', - 'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth', - 'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12', - 'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt', - 'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc', - 'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;', - 'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;', - 'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;', - 'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro', - 'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;', - 'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;', - 'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;', - 'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash', - 'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;', - 'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;', - 'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;', - 'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;', - 'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;', - 'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;', - 'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;', - 'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;', - 'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc', - 'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;', - 'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;'); - - const PCDATA = 0; - const RCDATA = 1; - const CDATA = 2; - const PLAINTEXT = 3; - - const DOCTYPE = 0; - const STARTTAG = 1; - const ENDTAG = 2; - const COMMENT = 3; - const CHARACTR = 4; - const EOF = 5; - - public function __construct($data) { - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", null, $data); - - $this->data = $data; - $this->char = -1; - $this->EOF = strlen($data); - $this->tree = new HTML5TreeConstructer; - $this->content_model = self::PCDATA; - - $this->state = 'data'; - - while($this->state !== null) { - $this->{$this->state.'State'}(); - } - } - - public function save() { - return $this->tree->save(); - } - - private function char() { - return ($this->char < $this->EOF) - ? $this->data[$this->char] - : false; - } - - private function character($s, $l = 0) { - if($s + $l < $this->EOF) { - if($l === 0) { - return $this->data[$s]; - } else { - return substr($this->data, $s, $l); - } - } - } - - private function characters($char_class, $start) { - return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start)); - } - - private function dataState() { - // Consume the next input character - $this->char++; - $char = $this->char(); - - if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { - /* U+0026 AMPERSAND (&) - When the content model flag is set to one of the PCDATA or RCDATA - states: switch to the entity data state. Otherwise: treat it as per - the "anything else" entry below. */ - $this->state = 'entityData'; - - } elseif($char === '-') { - /* If the content model flag is set to either the RCDATA state or - the CDATA state, and the escape flag is false, and there are at - least three characters before this one in the input stream, and the - last four characters in the input stream, including this one, are - U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, - and U+002D HYPHEN-MINUS (""), - set the escape flag to false. */ - if(($this->content_model === self::RCDATA || - $this->content_model === self::CDATA) && $this->escape === true && - $this->character($this->char, 3) === '-->') { - $this->escape = false; - } - - /* In any case, emit the input character as a character token. - Stay in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - } elseif($this->char === $this->EOF) { - /* EOF - Emit an end-of-file token. */ - $this->EOF(); - - } elseif($this->content_model === self::PLAINTEXT) { - /* When the content model flag is set to the PLAINTEXT state - THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of - the text and emit it as a character token. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => substr($this->data, $this->char) - )); - - $this->EOF(); - - } else { - /* Anything else - THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that - otherwise would also be treated as a character token and emit it - as a single character token. Stay in the data state. */ - $len = strcspn($this->data, '<&', $this->char); - $char = substr($this->data, $this->char, $len); - $this->char += $len - 1; - - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - $this->state = 'data'; - } - } - - private function entityDataState() { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, emit a U+0026 AMPERSAND character token. - // Otherwise, emit the character token that was returned. - $char = (!$entity) ? '&' : $entity; - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - // Finally, switch to the data state. - $this->state = 'data'; - } - - private function tagOpenState() { - switch($this->content_model) { - case self::RCDATA: - case self::CDATA: - /* If the next input character is a U+002F SOLIDUS (/) character, - consume it and switch to the close tag open state. If the next - input character is not a U+002F SOLIDUS (/) character, emit a - U+003C LESS-THAN SIGN character token and switch to the data - state to process the next input character. */ - if($this->character($this->char + 1) === '/') { - $this->char++; - $this->state = 'closeTagOpen'; - - } else { - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<' - )); - - $this->state = 'data'; - } - break; - - case self::PCDATA: - // If the content model flag is set to the PCDATA state - // Consume the next input character: - $this->char++; - $char = $this->char(); - - if($char === '!') { - /* U+0021 EXCLAMATION MARK (!) - Switch to the markup declaration open state. */ - $this->state = 'markupDeclarationOpen'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the close tag open state. */ - $this->state = 'closeTagOpen'; - - } elseif(preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new start tag token, set its tag name to the lowercase - version of the input character (add 0x0020 to the character's code - point), then switch to the tag name state. (Don't emit the token - yet; further details will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::STARTTAG, - 'attr' => array() - ); - - $this->state = 'tagName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit a U+003C LESS-THAN SIGN character token and a - U+003E GREATER-THAN SIGN character token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<>' - )); - - $this->state = 'data'; - - } elseif($char === '?') { - /* U+003F QUESTION MARK (?) - Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - - } else { - /* Anything else - Parse error. Emit a U+003C LESS-THAN SIGN character token and - reconsume the current input character in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<' - )); - - $this->char--; - $this->state = 'data'; - } - break; - } - } - - private function closeTagOpenState() { - $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); - $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; - - if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && - (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/', - $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) { - /* If the content model flag is set to the RCDATA or CDATA states then - examine the next few characters. If they do not match the tag name of - the last start tag token emitted (case insensitively), or if they do but - they are not immediately followed by one of the following characters: - * U+0009 CHARACTER TABULATION - * U+000A LINE FEED (LF) - * U+000B LINE TABULATION - * U+000C FORM FEED (FF) - * U+0020 SPACE - * U+003E GREATER-THAN SIGN (>) - * U+002F SOLIDUS (/) - * EOF - ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character - token, a U+002F SOLIDUS character token, and switch to the data state - to process the next input character. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => 'state = 'data'; - - } else { - /* Otherwise, if the content model flag is set to the PCDATA state, - or if the next few characters do match that tag name, consume the - next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new end tag token, set its tag name to the lowercase version - of the input character (add 0x0020 to the character's code point), then - switch to the tag name state. (Don't emit the token yet; further details - will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::ENDTAG - ); - - $this->state = 'tagName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Switch to the data state. */ - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F - SOLIDUS character token. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => 'char--; - $this->state = 'data'; - - } else { - /* Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - } - } - } - - private function tagNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } else { - /* Anything else - Append the current input character to the current tag token's tag name. - Stay in the tag name state. */ - $this->token['name'] .= strtolower($char); - $this->state = 'tagName'; - } - } - - private function beforeAttributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Stay in the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function attributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's name. - Stay in the attribute name state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['name'] .= strtolower($char); - - $this->state = 'attributeName'; - } - } - - private function afterAttributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the - before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function beforeAttributeValueState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the attribute value (double-quoted) state. */ - $this->state = 'attributeValueDoubleQuoted'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the attribute value (unquoted) state and reconsume - this input character. */ - $this->char--; - $this->state = 'attributeValueUnquoted'; - - } elseif($char === '\'') { - /* U+0027 APOSTROPHE (') - Switch to the attribute value (single-quoted) state. */ - $this->state = 'attributeValueSingleQuoted'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Switch to the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function attributeValueDoubleQuotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('double'); - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (double-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueDoubleQuoted'; - } - } - - private function attributeValueSingleQuotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if($char === '\'') { - /* U+0022 QUOTATION MARK (') - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('single'); - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (single-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueSingleQuoted'; - } - } - - private function attributeValueUnquotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState(); - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function entityInAttributeValueState() { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, append a U+0026 AMPERSAND character to the - // current attribute's value. Otherwise, emit the character token that - // was returned. - $char = (!$entity) - ? '&' - : $entity; - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - } - - private function bogusCommentState() { - /* Consume every character up to the first U+003E GREATER-THAN SIGN - character (>) or the end of the file (EOF), whichever comes first. Emit - a comment token whose data is the concatenation of all the characters - starting from and including the character that caused the state machine - to switch into the bogus comment state, up to and including the last - consumed character before the U+003E character, if any, or up to the - end of the file otherwise. (If the comment was started by the end of - the file (EOF), the token is empty.) */ - $data = $this->characters('^>', $this->char); - $this->emitToken(array( - 'data' => $data, - 'type' => self::COMMENT - )); - - $this->char += strlen($data); - - /* Switch to the data state. */ - $this->state = 'data'; - - /* If the end of the file was reached, reconsume the EOF character. */ - if($this->char === $this->EOF) { - $this->char = $this->EOF - 1; - } - } - - private function markupDeclarationOpenState() { - /* If the next two characters are both U+002D HYPHEN-MINUS (-) - characters, consume those two characters, create a comment token whose - data is the empty string, and switch to the comment state. */ - if($this->character($this->char + 1, 2) === '--') { - $this->char += 2; - $this->state = 'comment'; - $this->token = array( - 'data' => null, - 'type' => self::COMMENT - ); - - /* Otherwise if the next seven chacacters are a case-insensitive match - for the word "DOCTYPE", then consume those characters and switch to the - DOCTYPE state. */ - } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') { - $this->char += 7; - $this->state = 'doctype'; - - /* Otherwise, is is a parse error. Switch to the bogus comment state. - The next character that is consumed, if any, is the first character - that will be in the comment. */ - } else { - $this->char++; - $this->state = 'bogusComment'; - } - } - - private function commentState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if($char === '-') { - /* Switch to the comment dash state */ - $this->state = 'commentDash'; - - /* EOF */ - } elseif($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append the input character to the comment token's data. Stay in - the comment state. */ - $this->token['data'] .= $char; - } - } - - private function commentDashState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if($char === '-') { - /* Switch to the comment end state */ - $this->state = 'commentEnd'; - - /* EOF */ - } elseif($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append a U+002D HYPHEN-MINUS (-) character and the input - character to the comment token's data. Switch to the comment state. */ - $this->token['data'] .= '-'.$char; - $this->state = 'comment'; - } - } - - private function commentEndState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '-') { - $this->token['data'] .= '-'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['data'] .= '--'.$char; - $this->state = 'comment'; - } - } - - private function doctypeState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'beforeDoctypeName'; - - } else { - $this->char--; - $this->state = 'beforeDoctypeName'; - } - } - - private function beforeDoctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the before DOCTYPE name state. - - } elseif(preg_match('/^[a-z]$/', $char)) { - $this->token = array( - 'name' => strtoupper($char), - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - - } elseif($char === '>') { - $this->emitToken(array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - )); - - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken(array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - )); - - $this->char--; - $this->state = 'data'; - - } else { - $this->token = array( - 'name' => $char, - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - } - } - - private function doctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'AfterDoctypeName'; - - } elseif($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif(preg_match('/^[a-z]$/', $char)) { - $this->token['name'] .= strtoupper($char); - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['name'] .= $char; - } - - $this->token['error'] = ($this->token['name'] === 'HTML') - ? false - : true; - } - - private function afterDoctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the DOCTYPE name state. - - } elseif($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['error'] = true; - $this->state = 'bogusDoctype'; - } - } - - private function bogusDoctypeState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - // Stay in the bogus DOCTYPE state. - } - } - - private function entity() { - $start = $this->char; - - // This section defines how to consume an entity. This definition is - // used when parsing entities in text and in attributes. - - // The behaviour depends on the identity of the next character (the - // one immediately after the U+0026 AMPERSAND character): - - switch($this->character($this->char + 1)) { - // U+0023 NUMBER SIGN (#) - case '#': - - // The behaviour further depends on the character after the - // U+0023 NUMBER SIGN: - switch($this->character($this->char + 1)) { - // U+0078 LATIN SMALL LETTER X - // U+0058 LATIN CAPITAL LETTER X - case 'x': - case 'X': - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 - // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER - // A, through to U+0046 LATIN CAPITAL LETTER F (in other - // words, 0-9, A-F, a-f). - $char = 1; - $char_class = '0-9A-Fa-f'; - break; - - // Anything else - default: - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE (i.e. just 0-9). - $char = 0; - $char_class = '0-9'; - break; - } - - // Consume as many characters as match the range of characters - // given above. - $this->char++; - $e_name = $this->characters($char_class, $this->char + $char + 1); - $entity = $this->character($start, $this->char); - $cond = strlen($e_name) > 0; - - // The rest of the parsing happens bellow. - break; - - // Anything else - default: - // Consume the maximum number of characters possible, with the - // consumed characters case-sensitively matching one of the - // identifiers in the first column of the entities table. - $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); - $len = strlen($e_name); - - for($c = 1; $c <= $len; $c++) { - $id = substr($e_name, 0, $c); - $this->char++; - - if(in_array($id, $this->entities)) { - if ($e_name[$c-1] !== ';') { - if ($c < $len && $e_name[$c] == ';') { - $this->char++; // consume extra semicolon - } - } - $entity = $id; - break; - } - } - - $cond = isset($entity); - // The rest of the parsing happens bellow. - break; - } - - if(!$cond) { - // If no match can be made, then this is a parse error. No - // characters are consumed, and nothing is returned. - $this->char = $start; - return false; - } - - // Return a character token for the character corresponding to the - // entity name (as given by the second column of the entities table). - return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8'); - } - - private function emitToken($token) { - $emit = $this->tree->emitToken($token); - - if(is_int($emit)) { - $this->content_model = $emit; - - } elseif($token['type'] === self::ENDTAG) { - $this->content_model = self::PCDATA; - } - } - - private function EOF() { - $this->state = null; - $this->tree->emitToken(array( - 'type' => self::EOF - )); - } -} - -class HTML5TreeConstructer { - public $stack = array(); - - private $phase; - private $mode; - private $dom; - private $foster_parent = null; - private $a_formatting = array(); - - private $head_pointer = null; - private $form_pointer = null; - - private $scoping = array('button','caption','html','marquee','object','table','td','th'); - private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u'); - private $special = array('address','area','base','basefont','bgsound', - 'blockquote','body','br','center','col','colgroup','dd','dir','div','dl', - 'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5', - 'h6','head','hr','iframe','image','img','input','isindex','li','link', - 'listing','menu','meta','noembed','noframes','noscript','ol','optgroup', - 'option','p','param','plaintext','pre','script','select','spacer','style', - 'tbody','textarea','tfoot','thead','title','tr','ul','wbr'); - - // The different phases. - const INIT_PHASE = 0; - const ROOT_PHASE = 1; - const MAIN_PHASE = 2; - const END_PHASE = 3; - - // The different insertion modes for the main phase. - const BEFOR_HEAD = 0; - const IN_HEAD = 1; - const AFTER_HEAD = 2; - const IN_BODY = 3; - const IN_TABLE = 4; - const IN_CAPTION = 5; - const IN_CGROUP = 6; - const IN_TBODY = 7; - const IN_ROW = 8; - const IN_CELL = 9; - const IN_SELECT = 10; - const AFTER_BODY = 11; - const IN_FRAME = 12; - const AFTR_FRAME = 13; - - // The different types of elements. - const SPECIAL = 0; - const SCOPING = 1; - const FORMATTING = 2; - const PHRASING = 3; - - const MARKER = 0; - - public function __construct() { - $this->phase = self::INIT_PHASE; - $this->mode = self::BEFOR_HEAD; - $this->dom = new DOMDocument; - - $this->dom->encoding = 'UTF-8'; - $this->dom->preserveWhiteSpace = true; - $this->dom->substituteEntities = true; - $this->dom->strictErrorChecking = false; - } - - // Process tag tokens - public function emitToken($token) { - switch($this->phase) { - case self::INIT_PHASE: return $this->initPhase($token); break; - case self::ROOT_PHASE: return $this->rootElementPhase($token); break; - case self::MAIN_PHASE: return $this->mainPhase($token); break; - case self::END_PHASE : return $this->trailingEndPhase($token); break; - } - } - - private function initPhase($token) { - /* Initially, the tree construction stage must handle each token - emitted from the tokenisation stage as follows: */ - - /* A DOCTYPE token that is marked as being in error - A comment token - A start tag token - An end tag token - A character token that is not one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE - An end-of-file token */ - if((isset($token['error']) && $token['error']) || - $token['type'] === HTML5::COMMENT || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF || - ($token['type'] === HTML5::CHARACTR && isset($token['data']) && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) { - /* This specification does not define how to handle this case. In - particular, user agents may ignore the entirety of this specification - altogether for such documents, and instead invoke special parse modes - with a greater emphasis on backwards compatibility. */ - - $this->phase = self::ROOT_PHASE; - return $this->rootElementPhase($token); - - /* A DOCTYPE token marked as being correct */ - } elseif(isset($token['error']) && !$token['error']) { - /* Append a DocumentType node to the Document node, with the name - attribute set to the name given in the DOCTYPE token (which will be - "HTML"), and the other attributes specific to DocumentType objects - set to null, empty lists, or the empty string as appropriate. */ - $doctype = new DOMDocumentType(null, null, 'HTML'); - - /* Then, switch to the root element phase of the tree construction - stage. */ - $this->phase = self::ROOT_PHASE; - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/', - $token['data'])) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - } - } - - private function rootElementPhase($token) { - /* After the initial phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED - (FF), or U+0020 SPACE - A start tag token - An end tag token - An end-of-file token */ - } elseif(($token['type'] === HTML5::CHARACTR && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF) { - /* Create an HTMLElement node with the tag name html, in the HTML - namespace. Append it to the Document object. Switch to the main - phase and reprocess the current token. */ - $html = $this->dom->createElement('html'); - $this->dom->appendChild($html); - $this->stack[] = $html; - - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - } - } - - private function mainPhase($token) { - /* Tokens in the main phase must be handled as follows: */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A start tag token with the tag name "html" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { - /* If this start tag token was not the first start tag token, then - it is a parse error. */ - - /* For each attribute on the token, check to see if the attribute - is already present on the top element of the stack of open elements. - If it is not, add the attribute and its corresponding value to that - element. */ - foreach($token['attr'] as $attr) { - if(!$this->stack[0]->hasAttribute($attr['name'])) { - $this->stack[0]->setAttribute($attr['name'], $attr['value']); - } - } - - /* An end-of-file token */ - } elseif($token['type'] === HTML5::EOF) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Anything else. */ - } else { - /* Depends on the insertion mode: */ - switch($this->mode) { - case self::BEFOR_HEAD: return $this->beforeHead($token); break; - case self::IN_HEAD: return $this->inHead($token); break; - case self::AFTER_HEAD: return $this->afterHead($token); break; - case self::IN_BODY: return $this->inBody($token); break; - case self::IN_TABLE: return $this->inTable($token); break; - case self::IN_CAPTION: return $this->inCaption($token); break; - case self::IN_CGROUP: return $this->inColumnGroup($token); break; - case self::IN_TBODY: return $this->inTableBody($token); break; - case self::IN_ROW: return $this->inRow($token); break; - case self::IN_CELL: return $this->inCell($token); break; - case self::IN_SELECT: return $this->inSelect($token); break; - case self::AFTER_BODY: return $this->afterBody($token); break; - case self::IN_FRAME: return $this->inFrameset($token); break; - case self::AFTR_FRAME: return $this->afterFrameset($token); break; - case self::END_PHASE: return $this->trailingEndPhase($token); break; - } - } - } - - private function beforeHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "head" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { - /* Create an element for the token, append the new element to the - current node and push it onto the stack of open elements. */ - $element = $this->insertElement($token); - - /* Set the head element pointer to this new element node. */ - $this->head_pointer = $element; - - /* Change the insertion mode to "in head". */ - $this->mode = self::IN_HEAD; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title". Or an end tag with the tag name "html". - Or a character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or any other start tag token */ - } elseif($token['type'] === HTML5::STARTTAG || - ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || - ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/', - $token['data']))) { - /* Act as if a start tag token with the tag name "head" and no - attributes had been seen, then reprocess the current token. */ - $this->beforeHead(array( - 'name' => 'head', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inHead($token); - - /* Any other end tag */ - } elseif($token['type'] === HTML5::ENDTAG) { - /* Parse error. Ignore the token. */ - } - } - - private function inHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. - - THIS DIFFERS FROM THE SPEC: If the current node is either a title, style - or script element, append the character to the current node regardless - of its content. */ - if(($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( - $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName, - array('title', 'style', 'script')))) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('title', 'style', 'script'))) { - array_pop($this->stack); - return HTML5::PCDATA; - - /* A start tag with the tag name "title" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $element = $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the RCDATA state. */ - return HTML5::RCDATA; - - /* A start tag with the tag name "style" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "script" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { - /* Create an element for the token. */ - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "base", "link", or "meta" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta'))) { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - array_pop($this->stack); - - } else { - $this->insertElement($token); - } - - /* An end tag with the tag name "head" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { - /* If the current node is a head element, pop the current node off - the stack of open elements. */ - if($this->head_pointer->isSameNode(end($this->stack))) { - array_pop($this->stack); - - /* Otherwise, this is a parse error. */ - } else { - // k - } - - /* Change the insertion mode to "after head". */ - $this->mode = self::AFTER_HEAD; - - /* A start tag with the tag name "head" or an end tag except "html". */ - } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* If the current node is a head element, act as if an end tag - token with the tag name "head" had been seen. */ - if($this->head_pointer->isSameNode(end($this->stack))) { - $this->inHead(array( - 'name' => 'head', - 'type' => HTML5::ENDTAG - )); - - /* Otherwise, change the insertion mode to "after head". */ - } else { - $this->mode = self::AFTER_HEAD; - } - - /* Then, reprocess the current token. */ - return $this->afterHead($token); - } - } - - private function afterHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "body" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { - /* Insert a body element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in body". */ - $this->mode = self::IN_BODY; - - /* A start tag token with the tag name "frameset" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { - /* Insert a frameset element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in frameset". */ - $this->mode = self::IN_FRAME; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta', 'script', 'style', 'title'))) { - /* Parse error. Switch the insertion mode back to "in head" and - reprocess the token. */ - $this->mode = self::IN_HEAD; - return $this->inHead($token); - - /* Anything else */ - } else { - /* Act as if a start tag token with the tag name "body" and no - attributes had been seen, and then reprocess the current token. */ - $this->afterHead(array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inBody($token); - } - } - - private function inBody($token) { - /* Handle the token as follows: */ - - switch($token['type']) { - /* A character token */ - case HTML5::CHARACTR: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - break; - - /* A comment token */ - case HTML5::COMMENT: - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - break; - - case HTML5::STARTTAG: - switch($token['name']) { - /* A start tag token whose tag name is one of: "script", - "style" */ - case 'script': case 'style': - /* Process the token as if the insertion mode had been "in - head". */ - return $this->inHead($token); - break; - - /* A start tag token whose tag name is one of: "base", "link", - "meta", "title" */ - case 'base': case 'link': case 'meta': case 'title': - /* Parse error. Process the token as if the insertion mode - had been "in head". */ - return $this->inHead($token); - break; - - /* A start tag token with the tag name "body" */ - case 'body': - /* Parse error. If the second element on the stack of open - elements is not a body element, or, if the stack of open - elements has only one node on it, then ignore the token. - (innerHTML case) */ - if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { - // Ignore - - /* Otherwise, for each attribute on the token, check to see - if the attribute is already present on the body element (the - second element) on the stack of open elements. If it is not, - add the attribute and its corresponding value to that - element. */ - } else { - foreach($token['attr'] as $attr) { - if(!$this->stack[1]->hasAttribute($attr['name'])) { - $this->stack[1]->setAttribute($attr['name'], $attr['value']); - } - } - } - break; - - /* A start tag whose tag name is one of: "address", - "blockquote", "center", "dir", "div", "dl", "fieldset", - "listing", "menu", "ol", "p", "ul" */ - case 'address': case 'blockquote': case 'center': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'listing': - case 'menu': case 'ol': case 'p': case 'ul': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "form" */ - case 'form': - /* If the form element pointer is not null, ignore the - token with a parse error. */ - if($this->form_pointer !== null) { - // Ignore. - - /* Otherwise: */ - } else { - /* If the stack of open elements has a p element in - scope, then act as if an end tag with the tag name p - had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token, and set the - form element pointer to point to the element created. */ - $element = $this->insertElement($token); - $this->form_pointer = $element; - } - break; - - /* A start tag whose tag name is "li", "dd" or "dt" */ - case 'li': case 'dd': case 'dt': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - $stack_length = count($this->stack) - 1; - - for($n = $stack_length; 0 <= $n; $n--) { - /* 1. Initialise node to be the current node (the - bottommost node of the stack). */ - $stop = false; - $node = $this->stack[$n]; - $cat = $this->getElementCategory($node->tagName); - - /* 2. If node is an li, dd or dt element, then pop all - the nodes from the current node up to node, including - node, then stop this algorithm. */ - if($token['name'] === $node->tagName || ($token['name'] !== 'li' - && ($node->tagName === 'dd' || $node->tagName === 'dt'))) { - for($x = $stack_length; $x >= $n ; $x--) { - array_pop($this->stack); - } - - break; - } - - /* 3. If node is not in the formatting category, and is - not in the phrasing category, and is not an address or - div element, then stop this algorithm. */ - if($cat !== self::FORMATTING && $cat !== self::PHRASING && - $node->tagName !== 'address' && $node->tagName !== 'div') { - break; - } - } - - /* Finally, insert an HTML element with the same tag - name as the token's. */ - $this->insertElement($token); - break; - - /* A start tag token whose tag name is "plaintext" */ - case 'plaintext': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - return HTML5::PLAINTEXT; - break; - - /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - this is a parse error; pop elements from the stack until an - element with one of those tag names has been popped from the - stack. */ - while($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { - array_pop($this->stack); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "a" */ - case 'a': - /* If the list of active formatting elements contains - an element whose tag name is "a" between the end of the - list and the last marker on the list (or the start of - the list if there is no marker on the list), then this - is a parse error; act as if an end tag with the tag name - "a" had been seen, then remove that element from the list - of active formatting elements and the stack of open - elements if the end tag didn't already remove it (it - might not have if the element is not in table scope). */ - $leng = count($this->a_formatting); - - for($n = $leng - 1; $n >= 0; $n--) { - if($this->a_formatting[$n] === self::MARKER) { - break; - - } elseif($this->a_formatting[$n]->nodeName === 'a') { - $this->emitToken(array( - 'name' => 'a', - 'type' => HTML5::ENDTAG - )); - break; - } - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag whose tag name is one of: "b", "big", "em", "font", - "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'b': case 'big': case 'em': case 'font': case 'i': - case 'nobr': case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag token whose tag name is "button" */ - case 'button': - /* If the stack of open elements has a button element in scope, - then this is a parse error; act as if an end tag with the tag - name "button" had been seen, then reprocess the token. (We don't - do that. Unnecessary.) */ - if($this->elementInScope('button')) { - $this->inBody(array( - 'name' => 'button', - 'type' => HTML5::ENDTAG - )); - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is one of: "marquee", "object" */ - case 'marquee': case 'object': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is "xmp" */ - case 'xmp': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Switch the content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "table" */ - case 'table': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - break; - - /* A start tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'img': case 'param': case 'spacer': - case 'wbr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "hr" */ - case 'hr': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "image" */ - case 'image': - /* Parse error. Change the token's tag name to "img" and - reprocess it. (Don't ask.) */ - $token['name'] = 'img'; - return $this->inBody($token); - break; - - /* A start tag whose tag name is "input" */ - case 'input': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an input element for the token. */ - $element = $this->insertElement($token, false); - - /* If the form element pointer is not null, then associate the - input element with the form element pointed to by the form - element pointer. */ - $this->form_pointer !== null - ? $this->form_pointer->appendChild($element) - : end($this->stack)->appendChild($element); - - /* Pop that input element off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "isindex" */ - case 'isindex': - /* Parse error. */ - // w/e - - /* If the form element pointer is not null, - then ignore the token. */ - if($this->form_pointer === null) { - /* Act as if a start tag token with the tag name "form" had - been seen. */ - $this->inBody(array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody(array( - 'name' => 'hr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "p" had - been seen. */ - $this->inBody(array( - 'name' => 'p', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "label" - had been seen. */ - $this->inBody(array( - 'name' => 'label', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a stream of character tokens had been seen. */ - $this->insertText('This is a searchable index. '. - 'Insert your search keywords here: '); - - /* Act as if a start tag token with the tag name "input" - had been seen, with all the attributes from the "isindex" - token, except with the "name" attribute set to the value - "isindex" (ignoring any explicit "name" attribute). */ - $attr = $token['attr']; - $attr[] = array('name' => 'name', 'value' => 'isindex'); - - $this->inBody(array( - 'name' => 'input', - 'type' => HTML5::STARTTAG, - 'attr' => $attr - )); - - /* Act as if a stream of character tokens had been seen - (see below for what they should say). */ - $this->insertText('This is a searchable index. '. - 'Insert your search keywords here: '); - - /* Act as if an end tag token with the tag name "label" - had been seen. */ - $this->inBody(array( - 'name' => 'label', - 'type' => HTML5::ENDTAG - )); - - /* Act as if an end tag token with the tag name "p" had - been seen. */ - $this->inBody(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody(array( - 'name' => 'hr', - 'type' => HTML5::ENDTAG - )); - - /* Act as if an end tag token with the tag name "form" had - been seen. */ - $this->inBody(array( - 'name' => 'form', - 'type' => HTML5::ENDTAG - )); - } - break; - - /* A start tag whose tag name is "textarea" */ - case 'textarea': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the - RCDATA state. */ - return HTML5::RCDATA; - break; - - /* A start tag whose tag name is one of: "iframe", "noembed", - "noframes" */ - case 'iframe': case 'noembed': case 'noframes': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "select" */ - case 'select': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in select". */ - $this->mode = self::IN_SELECT; - break; - - /* A start or end tag whose tag name is one of: "caption", "col", - "colgroup", "frame", "frameset", "head", "option", "optgroup", - "tbody", "td", "tfoot", "th", "thead", "tr". */ - case 'caption': case 'col': case 'colgroup': case 'frame': - case 'frameset': case 'head': case 'option': case 'optgroup': - case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': - case 'tr': - // Parse error. Ignore the token. - break; - - /* A start or end tag whose tag name is one of: "event-source", - "section", "nav", "article", "aside", "header", "footer", - "datagrid", "command" */ - case 'event-source': case 'section': case 'nav': case 'article': - case 'aside': case 'header': case 'footer': case 'datagrid': - case 'command': - // Work in progress! - break; - - /* A start tag token not covered by the previous entries */ - default: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - $this->insertElement($token, true, true); - break; - } - break; - - case HTML5::ENDTAG: - switch($token['name']) { - /* An end tag with the tag name "body" */ - case 'body': - /* If the second element in the stack of open elements is - not a body element, this is a parse error. Ignore the token. - (innerHTML case) */ - if(count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { - // Ignore. - - /* If the current node is not the body element, then this - is a parse error. */ - } elseif(end($this->stack)->nodeName !== 'body') { - // Parse error. - } - - /* Change the insertion mode to "after body". */ - $this->mode = self::AFTER_BODY; - break; - - /* An end tag with the tag name "html" */ - case 'html': - /* Act as if an end tag with tag name "body" had been seen, - then, if that token wasn't ignored, reprocess the current - token. */ - $this->inBody(array( - 'name' => 'body', - 'type' => HTML5::ENDTAG - )); - - return $this->afterBody($token); - break; - - /* An end tag whose tag name is one of: "address", "blockquote", - "center", "dir", "div", "dl", "fieldset", "listing", "menu", - "ol", "pre", "ul" */ - case 'address': case 'blockquote': case 'center': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'listing': - case 'menu': case 'ol': case 'pre': case 'ul': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with - the same tag name as that of the token, then this - is a parse error. */ - // w/e - - /* If the stack of open elements has an element in - scope with the same tag name as that of the token, - then pop elements from this stack until an element - with that tag name has been popped from the stack. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is "form" */ - case 'form': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - } - - if(end($this->stack)->nodeName !== $token['name']) { - /* Now, if the current node is not an element with the - same tag name as that of the token, then this is a parse - error. */ - // w/e - - } else { - /* Otherwise, if the current node is an element with - the same tag name as that of the token pop that element - from the stack. */ - array_pop($this->stack); - } - - /* In any case, set the form element pointer to null. */ - $this->form_pointer = null; - break; - - /* An end tag whose tag name is "p" */ - case 'p': - /* If the stack of open elements has a p element in scope, - then generate implied end tags, except for p elements. */ - if($this->elementInScope('p')) { - $this->generateImpliedEndTags(array('p')); - - /* If the current node is not a p element, then this is - a parse error. */ - // k - - /* If the stack of open elements has a p element in - scope, then pop elements from this stack until the stack - no longer has a p element in scope. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->elementInScope('p')) { - array_pop($this->stack); - - } else { - break; - } - } - } - break; - - /* An end tag whose tag name is "dd", "dt", or "li" */ - case 'dd': case 'dt': case 'li': - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - generate implied end tags, except for elements with the - same tag name as the token. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(array($token['name'])); - - /* If the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - pop elements from this stack until an element with that - tag name has been popped from the stack. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - generate implied end tags. */ - if($this->elementInScope($elements)) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as that of the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has in scope an element - whose tag name is one of "h1", "h2", "h3", "h4", "h5", or - "h6", then pop elements from the stack until an element - with one of those tag names has been popped from the stack. */ - while($this->elementInScope($elements)) { - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "a", "b", "big", "em", - "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'a': case 'b': case 'big': case 'em': case 'font': - case 'i': case 'nobr': case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* 1. Let the formatting element be the last element in - the list of active formatting elements that: - * is between the end of the list and the last scope - marker in the list, if any, or the start of the list - otherwise, and - * has the same tag name as the token. - */ - while(true) { - for($a = count($this->a_formatting) - 1; $a >= 0; $a--) { - if($this->a_formatting[$a] === self::MARKER) { - break; - - } elseif($this->a_formatting[$a]->tagName === $token['name']) { - $formatting_element = $this->a_formatting[$a]; - $in_stack = in_array($formatting_element, $this->stack, true); - $fe_af_pos = $a; - break; - } - } - - /* If there is no such node, or, if that node is - also in the stack of open elements but the element - is not in scope, then this is a parse error. Abort - these steps. The token is ignored. */ - if(!isset($formatting_element) || ($in_stack && - !$this->elementInScope($token['name']))) { - break; - - /* Otherwise, if there is such a node, but that node - is not in the stack of open elements, then this is a - parse error; remove the element from the list, and - abort these steps. */ - } elseif(isset($formatting_element) && !$in_stack) { - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 2. Let the furthest block be the topmost node in the - stack of open elements that is lower in the stack - than the formatting element, and is not an element in - the phrasing or formatting categories. There might - not be one. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $length = count($this->stack); - - for($s = $fe_s_pos + 1; $s < $length; $s++) { - $category = $this->getElementCategory($this->stack[$s]->nodeName); - - if($category !== self::PHRASING && $category !== self::FORMATTING) { - $furthest_block = $this->stack[$s]; - } - } - - /* 3. If there is no furthest block, then the UA must - skip the subsequent steps and instead just pop all - the nodes from the bottom of the stack of open - elements, from the current node up to the formatting - element, and remove the formatting element from the - list of active formatting elements. */ - if(!isset($furthest_block)) { - for($n = $length - 1; $n >= $fe_s_pos; $n--) { - array_pop($this->stack); - } - - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 4. Let the common ancestor be the element - immediately above the formatting element in the stack - of open elements. */ - $common_ancestor = $this->stack[$fe_s_pos - 1]; - - /* 5. If the furthest block has a parent node, then - remove the furthest block from its parent node. */ - if($furthest_block->parentNode !== null) { - $furthest_block->parentNode->removeChild($furthest_block); - } - - /* 6. Let a bookmark note the position of the - formatting element in the list of active formatting - elements relative to the elements on either side - of it in the list. */ - $bookmark = $fe_af_pos; - - /* 7. Let node and last node be the furthest block. - Follow these steps: */ - $node = $furthest_block; - $last_node = $furthest_block; - - while(true) { - for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { - /* 7.1 Let node be the element immediately - prior to node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 7.2 If node is not in the list of active - formatting elements, then remove node from - the stack of open elements and then go back - to step 1. */ - if(!in_array($node, $this->a_formatting, true)) { - unset($this->stack[$n]); - $this->stack = array_merge($this->stack); - - } else { - break; - } - } - - /* 7.3 Otherwise, if node is the formatting - element, then go to the next step in the overall - algorithm. */ - if($node === $formatting_element) { - break; - - /* 7.4 Otherwise, if last node is the furthest - block, then move the aforementioned bookmark to - be immediately after the node in the list of - active formatting elements. */ - } elseif($last_node === $furthest_block) { - $bookmark = array_search($node, $this->a_formatting, true) + 1; - } - - /* 7.5 If node has any children, perform a - shallow clone of node, replace the entry for - node in the list of active formatting elements - with an entry for the clone, replace the entry - for node in the stack of open elements with an - entry for the clone, and let node be the clone. */ - if($node->hasChildNodes()) { - $clone = $node->cloneNode(); - $s_pos = array_search($node, $this->stack, true); - $a_pos = array_search($node, $this->a_formatting, true); - - $this->stack[$s_pos] = $clone; - $this->a_formatting[$a_pos] = $clone; - $node = $clone; - } - - /* 7.6 Insert last node into node, first removing - it from its previous parent node if any. */ - if($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $node->appendChild($last_node); - - /* 7.7 Let last node be node. */ - $last_node = $node; - } - - /* 8. Insert whatever last node ended up being in - the previous step into the common ancestor node, - first removing it from its previous parent node if - any. */ - if($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $common_ancestor->appendChild($last_node); - - /* 9. Perform a shallow clone of the formatting - element. */ - $clone = $formatting_element->cloneNode(); - - /* 10. Take all of the child nodes of the furthest - block and append them to the clone created in the - last step. */ - while($furthest_block->hasChildNodes()) { - $child = $furthest_block->firstChild; - $furthest_block->removeChild($child); - $clone->appendChild($child); - } - - /* 11. Append that clone to the furthest block. */ - $furthest_block->appendChild($clone); - - /* 12. Remove the formatting element from the list - of active formatting elements, and insert the clone - into the list of active formatting elements at the - position of the aforementioned bookmark. */ - $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - - $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); - $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); - $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); - - /* 13. Remove the formatting element from the stack - of open elements, and insert the clone into the stack - of open elements immediately after (i.e. in a more - deeply nested position than) the position of the - furthest block in that stack. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $fb_s_pos = array_search($furthest_block, $this->stack, true); - unset($this->stack[$fe_s_pos]); - - $s_part1 = array_slice($this->stack, 0, $fb_s_pos); - $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); - $this->stack = array_merge($s_part1, array($clone), $s_part2); - - /* 14. Jump back to step 1 in this series of steps. */ - unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); - } - break; - - /* An end tag token whose tag name is one of: "button", - "marquee", "object" */ - case 'button': case 'marquee': case 'object': - /* If the stack of open elements has an element in scope whose - tag name matches the tag name of the token, then generate implied - tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // k - - /* Now, if the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then pop - elements from the stack until that element has been popped from - the stack, and clear the list of active formatting elements up - to the last marker. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - - $marker = end(array_keys($this->a_formatting, self::MARKER, true)); - - for($n = count($this->a_formatting) - 1; $n > $marker; $n--) { - array_pop($this->a_formatting); - } - } - break; - - /* Or an end tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "hr", "iframe", "image", "img", - "input", "isindex", "noembed", "noframes", "param", "select", - "spacer", "table", "textarea", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'hr': case 'iframe': case 'image': - case 'img': case 'input': case 'isindex': case 'noembed': - case 'noframes': case 'param': case 'select': case 'spacer': - case 'table': case 'textarea': case 'wbr': - // Parse error. Ignore the token. - break; - - /* An end tag token not covered by the previous entries */ - default: - for($n = count($this->stack) - 1; $n >= 0; $n--) { - /* Initialise node to be the current node (the bottommost - node of the stack). */ - $node = end($this->stack); - - /* If node has the same tag name as the end tag token, - then: */ - if($token['name'] === $node->nodeName) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* If the tag name of the end tag token does not - match the tag name of the current node, this is a - parse error. */ - // k - - /* Pop all the nodes from the current node up to - node, including node, then stop this algorithm. */ - for($x = count($this->stack) - $n; $x >= $n; $x--) { - array_pop($this->stack); - } - - } else { - $category = $this->getElementCategory($node); - - if($category !== self::SPECIAL && $category !== self::SCOPING) { - /* Otherwise, if node is in neither the formatting - category nor the phrasing category, then this is a - parse error. Stop this algorithm. The end tag token - is ignored. */ - return false; - } - } - } - break; - } - break; - } - } - - private function inTable($token) { - $clear = array('html', 'table'); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "caption" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'caption') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - /* Insert an HTML element for the token, then switch the - insertion mode to "in caption". */ - $this->insertElement($token); - $this->mode = self::IN_CAPTION; - - /* A start tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'colgroup') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the - insertion mode to "in column group". */ - $this->insertElement($token); - $this->mode = self::IN_CGROUP; - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'col') { - $this->inTable(array( - 'name' => 'colgroup', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - $this->inColumnGroup($token); - - /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('tbody', 'tfoot', 'thead'))) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in table body". */ - $this->insertElement($token); - $this->mode = self::IN_TBODY; - - /* A start tag whose tag name is one of: "td", "th", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && - in_array($token['name'], array('td', 'th', 'tr'))) { - /* Act as if a start tag token with the tag name "tbody" had been - seen, then reprocess the current token. */ - $this->inTable(array( - 'name' => 'tbody', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inTableBody($token); - - /* A start tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'table') { - /* Parse error. Act as if an end tag token with the tag name "table" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inTable(array( - 'name' => 'table', - 'type' => HTML5::ENDTAG - )); - - return $this->mainPhase($token); - - /* An end tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - return false; - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a table element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a table element has been - popped from the stack. */ - while(true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if($current === 'table') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td', - 'tfoot', 'th', 'thead', 'tr'))) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Parse error. Process the token as if the insertion mode was "in - body", with the following exception: */ - - /* If the current node is a table, tbody, tfoot, thead, or tr - element, then, whenever a node would be inserted into the current - node, it must instead be inserted into the foster parent element. */ - if(in_array(end($this->stack)->nodeName, - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* The foster parent element is the parent element of the last - table element in the stack of open elements, if there is a - table element and it has such a parent element. If there is no - table element in the stack of open elements (innerHTML case), - then the foster parent element is the first element in the - stack of open elements (the html element). Otherwise, if there - is a table element in the stack of open elements, but the last - table element in the stack of open elements has no parent, or - its parent node is not an element, then the foster parent - element is the element before the last table element in the - stack of open elements. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === 'table') { - $table = $this->stack[$n]; - break; - } - } - - if(isset($table) && $table->parentNode !== null) { - $this->foster_parent = $table->parentNode; - - } elseif(!isset($table)) { - $this->foster_parent = $this->stack[0]; - - } elseif(isset($table) && ($table->parentNode === null || - $table->parentNode->nodeType !== XML_ELEMENT_NODE)) { - $this->foster_parent = $this->stack[$n - 1]; - } - } - - $this->inBody($token); - } - } - - private function inCaption($token) { - /* An end tag whose tag name is "caption" */ - if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a caption element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a caption element has - been popped from the stack. */ - while(true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if($node === 'caption') { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag - name is "table" */ - } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table')) { - /* Parse error. Act as if an end tag with the tag name "caption" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inCaption(array( - 'name' => 'caption', - 'type' => HTML5::ENDTAG - )); - - return $this->inTable($token); - - /* An end tag whose tag name is one of: "body", "col", "colgroup", - "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th', - 'thead', 'tr'))) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inColumnGroup($token) { - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { - /* Insert a col element for the token. Immediately pop the current - node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - /* An end tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'colgroup') { - /* If the current node is the root html element, then this is a - parse error, ignore the token. (innerHTML case) */ - if(end($this->stack)->nodeName === 'html') { - // Ignore - - /* Otherwise, pop the current node (which will be a colgroup - element) from the stack of open elements. Switch the insertion - mode to "in table". */ - } else { - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* An end tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Act as if an end tag with the tag name "colgroup" had been seen, - and then, if that token wasn't ignored, reprocess the current token. */ - $this->inColumnGroup(array( - 'name' => 'colgroup', - 'type' => HTML5::ENDTAG - )); - - return $this->inTable($token); - } - } - - private function inTableBody($token) { - $clear = array('tbody', 'tfoot', 'thead', 'html'); - - /* A start tag whose tag name is "tr" */ - if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Insert a tr element for the token, then switch the insertion - mode to "in row". */ - $this->insertElement($token); - $this->mode = self::IN_ROW; - - /* A start tag whose tag name is one of: "th", "td" */ - } elseif($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Parse error. Act as if a start tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inTableBody(array( - 'name' => 'tr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inRow($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node from the stack of open elements. Switch - the insertion mode to "in table". */ - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ - } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) || - ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')) { - /* If the stack of open elements does not have a tbody, thead, or - tfoot element in table scope, this is a parse error. Ignore the - token. (innerHTML case) */ - if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Act as if an end tag with the same tag name as the current - node ("tbody", "tfoot", or "thead") had been seen, then - reprocess the current token. */ - $this->inTableBody(array( - 'name' => end($this->stack)->nodeName, - 'type' => HTML5::ENDTAG - )); - - return $this->mainPhase($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inRow($token) { - $clear = array('tr', 'html'); - - /* A start tag whose tag name is one of: "th", "td" */ - if($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in cell". */ - $this->insertElement($token); - $this->mode = self::IN_CELL; - - /* Insert a marker at the end of the list of active formatting - elements. */ - $this->a_formatting[] = self::MARKER; - - /* An end tag whose tag name is "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node (which will be a tr element) from the - stack of open elements. Switch the insertion mode to "in table - body". */ - array_pop($this->stack); - $this->mode = self::IN_TBODY; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* Act as if an end tag with the tag name "tr" had been seen, then, - if that token wasn't ignored, reprocess the current token. */ - $this->inRow(array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - )); - - return $this->inCell($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Otherwise, act as if an end tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inRow(array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - )); - - return $this->inCell($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inCell($token) { - /* An end tag whose tag name is one of: "td", "th" */ - if($token['type'] === HTML5::ENDTAG && - ($token['name'] === 'td' || $token['name'] === 'th')) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token, then this is a - parse error and the token must be ignored. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Generate implied end tags, except for elements with the same - tag name as the token. */ - $this->generateImpliedEndTags(array($token['name'])); - - /* Now, if the current node is not an element with the same tag - name as the token, then this is a parse error. */ - // k - - /* Pop elements from this stack until an element with the same - tag name as the token has been popped from the stack. */ - while(true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if($node === $token['name']) { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in row". (The current node - will be a tr element at this point.) */ - $this->mode = self::IN_ROW; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html'))) { - /* Parse error. Ignore the token. */ - - /* An end tag whose tag name is one of: "table", "tbody", "tfoot", - "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token (which can only - happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), - then this is a parse error and the token must be ignored. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inSelect($token) { - /* Handle the token as follows: */ - - /* A character token */ - if($token['type'] === HTML5::CHARACTR) { - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'option') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->nodeName === 'option') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* A start tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'optgroup') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->nodeName === 'option') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* If the current node is an optgroup element, act as if an end tag - with the tag name "optgroup" had been seen. */ - if(end($this->stack)->nodeName === 'optgroup') { - $this->inSelect(array( - 'name' => 'optgroup', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* An end tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'optgroup') { - /* First, if the current node is an option element, and the node - immediately before it in the stack of open elements is an optgroup - element, then act as if an end tag with the tag name "option" had - been seen. */ - $elements_in_stack = count($this->stack); - - if($this->stack[$elements_in_stack - 1]->nodeName === 'option' && - $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* If the current node is an optgroup element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if($this->stack[$elements_in_stack - 1] === 'optgroup') { - array_pop($this->stack); - } - - /* An end tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'option') { - /* If the current node is an option element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if(end($this->stack)->nodeName === 'option') { - array_pop($this->stack); - } - - /* An end tag whose tag name is "select" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'select') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // w/e - - /* Otherwise: */ - } else { - /* Pop elements from the stack of open elements until a select - element has been popped from the stack. */ - while(true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if($current === 'select') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* A start tag whose tag name is "select" */ - } elseif($token['name'] === 'select' && - $token['type'] === HTML5::STARTTAG) { - /* Parse error. Act as if the token had been an end tag with the - tag name "select" instead. */ - $this->inSelect(array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - )); - - /* An end tag whose tag name is one of: "caption", "table", "tbody", - "tfoot", "thead", "tr", "td", "th" */ - } elseif(in_array($token['name'], array('caption', 'table', 'tbody', - 'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) { - /* Parse error. */ - // w/e - - /* If the stack of open elements has an element in table scope with - the same tag name as that of the token, then act as if an end tag - with the tag name "select" had been seen, and reprocess the token. - Otherwise, ignore the token. */ - if($this->elementInScope($token['name'], true)) { - $this->inSelect(array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - )); - - $this->mainPhase($token); - } - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterBody($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Process the token as it would be processed if the insertion mode - was "in body". */ - $this->inBody($token); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the first element in the stack of open - elements (the html element), with the data attribute set to the - data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->stack[0]->appendChild($comment); - - /* An end tag with the tag name "html" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { - /* If the parser was originally created in order to handle the - setting of an element's innerHTML attribute, this is a parse error; - ignore the token. (The element will be an html element in this - case.) (innerHTML case) */ - - /* Otherwise, switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* Anything else */ - } else { - /* Parse error. Set the insertion mode to "in body" and reprocess - the token. */ - $this->mode = self::IN_BODY; - return $this->inBody($token); - } - } - - private function inFrameset($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag with the tag name "frameset" */ - } elseif($token['name'] === 'frameset' && - $token['type'] === HTML5::STARTTAG) { - $this->insertElement($token); - - /* An end tag with the tag name "frameset" */ - } elseif($token['name'] === 'frameset' && - $token['type'] === HTML5::ENDTAG) { - /* If the current node is the root html element, then this is a - parse error; ignore the token. (innerHTML case) */ - if(end($this->stack)->nodeName === 'html') { - // Ignore - - } else { - /* Otherwise, pop the current node from the stack of open - elements. */ - array_pop($this->stack); - - /* If the parser was not originally created in order to handle - the setting of an element's innerHTML attribute (innerHTML case), - and the current node is no longer a frameset element, then change - the insertion mode to "after frameset". */ - $this->mode = self::AFTR_FRAME; - } - - /* A start tag with the tag name "frame" */ - } elseif($token['name'] === 'frame' && - $token['type'] === HTML5::STARTTAG) { - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - /* A start tag with the tag name "noframes" */ - } elseif($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterFrameset($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* An end tag with the tag name "html" */ - } elseif($token['name'] === 'html' && - $token['type'] === HTML5::ENDTAG) { - /* Switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* A start tag with the tag name "noframes" */ - } elseif($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function trailingEndPhase($token) { - /* After the main phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Process the token as it would be processed in the main phase. */ - $this->mainPhase($token); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or a start tag token. Or an end tag token. */ - } elseif(($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) { - /* Parse error. Switch back to the main phase and reprocess the - token. */ - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - - /* An end-of-file token */ - } elseif($token['type'] === HTML5::EOF) { - /* OMG DONE!! */ - } - } - - private function insertElement($token, $append = true, $check = false) { - // Proprietary workaround for libxml2's limitations with tag names - if ($check) { - // Slightly modified HTML5 tag-name modification, - // removing anything that's not an ASCII letter, digit, or hyphen - $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); - // Remove leading hyphens and numbers - $token['name'] = ltrim($token['name'], '-0..9'); - // In theory, this should ever be needed, but just in case - if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice - } - - $el = $this->dom->createElement($token['name']); - - foreach($token['attr'] as $attr) { - if(!$el->hasAttribute($attr['name'])) { - $el->setAttribute($attr['name'], $attr['value']); - } - } - - $this->appendToRealParent($el); - $this->stack[] = $el; - - return $el; - } - - private function insertText($data) { - $text = $this->dom->createTextNode($data); - $this->appendToRealParent($text); - } - - private function insertComment($data) { - $comment = $this->dom->createComment($data); - $this->appendToRealParent($comment); - } - - private function appendToRealParent($node) { - if($this->foster_parent === null) { - end($this->stack)->appendChild($node); - - } elseif($this->foster_parent !== null) { - /* If the foster parent element is the parent element of the - last table element in the stack of open elements, then the new - node must be inserted immediately before the last table element - in the stack of open elements in the foster parent element; - otherwise, the new node must be appended to the foster parent - element. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === 'table' && - $this->stack[$n]->parentNode !== null) { - $table = $this->stack[$n]; - break; - } - } - - if(isset($table) && $this->foster_parent->isSameNode($table->parentNode)) - $this->foster_parent->insertBefore($node, $table); - else - $this->foster_parent->appendChild($node); - - $this->foster_parent = null; - } - } - - private function elementInScope($el, $table = false) { - if(is_array($el)) { - foreach($el as $element) { - if($this->elementInScope($element, $table)) { - return true; - } - } - - return false; - } - - $leng = count($this->stack); - - for($n = 0; $n < $leng; $n++) { - /* 1. Initialise node to be the current node (the bottommost node of - the stack). */ - $node = $this->stack[$leng - 1 - $n]; - - if($node->tagName === $el) { - /* 2. If node is the target node, terminate in a match state. */ - return true; - - } elseif($node->tagName === 'table') { - /* 3. Otherwise, if node is a table element, terminate in a failure - state. */ - return false; - - } elseif($table === true && in_array($node->tagName, array('caption', 'td', - 'th', 'button', 'marquee', 'object'))) { - /* 4. Otherwise, if the algorithm is the "has an element in scope" - variant (rather than the "has an element in table scope" variant), - and node is one of the following, terminate in a failure state. */ - return false; - - } elseif($node === $node->ownerDocument->documentElement) { - /* 5. Otherwise, if node is an html element (root element), terminate - in a failure state. (This can only happen if the node is the topmost - node of the stack of open elements, and prevents the next step from - being invoked if there are no more elements in the stack.) */ - return false; - } - - /* Otherwise, set node to the previous entry in the stack of open - elements and return to step 2. (This will never fail, since the loop - will always terminate in the previous step if the top of the stack - is reached.) */ - } - } - - private function reconstructActiveFormattingElements() { - /* 1. If there are no entries in the list of active formatting elements, - then there is nothing to reconstruct; stop this algorithm. */ - $formatting_elements = count($this->a_formatting); - - if($formatting_elements === 0) { - return false; - } - - /* 3. Let entry be the last (most recently added) element in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. If the last (most recently added) entry in the list of active - formatting elements is a marker, or if it is an element that is in the - stack of open elements, then there is nothing to reconstruct; stop this - algorithm. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - return false; - } - - for($a = $formatting_elements - 1; $a >= 0; true) { - /* 4. If there are no entries before entry in the list of active - formatting elements, then jump to step 8. */ - if($a === 0) { - $step_seven = false; - break; - } - - /* 5. Let entry be the entry one earlier than entry in the list of - active formatting elements. */ - $a--; - $entry = $this->a_formatting[$a]; - - /* 6. If entry is neither a marker nor an element that is also in - thetack of open elements, go to step 4. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - break; - } - } - - while(true) { - /* 7. Let entry be the element one later than entry in the list of - active formatting elements. */ - if(isset($step_seven) && $step_seven === true) { - $a++; - $entry = $this->a_formatting[$a]; - } - - /* 8. Perform a shallow clone of the element entry to obtain clone. */ - $clone = $entry->cloneNode(); - - /* 9. Append clone to the current node and push it onto the stack - of open elements so that it is the new current node. */ - end($this->stack)->appendChild($clone); - $this->stack[] = $clone; - - /* 10. Replace the entry for entry in the list with an entry for - clone. */ - $this->a_formatting[$a] = $clone; - - /* 11. If the entry for clone in the list of active formatting - elements is not the last entry in the list, return to step 7. */ - if(end($this->a_formatting) !== $clone) { - $step_seven = true; - } else { - break; - } - } - } - - private function clearTheActiveFormattingElementsUpToTheLastMarker() { - /* When the steps below require the UA to clear the list of active - formatting elements up to the last marker, the UA must perform the - following steps: */ - - while(true) { - /* 1. Let entry be the last (most recently added) entry in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. Remove entry from the list of active formatting elements. */ - array_pop($this->a_formatting); - - /* 3. If entry was a marker, then stop the algorithm at this point. - The list has been cleared up to the last marker. */ - if($entry === self::MARKER) { - break; - } - } - } - - private function generateImpliedEndTags($exclude = array()) { - /* When the steps below require the UA to generate implied end tags, - then, if the current node is a dd element, a dt element, an li element, - a p element, a td element, a th element, or a tr element, the UA must - act as if an end tag with the respective tag name had been seen and - then generate implied end tags again. */ - $node = end($this->stack); - $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); - - while(in_array(end($this->stack)->nodeName, $elements)) { - array_pop($this->stack); - } - } - - private function getElementCategory($node) { - $name = $node->tagName; - if(in_array($name, $this->special)) - return self::SPECIAL; - - elseif(in_array($name, $this->scoping)) - return self::SCOPING; - - elseif(in_array($name, $this->formatting)) - return self::FORMATTING; - - else - return self::PHRASING; - } - - private function clearStackToTableContext($elements) { - /* When the steps above require the UA to clear the stack back to a - table context, it means that the UA must, while the current node is not - a table element or an html element, pop elements from the stack of open - elements. If this causes any elements to be popped from the stack, then - this is a parse error. */ - while(true) { - $node = end($this->stack)->nodeName; - - if(in_array($node, $elements)) { - break; - } else { - array_pop($this->stack); - } - } - } - - private function resetInsertionMode() { - /* 1. Let last be false. */ - $last = false; - $leng = count($this->stack); - - for($n = $leng - 1; $n >= 0; $n--) { - /* 2. Let node be the last node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 3. If node is the first node in the stack of open elements, then - set last to true. If the element whose innerHTML attribute is being - set is neither a td element nor a th element, then set node to the - element whose innerHTML attribute is being set. (innerHTML case) */ - if($this->stack[0]->isSameNode($node)) { - $last = true; - } - - /* 4. If node is a select element, then switch the insertion mode to - "in select" and abort these steps. (innerHTML case) */ - if($node->nodeName === 'select') { - $this->mode = self::IN_SELECT; - break; - - /* 5. If node is a td or th element, then switch the insertion mode - to "in cell" and abort these steps. */ - } elseif($node->nodeName === 'td' || $node->nodeName === 'th') { - $this->mode = self::IN_CELL; - break; - - /* 6. If node is a tr element, then switch the insertion mode to - "in row" and abort these steps. */ - } elseif($node->nodeName === 'tr') { - $this->mode = self::IN_ROW; - break; - - /* 7. If node is a tbody, thead, or tfoot element, then switch the - insertion mode to "in table body" and abort these steps. */ - } elseif(in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { - $this->mode = self::IN_TBODY; - break; - - /* 8. If node is a caption element, then switch the insertion mode - to "in caption" and abort these steps. */ - } elseif($node->nodeName === 'caption') { - $this->mode = self::IN_CAPTION; - break; - - /* 9. If node is a colgroup element, then switch the insertion mode - to "in column group" and abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'colgroup') { - $this->mode = self::IN_CGROUP; - break; - - /* 10. If node is a table element, then switch the insertion mode - to "in table" and abort these steps. */ - } elseif($node->nodeName === 'table') { - $this->mode = self::IN_TABLE; - break; - - /* 11. If node is a head element, then switch the insertion mode - to "in body" ("in body"! not "in head"!) and abort these steps. - (innerHTML case) */ - } elseif($node->nodeName === 'head') { - $this->mode = self::IN_BODY; - break; - - /* 12. If node is a body element, then switch the insertion mode to - "in body" and abort these steps. */ - } elseif($node->nodeName === 'body') { - $this->mode = self::IN_BODY; - break; - - /* 13. If node is a frameset element, then switch the insertion - mode to "in frameset" and abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'frameset') { - $this->mode = self::IN_FRAME; - break; - - /* 14. If node is an html element, then: if the head element - pointer is null, switch the insertion mode to "before head", - otherwise, switch the insertion mode to "after head". In either - case, abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'html') { - $this->mode = ($this->head_pointer === null) - ? self::BEFOR_HEAD - : self::AFTER_HEAD; - - break; - - /* 15. If last is true, then set the insertion mode to "in body" - and abort these steps. (innerHTML case) */ - } elseif($last) { - $this->mode = self::IN_BODY; - break; - } - } - } - - private function closeCell() { - /* If the stack of open elements has a td or th element in table scope, - then act as if an end tag token with that tag name had been seen. */ - foreach(array('td', 'th') as $cell) { - if($this->elementInScope($cell, true)) { - $this->inCell(array( - 'name' => $cell, - 'type' => HTML5::ENDTAG - )); - - break; - } - } - } - - public function save() { - return $this->dom; - } -} -?> diff --git a/library/HTMLPurifier/Strategy/FixNesting.php b/library/HTMLPurifier/Strategy/FixNesting.php deleted file mode 100644 index f81802391b..0000000000 --- a/library/HTMLPurifier/Strategy/FixNesting.php +++ /dev/null @@ -1,328 +0,0 @@ -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 diff --git a/library/HTMLPurifier/Token.php b/library/HTMLPurifier/Token.php deleted file mode 100644 index 7900e6cb10..0000000000 --- a/library/HTMLPurifier/Token.php +++ /dev/null @@ -1,57 +0,0 @@ -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 diff --git a/library/HTMLPurifier/Token/Comment.php b/library/HTMLPurifier/Token/Comment.php deleted file mode 100644 index dc6bdcabb8..0000000000 --- a/library/HTMLPurifier/Token/Comment.php +++ /dev/null @@ -1,22 +0,0 @@ -data = $data; - $this->line = $line; - $this->col = $col; - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/TokenFactory.php b/library/HTMLPurifier/TokenFactory.php deleted file mode 100644 index 7cf48fb41c..0000000000 --- a/library/HTMLPurifier/TokenFactory.php +++ /dev/null @@ -1,94 +0,0 @@ -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 diff --git a/library/HTMLPurifier/URI.php b/library/HTMLPurifier/URI.php deleted file mode 100644 index 8b50d0d18d..0000000000 --- a/library/HTMLPurifier/URI.php +++ /dev/null @@ -1,173 +0,0 @@ -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 diff --git a/library/HTMLPurifier/URIFilter.php b/library/HTMLPurifier/URIFilter.php deleted file mode 100644 index c116f93dff..0000000000 --- a/library/HTMLPurifier/URIFilter.php +++ /dev/null @@ -1,45 +0,0 @@ -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 diff --git a/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/library/HTMLPurifier/URIFilter/DisableExternalResources.php deleted file mode 100644 index 881abc43cf..0000000000 --- a/library/HTMLPurifier/URIFilter/DisableExternalResources.php +++ /dev/null @@ -1,12 +0,0 @@ -get('EmbeddedURI', true)) return true; - return parent::filter($uri, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIFilter/HostBlacklist.php b/library/HTMLPurifier/URIFilter/HostBlacklist.php deleted file mode 100644 index 045aa0992c..0000000000 --- a/library/HTMLPurifier/URIFilter/HostBlacklist.php +++ /dev/null @@ -1,21 +0,0 @@ -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 diff --git a/library/HTMLPurifier/URIFilter/Munge.php b/library/HTMLPurifier/URIFilter/Munge.php deleted file mode 100644 index efa10a6458..0000000000 --- a/library/HTMLPurifier/URIFilter/Munge.php +++ /dev/null @@ -1,58 +0,0 @@ -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 diff --git a/library/HTMLPurifier/URIScheme.php b/library/HTMLPurifier/URIScheme.php deleted file mode 100644 index 039710fd15..0000000000 --- a/library/HTMLPurifier/URIScheme.php +++ /dev/null @@ -1,42 +0,0 @@ -, 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 diff --git a/library/HTMLPurifier/URIScheme/news.php b/library/HTMLPurifier/URIScheme/news.php deleted file mode 100644 index f5f54f4f56..0000000000 --- a/library/HTMLPurifier/URIScheme/news.php +++ /dev/null @@ -1,22 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - $uri->query = null; - // typecode check needed on path - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/nntp.php b/library/HTMLPurifier/URIScheme/nntp.php deleted file mode 100644 index 5bf93ea784..0000000000 --- a/library/HTMLPurifier/URIScheme/nntp.php +++ /dev/null @@ -1,20 +0,0 @@ -userinfo = null; - $uri->query = null; - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/VarParser.php b/library/HTMLPurifier/VarParser.php deleted file mode 100644 index 68e72ae869..0000000000 --- a/library/HTMLPurifier/VarParser.php +++ /dev/null @@ -1,154 +0,0 @@ - self::STRING, - 'istring' => self::ISTRING, - 'text' => self::TEXT, - 'itext' => self::ITEXT, - 'int' => self::INT, - 'float' => self::FLOAT, - 'bool' => self::BOOL, - 'lookup' => self::LOOKUP, - 'list' => self::ALIST, - 'hash' => self::HASH, - 'mixed' => self::MIXED - ); - - /** - * Lookup table of types that are string, and can have aliases or - * allowed value lists. - */ - static public $stringTypes = array( - self::STRING => true, - self::ISTRING => true, - self::TEXT => true, - self::ITEXT => true, - ); - - /** - * Validate a variable according to type. Throws - * HTMLPurifier_VarParserException if invalid. - * It may return NULL as a valid type if $allow_null is true. - * - * @param $var Variable to validate - * @param $type Type of variable, see HTMLPurifier_VarParser->types - * @param $allow_null Whether or not to permit null as a value - * @return Validated and type-coerced variable - */ - final public function parse($var, $type, $allow_null = false) { - if (is_string($type)) { - if (!isset(HTMLPurifier_VarParser::$types[$type])) { - throw new HTMLPurifier_VarParserException("Invalid type '$type'"); - } else { - $type = HTMLPurifier_VarParser::$types[$type]; - } - } - $var = $this->parseImplementation($var, $type, $allow_null); - if ($allow_null && $var === null) return null; - // These are basic checks, to make sure nothing horribly wrong - // happened in our implementations. - switch ($type) { - case (self::STRING): - case (self::ISTRING): - case (self::TEXT): - case (self::ITEXT): - if (!is_string($var)) break; - if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var); - return $var; - case (self::INT): - if (!is_int($var)) break; - return $var; - case (self::FLOAT): - if (!is_float($var)) break; - return $var; - case (self::BOOL): - if (!is_bool($var)) break; - return $var; - case (self::LOOKUP): - case (self::ALIST): - case (self::HASH): - if (!is_array($var)) break; - if ($type === self::LOOKUP) { - foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true'); - } elseif ($type === self::ALIST) { - $keys = array_keys($var); - if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform'); - } - return $var; - case (self::MIXED): - return $var; - default: - $this->errorInconsistent(get_class($this), $type); - } - $this->errorGeneric($var, $type); - } - - /** - * Actually implements the parsing. Base implementation is to not - * do anything to $var. Subclasses should overload this! - */ - protected function parseImplementation($var, $type, $allow_null) { - return $var; - } - - /** - * Throws an exception. - */ - protected function error($msg) { - throw new HTMLPurifier_VarParserException($msg); - } - - /** - * Throws an inconsistency exception. - * @note This should not ever be called. It would be called if we - * extend the allowed values of HTMLPurifier_VarParser without - * updating subclasses. - */ - protected function errorInconsistent($class, $type) { - throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented"); - } - - /** - * Generic error for if a type didn't work. - */ - protected function errorGeneric($var, $type) { - $vtype = gettype($var); - $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype"); - } - - static public function getTypeName($type) { - static $lookup; - if (!$lookup) { - // Lazy load the alternative lookup table - $lookup = array_flip(HTMLPurifier_VarParser::$types); - } - if (!isset($lookup[$type])) return 'unknown'; - return $lookup[$type]; - } - -} - -// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/CREDITS b/library/ezyang/htmlpurifier/CREDITS new file mode 100644 index 0000000000..7921b45af7 --- /dev/null +++ b/library/ezyang/htmlpurifier/CREDITS @@ -0,0 +1,9 @@ + +CREDITS + +Almost everything written by Edward Z. Yang (Ambush Commander). Lots of thanks +to the DevNetwork Community for their help (see docs/ref-devnetwork.html for +more details), Feyd especially (namely IPv6 and optimization). Thanks to RSnake +for letting me package his fantastic XSS cheatsheet for a smoketest. + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/INSTALL b/library/ezyang/htmlpurifier/INSTALL new file mode 100644 index 0000000000..677c04aa04 --- /dev/null +++ b/library/ezyang/htmlpurifier/INSTALL @@ -0,0 +1,374 @@ + +Install + How to install HTML Purifier + +HTML Purifier is designed to run out of the box, so actually using the +library is extremely easy. (Although... if you were looking for a +step-by-step installation GUI, you've downloaded the wrong software!) + +While the impatient can get going immediately with some of the sample +code at the bottom of this library, it's well worth reading this entire +document--most of the other documentation assumes that you are familiar +with these contents. + + +--------------------------------------------------------------------------- +1. Compatibility + +HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and +up. It has no core dependencies with other libraries. PHP +4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0. +HTML Purifier is not compatible with zend.ze1_compatibility_mode. + +These optional extensions can enhance the capabilities of HTML Purifier: + + * iconv : Converts text to and from non-UTF-8 encodings + * bcmath : Used for unit conversion and imagecrash protection + * tidy : Used for pretty-printing HTML + +These optional libraries can enhance the capabilities of HTML Purifier: + + * CSSTidy : Clean CSS stylesheets using %Core.ExtractStyleBlocks + * Net_IDNA2 (PEAR) : IRI support using %Core.EnableIDNA + +--------------------------------------------------------------------------- +2. Reconnaissance + +A big plus of HTML Purifier is its inerrant support of standards, so +your web-pages should be standards-compliant. (They should also use +semantic markup, but that's another issue altogether, one HTML Purifier +cannot fix without reading your mind.) + +HTML Purifier can process these doctypes: + +* XHTML 1.0 Transitional (default) +* XHTML 1.0 Strict +* HTML 4.01 Transitional +* HTML 4.01 Strict +* XHTML 1.1 + +...and these character encodings: + +* UTF-8 (default) +* Any encoding iconv supports (with crippled internationalization support) + +These defaults reflect what my choices would be if I were authoring an +HTML document, however, what you choose depends on the nature of your +codebase. If you don't know what doctype you are using, you can determine +the doctype from this identifier at the top of your source code: + + + +...and the character encoding from this code: + + + +If the character encoding declaration is missing, STOP NOW, and +read 'docs/enduser-utf8.html' (web accessible at +http://htmlpurifier.org/docs/enduser-utf8.html). In fact, even if it is +present, read this document anyway, as many websites specify their +document's character encoding incorrectly. + + +--------------------------------------------------------------------------- +3. Including the library + +The procedure is quite simple: + + require_once '/path/to/library/HTMLPurifier.auto.php'; + +This will setup an autoloader, so the library's files are only included +when you use them. + +Only the contents in the library/ folder are necessary, so you can remove +everything else when using HTML Purifier in a production environment. + +If you installed HTML Purifier via PEAR, all you need to do is: + + require_once 'HTMLPurifier.auto.php'; + +Please note that the usual PEAR practice of including just the classes you +want will not work with HTML Purifier's autoloading scheme. + +Advanced users, read on; other users can skip to section 4. + +Autoload compatibility +---------------------- + + HTML Purifier attempts to be as smart as possible when registering an + autoloader, but there are some cases where you will need to change + your own code to accomodate HTML Purifier. These are those cases: + + PHP VERSION IS LESS THAN 5.1.2, AND YOU'VE DEFINED __autoload + Because spl_autoload_register() doesn't exist in early versions + of PHP 5, HTML Purifier has no way of adding itself to the autoload + stack. Modify your __autoload function to test + HTMLPurifier_Bootstrap::autoload($class) + + For example, suppose your autoload function looks like this: + + function __autoload($class) { + require str_replace('_', '/', $class) . '.php'; + return true; + } + + A modified version with HTML Purifier would look like this: + + function __autoload($class) { + if (HTMLPurifier_Bootstrap::autoload($class)) return true; + require str_replace('_', '/', $class) . '.php'; + return true; + } + + Note that there *is* some custom behavior in our autoloader; the + original autoloader in our example would work for 99% of the time, + but would fail when including language files. + + AN __autoload FUNCTION IS DECLARED AFTER OUR AUTOLOADER IS REGISTERED + spl_autoload_register() has the curious behavior of disabling + the existing __autoload() handler. Users need to explicitly + spl_autoload_register('__autoload'). Because we use SPL when it + is available, __autoload() will ALWAYS be disabled. If __autoload() + is declared before HTML Purifier is loaded, this is not a problem: + HTML Purifier will register the function for you. But if it is + declared afterwards, it will mysteriously not work. This + snippet of code (after your autoloader is defined) will fix it: + + spl_autoload_register('__autoload') + + Users should also be on guard if they use a version of PHP previous + to 5.1.2 without an autoloader--HTML Purifier will define __autoload() + for you, which can collide with an autoloader that was added by *you* + later. + + +For better performance +---------------------- + + Opcode caches, which greatly speed up PHP initialization for scripts + with large amounts of code (HTML Purifier included), don't like + autoloaders. We offer an include file that includes all of HTML Purifier's + files in one go in an opcode cache friendly manner: + + // If /path/to/library isn't already in your include path, uncomment + // the below line: + // require '/path/to/library/HTMLPurifier.path.php'; + + require 'HTMLPurifier.includes.php'; + + Optional components still need to be included--you'll know if you try to + use a feature and you get a class doesn't exists error! The autoloader + can be used in conjunction with this approach to catch classes that are + missing. Simply add this afterwards: + + require 'HTMLPurifier.autoload.php'; + +Standalone version +------------------ + + HTML Purifier has a standalone distribution; you can also generate + a standalone file from the full version by running the script + maintenance/generate-standalone.php . The standalone version has the + benefit of having most of its code in one file, so parsing is much + faster and the library is easier to manage. + + If HTMLPurifier.standalone.php exists in the library directory, you + can use it like this: + + require '/path/to/HTMLPurifier.standalone.php'; + + This is equivalent to including HTMLPurifier.includes.php, except that + the contents of standalone/ will be added to your path. To override this + behavior, specify a new HTMLPURIFIER_PREFIX where standalone files can + be found (usually, this will be one directory up, the "true" library + directory in full distributions). Don't forget to set your path too! + + The autoloader can be added to the end to ensure the classes are + loaded when necessary; otherwise you can manually include them. + To use the autoloader, use this: + + require 'HTMLPurifier.autoload.php'; + +For advanced users +------------------ + + HTMLPurifier.auto.php performs a number of operations that can be done + individually. These are: + + HTMLPurifier.path.php + Puts /path/to/library in the include path. For high performance, + this should be done in php.ini. + + HTMLPurifier.autoload.php + Registers our autoload handler HTMLPurifier_Bootstrap::autoload($class). + + You can do these operations by yourself--in fact, you must modify your own + autoload handler if you are using a version of PHP earlier than PHP 5.1.2 + (See "Autoload compatibility" above). + + +--------------------------------------------------------------------------- +4. Configuration + +HTML Purifier is designed to run out-of-the-box, but occasionally HTML +Purifier needs to be told what to do. If you answer no to any of these +questions, read on; otherwise, you can skip to the next section (or, if you're +into configuring things just for the heck of it, skip to 4.3). + +* Am I using UTF-8? +* Am I using XHTML 1.0 Transitional? + +If you answered no to any of these questions, instantiate a configuration +object and read on: + + $config = HTMLPurifier_Config::createDefault(); + + +4.1. Setting a different character encoding + +You really shouldn't use any other encoding except UTF-8, especially if you +plan to support multilingual websites (read section three for more details). +However, switching to UTF-8 is not always immediately feasible, so we can +adapt. + +HTML Purifier uses iconv to support other character encodings, as such, +any encoding that iconv supports +HTML Purifier supports with this code: + + $config->set('Core.Encoding', /* put your encoding here */); + +An example usage for Latin-1 websites (the most common encoding for English +websites): + + $config->set('Core.Encoding', 'ISO-8859-1'); + +Note that HTML Purifier's support for non-Unicode encodings is crippled by the +fact that any character not supported by that encoding will be silently +dropped, EVEN if it is ampersand escaped. If you want to work around +this, you are welcome to read docs/enduser-utf8.html for a fix, +but please be cognizant of the issues the "solution" creates (for this +reason, I do not include the solution in this document). + + +4.2. Setting a different doctype + +For those of you using HTML 4.01 Transitional, you can disable +XHTML output like this: + + $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); + +Other supported doctypes include: + + * HTML 4.01 Strict + * HTML 4.01 Transitional + * XHTML 1.0 Strict + * XHTML 1.0 Transitional + * XHTML 1.1 + + +4.3. Other settings + +There are more configuration directives which can be read about +here: They're a bit boring, +but they can help out for those of you who like to exert maximum control over +your code. Some of the more interesting ones are configurable at the +demo and are well worth looking into +for your own system. + +For example, you can fine tune allowed elements and attributes, convert +relative URLs to absolute ones, and even autoparagraph input text! These +are, respectively, %HTML.Allowed, %URI.MakeAbsolute and %URI.Base, and +%AutoFormat.AutoParagraph. The %Namespace.Directive naming convention +translates to: + + $config->set('Namespace.Directive', $value); + +E.g. + + $config->set('HTML.Allowed', 'p,b,a[href],i'); + $config->set('URI.Base', 'http://www.example.com'); + $config->set('URI.MakeAbsolute', true); + $config->set('AutoFormat.AutoParagraph', true); + + +--------------------------------------------------------------------------- +5. Caching + +HTML Purifier generates some cache files (generally one or two) to speed up +its execution. For maximum performance, make sure that +library/HTMLPurifier/DefinitionCache/Serializer is writeable by the webserver. + +If you are in the library/ folder of HTML Purifier, you can set the +appropriate permissions using: + + chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer + +If the above command doesn't work, you may need to assign write permissions +to all. This may be necessary if your webserver runs as nobody, but is +not recommended since it means any other user can write files in the +directory. Use: + + chmod -R 0777 HTMLPurifier/DefinitionCache/Serializer + +You can also chmod files via your FTP client; this option +is usually accessible by right clicking the corresponding directory and +then selecting "chmod" or "file permissions". + +Starting with 2.0.1, HTML Purifier will generate friendly error messages +that will tell you exactly what you have to chmod the directory to, if in doubt, +follow its advice. + +If you are unable or unwilling to give write permissions to the cache +directory, you can either disable the cache (and suffer a performance +hit): + + $config->set('Core.DefinitionCache', null); + +Or move the cache directory somewhere else (no trailing slash): + + $config->set('Cache.SerializerPath', '/home/user/absolute/path'); + + +--------------------------------------------------------------------------- +6. Using the code + +The interface is mind-numbingly simple: + + $purifier = new HTMLPurifier($config); + $clean_html = $purifier->purify( $dirty_html ); + +That's it! For more examples, check out docs/examples/ (they aren't very +different though). Also, docs/enduser-slow.html gives advice on what to +do if HTML Purifier is slowing down your application. + + +--------------------------------------------------------------------------- +7. Quick install + +First, make sure library/HTMLPurifier/DefinitionCache/Serializer is +writable by the webserver (see Section 5: Caching above for details). +If your website is in UTF-8 and XHTML Transitional, use this code: + +purify($dirty_html); +?> + +If your website is in a different encoding or doctype, use this code: + +set('Core.Encoding', 'ISO-8859-1'); // replace with your encoding + $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); // replace with your doctype + $purifier = new HTMLPurifier($config); + + $clean_html = $purifier->purify($dirty_html); +?> + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/INSTALL.fr.utf8 b/library/ezyang/htmlpurifier/INSTALL.fr.utf8 new file mode 100644 index 0000000000..06e628cc96 --- /dev/null +++ b/library/ezyang/htmlpurifier/INSTALL.fr.utf8 @@ -0,0 +1,60 @@ + +Installation + Comment installer HTML Purifier + +Attention : Ce document est encodé en UTF-8, si les lettres avec des accents +ne s'affichent pas, prenez un meilleur éditeur de texte. + +L'installation de HTML Purifier est très simple, parce qu'il n'a pas besoin +de configuration. Pour les utilisateurs impatients, le code se trouve dans le +pied de page, mais je recommande de lire le document. + +1. Compatibilité + +HTML Purifier fonctionne avec PHP 5. PHP 5.0.5 est la dernière version testée. +Il ne dépend pas d'autres librairies. + +Les extensions optionnelles sont iconv (généralement déjà installée) et tidy +(répendue aussi). Si vous utilisez UTF-8 et que vous ne voulez pas l'indentation, +vous pouvez utiliser HTML Purifier sans ces extensions. + + +2. Inclure la librairie + +Quand vous devez l'utilisez, incluez le : + + require_once('/path/to/library/HTMLPurifier.auto.php'); + +Ne pas l'inclure si ce n'est pas nécessaire, car HTML Purifier est lourd. + +HTML Purifier utilise "autoload". Si vous avez défini la fonction __autoload, +vous devez ajouter cette fonction : + + spl_autoload_register('__autoload') + +Plus d'informations dans le document "INSTALL". + +3. Installation rapide + +Si votre site Web est en UTF-8 et XHTML Transitional, utilisez : + +purify($html_a_purifier); +?> + +Sinon, utilisez : + +set('Core', 'Encoding', 'ISO-8859-1'); //Remplacez par votre + encodage + $config->set('Core', 'XHTML', true); //Remplacer par false si HTML 4.01 + $purificateur = new HTMLPurifier($config); + $html_propre = $purificateur->purify($html_a_purifier); +?> + + + vim: et sw=4 sts=4 diff --git a/library/simplepie/idn/LICENCE b/library/ezyang/htmlpurifier/LICENSE similarity index 98% rename from library/simplepie/idn/LICENCE rename to library/ezyang/htmlpurifier/LICENSE index 25a1d22dfe..8c88a20d45 100644 --- a/library/simplepie/idn/LICENCE +++ b/library/ezyang/htmlpurifier/LICENSE @@ -1,8 +1,8 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -10,7 +10,7 @@ as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. - + Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a @@ -111,8 +111,8 @@ modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE + + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other @@ -158,7 +158,7 @@ Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - + 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 @@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. - + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. @@ -267,7 +267,7 @@ Library will still fall under Section 6.) distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - + 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work @@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - + 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined @@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. - + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -422,7 +422,7 @@ conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - + 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is @@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. @@ -455,8 +455,8 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS - + END OF TERMS AND CONDITIONS + How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. @@ -500,3 +500,5 @@ necessary. Here is a sample; alter the names: Ty Coon, President of Vice That's all there is to it! + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/NEWS b/library/ezyang/htmlpurifier/NEWS new file mode 100644 index 0000000000..a9124af1a1 --- /dev/null +++ b/library/ezyang/htmlpurifier/NEWS @@ -0,0 +1,1094 @@ +NEWS ( CHANGELOG and HISTORY ) HTMLPurifier +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + += KEY ==================== + # Breaks back-compat + ! Feature + - Bugfix + + Sub-comment + . Internal change +========================== + +4.7.0, released 2015-08-04 +# opacity is now considered a "tricky" CSS property rather than a + proprietary one. +! %AutoFormat.RemoveEmpty.Predicate for specifying exactly when + an element should be considered "empty" (maybe preserve if it + has attributes), and modify iframe support so that the iframe + is removed if it is missing a src attribute. Thanks meeva for + reporting. +- Don't truncate upon encountering
when using DOMLex. Thanks + Myrto Christina for finally convincing me to fix this. +- Update YouTube filter for new code. +- Fix parsing of rgb() values with spaces in them for 'border' + attribute. +- Don't remove foo="" attributes if foo is a boolean attribute. Thanks + valME for reporting. + +4.6.0, released 2013-11-30 +# Secure URI munge hashing algorithm has changed to hash_hmac("sha256", $url, $secret). + Please update any verification scripts you may have. +# URI parsing algorithm was made more strict, so only prefixes which + looks like schemes will actually be schemes. Thanks + Michael Gusev for fixing. +# %Core.EscapeInvalidChildren is no longer supported, and no longer does + anything. +! New directive %Core.AllowHostnameUnderscore which allows underscores + in hostnames. +- Eliminate quadratic behavior in DOMLex by using a proper queue. + Thanks Ole Laursen for noticing this. +- Rewritten MakeWellFormed/FixNesting implementation eliminates quadratic + behavior in the rest of the purificaiton pipeline. Thanks Chedburn + Networks for sponsoring this work. +- Made Linkify URL parser a bit less permissive, so that non-breaking + spaces and commas are not included as part of URL. Thanks nAS for fixing. +- Fix some bad interactions with %HTML.Allowed and injectors. Thanks + David Hirtz for reporting. +- Fix infinite loop in DirectLex. Thanks Ashar Javed (@soaj1664ashar) + for reporting. + +4.5.0, released 2013-02-17 +# Fix bug where stacked attribute transforms clobber each other; + this also means it's no longer possible to override attribute + transforms in later modules. No internal code was using this + but this may break some clients. +# We now use SHA-1 to identify cached definitions, instead of MD5. +! Support display:inline-block +! Support for more white-space CSS values. +! Permit underscores in font families +! Support for page-break-* CSS3 properties when proprietary properties + are enabled. +! New directive %Core.DisableExcludes; can be set to 'true' to turn off + SGML excludes checking. If HTML Purifier is removing too much text + and you don't care about full standards compliance, try setting this to + 'true'. +- Use prepend for SPL autoloading on PHP 5.3 and later. +- Fix bug with nofollow transform when pre-existing rel exists. +- Fix bug where background:url() always gets lower-cased + (but not background-image:url()) +- Fix bug with non lower-case color names in HTML +- Fix bug where data URI validation doesn't remove temporary files. + Thanks Javier Marín Ros for reporting. +- Don't remove certain empty tags on RemoveEmpty. + +4.4.0, released 2012-01-18 +# Removed PEARSax3 handler. +# URI.Munge now munges URIs inside the same host that go from https + to http. Reported by Neike Taika-Tessaro. +# Core.EscapeNonASCIICharacters now always transforms entities to + entities, even if target encoding is UTF-8. +# Tighten up selector validation in ExtractStyleBlocks. + Non-syntactically valid selectors are now rejected, along with + some of the more obscure ones such as attribute selectors, the + :lang pseudoselector, and anything not in CSS2.1. Furthermore, + ID and class selectors now work properly with the relevant + configuration attributes. Also, mute errors when parsing CSS + with CSS Tidy. Reported by Mario Heiderich and Norman Hippert. +! Added support for 'scope' attribute on tables. +! Added %HTML.TargetBlank, which adds target="blank" to all outgoing links. +! Properly handle sub-lists directly nested inside of lists in + a standards compliant way, by moving them into the preceding
  • +! Added %HTML.AllowedComments and %HTML.AllowedCommentsRegexp for + limited allowed comments in untrusted situations. +! Implement iframes, and allow them to be used in untrusted mode with + %HTML.SafeIframe and %URI.SafeIframeRegexp. Thanks Bradley M. Froehle + for submitting an initial version of the patch. +! The Forms module now works properly for transitional doctypes. +! Added support for internationalized domain names. You need the PEAR + Net_IDNA2 module to be in your path; if it is installed, ensure the + class can be loaded and then set %Core.EnableIDNA to true. +- Color keywords are now case insensitive. Thanks Yzmir Ramirez + for reporting. +- Explicitly initialize anonModule variable to null. +- Do not duplicate nofollow if already present. Thanks 178 + for reporting. +- Do not add nofollow if hostname matches our current host. Thanks 178 + for reporting, and Neike Taika-Tessaro for helping diagnose. +- Do not unset parser variable; this fixes intermittent serialization + problems. Thanks Neike Taika-Tessaro for reporting, bill + <10010tiger@gmail.com> for diagnosing. +- Fix iconv truncation bug, where non-UTF-8 target encodings see + output truncated after around 8000 characters. Thanks Jörg Ludwig + for reporting. +- Fix broken table content model for XHTML1.1 (and also earlier + versions, although the W3C validator doesn't catch those violations). + Thanks GlitchMr for reporting. + +4.3.0, released 2011-03-27 +# Fixed broken caching of customized raw definitions, but requires an + API change. The old API still works but will emit a warning, + see http://htmlpurifier.org/docs/enduser-customize.html#optimized + for how to upgrade your code. +# Protect against Internet Explorer innerHTML behavior by specially + treating attributes with backticks but no angled brackets, quotes or + spaces. This constitutes a slight semantic change, which can be + reverted using %Output.FixInnerHTML. Reported by Neike Taika-Tessaro + and Mario Heiderich. +# Protect against cssText/innerHTML by restricting allowed characters + used in fonts further than mandated by the specification and encoding + some extra special characters in URLs. Reported by Neike + Taika-Tessaro and Mario Heiderich. +! Added %HTML.Nofollow to add rel="nofollow" to external links. +! More types of SPL autoloaders allowed on later versions of PHP. +! Implementations for position, top, left, right, bottom, z-index + when %CSS.Trusted is on. +! Add %Cache.SerializerPermissions option for custom serializer + directory/file permissions +! Fix longstanding bug in Flash support for non-IE browsers, and + allow more wmode attributes. +! Add %CSS.AllowedFonts to restrict permissible font names. +- Switch to an iterative traversal of the DOM, which prevents us + from running out of stack space for deeply nested documents. + Thanks Maxim Krizhanovsky for contributing a patch. +- Make removal of conditional IE comments ungreedy; thanks Bernd + for reporting. +- Escape CDATA before removing Internet Explorer comments. +- Fix removal of id attributes under certain conditions by ensuring + armor attributes are preserved when recreating tags. +- Check if schema.ser was corrupted. +- Check if zend.ze1_compatibility_mode is on, and error out if it is. + This safety check is only done for HTMLPurifier.auto.php; if you + are using standalone or the specialized includes files, you're + expected to know what you're doing. +- Stop repeatedly writing the cache file after I'm done customizing a + raw definition. Reported by ajh. +- Switch to using require_once in the Bootstrap to work around bad + interaction with Zend Debugger and APC. Reported by Antonio Parraga. +- Fix URI handling when hostname is missing but scheme is present. + Reported by Neike Taika-Tessaro. +- Fix missing numeric entities on DirectLex; thanks Neike Taika-Tessaro + for reporting. +- Fix harmless notice from indexing into empty string. Thanks Matthijs + Kooijman for reporting. +- Don't autoclose no parent elements are able to support the element + that triggered the autoclose. In particular fixes strange behavior + of stray
  • tags. Thanks pkuliga@gmail.com for reporting and + Neike Taika-Tessaro for debugging assistance. + +4.2.0, released 2010-09-15 +! Added %Core.RemoveProcessingInstructions, which lets you remove + statements. +! Added %URI.DisableResources functionality; the directive originally + did nothing. Thanks David Rothstein for reporting. +! Add documentation about configuration directive types. +! Add %CSS.ForbiddenProperties configuration directive. +! Add %HTML.FlashAllowFullScreen to permit embedded Flash objects + to utilize full-screen mode. +! Add optional support for the file URI scheme, enable + by explicitly setting %URI.AllowedSchemes. +! Add %Core.NormalizeNewlines options to allow turning off newline + normalization. +- Fix improper handling of Internet Explorer conditional comments + by parser. Thanks zmonteca for reporting. +- Fix missing attributes bug when running on Mac Snow Leopard and APC. + Thanks sidepodcast for the fix. +- Warn if an element is allowed, but an attribute it requires is + not allowed. + +4.1.1, released 2010-05-31 +- Fix undefined index warnings in maintenance scripts. +- Fix bug in DirectLex for parsing elements with a single attribute + with entities. +- Rewrite CSS output logic for font-family and url(). Thanks Mario + Heiderich for reporting and Takeshi + Terada for suggesting the fix. +- Emit an error for CollectErrors if a body is extracted +- Fix bug where in background-position for center keyword handling. +- Fix infinite loop when a wrapper element is inserted in a context + where it's not allowed. Thanks Lars for reporting. +- Remove +x bit and shebang from index.php; only supported mode is to + explicitly call it with php. +- Make test script less chatty when log_errors is on. + +4.1.0, released 2010-04-26 +! Support proprietary height attribute on table element +! Support YouTube slideshows that contain /cp/ in their URL. +! Support for data: URI scheme; not enabled by default, add it using + %URI.AllowedSchemes +! Support flashvars when using %HTML.SafeObject and %HTML.SafeEmbed. +! Support for Internet Explorer compatibility with %HTML.SafeObject + using %Output.FlashCompat. +! Handle
        properly, by inserting the necessary
      1. tag. +- Always quote the insides of url(...) in CSS. + +4.0.0, released 2009-07-07 +# APIs for ConfigSchema subsystem have substantially changed. See + docs/dev-config-bcbreaks.txt for details; in essence, anything that + had both namespace and directive now have a single unified key. +# Some configuration directives were renamed, specifically: + %AutoFormatParam.PurifierLinkifyDocURL -> %AutoFormat.PurifierLinkify.DocURL + %FilterParam.ExtractStyleBlocksEscaping -> %Filter.ExtractStyleBlocks.Escaping + %FilterParam.ExtractStyleBlocksScope -> %Filter.ExtractStyleBlocks.Scope + %FilterParam.ExtractStyleBlocksTidyImpl -> %Filter.ExtractStyleBlocks.TidyImpl + As usual, the old directive names will still work, but will throw E_NOTICE + errors. +# The allowed values for class have been relaxed to allow all of CDATA for + doctypes that are not XHTML 1.1 or XHTML 2.0. For old behavior, set + %Attr.ClassUseCDATA to false. +# Instead of appending the content model to an old content model, a blank + element will replace the old content model. You can use #SUPER to get + the old content model. +! More robust support for name="" and id="" +! HTMLPurifier_Config::inherit($config) allows you to inherit one + configuration, and have changes to that configuration be propagated + to all of its children. +! Implement %HTML.Attr.Name.UseCDATA, which relaxes validation rules on + the name attribute when set. Use with care. Thanks Ian Cook for + sponsoring. +! Implement %AutoFormat.RemoveEmpty.RemoveNbsp, which removes empty + tags that contain non-breaking spaces as well other whitespace. You + can also modify which tags should have   maintained with + %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions. +! Implement %Attr.AllowedClasses, which allows administrators to restrict + classes users can use to a specified finite set of classes, and + %Attr.ForbiddenClasses, which is the logical inverse. +! You can now maintain your own configuration schema directories by + creating a config-schema.php file or passing an extra argument. Check + docs/dev-config-schema.html for more details. +! Added HTMLPurifier_Config->serialize() method, which lets you save away + your configuration in a compact serial file, which you can unserialize + and use directly without having to go through the overhead of setup. +- Fix bug where URIDefinition would not get cleared if it's directives got + changed. +- Fix fatal error in HTMLPurifier_Encoder on certain platforms (probably NetBSD 5.0) +- Fix bug in Linkify autoformatter involving http://foo +- Make %URI.Munge not apply to links that have the same host as your host. +- Prevent stray tag from truncating output, if a second + is present. +. Created script maintenance/rename-config.php for renaming a configuration + directive while maintaining its alias. This script does not change source code. +. Implement namespace locking for definition construction, to prevent + bugs where a directive is used for definition construction but is not + used to construct the cache hash. + +3.3.0, released 2009-02-16 +! Implement CSS property 'overflow' when %CSS.AllowTricky is true. +! Implement generic property list classess +- Fix bug with testEncodingSupportsASCII() algorithm when iconv() implementation + does not do the "right thing" with characters not supported in the output + set. +- Spellcheck UTF-8: The Secret To Character Encoding +- Fix improper removal of the contents of elements with only whitespace. Thanks + Eric Wald for reporting. +- Fix broken test suite in versions of PHP without spl_autoload_register() +- Fix degenerate case with YouTube filter involving double hyphens. + Thanks Pierre Attar for reporting. +- Fix YouTube rendering problem on certain versions of Firefox. +- Fix CSSDefinition Printer problems with decorators +- Add text parameter to unit tests, forces text output +. Add verbose mode to command line test runner, use (--verbose) +. Turn on unit tests for UnitConverter +. Fix missing version number in configuration %Attr.DefaultImageAlt (added 3.2.0) +. Fix newline errors that caused spurious failures when CRLF HTML Purifier was + tested on Linux. +. Removed trailing whitespace from all text files, see + remote-trailing-whitespace.php maintenance script. +. Convert configuration to use property list backend. + +3.2.0, released 2008-10-31 +# Using %Core.CollectErrors forces line number/column tracking on, whereas + previously you could theoretically turn it off. +# HTMLPurifier_Injector->notifyEnd() is formally deprecated. Please + use handleEnd() instead. +! %Output.AttrSort for when you need your attributes in alphabetical order to + deal with a bug in FCKEditor. Requested by frank farmer. +! Enable HTML comments when %HTML.Trusted is on. Requested by Waldo Jaquith. +! Proper support for name attribute. It is now allowed and equivalent to the id + attribute in a and img tags, and is only converted to id when %HTML.TidyLevel + is heavy (for all doctypes). +! %AutoFormat.RemoveEmpty to remove some empty tags from documents. Please don't + use on hand-written HTML. +! Add error-cases for unsupported elements in MakeWellFormed. This enables + the strategy to be used, standalone, on untrusted input. +! %Core.AggressivelyFixLt is on by default. This causes more sensible + processing of left angled brackets in smileys and other whatnot. +! Test scripts now have a 'type' parameter, which lets you say 'htmlpurifier', + 'phpt', 'vtest', etc. in order to only execute those tests. This supercedes + the --only-phpt parameter, although for backwards-compatibility the flag + will still work. +! AutoParagraph auto-formatter will now preserve double-newlines upon output. + Users who are not performing inbound filtering, this may seem a little + useless, but as a bonus, the test suite and handling of edge cases is also + improved. +! Experimental implementation of forms for %HTML.Trusted +! Track column numbers when maintain line numbers is on +! Proprietary 'background' attribute on table-related elements converted into + corresponding CSS. Thanks Fusemail for sponsoring this feature! +! Add forward(), forwardUntilEndToken(), backward() and current() to Injector + supertype. +! HTMLPurifier_Injector->handleEnd() permits modification to end tokens. The + time of operation varies slightly from notifyEnd() as *all* end tokens are + processed by the injector before they are subject to the well-formedness rules. +! %Attr.DefaultImageAlt allows overriding default behavior of setting alt to + basename of image when not present. +! %AutoFormat.DisplayLinkURI neuters tags into plain text URLs. +- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs, + the other involving an undefined $is_folder error. +- Throw error when %Core.Encoding is set to a spurious value. Previously, + this errored silently and returned false. +- Redirected stderr to stdout for flush error output. +- %URI.DisableExternal will now use the host in %URI.Base if %URI.Host is not + available. +- Do not re-munge URL if the output URL has the same host as the input URL. + Requested by Chris. +- Fix error in documentation regarding %Filter.ExtractStyleBlocks +- Prevent ]]> from triggering %Core.ConvertDocumentToFragment +- Fix bug with inline elements in blockquotes conflicting with strict doctype +- Detect if HTML support is disabled for DOM by checking for loadHTML() method. +- Fix bug where dots and double-dots in absolute URLs without hostname were + not collapsed by URIFilter_MakeAbsolute. +- Fix bug with anonymous modules operating on SafeEmbed or SafeObject elements + by reordering their addition. +- Will now throw exception on many error conditions during lexer creation; also + throw an exception when MaintainLineNumbers is true, but a non-tracksLineNumbers + is being used. +- Detect if domxml extension is loaded, and use DirectLEx accordingly. +- Improve handling of big numbers with floating point arithmetic in UnitConverter. + Reported by David Morton. +. Strategy_MakeWellFormed now operates in-place, saving memory and allowing + for more interesting filter-backtracking +. New HTMLPurifier_Injector->rewind() functionality, allows injectors to rewind + index to reprocess tokens. +. StringHashParser now allows for multiline sections with "empty" content; + previously the section would remain undefined. +. Added --quick option to multitest.php, which tests only the most recent + release for each series. +. Added --distro option to multitest.php, which accepts either 'normal' or + 'standalone'. This supercedes --exclude-normal and --exclude-standalone + +3.1.1, released 2008-06-19 +# %URI.Munge now, by default, does not munge resources (for example, ) + In order to enable this again, please set %URI.MungeResources to true. +! More robust imagecrash protection with height/width CSS with %CSS.MaxImgLength, + and height/width HTML with %HTML.MaxImgLength. +! %URI.MungeSecretKey for secure URI munging. Thanks Chris + for sponsoring this feature. Check out the corresponding documentation + for details. (Att Nightly testers: The API for this feature changed before + the general release. Namely, rename your directives %URI.SecureMungeSecretKey => + %URI.MungeSecretKey and and %URI.SecureMunge => %URI.Munge) +! Implemented post URI filtering. Set member variable $post to true to set + a URIFilter as such. +! Allow modules to define injectors via $info_injector. Injectors are + automatically disabled if injector's needed elements are not found. +! Support for "safe" objects added, use %HTML.SafeObject and %HTML.SafeEmbed. + Thanks Chris for sponsoring. If you've been using ad hoc code from the + forums, PLEASE use this instead. +! Added substitutions for %e, %n, %a and %p in %URI.Munge (in order, + embedded, tag name, attribute name, CSS property name). See %URI.Munge + for more details. Requested by Jochem Blok. +- Disable percent height/width attributes for img. +- AttrValidator operations are now atomic; updates to attributes are not + manifest in token until end of operations. This prevents naughty internal + code from directly modifying CurrentToken when they're not supposed to. + This semantics change was requested by frank farmer. +- Percent encoding checks enabled for URI query and fragment +- Fix stray backslashes in font-family; CSS Unicode character escapes are + now properly resolved (although *only* in font-family). Thanks Takeshi Terada + for reporting. +- Improve parseCDATA algorithm to take into account newline normalization +- Account for browser confusion between Yen character and backslash in + Shift_JIS encoding. This fix generalizes to any other encoding which is not + a strict superset of printable ASCII. Thanks Takeshi Terada for reporting. +- Fix missing configuration parameter in Generator calls. Thanks vs for the + partial patch. +- Improved adherence to Unicode by checking for non-character codepoints. + Thanks Geoffrey Sneddon for reporting. This may result in degraded + performance for extremely large inputs. +- Allow CSS property-value pair ''text-decoration: none''. Thanks Jochem Blok + for reporting. +. Added HTMLPurifier_UnitConverter and HTMLPurifier_Length for convenient + handling of CSS-style lengths. HTMLPurifier_AttrDef_CSS_Length now uses + this class. +. API of HTMLPurifier_AttrDef_CSS_Length changed from __construct($disable_negative) + to __construct($min, $max). __construct(true) is equivalent to + __construct('0'). +. Added HTMLPurifier_AttrDef_Switch class +. Rename HTMLPurifier_HTMLModule_Tidy->construct() to setup() and bubble method + up inheritance hierarchy to HTMLPurifier_HTMLModule. All HTMLModules + get this called with the configuration object. All modules now + use this rather than __construct(), although legacy code using constructors + will still work--the new format, however, lets modules access the + configuration object for HTML namespace dependant tweaks. +. AttrDef_HTML_Pixels now takes a single construction parameter, pixels. +. ConfigSchema data-structure heavily optimized; on average it uses a third + the memory it did previously. The interface has changed accordingly, + consult changes to HTMLPurifier_Config for details. +. Variable parsing types now are magic integers instead of strings +. Added benchmark for ConfigSchema +. HTMLPurifier_Generator requires $config and $context parameters. If you + don't know what they should be, use HTMLPurifier_Config::createDefault() + and new HTMLPurifier_Context(). +. Printers now properly distinguish between output configuration, and + target configuration. This is not applicable to scripts using + the Printers for HTML Purifier related tasks. +. HTML/CSS Printers must be primed with prepareGenerator($gen_config), otherwise + fatal errors will ensue. +. URIFilter->prepare can return false in order to abort loading of the filter +. Factory for AttrDef_URI implemented, URI#embedded to indicate URI that embeds + an external resource. +. %URI.Munge functionality factored out into a post-filter class. +. Added CurrentCSSProperty context variable during CSS validation + +3.1.0, released 2008-05-18 +# Unnecessary references to objects (vestiges of PHP4) removed from method + signatures. The following methods do not need references when assigning from + them and will result in E_STRICT errors if you try: + + HTMLPurifier_Config->get*Definition() [* = HTML, CSS] + + HTMLPurifier_ConfigSchema::instance() + + HTMLPurifier_DefinitionCacheFactory::instance() + + HTMLPurifier_DefinitionCacheFactory->create() + + HTMLPurifier_DoctypeRegistry->register() + + HTMLPurifier_DoctypeRegistry->get() + + HTMLPurifier_HTMLModule->addElement() + + HTMLPurifier_HTMLModule->addBlankElement() + + HTMLPurifier_LanguageFactory::instance() +# Printer_ConfigForm's get*() functions were static-ified +# %HTML.ForbiddenAttributes requires attribute declarations to be in the + form of tag@attr, NOT tag.attr (which will throw an error and won't do + anything). This is for forwards compatibility with XML; you'd do best + to migrate an %HTML.AllowedAttributes directives to this syntax too. +! Allow index to be false for config from form creation +! Added HTMLPurifier::VERSION constant +! Commas, not dashes, used for serializer IDs. This change is forwards-compatible + and allows for version numbers like "3.1.0-dev". +! %HTML.Allowed deals gracefully with whitespace anywhere, anytime! +! HTML Purifier's URI handling is a lot more robust, with much stricter + validation checks and better percent encoding handling. Thanks Gareth Heyes + for indicating security vulnerabilities from lax percent encoding. +! Bootstrap autoloader deals more robustly with classes that don't exist, + preventing class_exists($class, true) from barfing. +- InterchangeBuilder now alphabetizes its lists +- Validation error in configdoc output fixed +- Iconv and other encoding errors muted even with custom error handlers that + do not honor error_reporting +- Add protection against imagecrash attack with CSS height/width +- HTMLPurifier::instance() created for consistency, is equivalent to getInstance() +- Fixed and revamped broken ConfigForm smoketest +- Bug with bool/null fields in Printer_ConfigForm fixed +- Bug with global forbidden attributes fixed +- Improved error messages for allowed and forbidden HTML elements and attributes +- Missing (or null) in configdoc documentation restored +- If DOM throws and exception during parsing with PH5P (occurs in newer versions + of DOM), HTML Purifier punts to DirectLex +- Fatal error with unserialization of ScriptRequired +- Created directories are now chmod'ed properly +- Fixed bug with fallback languages in LanguageFactory +- Standalone testing setup properly with autoload +. Out-of-date documentation revised +. UTF-8 encoding check optimization as suggested by Diego +. HTMLPurifier_Error removed in favor of exceptions +. More copy() function removed; should use clone instead +. More extensive unit tests for HTMLDefinition +. assertPurification moved to central harness +. HTMLPurifier_Generator accepts $config and $context parameters during + instantiation, not runtime +. Double-quotes outside of attribute values are now unescaped + +3.1.0rc1, released 2008-04-22 +# Autoload support added. Internal require_once's removed in favor of an + explicit require list or autoloading. To use HTML Purifier, + you must now either use HTMLPurifier.auto.php + or HTMLPurifier.includes.php; setting the include path and including + HTMLPurifier.php is insufficient--in such cases include HTMLPurifier.autoload.php + as well to register our autoload handler (or modify your autoload function + to check HTMLPurifier_Bootstrap::getPath($class)). You can also use + HTMLPurifier.safe-includes.php for a less performance friendly but more + user-friendly library load. +# HTMLPurifier_ConfigSchema static functions are officially deprecated. Schema + information is stored in the ConfigSchema directory, and the + maintenance/generate-schema-cache.php generates the schema.ser file, which + is now instantiated. Support for userland schema changes coming soon! +# HTMLPurifier_Config will now throw E_USER_NOTICE when you use a directive + alias; to get rid of these errors just modify your configuration to use + the new directive name. +# HTMLPurifier->addFilter is deprecated; built-in filters can now be + enabled using %Filter.$filter_name or by setting your own filters using + %Filter.Custom +# Directive-level safety properties superceded in favor of module-level + safety. Internal method HTMLModule->addElement() has changed, although + the externally visible HTMLDefinition->addElement has *not* changed. +! Extra utility classes for testing and non-library operations can + be found in extras/. Specifically, these are FSTools and ConfigDoc. + You may find a use for these in your own project, but right now they + are highly experimental and volatile. +! Integration with PHPT allows for automated smoketests +! Limited support for proprietary HTML elements, namely , sponsored + by Chris. You can enable them with %HTML.Proprietary if your client + demands them. +! Support for !important CSS cascade modifier. By default, this will be stripped + from CSS, but you can enable it using %CSS.AllowImportant +! Support for display and visibility CSS properties added, set %CSS.AllowTricky + to true to use them. +! HTML Purifier now has its own Exception hierarchy under HTMLPurifier_Exception. + Developer error (not enduser error) can cause these to be triggered. +! Experimental kses() wrapper introduced with HTMLPurifier.kses.php +! Finally %CSS.AllowedProperties for tweaking allowed CSS properties without + mucking around with HTMLPurifier_CSSDefinition +! ConfigDoc output has been enhanced with version and deprecation info. +! %HTML.ForbiddenAttributes and %HTML.ForbiddenElements implemented. +- Autoclose now operates iteratively, i.e.
        now has + both span tags closed. +- Various HTMLPurifier_Config convenience functions now accept another parameter + $schema which defines what HTMLPurifier_ConfigSchema to use besides the + global default. +- Fix bug with trusted script handling in libxml versions later than 2.6.28. +- Fix bug in ExtractStyleBlocks with comments in style tags +- Fix bug in comment parsing for DirectLex +- Flush output now displayed when in command line mode for unit tester +- Fix bug with rgb(0, 1, 2) color syntax with spaces inside shorthand syntax +- HTMLPurifier_HTMLDefinition->addAttribute can now be called multiple times + on the same element without emitting errors. +- Fixed fatal error in PH5P lexer with invalid tag names +. Plugins now get their own changelogs according to project conventions. +. Convert tokens to use instanceof, reducing memory footprint and + improving comparison speed. +. Dry runs now supported in SimpleTest; testing facilities improved +. Bootstrap class added for handling autoloading functionality +. Implemented recursive glob at FSTools->globr +. ConfigSchema now has instance methods for all corresponding define* + static methods. +. A couple of new historical maintenance scripts were added. +. HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php split into two files +. tests/index.php can now be run from any directory. +. HTMLPurifier_Token subclasses split into seperate files +. HTMLPURIFIER_PREFIX now is defined in Bootstrap.php, NOT HTMLPurifier.php +. HTMLPURIFIER_PREFIX can now be defined outside of HTML Purifier +. New --php=php flag added, allows PHP executable to be specified (command + line only!) +. htmlpurifier_add_test() preferred method to translate test files in to + classes, because it handles PHPT files too. +. Debugger class is deprecated and will be removed soon. +. Command line argument parsing for testing scripts revamped, now --opt value + format is supported. +. Smoketests now cleanup after magic quotes +. Generator now can output comments (however, comments are still stripped + from HTML Purifier output) +. HTMLPurifier_ConfigSchema->validate() deprecated in favor of + HTMLPurifier_VarParser->parse() +. Integers auto-cast into float type by VarParser. +. HTMLPURIFIER_STRICT removed; no validation is performed on runtime, only + during cache generation +. Reordered script calls in maintenance/flush.php +. Command line scripts now honor exit codes +. When --flush fails in unit testers, abort tests and print message +. Improved documentation in docs/dev-flush.html about the maintenance scripts +. copy() methods removed in favor of clone keyword + +3.0.0, released 2008-01-06 +# HTML Purifier is PHP 5 only! The 2.1.x branch will be maintained + until PHP 4 is completely deprecated, but no new features will be added + to it. + + Visibility declarations added + + Constructor methods renamed to __construct() + + PHP4 reference cruft removed (in progress) +! CSS properties are now case-insensitive +! DefinitionCacheFactory now can register new implementations +! New HTMLPurifier_Filter_ExtractStyleBlocks for extracting #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 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 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php new file mode 100644 index 0000000000..276d8362fa --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Filter/YouTube.php @@ -0,0 +1,65 @@ +]+>.+?' . + '(?:http:)?//www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; + $pre_replace = '\1'; + return preg_replace($pre_regex, $pre_replace, $html); + } + + /** + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function postFilter($html, $config, $context) + { + $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; + return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); + } + + /** + * @param $url + * @return string + */ + protected function armorUrl($url) + { + return str_replace('--', '--', $url); + } + + /** + * @param array $matches + * @return string + */ + protected function postFilterCallback($matches) + { + $url = $this->armorUrl($matches[1]); + return '' . + '' . + '' . + ''; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Generator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php similarity index 56% rename from library/HTMLPurifier/Generator.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php index 4a62417271..6fb5687146 100644 --- a/library/HTMLPurifier/Generator.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Generator.php @@ -11,49 +11,64 @@ class HTMLPurifier_Generator { /** - * Whether or not generator should produce XML output + * Whether or not generator should produce XML output. + * @type bool */ private $_xhtml = true; /** - * :HACK: Whether or not generator should comment the insides of )#si', - array($this, 'scriptCallback'), $html); + $html = preg_replace_callback( + '#(]*>)(\s*[^<].+?)()#si', + array($this, 'scriptCallback'), + $html + ); } $html = $this->normalize($html, $config, $context); @@ -55,15 +69,15 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer if ($maintain_line_numbers) { $current_line = 1; - $current_col = 0; + $current_col = 0; $length = strlen($html); } else { $current_line = false; - $current_col = false; + $current_col = false; $length = false; } $context->register('CurrentLine', $current_line); - $context->register('CurrentCol', $current_col); + $context->register('CurrentCol', $current_col); $nl = "\n"; // how often to manually recalculate. This will ALWAYS be right, // but it's pretty wasteful. Set to 0 to turn off @@ -77,16 +91,14 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer // for testing synchronization $loops = 0; - while(++$loops) { - + while (++$loops) { // $cursor is either at the start of a token, or inside of // a tag (i.e. there was a < immediately before it), as indicated // by $inside_tag if ($maintain_line_numbers) { - // $rcursor, however, is always at the start of a token. - $rcursor = $cursor - (int) $inside_tag; + $rcursor = $cursor - (int)$inside_tag; // Column number is cheap, so we calculate it every round. // We're interested at the *end* of the newline string, so @@ -96,14 +108,11 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); // recalculate lines - if ( - $synchronize_interval && // synchronization is on - $cursor > 0 && // cursor is further than zero - $loops % $synchronize_interval === 0 // time to synchronize! - ) { + if ($synchronize_interval && // synchronization is on + $cursor > 0 && // cursor is further than zero + $loops % $synchronize_interval === 0) { // time to synchronize! $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); } - } $position_next_lt = strpos($html, '<', $cursor); @@ -119,35 +128,42 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer if (!$inside_tag && $position_next_lt !== false) { // We are not inside tag and there still is another tag to parse $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor, $position_next_lt - $cursor - ) + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, + $cursor, + $position_next_lt - $cursor ) - ); + ) + ); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); } $array[] = $token; - $cursor = $position_next_lt + 1; + $cursor = $position_next_lt + 1; $inside_tag = true; continue; } elseif (!$inside_tag) { // We are not inside tag but there are no more tags // If we're already at the end, break - if ($cursor === strlen($html)) break; + if ($cursor === strlen($html)) { + break; + } // Create Text of rest of string $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor - ) + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, + $cursor ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } $array[] = $token; break; } elseif ($inside_tag && $position_next_gt !== false) { @@ -171,16 +187,16 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer } // Check if it's a comment - if ( - substr($segment, 0, 3) === '!--' - ) { + if (substr($segment, 0, 3) === '!--') { // re-determine segment length, looking for --> $position_comment_end = strpos($html, '-->', $cursor); if ($position_comment_end === false) { // uh oh, we have a comment that extends to // infinity. Can't be helped: set comment // end position to end of string - if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); + if ($e) { + $e->send(E_WARNING, 'Lexer: Unclosed comment'); + } $position_comment_end = strlen($html); $end = true; } else { @@ -189,11 +205,13 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer $strlen_segment = $position_comment_end - $cursor; $segment = substr($html, $cursor, $strlen_segment); $token = new - HTMLPurifier_Token_Comment( - substr( - $segment, 3, $strlen_segment - 3 - ) - ); + HTMLPurifier_Token_Comment( + substr( + $segment, + 3, + $strlen_segment - 3 + ) + ); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); @@ -205,7 +223,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer } // Check if it's an end tag - $is_end_tag = (strpos($segment,'/') === 0); + $is_end_tag = (strpos($segment, '/') === 0); if ($is_end_tag) { $type = substr($segment, 1); $token = new HTMLPurifier_Token_End($type); @@ -224,7 +242,9 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer // text and go our merry way if (!ctype_alpha($segment[0])) { // XML: $segment[0] !== '_' && $segment[0] !== ':' - if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + if ($e) { + $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + } $token = new HTMLPurifier_Token_Text('<'); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); @@ -239,7 +259,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer // trailing slash. Remember, we could have a tag like
        , so // any later token processing scripts must convert improperly // classified EmptyTags from StartTags. - $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); + $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1); if ($is_self_closing) { $strlen_segment--; $segment = substr($segment, 0, $strlen_segment); @@ -269,14 +289,16 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer $attribute_string = trim( substr( - $segment, $position_first_space + $segment, + $position_first_space ) ); if ($attribute_string) { $attr = $this->parseAttributeString( - $attribute_string - , $config, $context - ); + $attribute_string, + $config, + $context + ); } else { $attr = array(); } @@ -296,15 +318,19 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer continue; } else { // inside tag, but there's no ending > sign - if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); + if ($e) { + $e->send(E_WARNING, 'Lexer: Missing gt'); + } $token = new - HTMLPurifier_Token_Text( - '<' . - $this->parseData( - substr($html, $cursor) - ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + HTMLPurifier_Token_Text( + '<' . + $this->parseData( + substr($html, $cursor) + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } // no cursor scroll? Hmm... $array[] = $token; break; @@ -319,8 +345,14 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer /** * PHP 5.0.x compatible substr_count that implements offset and length + * @param string $haystack + * @param string $needle + * @param int $offset + * @param int $length + * @return int */ - protected function substrCount($haystack, $needle, $offset, $length) { + protected function substrCount($haystack, $needle, $offset, $length) + { static $oldVersion; if ($oldVersion === null) { $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); @@ -336,13 +368,18 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer /** * Takes the inside of an HTML tag and makes an assoc array of attributes. * - * @param $string Inside of tag excluding name. - * @returns Assoc array of attributes. + * @param string $string Inside of tag excluding name. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array Assoc array of attributes. */ - public function parseAttributeString($string, $config, $context) { - $string = (string) $string; // quick typecast + public function parseAttributeString($string, $config, $context) + { + $string = (string)$string; // quick typecast - if ($string == '') return array(); // no attributes + if ($string == '') { + return array(); + } // no attributes $e = false; if ($config->get('Core.CollectErrors')) { @@ -361,46 +398,55 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer list($key, $quoted_value) = explode('=', $string); $quoted_value = trim($quoted_value); if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } return array(); } - if (!$quoted_value) return array($key => ''); + if (!$quoted_value) { + return array($key => ''); + } $first_char = @$quoted_value[0]; - $last_char = @$quoted_value[strlen($quoted_value)-1]; + $last_char = @$quoted_value[strlen($quoted_value) - 1]; $same_quote = ($first_char == $last_char); $open_quote = ($first_char == '"' || $first_char == "'"); - if ( $same_quote && $open_quote) { + if ($same_quote && $open_quote) { // well behaved $value = substr($quoted_value, 1, strlen($quoted_value) - 2); } else { // not well behaved if ($open_quote) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing end quote'); + } $value = substr($quoted_value, 1); } else { $value = $quoted_value; } } - if ($value === false) $value = ''; + if ($value === false) { + $value = ''; + } return array($key => $this->parseData($value)); } // setup loop environment - $array = array(); // return assoc array of attributes + $array = array(); // return assoc array of attributes $cursor = 0; // current position in string (moves forward) - $size = strlen($string); // size of the string (stays the same) + $size = strlen($string); // size of the string (stays the same) // if we have unquoted attributes, the parser expects a terminating // space, so let's guarantee that there's always a terminating space. $string .= ' '; - while(true) { - - if ($cursor >= $size) { - break; + $old_cursor = -1; + while ($cursor < $size) { + if ($old_cursor >= $cursor) { + throw new Exception("Infinite loop detected"); } + $old_cursor = $cursor; $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); // grab the key @@ -415,8 +461,10 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer $key = substr($string, $key_begin, $key_end - $key_begin); if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); - $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop continue; // empty key } @@ -467,24 +515,25 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer } $value = substr($string, $value_begin, $value_end - $value_begin); - if ($value === false) $value = ''; + if ($value === false) { + $value = ''; + } $array[$key] = $this->parseData($value); $cursor++; - } else { // boolattr if ($key !== '') { $array[$key] = $key; } else { // purely theoretical - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } } - } } return $array; } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php new file mode 100644 index 0000000000..ff4fa218fb --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Lexer/PH5P.php @@ -0,0 +1,4787 @@ +normalize($html, $config, $context); + $new_html = $this->wrapHTML($new_html, $config, $context); + try { + $parser = new HTML5($new_html); + $doc = $parser->save(); + } catch (DOMException $e) { + // Uh oh, it failed. Punt to DirectLex. + $lexer = new HTMLPurifier_Lexer_DirectLex(); + $context->register('PH5PError', $e); // save the error, so we can detect it + return $lexer->tokenizeHTML($html, $config, $context); // use original HTML + } + $tokens = array(); + $this->tokenizeDOM( + $doc->getElementsByTagName('html')->item(0)-> // + getElementsByTagName('body')->item(0) // + , + $tokens + ); + return $tokens; + } +} + +/* + +Copyright 2007 Jeroen van der Meer + +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, and/or 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. + +*/ + +class HTML5 +{ + private $data; + private $char; + private $EOF; + private $state; + private $tree; + private $token; + private $content_model; + private $escape = false; + private $entities = array( + 'AElig;', + 'AElig', + 'AMP;', + 'AMP', + 'Aacute;', + 'Aacute', + 'Acirc;', + 'Acirc', + 'Agrave;', + 'Agrave', + 'Alpha;', + 'Aring;', + 'Aring', + 'Atilde;', + 'Atilde', + 'Auml;', + 'Auml', + 'Beta;', + 'COPY;', + 'COPY', + 'Ccedil;', + 'Ccedil', + 'Chi;', + 'Dagger;', + 'Delta;', + 'ETH;', + 'ETH', + 'Eacute;', + 'Eacute', + 'Ecirc;', + 'Ecirc', + 'Egrave;', + 'Egrave', + 'Epsilon;', + 'Eta;', + 'Euml;', + 'Euml', + 'GT;', + 'GT', + 'Gamma;', + 'Iacute;', + 'Iacute', + 'Icirc;', + 'Icirc', + 'Igrave;', + 'Igrave', + 'Iota;', + 'Iuml;', + 'Iuml', + 'Kappa;', + 'LT;', + 'LT', + 'Lambda;', + 'Mu;', + 'Ntilde;', + 'Ntilde', + 'Nu;', + 'OElig;', + 'Oacute;', + 'Oacute', + 'Ocirc;', + 'Ocirc', + 'Ograve;', + 'Ograve', + 'Omega;', + 'Omicron;', + 'Oslash;', + 'Oslash', + 'Otilde;', + 'Otilde', + 'Ouml;', + 'Ouml', + 'Phi;', + 'Pi;', + 'Prime;', + 'Psi;', + 'QUOT;', + 'QUOT', + 'REG;', + 'REG', + 'Rho;', + 'Scaron;', + 'Sigma;', + 'THORN;', + 'THORN', + 'TRADE;', + 'Tau;', + 'Theta;', + 'Uacute;', + 'Uacute', + 'Ucirc;', + 'Ucirc', + 'Ugrave;', + 'Ugrave', + 'Upsilon;', + 'Uuml;', + 'Uuml', + 'Xi;', + 'Yacute;', + 'Yacute', + 'Yuml;', + 'Zeta;', + 'aacute;', + 'aacute', + 'acirc;', + 'acirc', + 'acute;', + 'acute', + 'aelig;', + 'aelig', + 'agrave;', + 'agrave', + 'alefsym;', + 'alpha;', + 'amp;', + 'amp', + 'and;', + 'ang;', + 'apos;', + 'aring;', + 'aring', + 'asymp;', + 'atilde;', + 'atilde', + 'auml;', + 'auml', + 'bdquo;', + 'beta;', + 'brvbar;', + 'brvbar', + 'bull;', + 'cap;', + 'ccedil;', + 'ccedil', + 'cedil;', + 'cedil', + 'cent;', + 'cent', + 'chi;', + 'circ;', + 'clubs;', + 'cong;', + 'copy;', + 'copy', + 'crarr;', + 'cup;', + 'curren;', + 'curren', + 'dArr;', + 'dagger;', + 'darr;', + 'deg;', + 'deg', + 'delta;', + 'diams;', + 'divide;', + 'divide', + 'eacute;', + 'eacute', + 'ecirc;', + 'ecirc', + 'egrave;', + 'egrave', + 'empty;', + 'emsp;', + 'ensp;', + 'epsilon;', + 'equiv;', + 'eta;', + 'eth;', + 'eth', + 'euml;', + 'euml', + 'euro;', + 'exist;', + 'fnof;', + 'forall;', + 'frac12;', + 'frac12', + 'frac14;', + 'frac14', + 'frac34;', + 'frac34', + 'frasl;', + 'gamma;', + 'ge;', + 'gt;', + 'gt', + 'hArr;', + 'harr;', + 'hearts;', + 'hellip;', + 'iacute;', + 'iacute', + 'icirc;', + 'icirc', + 'iexcl;', + 'iexcl', + 'igrave;', + 'igrave', + 'image;', + 'infin;', + 'int;', + 'iota;', + 'iquest;', + 'iquest', + 'isin;', + 'iuml;', + 'iuml', + 'kappa;', + 'lArr;', + 'lambda;', + 'lang;', + 'laquo;', + 'laquo', + 'larr;', + 'lceil;', + 'ldquo;', + 'le;', + 'lfloor;', + 'lowast;', + 'loz;', + 'lrm;', + 'lsaquo;', + 'lsquo;', + 'lt;', + 'lt', + 'macr;', + 'macr', + 'mdash;', + 'micro;', + 'micro', + 'middot;', + 'middot', + 'minus;', + 'mu;', + 'nabla;', + 'nbsp;', + 'nbsp', + 'ndash;', + 'ne;', + 'ni;', + 'not;', + 'not', + 'notin;', + 'nsub;', + 'ntilde;', + 'ntilde', + 'nu;', + 'oacute;', + 'oacute', + 'ocirc;', + 'ocirc', + 'oelig;', + 'ograve;', + 'ograve', + 'oline;', + 'omega;', + 'omicron;', + 'oplus;', + 'or;', + 'ordf;', + 'ordf', + 'ordm;', + 'ordm', + 'oslash;', + 'oslash', + 'otilde;', + 'otilde', + 'otimes;', + 'ouml;', + 'ouml', + 'para;', + 'para', + 'part;', + 'permil;', + 'perp;', + 'phi;', + 'pi;', + 'piv;', + 'plusmn;', + 'plusmn', + 'pound;', + 'pound', + 'prime;', + 'prod;', + 'prop;', + 'psi;', + 'quot;', + 'quot', + 'rArr;', + 'radic;', + 'rang;', + 'raquo;', + 'raquo', + 'rarr;', + 'rceil;', + 'rdquo;', + 'real;', + 'reg;', + 'reg', + 'rfloor;', + 'rho;', + 'rlm;', + 'rsaquo;', + 'rsquo;', + 'sbquo;', + 'scaron;', + 'sdot;', + 'sect;', + 'sect', + 'shy;', + 'shy', + 'sigma;', + 'sigmaf;', + 'sim;', + 'spades;', + 'sub;', + 'sube;', + 'sum;', + 'sup1;', + 'sup1', + 'sup2;', + 'sup2', + 'sup3;', + 'sup3', + 'sup;', + 'supe;', + 'szlig;', + 'szlig', + 'tau;', + 'there4;', + 'theta;', + 'thetasym;', + 'thinsp;', + 'thorn;', + 'thorn', + 'tilde;', + 'times;', + 'times', + 'trade;', + 'uArr;', + 'uacute;', + 'uacute', + 'uarr;', + 'ucirc;', + 'ucirc', + 'ugrave;', + 'ugrave', + 'uml;', + 'uml', + 'upsih;', + 'upsilon;', + 'uuml;', + 'uuml', + 'weierp;', + 'xi;', + 'yacute;', + 'yacute', + 'yen;', + 'yen', + 'yuml;', + 'yuml', + 'zeta;', + 'zwj;', + 'zwnj;' + ); + + const PCDATA = 0; + const RCDATA = 1; + const CDATA = 2; + const PLAINTEXT = 3; + + const DOCTYPE = 0; + const STARTTAG = 1; + const ENDTAG = 2; + const COMMENT = 3; + const CHARACTR = 4; + const EOF = 5; + + public function __construct($data) + { + $this->data = $data; + $this->char = -1; + $this->EOF = strlen($data); + $this->tree = new HTML5TreeConstructer; + $this->content_model = self::PCDATA; + + $this->state = 'data'; + + while ($this->state !== null) { + $this->{$this->state . 'State'}(); + } + } + + public function save() + { + return $this->tree->save(); + } + + private function char() + { + return ($this->char < $this->EOF) + ? $this->data[$this->char] + : false; + } + + private function character($s, $l = 0) + { + if ($s + $l < $this->EOF) { + if ($l === 0) { + return $this->data[$s]; + } else { + return substr($this->data, $s, $l); + } + } + } + + private function characters($char_class, $start) + { + return preg_replace('#^([' . $char_class . ']+).*#s', '\\1', substr($this->data, $start)); + } + + private function dataState() + { + // Consume the next input character + $this->char++; + $char = $this->char(); + + if ($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { + /* U+0026 AMPERSAND (&) + When the content model flag is set to one of the PCDATA or RCDATA + states: switch to the entity data state. Otherwise: treat it as per + the "anything else" entry below. */ + $this->state = 'entityData'; + + } elseif ($char === '-') { + /* If the content model flag is set to either the RCDATA state or + the CDATA state, and the escape flag is false, and there are at + least three characters before this one in the input stream, and the + last four characters in the input stream, including this one, are + U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, + and U+002D HYPHEN-MINUS (""), + set the escape flag to false. */ + if (($this->content_model === self::RCDATA || + $this->content_model === self::CDATA) && $this->escape === true && + $this->character($this->char, 3) === '-->' + ) { + $this->escape = false; + } + + /* In any case, emit the input character as a character token. + Stay in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + } elseif ($this->char === $this->EOF) { + /* EOF + Emit an end-of-file token. */ + $this->EOF(); + + } elseif ($this->content_model === self::PLAINTEXT) { + /* When the content model flag is set to the PLAINTEXT state + THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of + the text and emit it as a character token. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => substr($this->data, $this->char) + ) + ); + + $this->EOF(); + + } else { + /* Anything else + THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that + otherwise would also be treated as a character token and emit it + as a single character token. Stay in the data state. */ + $len = strcspn($this->data, '<&', $this->char); + $char = substr($this->data, $this->char, $len); + $this->char += $len - 1; + + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + $this->state = 'data'; + } + } + + private function entityDataState() + { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, emit a U+0026 AMPERSAND character token. + // Otherwise, emit the character token that was returned. + $char = (!$entity) ? '&' : $entity; + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => $char + ) + ); + + // Finally, switch to the data state. + $this->state = 'data'; + } + + private function tagOpenState() + { + switch ($this->content_model) { + case self::RCDATA: + case self::CDATA: + /* If the next input character is a U+002F SOLIDUS (/) character, + consume it and switch to the close tag open state. If the next + input character is not a U+002F SOLIDUS (/) character, emit a + U+003C LESS-THAN SIGN character token and switch to the data + state to process the next input character. */ + if ($this->character($this->char + 1) === '/') { + $this->char++; + $this->state = 'closeTagOpen'; + + } else { + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<' + ) + ); + + $this->state = 'data'; + } + break; + + case self::PCDATA: + // If the content model flag is set to the PCDATA state + // Consume the next input character: + $this->char++; + $char = $this->char(); + + if ($char === '!') { + /* U+0021 EXCLAMATION MARK (!) + Switch to the markup declaration open state. */ + $this->state = 'markupDeclarationOpen'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Switch to the close tag open state. */ + $this->state = 'closeTagOpen'; + + } elseif (preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new start tag token, set its tag name to the lowercase + version of the input character (add 0x0020 to the character's code + point), then switch to the tag name state. (Don't emit the token + yet; further details will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::STARTTAG, + 'attr' => array() + ); + + $this->state = 'tagName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Emit a U+003C LESS-THAN SIGN character token and a + U+003E GREATER-THAN SIGN character token. Switch to the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<>' + ) + ); + + $this->state = 'data'; + + } elseif ($char === '?') { + /* U+003F QUESTION MARK (?) + Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + + } else { + /* Anything else + Parse error. Emit a U+003C LESS-THAN SIGN character token and + reconsume the current input character in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => '<' + ) + ); + + $this->char--; + $this->state = 'data'; + } + break; + } + } + + private function closeTagOpenState() + { + $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); + $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; + + if (($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && + (!$the_same || ($the_same && (!preg_match( + '/[\t\n\x0b\x0c >\/]/', + $this->character($this->char + 1 + strlen($next_node)) + ) || $this->EOF === $this->char))) + ) { + /* If the content model flag is set to the RCDATA or CDATA states then + examine the next few characters. If they do not match the tag name of + the last start tag token emitted (case insensitively), or if they do but + they are not immediately followed by one of the following characters: + * U+0009 CHARACTER TABULATION + * U+000A LINE FEED (LF) + * U+000B LINE TABULATION + * U+000C FORM FEED (FF) + * U+0020 SPACE + * U+003E GREATER-THAN SIGN (>) + * U+002F SOLIDUS (/) + * EOF + ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character + token, a U+002F SOLIDUS character token, and switch to the data state + to process the next input character. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => 'state = 'data'; + + } else { + /* Otherwise, if the content model flag is set to the PCDATA state, + or if the next few characters do match that tag name, consume the + next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[A-Za-z]$/', $char)) { + /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z + Create a new end tag token, set its tag name to the lowercase version + of the input character (add 0x0020 to the character's code point), then + switch to the tag name state. (Don't emit the token yet; further details + will be filled in before it is emitted.) */ + $this->token = array( + 'name' => strtolower($char), + 'type' => self::ENDTAG + ); + + $this->state = 'tagName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Parse error. Switch to the data state. */ + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F + SOLIDUS character token. Reconsume the EOF character in the data state. */ + $this->emitToken( + array( + 'type' => self::CHARACTR, + 'data' => 'char--; + $this->state = 'data'; + + } else { + /* Parse error. Switch to the bogus comment state. */ + $this->state = 'bogusComment'; + } + } + } + + private function tagNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } else { + /* Anything else + Append the current input character to the current tag token's tag name. + Stay in the tag name state. */ + $this->token['name'] .= strtolower($char); + $this->state = 'tagName'; + } + } + + private function beforeAttributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Stay in the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function attributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif ($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the before + attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's name. + Stay in the attribute name state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['name'] .= strtolower($char); + + $this->state = 'attributeName'; + } + } + + private function afterAttributeNameState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the after attribute name state. */ + $this->state = 'afterAttributeName'; + + } elseif ($char === '=') { + /* U+003D EQUALS SIGN (=) + Switch to the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '/' && $this->character($this->char + 1) !== '>') { + /* U+002F SOLIDUS (/) + Parse error unless this is a permitted slash. Switch to the + before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the EOF + character in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Start a new attribute in the current tag token. Set that attribute's + name to the current input character, and its value to the empty string. + Switch to the attribute name state. */ + $this->token['attr'][] = array( + 'name' => strtolower($char), + 'value' => null + ); + + $this->state = 'attributeName'; + } + } + + private function beforeAttributeValueState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Stay in the before attribute value state. */ + $this->state = 'beforeAttributeValue'; + + } elseif ($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the attribute value (double-quoted) state. */ + $this->state = 'attributeValueDoubleQuoted'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the attribute value (unquoted) state and reconsume + this input character. */ + $this->char--; + $this->state = 'attributeValueUnquoted'; + + } elseif ($char === '\'') { + /* U+0027 APOSTROPHE (') + Switch to the attribute value (single-quoted) state. */ + $this->state = 'attributeValueSingleQuoted'; + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Switch to the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function attributeValueDoubleQuotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if ($char === '"') { + /* U+0022 QUOTATION MARK (") + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('double'); + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (double-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueDoubleQuoted'; + } + } + + private function attributeValueSingleQuotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if ($char === '\'') { + /* U+0022 QUOTATION MARK (') + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState('single'); + + } elseif ($this->char === $this->EOF) { + /* EOF + Parse error. Emit the current tag token. Reconsume the character + in the data state. */ + $this->emitToken($this->token); + + $this->char--; + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (single-quoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueSingleQuoted'; + } + } + + private function attributeValueUnquotedState() + { + // Consume the next input character: + $this->char++; + $char = $this->character($this->char); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + /* U+0009 CHARACTER TABULATION + U+000A LINE FEED (LF) + U+000B LINE TABULATION + U+000C FORM FEED (FF) + U+0020 SPACE + Switch to the before attribute name state. */ + $this->state = 'beforeAttributeName'; + + } elseif ($char === '&') { + /* U+0026 AMPERSAND (&) + Switch to the entity in attribute value state. */ + $this->entityInAttributeValueState(); + + } elseif ($char === '>') { + /* U+003E GREATER-THAN SIGN (>) + Emit the current tag token. Switch to the data state. */ + $this->emitToken($this->token); + $this->state = 'data'; + + } else { + /* Anything else + Append the current input character to the current attribute's value. + Stay in the attribute value (unquoted) state. */ + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + + $this->state = 'attributeValueUnquoted'; + } + } + + private function entityInAttributeValueState() + { + // Attempt to consume an entity. + $entity = $this->entity(); + + // If nothing is returned, append a U+0026 AMPERSAND character to the + // current attribute's value. Otherwise, emit the character token that + // was returned. + $char = (!$entity) + ? '&' + : $entity; + + $last = count($this->token['attr']) - 1; + $this->token['attr'][$last]['value'] .= $char; + } + + private function bogusCommentState() + { + /* Consume every character up to the first U+003E GREATER-THAN SIGN + character (>) or the end of the file (EOF), whichever comes first. Emit + a comment token whose data is the concatenation of all the characters + starting from and including the character that caused the state machine + to switch into the bogus comment state, up to and including the last + consumed character before the U+003E character, if any, or up to the + end of the file otherwise. (If the comment was started by the end of + the file (EOF), the token is empty.) */ + $data = $this->characters('^>', $this->char); + $this->emitToken( + array( + 'data' => $data, + 'type' => self::COMMENT + ) + ); + + $this->char += strlen($data); + + /* Switch to the data state. */ + $this->state = 'data'; + + /* If the end of the file was reached, reconsume the EOF character. */ + if ($this->char === $this->EOF) { + $this->char = $this->EOF - 1; + } + } + + private function markupDeclarationOpenState() + { + /* If the next two characters are both U+002D HYPHEN-MINUS (-) + characters, consume those two characters, create a comment token whose + data is the empty string, and switch to the comment state. */ + if ($this->character($this->char + 1, 2) === '--') { + $this->char += 2; + $this->state = 'comment'; + $this->token = array( + 'data' => null, + 'type' => self::COMMENT + ); + + /* Otherwise if the next seven chacacters are a case-insensitive match + for the word "DOCTYPE", then consume those characters and switch to the + DOCTYPE state. */ + } elseif (strtolower($this->character($this->char + 1, 7)) === 'doctype') { + $this->char += 7; + $this->state = 'doctype'; + + /* Otherwise, is is a parse error. Switch to the bogus comment state. + The next character that is consumed, if any, is the first character + that will be in the comment. */ + } else { + $this->char++; + $this->state = 'bogusComment'; + } + } + + private function commentState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if ($char === '-') { + /* Switch to the comment dash state */ + $this->state = 'commentDash'; + + /* EOF */ + } elseif ($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append the input character to the comment token's data. Stay in + the comment state. */ + $this->token['data'] .= $char; + } + } + + private function commentDashState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + /* U+002D HYPHEN-MINUS (-) */ + if ($char === '-') { + /* Switch to the comment end state */ + $this->state = 'commentEnd'; + + /* EOF */ + } elseif ($this->char === $this->EOF) { + /* Parse error. Emit the comment token. Reconsume the EOF character + in the data state. */ + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + /* Anything else */ + } else { + /* Append a U+002D HYPHEN-MINUS (-) character and the input + character to the comment token's data. Switch to the comment state. */ + $this->token['data'] .= '-' . $char; + $this->state = 'comment'; + } + } + + private function commentEndState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($char === '-') { + $this->token['data'] .= '-'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['data'] .= '--' . $char; + $this->state = 'comment'; + } + } + + private function doctypeState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'beforeDoctypeName'; + + } else { + $this->char--; + $this->state = 'beforeDoctypeName'; + } + } + + private function beforeDoctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the before DOCTYPE name state. + + } elseif (preg_match('/^[a-z]$/', $char)) { + $this->token = array( + 'name' => strtoupper($char), + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + + } elseif ($char === '>') { + $this->emitToken( + array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + ) + ); + + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken( + array( + 'name' => null, + 'type' => self::DOCTYPE, + 'error' => true + ) + ); + + $this->char--; + $this->state = 'data'; + + } else { + $this->token = array( + 'name' => $char, + 'type' => self::DOCTYPE, + 'error' => true + ); + + $this->state = 'doctypeName'; + } + } + + private function doctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + $this->state = 'AfterDoctypeName'; + + } elseif ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif (preg_match('/^[a-z]$/', $char)) { + $this->token['name'] .= strtoupper($char); + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['name'] .= $char; + } + + $this->token['error'] = ($this->token['name'] === 'HTML') + ? false + : true; + } + + private function afterDoctypeNameState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if (preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { + // Stay in the DOCTYPE name state. + + } elseif ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + $this->token['error'] = true; + $this->state = 'bogusDoctype'; + } + } + + private function bogusDoctypeState() + { + /* Consume the next input character: */ + $this->char++; + $char = $this->char(); + + if ($char === '>') { + $this->emitToken($this->token); + $this->state = 'data'; + + } elseif ($this->char === $this->EOF) { + $this->emitToken($this->token); + $this->char--; + $this->state = 'data'; + + } else { + // Stay in the bogus DOCTYPE state. + } + } + + private function entity() + { + $start = $this->char; + + // This section defines how to consume an entity. This definition is + // used when parsing entities in text and in attributes. + + // The behaviour depends on the identity of the next character (the + // one immediately after the U+0026 AMPERSAND character): + + switch ($this->character($this->char + 1)) { + // U+0023 NUMBER SIGN (#) + case '#': + + // The behaviour further depends on the character after the + // U+0023 NUMBER SIGN: + switch ($this->character($this->char + 1)) { + // U+0078 LATIN SMALL LETTER X + // U+0058 LATIN CAPITAL LETTER X + case 'x': + case 'X': + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 + // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER + // A, through to U+0046 LATIN CAPITAL LETTER F (in other + // words, 0-9, A-F, a-f). + $char = 1; + $char_class = '0-9A-Fa-f'; + break; + + // Anything else + default: + // Follow the steps below, but using the range of + // characters U+0030 DIGIT ZERO through to U+0039 DIGIT + // NINE (i.e. just 0-9). + $char = 0; + $char_class = '0-9'; + break; + } + + // Consume as many characters as match the range of characters + // given above. + $this->char++; + $e_name = $this->characters($char_class, $this->char + $char + 1); + $entity = $this->character($start, $this->char); + $cond = strlen($e_name) > 0; + + // The rest of the parsing happens bellow. + break; + + // Anything else + default: + // Consume the maximum number of characters possible, with the + // consumed characters case-sensitively matching one of the + // identifiers in the first column of the entities table. + $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); + $len = strlen($e_name); + + for ($c = 1; $c <= $len; $c++) { + $id = substr($e_name, 0, $c); + $this->char++; + + if (in_array($id, $this->entities)) { + if ($e_name[$c - 1] !== ';') { + if ($c < $len && $e_name[$c] == ';') { + $this->char++; // consume extra semicolon + } + } + $entity = $id; + break; + } + } + + $cond = isset($entity); + // The rest of the parsing happens bellow. + break; + } + + if (!$cond) { + // If no match can be made, then this is a parse error. No + // characters are consumed, and nothing is returned. + $this->char = $start; + return false; + } + + // Return a character token for the character corresponding to the + // entity name (as given by the second column of the entities table). + return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8'); + } + + private function emitToken($token) + { + $emit = $this->tree->emitToken($token); + + if (is_int($emit)) { + $this->content_model = $emit; + + } elseif ($token['type'] === self::ENDTAG) { + $this->content_model = self::PCDATA; + } + } + + private function EOF() + { + $this->state = null; + $this->tree->emitToken( + array( + 'type' => self::EOF + ) + ); + } +} + +class HTML5TreeConstructer +{ + public $stack = array(); + + private $phase; + private $mode; + private $dom; + private $foster_parent = null; + private $a_formatting = array(); + + private $head_pointer = null; + private $form_pointer = null; + + private $scoping = array('button', 'caption', 'html', 'marquee', 'object', 'table', 'td', 'th'); + private $formatting = array( + 'a', + 'b', + 'big', + 'em', + 'font', + 'i', + 'nobr', + 's', + 'small', + 'strike', + 'strong', + 'tt', + 'u' + ); + private $special = array( + 'address', + 'area', + 'base', + 'basefont', + 'bgsound', + 'blockquote', + 'body', + 'br', + 'center', + 'col', + 'colgroup', + 'dd', + 'dir', + 'div', + 'dl', + 'dt', + 'embed', + 'fieldset', + 'form', + 'frame', + 'frameset', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'hr', + 'iframe', + 'image', + 'img', + 'input', + 'isindex', + 'li', + 'link', + 'listing', + 'menu', + 'meta', + 'noembed', + 'noframes', + 'noscript', + 'ol', + 'optgroup', + 'option', + 'p', + 'param', + 'plaintext', + 'pre', + 'script', + 'select', + 'spacer', + 'style', + 'tbody', + 'textarea', + 'tfoot', + 'thead', + 'title', + 'tr', + 'ul', + 'wbr' + ); + + // The different phases. + const INIT_PHASE = 0; + const ROOT_PHASE = 1; + const MAIN_PHASE = 2; + const END_PHASE = 3; + + // The different insertion modes for the main phase. + const BEFOR_HEAD = 0; + const IN_HEAD = 1; + const AFTER_HEAD = 2; + const IN_BODY = 3; + const IN_TABLE = 4; + const IN_CAPTION = 5; + const IN_CGROUP = 6; + const IN_TBODY = 7; + const IN_ROW = 8; + const IN_CELL = 9; + const IN_SELECT = 10; + const AFTER_BODY = 11; + const IN_FRAME = 12; + const AFTR_FRAME = 13; + + // The different types of elements. + const SPECIAL = 0; + const SCOPING = 1; + const FORMATTING = 2; + const PHRASING = 3; + + const MARKER = 0; + + public function __construct() + { + $this->phase = self::INIT_PHASE; + $this->mode = self::BEFOR_HEAD; + $this->dom = new DOMDocument; + + $this->dom->encoding = 'UTF-8'; + $this->dom->preserveWhiteSpace = true; + $this->dom->substituteEntities = true; + $this->dom->strictErrorChecking = false; + } + + // Process tag tokens + public function emitToken($token) + { + switch ($this->phase) { + case self::INIT_PHASE: + return $this->initPhase($token); + break; + case self::ROOT_PHASE: + return $this->rootElementPhase($token); + break; + case self::MAIN_PHASE: + return $this->mainPhase($token); + break; + case self::END_PHASE : + return $this->trailingEndPhase($token); + break; + } + } + + private function initPhase($token) + { + /* Initially, the tree construction stage must handle each token + emitted from the tokenisation stage as follows: */ + + /* A DOCTYPE token that is marked as being in error + A comment token + A start tag token + An end tag token + A character token that is not one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE + An end-of-file token */ + if ((isset($token['error']) && $token['error']) || + $token['type'] === HTML5::COMMENT || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF || + ($token['type'] === HTML5::CHARACTR && isset($token['data']) && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) + ) { + /* This specification does not define how to handle this case. In + particular, user agents may ignore the entirety of this specification + altogether for such documents, and instead invoke special parse modes + with a greater emphasis on backwards compatibility. */ + + $this->phase = self::ROOT_PHASE; + return $this->rootElementPhase($token); + + /* A DOCTYPE token marked as being correct */ + } elseif (isset($token['error']) && !$token['error']) { + /* Append a DocumentType node to the Document node, with the name + attribute set to the name given in the DOCTYPE token (which will be + "HTML"), and the other attributes specific to DocumentType objects + set to null, empty lists, or the empty string as appropriate. */ + $doctype = new DOMDocumentType(null, null, 'HTML'); + + /* Then, switch to the root element phase of the tree construction + stage. */ + $this->phase = self::ROOT_PHASE; + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif (isset($token['data']) && preg_match( + '/^[\t\n\x0b\x0c ]+$/', + $token['data'] + ) + ) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + } + } + + private function rootElementPhase($token) + { + /* After the initial phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append that character to the Document node. */ + $text = $this->dom->createTextNode($token['data']); + $this->dom->appendChild($text); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED + (FF), or U+0020 SPACE + A start tag token + An end tag token + An end-of-file token */ + } elseif (($token['type'] === HTML5::CHARACTR && + !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || + $token['type'] === HTML5::ENDTAG || + $token['type'] === HTML5::EOF + ) { + /* Create an HTMLElement node with the tag name html, in the HTML + namespace. Append it to the Document object. Switch to the main + phase and reprocess the current token. */ + $html = $this->dom->createElement('html'); + $this->dom->appendChild($html); + $this->stack[] = $html; + + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + } + } + + private function mainPhase($token) + { + /* Tokens in the main phase must be handled as follows: */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A start tag token with the tag name "html" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { + /* If this start tag token was not the first start tag token, then + it is a parse error. */ + + /* For each attribute on the token, check to see if the attribute + is already present on the top element of the stack of open elements. + If it is not, add the attribute and its corresponding value to that + element. */ + foreach ($token['attr'] as $attr) { + if (!$this->stack[0]->hasAttribute($attr['name'])) { + $this->stack[0]->setAttribute($attr['name'], $attr['value']); + } + } + + /* An end-of-file token */ + } elseif ($token['type'] === HTML5::EOF) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Anything else. */ + } else { + /* Depends on the insertion mode: */ + switch ($this->mode) { + case self::BEFOR_HEAD: + return $this->beforeHead($token); + break; + case self::IN_HEAD: + return $this->inHead($token); + break; + case self::AFTER_HEAD: + return $this->afterHead($token); + break; + case self::IN_BODY: + return $this->inBody($token); + break; + case self::IN_TABLE: + return $this->inTable($token); + break; + case self::IN_CAPTION: + return $this->inCaption($token); + break; + case self::IN_CGROUP: + return $this->inColumnGroup($token); + break; + case self::IN_TBODY: + return $this->inTableBody($token); + break; + case self::IN_ROW: + return $this->inRow($token); + break; + case self::IN_CELL: + return $this->inCell($token); + break; + case self::IN_SELECT: + return $this->inSelect($token); + break; + case self::AFTER_BODY: + return $this->afterBody($token); + break; + case self::IN_FRAME: + return $this->inFrameset($token); + break; + case self::AFTR_FRAME: + return $this->afterFrameset($token); + break; + case self::END_PHASE: + return $this->trailingEndPhase($token); + break; + } + } + } + + private function beforeHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "head" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { + /* Create an element for the token, append the new element to the + current node and push it onto the stack of open elements. */ + $element = $this->insertElement($token); + + /* Set the head element pointer to this new element node. */ + $this->head_pointer = $element; + + /* Change the insertion mode to "in head". */ + $this->mode = self::IN_HEAD; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title". Or an end tag with the tag name "html". + Or a character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or any other start tag token */ + } elseif ($token['type'] === HTML5::STARTTAG || + ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || + ($token['type'] === HTML5::CHARACTR && !preg_match( + '/^[\t\n\x0b\x0c ]$/', + $token['data'] + )) + ) { + /* Act as if a start tag token with the tag name "head" and no + attributes had been seen, then reprocess the current token. */ + $this->beforeHead( + array( + 'name' => 'head', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inHead($token); + + /* Any other end tag */ + } elseif ($token['type'] === HTML5::ENDTAG) { + /* Parse error. Ignore the token. */ + } + } + + private function inHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. + + THIS DIFFERS FROM THE SPEC: If the current node is either a title, style + or script element, append the character to the current node regardless + of its content. */ + if (($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( + $token['type'] === HTML5::CHARACTR && in_array( + end($this->stack)->nodeName, + array('title', 'style', 'script') + )) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('title', 'style', 'script')) + ) { + array_pop($this->stack); + return HTML5::PCDATA; + + /* A start tag with the tag name "title" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $element = $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the RCDATA state. */ + return HTML5::RCDATA; + + /* A start tag with the tag name "style" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + } else { + $this->insertElement($token); + } + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "script" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { + /* Create an element for the token. */ + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + + /* A start tag with the tag name "base", "link", or "meta" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('base', 'link', 'meta') + ) + ) { + /* Create an element for the token and append the new element to the + node pointed to by the head element pointer, or, if that is null + (innerHTML case), to the current node. */ + if ($this->head_pointer !== null) { + $element = $this->insertElement($token, false); + $this->head_pointer->appendChild($element); + array_pop($this->stack); + + } else { + $this->insertElement($token); + } + + /* An end tag with the tag name "head" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { + /* If the current node is a head element, pop the current node off + the stack of open elements. */ + if ($this->head_pointer->isSameNode(end($this->stack))) { + array_pop($this->stack); + + /* Otherwise, this is a parse error. */ + } else { + // k + } + + /* Change the insertion mode to "after head". */ + $this->mode = self::AFTER_HEAD; + + /* A start tag with the tag name "head" or an end tag except "html". */ + } elseif (($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || + ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html') + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* If the current node is a head element, act as if an end tag + token with the tag name "head" had been seen. */ + if ($this->head_pointer->isSameNode(end($this->stack))) { + $this->inHead( + array( + 'name' => 'head', + 'type' => HTML5::ENDTAG + ) + ); + + /* Otherwise, change the insertion mode to "after head". */ + } else { + $this->mode = self::AFTER_HEAD; + } + + /* Then, reprocess the current token. */ + return $this->afterHead($token); + } + } + + private function afterHead($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data attribute + set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token with the tag name "body" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { + /* Insert a body element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in body". */ + $this->mode = self::IN_BODY; + + /* A start tag token with the tag name "frameset" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { + /* Insert a frameset element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in frameset". */ + $this->mode = self::IN_FRAME; + + /* A start tag token whose tag name is one of: "base", "link", "meta", + "script", "style", "title" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('base', 'link', 'meta', 'script', 'style', 'title') + ) + ) { + /* Parse error. Switch the insertion mode back to "in head" and + reprocess the token. */ + $this->mode = self::IN_HEAD; + return $this->inHead($token); + + /* Anything else */ + } else { + /* Act as if a start tag token with the tag name "body" and no + attributes had been seen, and then reprocess the current token. */ + $this->afterHead( + array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inBody($token); + } + } + + private function inBody($token) + { + /* Handle the token as follows: */ + + switch ($token['type']) { + /* A character token */ + case HTML5::CHARACTR: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + break; + + /* A comment token */ + case HTML5::COMMENT: + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + break; + + case HTML5::STARTTAG: + switch ($token['name']) { + /* A start tag token whose tag name is one of: "script", + "style" */ + case 'script': + case 'style': + /* Process the token as if the insertion mode had been "in + head". */ + return $this->inHead($token); + break; + + /* A start tag token whose tag name is one of: "base", "link", + "meta", "title" */ + case 'base': + case 'link': + case 'meta': + case 'title': + /* Parse error. Process the token as if the insertion mode + had been "in head". */ + return $this->inHead($token); + break; + + /* A start tag token with the tag name "body" */ + case 'body': + /* Parse error. If the second element on the stack of open + elements is not a body element, or, if the stack of open + elements has only one node on it, then ignore the token. + (innerHTML case) */ + if (count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { + // Ignore + + /* Otherwise, for each attribute on the token, check to see + if the attribute is already present on the body element (the + second element) on the stack of open elements. If it is not, + add the attribute and its corresponding value to that + element. */ + } else { + foreach ($token['attr'] as $attr) { + if (!$this->stack[1]->hasAttribute($attr['name'])) { + $this->stack[1]->setAttribute($attr['name'], $attr['value']); + } + } + } + break; + + /* A start tag whose tag name is one of: "address", + "blockquote", "center", "dir", "div", "dl", "fieldset", + "listing", "menu", "ol", "p", "ul" */ + case 'address': + case 'blockquote': + case 'center': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'listing': + case 'menu': + case 'ol': + case 'p': + case 'ul': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "form" */ + case 'form': + /* If the form element pointer is not null, ignore the + token with a parse error. */ + if ($this->form_pointer !== null) { + // Ignore. + + /* Otherwise: */ + } else { + /* If the stack of open elements has a p element in + scope, then act as if an end tag with the tag name p + had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token, and set the + form element pointer to point to the element created. */ + $element = $this->insertElement($token); + $this->form_pointer = $element; + } + break; + + /* A start tag whose tag name is "li", "dd" or "dt" */ + case 'li': + case 'dd': + case 'dt': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + $stack_length = count($this->stack) - 1; + + for ($n = $stack_length; 0 <= $n; $n--) { + /* 1. Initialise node to be the current node (the + bottommost node of the stack). */ + $stop = false; + $node = $this->stack[$n]; + $cat = $this->getElementCategory($node->tagName); + + /* 2. If node is an li, dd or dt element, then pop all + the nodes from the current node up to node, including + node, then stop this algorithm. */ + if ($token['name'] === $node->tagName || ($token['name'] !== 'li' + && ($node->tagName === 'dd' || $node->tagName === 'dt')) + ) { + for ($x = $stack_length; $x >= $n; $x--) { + array_pop($this->stack); + } + + break; + } + + /* 3. If node is not in the formatting category, and is + not in the phrasing category, and is not an address or + div element, then stop this algorithm. */ + if ($cat !== self::FORMATTING && $cat !== self::PHRASING && + $node->tagName !== 'address' && $node->tagName !== 'div' + ) { + break; + } + } + + /* Finally, insert an HTML element with the same tag + name as the token's. */ + $this->insertElement($token); + break; + + /* A start tag token whose tag name is "plaintext" */ + case 'plaintext': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been + seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + return HTML5::PLAINTEXT; + break; + + /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + this is a parse error; pop elements from the stack until an + element with one of those tag names has been popped from the + stack. */ + while ($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { + array_pop($this->stack); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + break; + + /* A start tag whose tag name is "a" */ + case 'a': + /* If the list of active formatting elements contains + an element whose tag name is "a" between the end of the + list and the last marker on the list (or the start of + the list if there is no marker on the list), then this + is a parse error; act as if an end tag with the tag name + "a" had been seen, then remove that element from the list + of active formatting elements and the stack of open + elements if the end tag didn't already remove it (it + might not have if the element is not in table scope). */ + $leng = count($this->a_formatting); + + for ($n = $leng - 1; $n >= 0; $n--) { + if ($this->a_formatting[$n] === self::MARKER) { + break; + + } elseif ($this->a_formatting[$n]->nodeName === 'a') { + $this->emitToken( + array( + 'name' => 'a', + 'type' => HTML5::ENDTAG + ) + ); + break; + } + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag whose tag name is one of: "b", "big", "em", "font", + "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'b': + case 'big': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $el = $this->insertElement($token); + + /* Add that element to the list of active formatting + elements. */ + $this->a_formatting[] = $el; + break; + + /* A start tag token whose tag name is "button" */ + case 'button': + /* If the stack of open elements has a button element in scope, + then this is a parse error; act as if an end tag with the tag + name "button" had been seen, then reprocess the token. (We don't + do that. Unnecessary.) */ + if ($this->elementInScope('button')) { + $this->inBody( + array( + 'name' => 'button', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is one of: "marquee", "object" */ + case 'marquee': + case 'object': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + break; + + /* A start tag token whose tag name is "xmp" */ + case 'xmp': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Switch the content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "table" */ + case 'table': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + break; + + /* A start tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ + case 'area': + case 'basefont': + case 'bgsound': + case 'br': + case 'embed': + case 'img': + case 'param': + case 'spacer': + case 'wbr': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "hr" */ + case 'hr': + /* If the stack of open elements has a p element in scope, + then act as if an end tag with the tag name p had been seen. */ + if ($this->elementInScope('p')) { + $this->emitToken( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "image" */ + case 'image': + /* Parse error. Change the token's tag name to "img" and + reprocess it. (Don't ask.) */ + $token['name'] = 'img'; + return $this->inBody($token); + break; + + /* A start tag whose tag name is "input" */ + case 'input': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an input element for the token. */ + $element = $this->insertElement($token, false); + + /* If the form element pointer is not null, then associate the + input element with the form element pointed to by the form + element pointer. */ + $this->form_pointer !== null + ? $this->form_pointer->appendChild($element) + : end($this->stack)->appendChild($element); + + /* Pop that input element off the stack of open elements. */ + array_pop($this->stack); + break; + + /* A start tag whose tag name is "isindex" */ + case 'isindex': + /* Parse error. */ + // w/e + + /* If the form element pointer is not null, + then ignore the token. */ + if ($this->form_pointer === null) { + /* Act as if a start tag token with the tag name "form" had + been seen. */ + $this->inBody( + array( + 'name' => 'body', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody( + array( + 'name' => 'hr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "p" had + been seen. */ + $this->inBody( + array( + 'name' => 'p', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a start tag token with the tag name "label" + had been seen. */ + $this->inBody( + array( + 'name' => 'label', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + /* Act as if a stream of character tokens had been seen. */ + $this->insertText( + 'This is a searchable index. ' . + 'Insert your search keywords here: ' + ); + + /* Act as if a start tag token with the tag name "input" + had been seen, with all the attributes from the "isindex" + token, except with the "name" attribute set to the value + "isindex" (ignoring any explicit "name" attribute). */ + $attr = $token['attr']; + $attr[] = array('name' => 'name', 'value' => 'isindex'); + + $this->inBody( + array( + 'name' => 'input', + 'type' => HTML5::STARTTAG, + 'attr' => $attr + ) + ); + + /* Act as if a stream of character tokens had been seen + (see below for what they should say). */ + $this->insertText( + 'This is a searchable index. ' . + 'Insert your search keywords here: ' + ); + + /* Act as if an end tag token with the tag name "label" + had been seen. */ + $this->inBody( + array( + 'name' => 'label', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if an end tag token with the tag name "p" had + been seen. */ + $this->inBody( + array( + 'name' => 'p', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if a start tag token with the tag name "hr" had + been seen. */ + $this->inBody( + array( + 'name' => 'hr', + 'type' => HTML5::ENDTAG + ) + ); + + /* Act as if an end tag token with the tag name "form" had + been seen. */ + $this->inBody( + array( + 'name' => 'form', + 'type' => HTML5::ENDTAG + ) + ); + } + break; + + /* A start tag whose tag name is "textarea" */ + case 'textarea': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the + RCDATA state. */ + return HTML5::RCDATA; + break; + + /* A start tag whose tag name is one of: "iframe", "noembed", + "noframes" */ + case 'iframe': + case 'noembed': + case 'noframes': + $this->insertElement($token); + + /* Switch the tokeniser's content model flag to the CDATA state. */ + return HTML5::CDATA; + break; + + /* A start tag whose tag name is "select" */ + case 'select': + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Change the insertion mode to "in select". */ + $this->mode = self::IN_SELECT; + break; + + /* A start or end tag whose tag name is one of: "caption", "col", + "colgroup", "frame", "frameset", "head", "option", "optgroup", + "tbody", "td", "tfoot", "th", "thead", "tr". */ + case 'caption': + case 'col': + case 'colgroup': + case 'frame': + case 'frameset': + case 'head': + case 'option': + case 'optgroup': + case 'tbody': + case 'td': + case 'tfoot': + case 'th': + case 'thead': + case 'tr': + // Parse error. Ignore the token. + break; + + /* A start or end tag whose tag name is one of: "event-source", + "section", "nav", "article", "aside", "header", "footer", + "datagrid", "command" */ + case 'event-source': + case 'section': + case 'nav': + case 'article': + case 'aside': + case 'header': + case 'footer': + case 'datagrid': + case 'command': + // Work in progress! + break; + + /* A start tag token not covered by the previous entries */ + default: + /* Reconstruct the active formatting elements, if any. */ + $this->reconstructActiveFormattingElements(); + + $this->insertElement($token, true, true); + break; + } + break; + + case HTML5::ENDTAG: + switch ($token['name']) { + /* An end tag with the tag name "body" */ + case 'body': + /* If the second element in the stack of open elements is + not a body element, this is a parse error. Ignore the token. + (innerHTML case) */ + if (count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { + // Ignore. + + /* If the current node is not the body element, then this + is a parse error. */ + } elseif (end($this->stack)->nodeName !== 'body') { + // Parse error. + } + + /* Change the insertion mode to "after body". */ + $this->mode = self::AFTER_BODY; + break; + + /* An end tag with the tag name "html" */ + case 'html': + /* Act as if an end tag with tag name "body" had been seen, + then, if that token wasn't ignored, reprocess the current + token. */ + $this->inBody( + array( + 'name' => 'body', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->afterBody($token); + break; + + /* An end tag whose tag name is one of: "address", "blockquote", + "center", "dir", "div", "dl", "fieldset", "listing", "menu", + "ol", "pre", "ul" */ + case 'address': + case 'blockquote': + case 'center': + case 'dir': + case 'div': + case 'dl': + case 'fieldset': + case 'listing': + case 'menu': + case 'ol': + case 'pre': + case 'ul': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with + the same tag name as that of the token, then this + is a parse error. */ + // w/e + + /* If the stack of open elements has an element in + scope with the same tag name as that of the token, + then pop elements from this stack until an element + with that tag name has been popped from the stack. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is "form" */ + case 'form': + /* If the stack of open elements has an element in scope + with the same tag name as that of the token, then generate + implied end tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + } + + if (end($this->stack)->nodeName !== $token['name']) { + /* Now, if the current node is not an element with the + same tag name as that of the token, then this is a parse + error. */ + // w/e + + } else { + /* Otherwise, if the current node is an element with + the same tag name as that of the token pop that element + from the stack. */ + array_pop($this->stack); + } + + /* In any case, set the form element pointer to null. */ + $this->form_pointer = null; + break; + + /* An end tag whose tag name is "p" */ + case 'p': + /* If the stack of open elements has a p element in scope, + then generate implied end tags, except for p elements. */ + if ($this->elementInScope('p')) { + $this->generateImpliedEndTags(array('p')); + + /* If the current node is not a p element, then this is + a parse error. */ + // k + + /* If the stack of open elements has a p element in + scope, then pop elements from this stack until the stack + no longer has a p element in scope. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->elementInScope('p')) { + array_pop($this->stack); + + } else { + break; + } + } + } + break; + + /* An end tag whose tag name is "dd", "dt", or "li" */ + case 'dd': + case 'dt': + case 'li': + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + generate implied end tags, except for elements with the + same tag name as the token. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(array($token['name'])); + + /* If the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then + pop elements from this stack until an element with that + tag name has been popped from the stack. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", + "h5", "h6" */ + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); + + /* If the stack of open elements has in scope an element whose + tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then + generate implied end tags. */ + if ($this->elementInScope($elements)) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as that of the token, then this is a parse error. */ + // w/e + + /* If the stack of open elements has in scope an element + whose tag name is one of "h1", "h2", "h3", "h4", "h5", or + "h6", then pop elements from the stack until an element + with one of those tag names has been popped from the stack. */ + while ($this->elementInScope($elements)) { + array_pop($this->stack); + } + } + break; + + /* An end tag whose tag name is one of: "a", "b", "big", "em", + "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ + case 'a': + case 'b': + case 'big': + case 'em': + case 'font': + case 'i': + case 'nobr': + case 's': + case 'small': + case 'strike': + case 'strong': + case 'tt': + case 'u': + /* 1. Let the formatting element be the last element in + the list of active formatting elements that: + * is between the end of the list and the last scope + marker in the list, if any, or the start of the list + otherwise, and + * has the same tag name as the token. + */ + while (true) { + for ($a = count($this->a_formatting) - 1; $a >= 0; $a--) { + if ($this->a_formatting[$a] === self::MARKER) { + break; + + } elseif ($this->a_formatting[$a]->tagName === $token['name']) { + $formatting_element = $this->a_formatting[$a]; + $in_stack = in_array($formatting_element, $this->stack, true); + $fe_af_pos = $a; + break; + } + } + + /* If there is no such node, or, if that node is + also in the stack of open elements but the element + is not in scope, then this is a parse error. Abort + these steps. The token is ignored. */ + if (!isset($formatting_element) || ($in_stack && + !$this->elementInScope($token['name'])) + ) { + break; + + /* Otherwise, if there is such a node, but that node + is not in the stack of open elements, then this is a + parse error; remove the element from the list, and + abort these steps. */ + } elseif (isset($formatting_element) && !$in_stack) { + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 2. Let the furthest block be the topmost node in the + stack of open elements that is lower in the stack + than the formatting element, and is not an element in + the phrasing or formatting categories. There might + not be one. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $length = count($this->stack); + + for ($s = $fe_s_pos + 1; $s < $length; $s++) { + $category = $this->getElementCategory($this->stack[$s]->nodeName); + + if ($category !== self::PHRASING && $category !== self::FORMATTING) { + $furthest_block = $this->stack[$s]; + } + } + + /* 3. If there is no furthest block, then the UA must + skip the subsequent steps and instead just pop all + the nodes from the bottom of the stack of open + elements, from the current node up to the formatting + element, and remove the formatting element from the + list of active formatting elements. */ + if (!isset($furthest_block)) { + for ($n = $length - 1; $n >= $fe_s_pos; $n--) { + array_pop($this->stack); + } + + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + break; + } + + /* 4. Let the common ancestor be the element + immediately above the formatting element in the stack + of open elements. */ + $common_ancestor = $this->stack[$fe_s_pos - 1]; + + /* 5. If the furthest block has a parent node, then + remove the furthest block from its parent node. */ + if ($furthest_block->parentNode !== null) { + $furthest_block->parentNode->removeChild($furthest_block); + } + + /* 6. Let a bookmark note the position of the + formatting element in the list of active formatting + elements relative to the elements on either side + of it in the list. */ + $bookmark = $fe_af_pos; + + /* 7. Let node and last node be the furthest block. + Follow these steps: */ + $node = $furthest_block; + $last_node = $furthest_block; + + while (true) { + for ($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { + /* 7.1 Let node be the element immediately + prior to node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 7.2 If node is not in the list of active + formatting elements, then remove node from + the stack of open elements and then go back + to step 1. */ + if (!in_array($node, $this->a_formatting, true)) { + unset($this->stack[$n]); + $this->stack = array_merge($this->stack); + + } else { + break; + } + } + + /* 7.3 Otherwise, if node is the formatting + element, then go to the next step in the overall + algorithm. */ + if ($node === $formatting_element) { + break; + + /* 7.4 Otherwise, if last node is the furthest + block, then move the aforementioned bookmark to + be immediately after the node in the list of + active formatting elements. */ + } elseif ($last_node === $furthest_block) { + $bookmark = array_search($node, $this->a_formatting, true) + 1; + } + + /* 7.5 If node has any children, perform a + shallow clone of node, replace the entry for + node in the list of active formatting elements + with an entry for the clone, replace the entry + for node in the stack of open elements with an + entry for the clone, and let node be the clone. */ + if ($node->hasChildNodes()) { + $clone = $node->cloneNode(); + $s_pos = array_search($node, $this->stack, true); + $a_pos = array_search($node, $this->a_formatting, true); + + $this->stack[$s_pos] = $clone; + $this->a_formatting[$a_pos] = $clone; + $node = $clone; + } + + /* 7.6 Insert last node into node, first removing + it from its previous parent node if any. */ + if ($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $node->appendChild($last_node); + + /* 7.7 Let last node be node. */ + $last_node = $node; + } + + /* 8. Insert whatever last node ended up being in + the previous step into the common ancestor node, + first removing it from its previous parent node if + any. */ + if ($last_node->parentNode !== null) { + $last_node->parentNode->removeChild($last_node); + } + + $common_ancestor->appendChild($last_node); + + /* 9. Perform a shallow clone of the formatting + element. */ + $clone = $formatting_element->cloneNode(); + + /* 10. Take all of the child nodes of the furthest + block and append them to the clone created in the + last step. */ + while ($furthest_block->hasChildNodes()) { + $child = $furthest_block->firstChild; + $furthest_block->removeChild($child); + $clone->appendChild($child); + } + + /* 11. Append that clone to the furthest block. */ + $furthest_block->appendChild($clone); + + /* 12. Remove the formatting element from the list + of active formatting elements, and insert the clone + into the list of active formatting elements at the + position of the aforementioned bookmark. */ + $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); + unset($this->a_formatting[$fe_af_pos]); + $this->a_formatting = array_merge($this->a_formatting); + + $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); + $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); + $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); + + /* 13. Remove the formatting element from the stack + of open elements, and insert the clone into the stack + of open elements immediately after (i.e. in a more + deeply nested position than) the position of the + furthest block in that stack. */ + $fe_s_pos = array_search($formatting_element, $this->stack, true); + $fb_s_pos = array_search($furthest_block, $this->stack, true); + unset($this->stack[$fe_s_pos]); + + $s_part1 = array_slice($this->stack, 0, $fb_s_pos); + $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); + $this->stack = array_merge($s_part1, array($clone), $s_part2); + + /* 14. Jump back to step 1 in this series of steps. */ + unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); + } + break; + + /* An end tag token whose tag name is one of: "button", + "marquee", "object" */ + case 'button': + case 'marquee': + case 'object': + /* If the stack of open elements has an element in scope whose + tag name matches the tag name of the token, then generate implied + tags. */ + if ($this->elementInScope($token['name'])) { + $this->generateImpliedEndTags(); + + /* Now, if the current node is not an element with the same + tag name as the token, then this is a parse error. */ + // k + + /* Now, if the stack of open elements has an element in scope + whose tag name matches the tag name of the token, then pop + elements from the stack until that element has been popped from + the stack, and clear the list of active formatting elements up + to the last marker. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === $token['name']) { + $n = -1; + } + + array_pop($this->stack); + } + + $marker = end(array_keys($this->a_formatting, self::MARKER, true)); + + for ($n = count($this->a_formatting) - 1; $n > $marker; $n--) { + array_pop($this->a_formatting); + } + } + break; + + /* Or an end tag whose tag name is one of: "area", "basefont", + "bgsound", "br", "embed", "hr", "iframe", "image", "img", + "input", "isindex", "noembed", "noframes", "param", "select", + "spacer", "table", "textarea", "wbr" */ + case 'area': + case 'basefont': + case 'bgsound': + case 'br': + case 'embed': + case 'hr': + case 'iframe': + case 'image': + case 'img': + case 'input': + case 'isindex': + case 'noembed': + case 'noframes': + case 'param': + case 'select': + case 'spacer': + case 'table': + case 'textarea': + case 'wbr': + // Parse error. Ignore the token. + break; + + /* An end tag token not covered by the previous entries */ + default: + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + /* Initialise node to be the current node (the bottommost + node of the stack). */ + $node = end($this->stack); + + /* If node has the same tag name as the end tag token, + then: */ + if ($token['name'] === $node->nodeName) { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* If the tag name of the end tag token does not + match the tag name of the current node, this is a + parse error. */ + // k + + /* Pop all the nodes from the current node up to + node, including node, then stop this algorithm. */ + for ($x = count($this->stack) - $n; $x >= $n; $x--) { + array_pop($this->stack); + } + + } else { + $category = $this->getElementCategory($node); + + if ($category !== self::SPECIAL && $category !== self::SCOPING) { + /* Otherwise, if node is in neither the formatting + category nor the phrasing category, then this is a + parse error. Stop this algorithm. The end tag token + is ignored. */ + return false; + } + } + } + break; + } + break; + } + } + + private function inTable($token) + { + $clear = array('html', 'table'); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "caption" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'caption' + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert a marker at the end of the list of active + formatting elements. */ + $this->a_formatting[] = self::MARKER; + + /* Insert an HTML element for the token, then switch the + insertion mode to "in caption". */ + $this->insertElement($token); + $this->mode = self::IN_CAPTION; + + /* A start tag whose tag name is "colgroup" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'colgroup' + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the + insertion mode to "in column group". */ + $this->insertElement($token); + $this->mode = self::IN_CGROUP; + + /* A start tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'col' + ) { + $this->inTable( + array( + 'name' => 'colgroup', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + $this->inColumnGroup($token); + + /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('tbody', 'tfoot', 'thead') + ) + ) { + /* Clear the stack back to a table context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in table body". */ + $this->insertElement($token); + $this->mode = self::IN_TBODY; + + /* A start tag whose tag name is one of: "td", "th", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && + in_array($token['name'], array('td', 'th', 'tr')) + ) { + /* Act as if a start tag token with the tag name "tbody" had been + seen, then reprocess the current token. */ + $this->inTable( + array( + 'name' => 'tbody', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inTableBody($token); + + /* A start tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'table' + ) { + /* Parse error. Act as if an end tag token with the tag name "table" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inTable( + array( + 'name' => 'table', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->mainPhase($token); + + /* An end tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table' + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + return false; + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a table element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a table element has been + popped from the stack. */ + while (true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($current === 'table') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array( + 'body', + 'caption', + 'col', + 'colgroup', + 'html', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Parse error. Process the token as if the insertion mode was "in + body", with the following exception: */ + + /* If the current node is a table, tbody, tfoot, thead, or tr + element, then, whenever a node would be inserted into the current + node, it must instead be inserted into the foster parent element. */ + if (in_array( + end($this->stack)->nodeName, + array('table', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* The foster parent element is the parent element of the last + table element in the stack of open elements, if there is a + table element and it has such a parent element. If there is no + table element in the stack of open elements (innerHTML case), + then the foster parent element is the first element in the + stack of open elements (the html element). Otherwise, if there + is a table element in the stack of open elements, but the last + table element in the stack of open elements has no parent, or + its parent node is not an element, then the foster parent + element is the element before the last table element in the + stack of open elements. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === 'table') { + $table = $this->stack[$n]; + break; + } + } + + if (isset($table) && $table->parentNode !== null) { + $this->foster_parent = $table->parentNode; + + } elseif (!isset($table)) { + $this->foster_parent = $this->stack[0]; + + } elseif (isset($table) && ($table->parentNode === null || + $table->parentNode->nodeType !== XML_ELEMENT_NODE) + ) { + $this->foster_parent = $this->stack[$n - 1]; + } + } + + $this->inBody($token); + } + } + + private function inCaption($token) + { + /* An end tag whose tag name is "caption" */ + if ($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Generate implied end tags. */ + $this->generateImpliedEndTags(); + + /* Now, if the current node is not a caption element, then this + is a parse error. */ + // w/e + + /* Pop elements from this stack until a caption element has + been popped from the stack. */ + while (true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($node === 'caption') { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in table". */ + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag + name is "table" */ + } elseif (($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + )) || ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'table') + ) { + /* Parse error. Act as if an end tag with the tag name "caption" + had been seen, then, if that token wasn't ignored, reprocess the + current token. */ + $this->inCaption( + array( + 'name' => 'caption', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inTable($token); + + /* An end tag whose tag name is one of: "body", "col", "colgroup", + "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array( + 'body', + 'col', + 'colgroup', + 'html', + 'tbody', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + // Parse error. Ignore the token. + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inColumnGroup($token) + { + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $text = $this->dom->createTextNode($token['data']); + end($this->stack)->appendChild($text); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + end($this->stack)->appendChild($comment); + + /* A start tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { + /* Insert a col element for the token. Immediately pop the current + node off the stack of open elements. */ + $this->insertElement($token); + array_pop($this->stack); + + /* An end tag whose tag name is "colgroup" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'colgroup' + ) { + /* If the current node is the root html element, then this is a + parse error, ignore the token. (innerHTML case) */ + if (end($this->stack)->nodeName === 'html') { + // Ignore + + /* Otherwise, pop the current node (which will be a colgroup + element) from the stack of open elements. Switch the insertion + mode to "in table". */ + } else { + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* An end tag whose tag name is "col" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Act as if an end tag with the tag name "colgroup" had been seen, + and then, if that token wasn't ignored, reprocess the current token. */ + $this->inColumnGroup( + array( + 'name' => 'colgroup', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inTable($token); + } + } + + private function inTableBody($token) + { + $clear = array('tbody', 'tfoot', 'thead', 'html'); + + /* A start tag whose tag name is "tr" */ + if ($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Insert a tr element for the token, then switch the insertion + mode to "in row". */ + $this->insertElement($token); + $this->mode = self::IN_ROW; + + /* A start tag whose tag name is one of: "th", "td" */ + } elseif ($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td') + ) { + /* Parse error. Act as if a start tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inTableBody( + array( + 'name' => 'tr', + 'type' => HTML5::STARTTAG, + 'attr' => array() + ) + ); + + return $this->inRow($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead')) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node from the stack of open elements. Switch + the insertion mode to "in table". */ + array_pop($this->stack); + $this->mode = self::IN_TABLE; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ + } elseif (($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead') + )) || + ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table') + ) { + /* If the stack of open elements does not have a tbody, thead, or + tfoot element in table scope, this is a parse error. Ignore the + token. (innerHTML case) */ + if (!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table body context. */ + $this->clearStackToTableContext($clear); + + /* Act as if an end tag with the same tag name as the current + node ("tbody", "tfoot", or "thead") had been seen, then + reprocess the current token. */ + $this->inTableBody( + array( + 'name' => end($this->stack)->nodeName, + 'type' => HTML5::ENDTAG + ) + ); + + return $this->mainPhase($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') + ) + ) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inRow($token) + { + $clear = array('tr', 'html'); + + /* A start tag whose tag name is one of: "th", "td" */ + if ($token['type'] === HTML5::STARTTAG && + ($token['name'] === 'th' || $token['name'] === 'td') + ) { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Insert an HTML element for the token, then switch the insertion + mode to "in cell". */ + $this->insertElement($token); + $this->mode = self::IN_CELL; + + /* Insert a marker at the end of the list of active formatting + elements. */ + $this->a_formatting[] = self::MARKER; + + /* An end tag whose tag name is "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Clear the stack back to a table row context. */ + $this->clearStackToTableContext($clear); + + /* Pop the current node (which will be a tr element) from the + stack of open elements. Switch the insertion mode to "in table + body". */ + array_pop($this->stack); + $this->mode = self::IN_TBODY; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* Act as if an end tag with the tag name "tr" had been seen, then, + if that token wasn't ignored, reprocess the current token. */ + $this->inRow( + array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inCell($token); + + /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ + } elseif ($token['type'] === HTML5::ENDTAG && + in_array($token['name'], array('tbody', 'tfoot', 'thead')) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Otherwise, act as if an end tag with the tag name "tr" had + been seen, then reprocess the current token. */ + $this->inRow( + array( + 'name' => 'tr', + 'type' => HTML5::ENDTAG + ) + ); + + return $this->inCell($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html", "td", "th" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr') + ) + ) { + /* Parse error. Ignore the token. */ + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in table". */ + $this->inTable($token); + } + } + + private function inCell($token) + { + /* An end tag whose tag name is one of: "td", "th" */ + if ($token['type'] === HTML5::ENDTAG && + ($token['name'] === 'td' || $token['name'] === 'th') + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token, then this is a + parse error and the token must be ignored. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise: */ + } else { + /* Generate implied end tags, except for elements with the same + tag name as the token. */ + $this->generateImpliedEndTags(array($token['name'])); + + /* Now, if the current node is not an element with the same tag + name as the token, then this is a parse error. */ + // k + + /* Pop elements from this stack until an element with the same + tag name as the token has been popped from the stack. */ + while (true) { + $node = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($node === $token['name']) { + break; + } + } + + /* Clear the list of active formatting elements up to the last + marker. */ + $this->clearTheActiveFormattingElementsUpToTheLastMarker(); + + /* Switch the insertion mode to "in row". (The current node + will be a tr element at this point.) */ + $this->mode = self::IN_ROW; + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if (!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* A start tag whose tag name is one of: "caption", "col", "colgroup", + "tbody", "td", "tfoot", "th", "thead", "tr" */ + } elseif ($token['type'] === HTML5::STARTTAG && in_array( + $token['name'], + array( + 'caption', + 'col', + 'colgroup', + 'tbody', + 'td', + 'tfoot', + 'th', + 'thead', + 'tr' + ) + ) + ) { + /* If the stack of open elements does not have a td or th element + in table scope, then this is a parse error; ignore the token. + (innerHTML case) */ + if (!$this->elementInScope(array('td', 'th'), true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* An end tag whose tag name is one of: "body", "caption", "col", + "colgroup", "html" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('body', 'caption', 'col', 'colgroup', 'html') + ) + ) { + /* Parse error. Ignore the token. */ + + /* An end tag whose tag name is one of: "table", "tbody", "tfoot", + "thead", "tr" */ + } elseif ($token['type'] === HTML5::ENDTAG && in_array( + $token['name'], + array('table', 'tbody', 'tfoot', 'thead', 'tr') + ) + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as that of the token (which can only + happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), + then this is a parse error and the token must be ignored. */ + if (!$this->elementInScope($token['name'], true)) { + // Ignore. + + /* Otherwise, close the cell (see below) and reprocess the current + token. */ + } else { + $this->closeCell(); + return $this->inRow($token); + } + + /* Anything else */ + } else { + /* Process the token as if the insertion mode was "in body". */ + $this->inBody($token); + } + } + + private function inSelect($token) + { + /* Handle the token as follows: */ + + /* A character token */ + if ($token['type'] === HTML5::CHARACTR) { + /* Append the token's character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag token whose tag name is "option" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'option' + ) { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if (end($this->stack)->nodeName === 'option') { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* A start tag token whose tag name is "optgroup" */ + } elseif ($token['type'] === HTML5::STARTTAG && + $token['name'] === 'optgroup' + ) { + /* If the current node is an option element, act as if an end tag + with the tag name "option" had been seen. */ + if (end($this->stack)->nodeName === 'option') { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the current node is an optgroup element, act as if an end tag + with the tag name "optgroup" had been seen. */ + if (end($this->stack)->nodeName === 'optgroup') { + $this->inSelect( + array( + 'name' => 'optgroup', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* An end tag token whose tag name is "optgroup" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'optgroup' + ) { + /* First, if the current node is an option element, and the node + immediately before it in the stack of open elements is an optgroup + element, then act as if an end tag with the tag name "option" had + been seen. */ + $elements_in_stack = count($this->stack); + + if ($this->stack[$elements_in_stack - 1]->nodeName === 'option' && + $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup' + ) { + $this->inSelect( + array( + 'name' => 'option', + 'type' => HTML5::ENDTAG + ) + ); + } + + /* If the current node is an optgroup element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if ($this->stack[$elements_in_stack - 1] === 'optgroup') { + array_pop($this->stack); + } + + /* An end tag token whose tag name is "option" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'option' + ) { + /* If the current node is an option element, then pop that node + from the stack of open elements. Otherwise, this is a parse error, + ignore the token. */ + if (end($this->stack)->nodeName === 'option') { + array_pop($this->stack); + } + + /* An end tag whose tag name is "select" */ + } elseif ($token['type'] === HTML5::ENDTAG && + $token['name'] === 'select' + ) { + /* If the stack of open elements does not have an element in table + scope with the same tag name as the token, this is a parse error. + Ignore the token. (innerHTML case) */ + if (!$this->elementInScope($token['name'], true)) { + // w/e + + /* Otherwise: */ + } else { + /* Pop elements from the stack of open elements until a select + element has been popped from the stack. */ + while (true) { + $current = end($this->stack)->nodeName; + array_pop($this->stack); + + if ($current === 'select') { + break; + } + } + + /* Reset the insertion mode appropriately. */ + $this->resetInsertionMode(); + } + + /* A start tag whose tag name is "select" */ + } elseif ($token['name'] === 'select' && + $token['type'] === HTML5::STARTTAG + ) { + /* Parse error. Act as if the token had been an end tag with the + tag name "select" instead. */ + $this->inSelect( + array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + ) + ); + + /* An end tag whose tag name is one of: "caption", "table", "tbody", + "tfoot", "thead", "tr", "td", "th" */ + } elseif (in_array( + $token['name'], + array( + 'caption', + 'table', + 'tbody', + 'tfoot', + 'thead', + 'tr', + 'td', + 'th' + ) + ) && $token['type'] === HTML5::ENDTAG + ) { + /* Parse error. */ + // w/e + + /* If the stack of open elements has an element in table scope with + the same tag name as that of the token, then act as if an end tag + with the tag name "select" had been seen, and reprocess the token. + Otherwise, ignore the token. */ + if ($this->elementInScope($token['name'], true)) { + $this->inSelect( + array( + 'name' => 'select', + 'type' => HTML5::ENDTAG + ) + ); + + $this->mainPhase($token); + } + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterBody($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Process the token as it would be processed if the insertion mode + was "in body". */ + $this->inBody($token); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the first element in the stack of open + elements (the html element), with the data attribute set to the + data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->stack[0]->appendChild($comment); + + /* An end tag with the tag name "html" */ + } elseif ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { + /* If the parser was originally created in order to handle the + setting of an element's innerHTML attribute, this is a parse error; + ignore the token. (The element will be an html element in this + case.) (innerHTML case) */ + + /* Otherwise, switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* Anything else */ + } else { + /* Parse error. Set the insertion mode to "in body" and reprocess + the token. */ + $this->mode = self::IN_BODY; + return $this->inBody($token); + } + } + + private function inFrameset($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* A start tag with the tag name "frameset" */ + } elseif ($token['name'] === 'frameset' && + $token['type'] === HTML5::STARTTAG + ) { + $this->insertElement($token); + + /* An end tag with the tag name "frameset" */ + } elseif ($token['name'] === 'frameset' && + $token['type'] === HTML5::ENDTAG + ) { + /* If the current node is the root html element, then this is a + parse error; ignore the token. (innerHTML case) */ + if (end($this->stack)->nodeName === 'html') { + // Ignore + + } else { + /* Otherwise, pop the current node from the stack of open + elements. */ + array_pop($this->stack); + + /* If the parser was not originally created in order to handle + the setting of an element's innerHTML attribute (innerHTML case), + and the current node is no longer a frameset element, then change + the insertion mode to "after frameset". */ + $this->mode = self::AFTR_FRAME; + } + + /* A start tag with the tag name "frame" */ + } elseif ($token['name'] === 'frame' && + $token['type'] === HTML5::STARTTAG + ) { + /* Insert an HTML element for the token. */ + $this->insertElement($token); + + /* Immediately pop the current node off the stack of open elements. */ + array_pop($this->stack); + + /* A start tag with the tag name "noframes" */ + } elseif ($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG + ) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function afterFrameset($token) + { + /* Handle the token as follows: */ + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ + if ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Append the character to the current node. */ + $this->insertText($token['data']); + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the current node with the data + attribute set to the data given in the comment token. */ + $this->insertComment($token['data']); + + /* An end tag with the tag name "html" */ + } elseif ($token['name'] === 'html' && + $token['type'] === HTML5::ENDTAG + ) { + /* Switch to the trailing end phase. */ + $this->phase = self::END_PHASE; + + /* A start tag with the tag name "noframes" */ + } elseif ($token['name'] === 'noframes' && + $token['type'] === HTML5::STARTTAG + ) { + /* Process the token as if the insertion mode had been "in body". */ + $this->inBody($token); + + /* Anything else */ + } else { + /* Parse error. Ignore the token. */ + } + } + + private function trailingEndPhase($token) + { + /* After the main phase, as each token is emitted from the tokenisation + stage, it must be processed as described in this section. */ + + /* A DOCTYPE token */ + if ($token['type'] === HTML5::DOCTYPE) { + // Parse error. Ignore the token. + + /* A comment token */ + } elseif ($token['type'] === HTML5::COMMENT) { + /* Append a Comment node to the Document object with the data + attribute set to the data given in the comment token. */ + $comment = $this->dom->createComment($token['data']); + $this->dom->appendChild($comment); + + /* A character token that is one of one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE */ + } elseif ($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']) + ) { + /* Process the token as it would be processed in the main phase. */ + $this->mainPhase($token); + + /* A character token that is not one of U+0009 CHARACTER TABULATION, + U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), + or U+0020 SPACE. Or a start tag token. Or an end tag token. */ + } elseif (($token['type'] === HTML5::CHARACTR && + preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || + $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG + ) { + /* Parse error. Switch back to the main phase and reprocess the + token. */ + $this->phase = self::MAIN_PHASE; + return $this->mainPhase($token); + + /* An end-of-file token */ + } elseif ($token['type'] === HTML5::EOF) { + /* OMG DONE!! */ + } + } + + private function insertElement($token, $append = true, $check = false) + { + // Proprietary workaround for libxml2's limitations with tag names + if ($check) { + // Slightly modified HTML5 tag-name modification, + // removing anything that's not an ASCII letter, digit, or hyphen + $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); + // Remove leading hyphens and numbers + $token['name'] = ltrim($token['name'], '-0..9'); + // In theory, this should ever be needed, but just in case + if ($token['name'] === '') { + $token['name'] = 'span'; + } // arbitrary generic choice + } + + $el = $this->dom->createElement($token['name']); + + foreach ($token['attr'] as $attr) { + if (!$el->hasAttribute($attr['name'])) { + $el->setAttribute($attr['name'], $attr['value']); + } + } + + $this->appendToRealParent($el); + $this->stack[] = $el; + + return $el; + } + + private function insertText($data) + { + $text = $this->dom->createTextNode($data); + $this->appendToRealParent($text); + } + + private function insertComment($data) + { + $comment = $this->dom->createComment($data); + $this->appendToRealParent($comment); + } + + private function appendToRealParent($node) + { + if ($this->foster_parent === null) { + end($this->stack)->appendChild($node); + + } elseif ($this->foster_parent !== null) { + /* If the foster parent element is the parent element of the + last table element in the stack of open elements, then the new + node must be inserted immediately before the last table element + in the stack of open elements in the foster parent element; + otherwise, the new node must be appended to the foster parent + element. */ + for ($n = count($this->stack) - 1; $n >= 0; $n--) { + if ($this->stack[$n]->nodeName === 'table' && + $this->stack[$n]->parentNode !== null + ) { + $table = $this->stack[$n]; + break; + } + } + + if (isset($table) && $this->foster_parent->isSameNode($table->parentNode)) { + $this->foster_parent->insertBefore($node, $table); + } else { + $this->foster_parent->appendChild($node); + } + + $this->foster_parent = null; + } + } + + private function elementInScope($el, $table = false) + { + if (is_array($el)) { + foreach ($el as $element) { + if ($this->elementInScope($element, $table)) { + return true; + } + } + + return false; + } + + $leng = count($this->stack); + + for ($n = 0; $n < $leng; $n++) { + /* 1. Initialise node to be the current node (the bottommost node of + the stack). */ + $node = $this->stack[$leng - 1 - $n]; + + if ($node->tagName === $el) { + /* 2. If node is the target node, terminate in a match state. */ + return true; + + } elseif ($node->tagName === 'table') { + /* 3. Otherwise, if node is a table element, terminate in a failure + state. */ + return false; + + } elseif ($table === true && in_array( + $node->tagName, + array( + 'caption', + 'td', + 'th', + 'button', + 'marquee', + 'object' + ) + ) + ) { + /* 4. Otherwise, if the algorithm is the "has an element in scope" + variant (rather than the "has an element in table scope" variant), + and node is one of the following, terminate in a failure state. */ + return false; + + } elseif ($node === $node->ownerDocument->documentElement) { + /* 5. Otherwise, if node is an html element (root element), terminate + in a failure state. (This can only happen if the node is the topmost + node of the stack of open elements, and prevents the next step from + being invoked if there are no more elements in the stack.) */ + return false; + } + + /* Otherwise, set node to the previous entry in the stack of open + elements and return to step 2. (This will never fail, since the loop + will always terminate in the previous step if the top of the stack + is reached.) */ + } + } + + private function reconstructActiveFormattingElements() + { + /* 1. If there are no entries in the list of active formatting elements, + then there is nothing to reconstruct; stop this algorithm. */ + $formatting_elements = count($this->a_formatting); + + if ($formatting_elements === 0) { + return false; + } + + /* 3. Let entry be the last (most recently added) element in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. If the last (most recently added) entry in the list of active + formatting elements is a marker, or if it is an element that is in the + stack of open elements, then there is nothing to reconstruct; stop this + algorithm. */ + if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { + return false; + } + + for ($a = $formatting_elements - 1; $a >= 0; true) { + /* 4. If there are no entries before entry in the list of active + formatting elements, then jump to step 8. */ + if ($a === 0) { + $step_seven = false; + break; + } + + /* 5. Let entry be the entry one earlier than entry in the list of + active formatting elements. */ + $a--; + $entry = $this->a_formatting[$a]; + + /* 6. If entry is neither a marker nor an element that is also in + thetack of open elements, go to step 4. */ + if ($entry === self::MARKER || in_array($entry, $this->stack, true)) { + break; + } + } + + while (true) { + /* 7. Let entry be the element one later than entry in the list of + active formatting elements. */ + if (isset($step_seven) && $step_seven === true) { + $a++; + $entry = $this->a_formatting[$a]; + } + + /* 8. Perform a shallow clone of the element entry to obtain clone. */ + $clone = $entry->cloneNode(); + + /* 9. Append clone to the current node and push it onto the stack + of open elements so that it is the new current node. */ + end($this->stack)->appendChild($clone); + $this->stack[] = $clone; + + /* 10. Replace the entry for entry in the list with an entry for + clone. */ + $this->a_formatting[$a] = $clone; + + /* 11. If the entry for clone in the list of active formatting + elements is not the last entry in the list, return to step 7. */ + if (end($this->a_formatting) !== $clone) { + $step_seven = true; + } else { + break; + } + } + } + + private function clearTheActiveFormattingElementsUpToTheLastMarker() + { + /* When the steps below require the UA to clear the list of active + formatting elements up to the last marker, the UA must perform the + following steps: */ + + while (true) { + /* 1. Let entry be the last (most recently added) entry in the list + of active formatting elements. */ + $entry = end($this->a_formatting); + + /* 2. Remove entry from the list of active formatting elements. */ + array_pop($this->a_formatting); + + /* 3. If entry was a marker, then stop the algorithm at this point. + The list has been cleared up to the last marker. */ + if ($entry === self::MARKER) { + break; + } + } + } + + private function generateImpliedEndTags($exclude = array()) + { + /* When the steps below require the UA to generate implied end tags, + then, if the current node is a dd element, a dt element, an li element, + a p element, a td element, a th element, or a tr element, the UA must + act as if an end tag with the respective tag name had been seen and + then generate implied end tags again. */ + $node = end($this->stack); + $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); + + while (in_array(end($this->stack)->nodeName, $elements)) { + array_pop($this->stack); + } + } + + private function getElementCategory($node) + { + $name = $node->tagName; + if (in_array($name, $this->special)) { + return self::SPECIAL; + } elseif (in_array($name, $this->scoping)) { + return self::SCOPING; + } elseif (in_array($name, $this->formatting)) { + return self::FORMATTING; + } else { + return self::PHRASING; + } + } + + private function clearStackToTableContext($elements) + { + /* When the steps above require the UA to clear the stack back to a + table context, it means that the UA must, while the current node is not + a table element or an html element, pop elements from the stack of open + elements. If this causes any elements to be popped from the stack, then + this is a parse error. */ + while (true) { + $node = end($this->stack)->nodeName; + + if (in_array($node, $elements)) { + break; + } else { + array_pop($this->stack); + } + } + } + + private function resetInsertionMode() + { + /* 1. Let last be false. */ + $last = false; + $leng = count($this->stack); + + for ($n = $leng - 1; $n >= 0; $n--) { + /* 2. Let node be the last node in the stack of open elements. */ + $node = $this->stack[$n]; + + /* 3. If node is the first node in the stack of open elements, then + set last to true. If the element whose innerHTML attribute is being + set is neither a td element nor a th element, then set node to the + element whose innerHTML attribute is being set. (innerHTML case) */ + if ($this->stack[0]->isSameNode($node)) { + $last = true; + } + + /* 4. If node is a select element, then switch the insertion mode to + "in select" and abort these steps. (innerHTML case) */ + if ($node->nodeName === 'select') { + $this->mode = self::IN_SELECT; + break; + + /* 5. If node is a td or th element, then switch the insertion mode + to "in cell" and abort these steps. */ + } elseif ($node->nodeName === 'td' || $node->nodeName === 'th') { + $this->mode = self::IN_CELL; + break; + + /* 6. If node is a tr element, then switch the insertion mode to + "in row" and abort these steps. */ + } elseif ($node->nodeName === 'tr') { + $this->mode = self::IN_ROW; + break; + + /* 7. If node is a tbody, thead, or tfoot element, then switch the + insertion mode to "in table body" and abort these steps. */ + } elseif (in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { + $this->mode = self::IN_TBODY; + break; + + /* 8. If node is a caption element, then switch the insertion mode + to "in caption" and abort these steps. */ + } elseif ($node->nodeName === 'caption') { + $this->mode = self::IN_CAPTION; + break; + + /* 9. If node is a colgroup element, then switch the insertion mode + to "in column group" and abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'colgroup') { + $this->mode = self::IN_CGROUP; + break; + + /* 10. If node is a table element, then switch the insertion mode + to "in table" and abort these steps. */ + } elseif ($node->nodeName === 'table') { + $this->mode = self::IN_TABLE; + break; + + /* 11. If node is a head element, then switch the insertion mode + to "in body" ("in body"! not "in head"!) and abort these steps. + (innerHTML case) */ + } elseif ($node->nodeName === 'head') { + $this->mode = self::IN_BODY; + break; + + /* 12. If node is a body element, then switch the insertion mode to + "in body" and abort these steps. */ + } elseif ($node->nodeName === 'body') { + $this->mode = self::IN_BODY; + break; + + /* 13. If node is a frameset element, then switch the insertion + mode to "in frameset" and abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'frameset') { + $this->mode = self::IN_FRAME; + break; + + /* 14. If node is an html element, then: if the head element + pointer is null, switch the insertion mode to "before head", + otherwise, switch the insertion mode to "after head". In either + case, abort these steps. (innerHTML case) */ + } elseif ($node->nodeName === 'html') { + $this->mode = ($this->head_pointer === null) + ? self::BEFOR_HEAD + : self::AFTER_HEAD; + + break; + + /* 15. If last is true, then set the insertion mode to "in body" + and abort these steps. (innerHTML case) */ + } elseif ($last) { + $this->mode = self::IN_BODY; + break; + } + } + } + + private function closeCell() + { + /* If the stack of open elements has a td or th element in table scope, + then act as if an end tag token with that tag name had been seen. */ + foreach (array('td', 'th') as $cell) { + if ($this->elementInScope($cell, true)) { + $this->inCell( + array( + 'name' => $cell, + 'type' => HTML5::ENDTAG + ) + ); + + break; + } + } + } + + public function save() + { + return $this->dom; + } +} diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php new file mode 100644 index 0000000000..3995fec9fe --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node.php @@ -0,0 +1,49 @@ +data = $data; + $this->line = $line; + $this->col = $col; + } + + public function toTokenPair() { + return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null); + } +} diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php new file mode 100644 index 0000000000..6cbf56dada --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Element.php @@ -0,0 +1,59 @@ + form or the form, i.e. + * is it a pair of start/end tokens or an empty token. + * @bool + */ + public $empty = false; + + public $endCol = null, $endLine = null, $endArmor = array(); + + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { + $this->name = $name; + $this->attr = $attr; + $this->line = $line; + $this->col = $col; + $this->armor = $armor; + } + + public function toTokenPair() { + // XXX inefficiency here, normalization is not necessary + if ($this->empty) { + return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null); + } else { + $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor); + $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor); + //$end->start = $start; + return array($start, $end); + } + } +} + diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php new file mode 100644 index 0000000000..aec9166473 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Node/Text.php @@ -0,0 +1,54 @@ +data = $data; + $this->is_whitespace = $is_whitespace; + $this->line = $line; + $this->col = $col; + } + + public function toTokenPair() { + return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/PercentEncoder.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php similarity index 78% rename from library/HTMLPurifier/PercentEncoder.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php index a43c44f4c5..18c8bbb00a 100644 --- a/library/HTMLPurifier/PercentEncoder.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/PercentEncoder.php @@ -13,17 +13,26 @@ class HTMLPurifier_PercentEncoder /** * Reserved characters to preserve when using encode(). + * @type array */ protected $preserve = array(); /** * String of characters that should be preserved while using encode(). + * @param bool $preserve */ - public function __construct($preserve = false) { + public function __construct($preserve = false) + { // unreserved letters, ought to const-ify - for ($i = 48; $i <= 57; $i++) $this->preserve[$i] = true; // digits - for ($i = 65; $i <= 90; $i++) $this->preserve[$i] = true; // upper-case - for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case + for ($i = 48; $i <= 57; $i++) { // digits + $this->preserve[$i] = true; + } + for ($i = 65; $i <= 90; $i++) { // upper-case + $this->preserve[$i] = true; + } + for ($i = 97; $i <= 122; $i++) { // lower-case + $this->preserve[$i] = true; + } $this->preserve[45] = true; // Dash - $this->preserve[46] = true; // Period . $this->preserve[95] = true; // Underscore _ @@ -44,13 +53,14 @@ class HTMLPurifier_PercentEncoder * Assumes that the string has already been normalized, making any * and all percent escape sequences valid. Percents will not be * re-escaped, regardless of their status in $preserve - * @param $string String to be encoded - * @return Encoded string. + * @param string $string String to be encoded + * @return string Encoded string. */ - public function encode($string) { + public function encode($string) + { $ret = ''; for ($i = 0, $c = strlen($string); $i < $c; $i++) { - if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) { + if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])])) { $ret .= '%' . sprintf('%02X', $int); } else { $ret .= $string[$i]; @@ -64,10 +74,14 @@ class HTMLPurifier_PercentEncoder * @warning This function is affected by $preserve, even though the * usual desired behavior is for this not to preserve those * characters. Be careful when reusing instances of PercentEncoder! - * @param $string String to normalize + * @param string $string String to normalize + * @return string */ - public function normalize($string) { - if ($string == '') return ''; + public function normalize($string) + { + if ($string == '') { + return ''; + } $parts = explode('%', $string); $ret = array_shift($parts); foreach ($parts as $part) { @@ -92,7 +106,6 @@ class HTMLPurifier_PercentEncoder } return $ret; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Printer.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php similarity index 54% rename from library/HTMLPurifier/Printer.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php index e7eb82e83e..549e4cea1e 100644 --- a/library/HTMLPurifier/Printer.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer.php @@ -7,25 +7,30 @@ class HTMLPurifier_Printer { /** - * Instance of HTMLPurifier_Generator for HTML generation convenience funcs + * For HTML generation convenience funcs. + * @type HTMLPurifier_Generator */ protected $generator; /** - * Instance of HTMLPurifier_Config, for easy access + * For easy access. + * @type HTMLPurifier_Config */ protected $config; /** * Initialize $generator. */ - public function __construct() { + public function __construct() + { } /** * Give generator necessary configuration if possible + * @param HTMLPurifier_Config $config */ - public function prepareGenerator($config) { + public function prepareGenerator($config) + { $all = $config->getAll(); $context = new HTMLPurifier_Context(); $this->generator = new HTMLPurifier_Generator($config, $context); @@ -39,45 +44,62 @@ class HTMLPurifier_Printer /** * Returns a start tag - * @param $tag Tag name - * @param $attr Attribute array + * @param string $tag Tag name + * @param array $attr Attribute array + * @return string */ - protected function start($tag, $attr = array()) { + protected function start($tag, $attr = array()) + { return $this->generator->generateFromToken( - new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) - ); + new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) + ); } /** - * Returns an end teg - * @param $tag Tag name + * Returns an end tag + * @param string $tag Tag name + * @return string */ - protected function end($tag) { + protected function end($tag) + { return $this->generator->generateFromToken( - new HTMLPurifier_Token_End($tag) - ); + new HTMLPurifier_Token_End($tag) + ); } /** * Prints a complete element with content inside - * @param $tag Tag name - * @param $contents Element contents - * @param $attr Tag attributes - * @param $escape Bool whether or not to escape contents + * @param string $tag Tag name + * @param string $contents Element contents + * @param array $attr Tag attributes + * @param bool $escape whether or not to escape contents + * @return string */ - protected function element($tag, $contents, $attr = array(), $escape = true) { + protected function element($tag, $contents, $attr = array(), $escape = true) + { return $this->start($tag, $attr) . - ($escape ? $this->escape($contents) : $contents) . - $this->end($tag); + ($escape ? $this->escape($contents) : $contents) . + $this->end($tag); } - protected function elementEmpty($tag, $attr = array()) { + /** + * @param string $tag + * @param array $attr + * @return string + */ + protected function elementEmpty($tag, $attr = array()) + { return $this->generator->generateFromToken( new HTMLPurifier_Token_Empty($tag, $attr) ); } - protected function text($text) { + /** + * @param string $text + * @return string + */ + protected function text($text) + { return $this->generator->generateFromToken( new HTMLPurifier_Token_Text($text) ); @@ -85,24 +107,29 @@ class HTMLPurifier_Printer /** * Prints a simple key/value row in a table. - * @param $name Key - * @param $value Value + * @param string $name Key + * @param mixed $value Value + * @return string */ - protected function row($name, $value) { - if (is_bool($value)) $value = $value ? 'On' : 'Off'; + protected function row($name, $value) + { + if (is_bool($value)) { + $value = $value ? 'On' : 'Off'; + } return $this->start('tr') . "\n" . - $this->element('th', $name) . "\n" . - $this->element('td', $value) . "\n" . - $this->end('tr') - ; + $this->element('th', $name) . "\n" . + $this->element('td', $value) . "\n" . + $this->end('tr'); } /** * Escapes a string for HTML output. - * @param $string String to escape + * @param string $string String to escape + * @return string */ - protected function escape($string) { + protected function escape($string) + { $string = HTMLPurifier_Encoder::cleanUTF8($string); $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); return $string; @@ -110,32 +137,46 @@ class HTMLPurifier_Printer /** * Takes a list of strings and turns them into a single list - * @param $array List of strings - * @param $polite Bool whether or not to add an end before the last + * @param string[] $array List of strings + * @param bool $polite Bool whether or not to add an end before the last + * @return string */ - protected function listify($array, $polite = false) { - if (empty($array)) return 'None'; + protected function listify($array, $polite = false) + { + if (empty($array)) { + return 'None'; + } $ret = ''; $i = count($array); foreach ($array as $value) { $i--; $ret .= $value; - if ($i > 0 && !($polite && $i == 1)) $ret .= ', '; - if ($polite && $i == 1) $ret .= 'and '; + if ($i > 0 && !($polite && $i == 1)) { + $ret .= ', '; + } + if ($polite && $i == 1) { + $ret .= 'and '; + } } return $ret; } /** * Retrieves the class of an object without prefixes, as well as metadata - * @param $obj Object to determine class of - * @param $prefix Further prefix to remove + * @param object $obj Object to determine class of + * @param string $sec_prefix Further prefix to remove + * @return string */ - protected function getClass($obj, $sec_prefix = '') { + protected function getClass($obj, $sec_prefix = '') + { static $five = null; - if ($five === null) $five = version_compare(PHP_VERSION, '5', '>='); + if ($five === null) { + $five = version_compare(PHP_VERSION, '5', '>='); + } $prefix = 'HTMLPurifier_' . $sec_prefix; - if (!$five) $prefix = strtolower($prefix); + if (!$five) { + $prefix = strtolower($prefix); + } $class = str_replace($prefix, '', get_class($obj)); $lclass = strtolower($class); $class .= '('; @@ -164,13 +205,14 @@ class HTMLPurifier_Printer break; case 'css_importantdecorator': $class .= $this->getClass($obj->def, $sec_prefix); - if ($obj->allow) $class .= ', !important'; + if ($obj->allow) { + $class .= ', !important'; + } break; } $class .= ')'; return $class; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Printer/CSSDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php similarity index 85% rename from library/HTMLPurifier/Printer/CSSDefinition.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php index 81f9865901..29505fe12d 100644 --- a/library/HTMLPurifier/Printer/CSSDefinition.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/CSSDefinition.php @@ -2,10 +2,17 @@ class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer { - + /** + * @type HTMLPurifier_CSSDefinition + */ protected $def; - public function render($config) { + /** + * @param HTMLPurifier_Config $config + * @return string + */ + public function render($config) + { $this->def = $config->getCSSDefinition(); $ret = ''; @@ -32,7 +39,6 @@ class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer return $ret; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Printer/ConfigForm.css b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css similarity index 100% rename from library/HTMLPurifier/Printer/ConfigForm.css rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.css diff --git a/library/HTMLPurifier/Printer/ConfigForm.js b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js similarity index 100% rename from library/HTMLPurifier/Printer/ConfigForm.js rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.js diff --git a/library/HTMLPurifier/Printer/ConfigForm.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php similarity index 60% rename from library/HTMLPurifier/Printer/ConfigForm.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php index 02aa656894..36100ce738 100644 --- a/library/HTMLPurifier/Printer/ConfigForm.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/ConfigForm.php @@ -7,17 +7,20 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer { /** - * Printers for specific fields + * Printers for specific fields. + * @type HTMLPurifier_Printer[] */ protected $fields = array(); /** - * Documentation URL, can have fragment tagged on end + * Documentation URL, can have fragment tagged on end. + * @type string */ protected $docURL; /** - * Name of form element to stuff config in + * Name of form element to stuff config in. + * @type string */ protected $name; @@ -25,24 +28,27 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer * Whether or not to compress directive names, clipping them off * after a certain amount of letters. False to disable or integer letters * before clipping. + * @type bool */ protected $compress = false; /** - * @param $name Form element name for directives to be stuffed into - * @param $doc_url String documentation URL, will have fragment tagged on - * @param $compress Integer max length before compressing a directive name, set to false to turn off + * @param string $name Form element name for directives to be stuffed into + * @param string $doc_url String documentation URL, will have fragment tagged on + * @param bool $compress Integer max length before compressing a directive name, set to false to turn off */ public function __construct( - $name, $doc_url = null, $compress = false + $name, + $doc_url = null, + $compress = false ) { parent::__construct(); $this->docURL = $doc_url; - $this->name = $name; + $this->name = $name; $this->compress = $compress; // initialize sub-printers - $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); - $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); + $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); + $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); } /** @@ -50,32 +56,42 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer * @param $cols Integer columns of textarea, null to use default * @param $rows Integer rows of textarea, null to use default */ - public function setTextareaDimensions($cols = null, $rows = null) { - if ($cols) $this->fields['default']->cols = $cols; - if ($rows) $this->fields['default']->rows = $rows; + public function setTextareaDimensions($cols = null, $rows = null) + { + if ($cols) { + $this->fields['default']->cols = $cols; + } + if ($rows) { + $this->fields['default']->rows = $rows; + } } /** * Retrieves styling, in case it is not accessible by webserver */ - public static function getCSS() { + public static function getCSS() + { return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); } /** * Retrieves JavaScript, in case it is not accessible by webserver */ - public static function getJavaScript() { + public static function getJavaScript() + { return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); } /** * Returns HTML output for a configuration form - * @param $config Configuration object of current form state, or an array + * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array * where [0] has an HTML namespace and [1] is being rendered. - * @param $allowed Optional namespace(s) and directives to restrict form to. + * @param array|bool $allowed Optional namespace(s) and directives to restrict form to. + * @param bool $render_controls + * @return string */ - public function render($config, $allowed = true, $render_controls = true) { + public function render($config, $allowed = true, $render_controls = true) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -91,29 +107,29 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer $all = array(); foreach ($allowed as $key) { list($ns, $directive) = $key; - $all[$ns][$directive] = $config->get($ns .'.'. $directive); + $all[$ns][$directive] = $config->get($ns . '.' . $directive); } $ret = ''; $ret .= $this->start('table', array('class' => 'hp-config')); $ret .= $this->start('thead'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); - $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); + $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); + $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); $ret .= $this->end('tr'); $ret .= $this->end('thead'); foreach ($all as $ns => $directives) { $ret .= $this->renderNamespace($ns, $directives); } if ($render_controls) { - $ret .= $this->start('tbody'); - $ret .= $this->start('tr'); - $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); - $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); - $ret .= '[Reset]'; - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); + $ret .= $this->start('tbody'); + $ret .= $this->start('tr'); + $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); + $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); + $ret .= '[Reset]'; + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); } $ret .= $this->end('table'); return $ret; @@ -122,13 +138,15 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer /** * Renders a single namespace * @param $ns String namespace name - * @param $directive Associative array of directives to values + * @param array $directives array of directives to values + * @return string */ - protected function renderNamespace($ns, $directives) { + protected function renderNamespace($ns, $directives) + { $ret = ''; $ret .= $this->start('tbody', array('class' => 'namespace')); $ret .= $this->start('tr'); - $ret .= $this->element('th', $ns, array('colspan' => 2)); + $ret .= $this->element('th', $ns, array('colspan' => 2)); $ret .= $this->end('tr'); $ret .= $this->end('tbody'); $ret .= $this->start('tbody'); @@ -139,40 +157,44 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); $ret .= $this->start('a', array('href' => $url)); } - $attr = array('for' => "{$this->name}:$ns.$directive"); + $attr = array('for' => "{$this->name}:$ns.$directive"); - // crop directive name if it's too long - if (!$this->compress || (strlen($directive) < $this->compress)) { - $directive_disp = $directive; - } else { - $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; - $attr['title'] = $directive; - } + // crop directive name if it's too long + if (!$this->compress || (strlen($directive) < $this->compress)) { + $directive_disp = $directive; + } else { + $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; + $attr['title'] = $directive; + } - $ret .= $this->element( - 'label', - $directive_disp, - // component printers must create an element with this id - $attr - ); - if ($this->docURL) $ret .= $this->end('a'); + $ret .= $this->element( + 'label', + $directive_disp, + // component printers must create an element with this id + $attr + ); + if ($this->docURL) { + $ret .= $this->end('a'); + } $ret .= $this->end('th'); $ret .= $this->start('td'); - $def = $this->config->def->info["$ns.$directive"]; - if (is_int($def)) { - $allow_null = $def < 0; - $type = abs($def); - } else { - $type = $def->type; - $allow_null = isset($def->allow_null); - } - if (!isset($this->fields[$type])) $type = 0; // default - $type_obj = $this->fields[$type]; - if ($allow_null) { - $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); - } - $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); + $def = $this->config->def->info["$ns.$directive"]; + if (is_int($def)) { + $allow_null = $def < 0; + $type = abs($def); + } else { + $type = $def->type; + $allow_null = isset($def->allow_null); + } + if (!isset($this->fields[$type])) { + $type = 0; + } // default + $type_obj = $this->fields[$type]; + if ($allow_null) { + $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); + } + $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); $ret .= $this->end('td'); $ret .= $this->end('tr'); } @@ -185,19 +207,33 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer /** * Printer decorator for directives that accept null */ -class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { +class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer +{ /** * Printer being decorated + * @type HTMLPurifier_Printer */ protected $obj; + /** - * @param $obj Printer to decorate + * @param HTMLPurifier_Printer $obj Printer to decorate */ - public function __construct($obj) { + public function __construct($obj) + { parent::__construct(); $this->obj = $obj; } - public function render($ns, $directive, $value, $name, $config) { + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -215,15 +251,19 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer 'type' => 'checkbox', 'value' => '1', 'class' => 'null-toggle', - 'name' => "$name"."[Null_$ns.$directive]", + 'name' => "$name" . "[Null_$ns.$directive]", 'id' => "$name:Null_$ns.$directive", 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! ); if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { // modify inline javascript slightly - $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)"; + $attr['onclick'] = + "toggleWriteability('$name:Yes_$ns.$directive',checked);" . + "toggleWriteability('$name:No_$ns.$directive',checked)"; + } + if ($value === null) { + $attr['checked'] = 'checked'; } - if ($value === null) $attr['checked'] = 'checked'; $ret .= $this->elementEmpty('input', $attr); $ret .= $this->text(' or '); $ret .= $this->elementEmpty('br'); @@ -235,10 +275,28 @@ class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer /** * Swiss-army knife configuration form field printer */ -class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { +class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer +{ + /** + * @type int + */ public $cols = 18; + + /** + * @type int + */ public $rows = 5; - public function render($ns, $directive, $value, $name, $config) { + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -262,6 +320,7 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { foreach ($array as $val => $b) { $value[] = $val; } + //TODO does this need a break? case HTMLPurifier_VarParser::ALIST: $value = implode(PHP_EOL, $value); break; @@ -281,25 +340,27 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { $value = serialize($value); } $attr = array( - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:$ns.$directive" ); - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === null) { + $attr['disabled'] = 'disabled'; + } if (isset($def->allowed)) { $ret .= $this->start('select', $attr); foreach ($def->allowed as $val => $b) { $attr = array(); - if ($value == $val) $attr['selected'] = 'selected'; + if ($value == $val) { + $attr['selected'] = 'selected'; + } $ret .= $this->element('option', $val, $attr); } $ret .= $this->end('select'); - } elseif ( - $type === HTMLPurifier_VarParser::TEXT || - $type === HTMLPurifier_VarParser::ITEXT || - $type === HTMLPurifier_VarParser::ALIST || - $type === HTMLPurifier_VarParser::HASH || - $type === HTMLPurifier_VarParser::LOOKUP - ) { + } elseif ($type === HTMLPurifier_VarParser::TEXT || + $type === HTMLPurifier_VarParser::ITEXT || + $type === HTMLPurifier_VarParser::ALIST || + $type === HTMLPurifier_VarParser::HASH || + $type === HTMLPurifier_VarParser::LOOKUP) { $attr['cols'] = $this->cols; $attr['rows'] = $this->rows; $ret .= $this->start('textarea', $attr); @@ -317,8 +378,18 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { /** * Bool form field printer */ -class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { - public function render($ns, $directive, $value, $name, $config) { +class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer +{ + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -336,12 +407,16 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { $attr = array( 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:Yes_$ns.$directive", 'value' => '1' ); - if ($value === true) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === true) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } $ret .= $this->elementEmpty('input', $attr); $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); @@ -351,12 +426,16 @@ class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { $attr = array( 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:No_$ns.$directive", 'value' => '0' ); - if ($value === false) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === false) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } $ret .= $this->elementEmpty('input', $attr); $ret .= $this->end('div'); diff --git a/library/HTMLPurifier/Printer/HTMLDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php similarity index 53% rename from library/HTMLPurifier/Printer/HTMLDefinition.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php index 8a8f126b81..5f2f2f8a7c 100644 --- a/library/HTMLPurifier/Printer/HTMLDefinition.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Printer/HTMLDefinition.php @@ -4,11 +4,16 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer { /** - * Instance of HTMLPurifier_HTMLDefinition, for easy access + * @type HTMLPurifier_HTMLDefinition, for easy access */ protected $def; - public function render($config) { + /** + * @param HTMLPurifier_Config $config + * @return string + */ + public function render($config) + { $ret = ''; $this->config =& $config; @@ -28,8 +33,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Renders the Doctype table + * @return string */ - protected function renderDoctype() { + protected function renderDoctype() + { $doctype = $this->def->doctype; $ret = ''; $ret .= $this->start('table'); @@ -45,8 +52,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Renders environment table, which is miscellaneous info + * @return string */ - protected function renderEnvironment() { + protected function renderEnvironment() + { $def = $this->def; $ret = ''; @@ -59,28 +68,28 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer $ret .= $this->row('Block wrap name', $def->info_block_wrapper); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Global attributes'); - $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0); + $ret .= $this->element('th', 'Global attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Tag transforms'); - $list = array(); - foreach ($def->info_tag_transform as $old => $new) { - $new = $this->getClass($new, 'TagTransform_'); - $list[] = "<$old> with $new"; - } - $ret .= $this->element('td', $this->listify($list)); + $ret .= $this->element('th', 'Tag transforms'); + $list = array(); + foreach ($def->info_tag_transform as $old => $new) { + $new = $this->getClass($new, 'TagTransform_'); + $list[] = "<$old> with $new"; + } + $ret .= $this->element('td', $this->listify($list)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); $ret .= $this->end('tr'); $ret .= $this->end('table'); @@ -89,8 +98,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Renders the Content Sets table + * @return string */ - protected function renderContentSets() { + protected function renderContentSets() + { $ret = ''; $ret .= $this->start('table'); $ret .= $this->element('caption', 'Content Sets'); @@ -106,8 +117,10 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Renders the Elements ($info) table + * @return string */ - protected function renderInfo() { + protected function renderInfo() + { $ret = ''; $ret .= $this->start('table'); $ret .= $this->element('caption', 'Elements ($info)'); @@ -118,39 +131,39 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer $ret .= $this->end('tr'); foreach ($this->def->info as $name => $def) { $ret .= $this->start('tr'); - $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2)); + $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Inline content'); - $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); + $ret .= $this->element('th', 'Inline content'); + $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); $ret .= $this->end('tr'); if (!empty($def->excludes)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Excludes'); - $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); + $ret .= $this->element('th', 'Excludes'); + $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); $ret .= $this->end('tr'); } if (!empty($def->attr_transform_pre)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); $ret .= $this->end('tr'); } if (!empty($def->attr_transform_post)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); $ret .= $this->end('tr'); } if (!empty($def->auto_close)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Auto closed by'); - $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); + $ret .= $this->element('th', 'Auto closed by'); + $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); $ret .= $this->end('tr'); } $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Allowed attributes'); - $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0); + $ret .= $this->element('th', 'Allowed attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0); $ret .= $this->end('tr'); if (!empty($def->required_attr)) { @@ -165,64 +178,94 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Renders a row describing the allowed children of an element - * @param $def HTMLPurifier_ChildDef of pertinent element + * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element + * @return string */ - protected function renderChildren($def) { + protected function renderChildren($def) + { $context = new HTMLPurifier_Context(); $ret = ''; $ret .= $this->start('tr'); + $elements = array(); + $attr = array(); + if (isset($def->elements)) { + if ($def->type == 'strictblockquote') { + $def->validateChildren(array(), $this->config, $context); + } + $elements = $def->elements; + } + if ($def->type == 'chameleon') { + $attr['rowspan'] = 2; + } elseif ($def->type == 'empty') { $elements = array(); - $attr = array(); - if (isset($def->elements)) { - if ($def->type == 'strictblockquote') { - $def->validateChildren(array(), $this->config, $context); - } - $elements = $def->elements; - } - if ($def->type == 'chameleon') { - $attr['rowspan'] = 2; - } elseif ($def->type == 'empty') { - $elements = array(); - } elseif ($def->type == 'table') { - $elements = array_flip(array('col', 'caption', 'colgroup', 'thead', - 'tfoot', 'tbody', 'tr')); - } - $ret .= $this->element('th', 'Allowed children', $attr); + } elseif ($def->type == 'table') { + $elements = array_flip( + array( + 'col', + 'caption', + 'colgroup', + 'thead', + 'tfoot', + 'tbody', + 'tr' + ) + ); + } + $ret .= $this->element('th', 'Allowed children', $attr); - if ($def->type == 'chameleon') { + if ($def->type == 'chameleon') { - $ret .= $this->element('td', - 'Block: ' . - $this->escape($this->listifyTagLookup($def->block->elements)),0,0); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element('td', - 'Inline: ' . - $this->escape($this->listifyTagLookup($def->inline->elements)),0,0); + $ret .= $this->element( + 'td', + 'Block: ' . + $this->escape($this->listifyTagLookup($def->block->elements)), + null, + 0 + ); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element( + 'td', + 'Inline: ' . + $this->escape($this->listifyTagLookup($def->inline->elements)), + null, + 0 + ); - } elseif ($def->type == 'custom') { + } elseif ($def->type == 'custom') { - $ret .= $this->element('td', ''.ucfirst($def->type).': ' . - $def->dtd_regex); + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $def->dtd_regex + ); - } else { - $ret .= $this->element('td', - ''.ucfirst($def->type).': ' . - $this->escape($this->listifyTagLookup($elements)),0,0); - } + } else { + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $this->escape($this->listifyTagLookup($elements)), + null, + 0 + ); + } $ret .= $this->end('tr'); return $ret; } /** * Listifies a tag lookup table. - * @param $array Tag lookup array in form of array('tagname' => true) + * @param array $array Tag lookup array in form of array('tagname' => true) + * @return string */ - protected function listifyTagLookup($array) { + protected function listifyTagLookup($array) + { ksort($array); $list = array(); foreach ($array as $name => $discard) { - if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue; + if ($name !== '#PCDATA' && !isset($this->def->info[$name])) { + continue; + } $list[] = $name; } return $this->listify($list); @@ -230,13 +273,15 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Listifies a list of objects by retrieving class names and internal state - * @param $array List of objects + * @param array $array List of objects + * @return string * @todo Also add information about internal state */ - protected function listifyObjectList($array) { + protected function listifyObjectList($array) + { ksort($array); $list = array(); - foreach ($array as $discard => $obj) { + foreach ($array as $obj) { $list[] = $this->getClass($obj, 'AttrTransform_'); } return $this->listify($list); @@ -244,13 +289,17 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Listifies a hash of attributes to AttrDef classes - * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + * @return string */ - protected function listifyAttr($array) { + protected function listifyAttr($array) + { ksort($array); $list = array(); foreach ($array as $name => $obj) { - if ($obj === false) continue; + if ($obj === false) { + continue; + } $list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . ''; } return $this->listify($list); @@ -258,15 +307,18 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer /** * Creates a heavy header row + * @param string $text + * @param int $num + * @return string */ - protected function heavyHeader($text, $num = 1) { + protected function heavyHeader($text, $num = 1) + { $ret = ''; $ret .= $this->start('tr'); $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); $ret .= $this->end('tr'); return $ret; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/PropertyList.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php similarity index 50% rename from library/HTMLPurifier/PropertyList.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php index 2b99fb7bc3..189348fd9e 100644 --- a/library/HTMLPurifier/PropertyList.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyList.php @@ -6,61 +6,93 @@ class HTMLPurifier_PropertyList { /** - * Internal data-structure for properties + * Internal data-structure for properties. + * @type array */ protected $data = array(); /** - * Parent plist + * Parent plist. + * @type HTMLPurifier_PropertyList */ protected $parent; + /** + * Cache. + * @type array + */ protected $cache; - public function __construct($parent = null) { + /** + * @param HTMLPurifier_PropertyList $parent Parent plist + */ + public function __construct($parent = null) + { $this->parent = $parent; } /** * Recursively retrieves the value for a key + * @param string $name + * @throws HTMLPurifier_Exception */ - public function get($name) { - if ($this->has($name)) return $this->data[$name]; + public function get($name) + { + if ($this->has($name)) { + return $this->data[$name]; + } // possible performance bottleneck, convert to iterative if necessary - if ($this->parent) return $this->parent->get($name); + if ($this->parent) { + return $this->parent->get($name); + } throw new HTMLPurifier_Exception("Key '$name' not found"); } /** * Sets the value of a key, for this plist + * @param string $name + * @param mixed $value */ - public function set($name, $value) { + public function set($name, $value) + { $this->data[$name] = $value; } /** * Returns true if a given key exists + * @param string $name + * @return bool */ - public function has($name) { + public function has($name) + { return array_key_exists($name, $this->data); } /** * Resets a value to the value of it's parent, usually the default. If * no value is specified, the entire plist is reset. + * @param string $name */ - public function reset($name = null) { - if ($name == null) $this->data = array(); - else unset($this->data[$name]); + public function reset($name = null) + { + if ($name == null) { + $this->data = array(); + } else { + unset($this->data[$name]); + } } /** * Squashes this property list and all of its property lists into a single * array, and returns the array. This value is cached by default. - * @param $force If true, ignores the cache and regenerates the array. + * @param bool $force If true, ignores the cache and regenerates the array. + * @return array */ - public function squash($force = false) { - if ($this->cache !== null && !$force) return $this->cache; + public function squash($force = false) + { + if ($this->cache !== null && !$force) { + return $this->cache; + } if ($this->parent) { return $this->cache = array_merge($this->parent->squash($force), $this->data); } else { @@ -70,15 +102,19 @@ class HTMLPurifier_PropertyList /** * Returns the parent plist. + * @return HTMLPurifier_PropertyList */ - public function getParent() { + public function getParent() + { return $this->parent; } /** * Sets the parent plist. + * @param HTMLPurifier_PropertyList $plist Parent plist */ - public function setParent($plist) { + public function setParent($plist) + { $this->parent = $plist; } } diff --git a/library/HTMLPurifier/PropertyListIterator.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php similarity index 60% rename from library/HTMLPurifier/PropertyListIterator.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php index 8f250443e4..15b330ea30 100644 --- a/library/HTMLPurifier/PropertyListIterator.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/PropertyListIterator.php @@ -6,27 +6,37 @@ class HTMLPurifier_PropertyListIterator extends FilterIterator { + /** + * @type int + */ protected $l; + /** + * @type string + */ protected $filter; /** - * @param $data Array of data to iterate over - * @param $filter Optional prefix to only allow values of + * @param Iterator $iterator Array of data to iterate over + * @param string $filter Optional prefix to only allow values of */ - public function __construct(Iterator $iterator, $filter = null) { + public function __construct(Iterator $iterator, $filter = null) + { parent::__construct($iterator); $this->l = strlen($filter); $this->filter = $filter; } - public function accept() { + /** + * @return bool + */ + public function accept() + { $key = $this->getInnerIterator()->key(); - if( strncmp($key, $this->filter, $this->l) !== 0 ) { + if (strncmp($key, $this->filter, $this->l) !== 0) { return false; } return true; } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php new file mode 100644 index 0000000000..f58db9042a --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Queue.php @@ -0,0 +1,56 @@ +input = $input; + $this->output = array(); + } + + /** + * Shifts an element off the front of the queue. + */ + public function shift() { + if (empty($this->output)) { + $this->output = array_reverse($this->input); + $this->input = array(); + } + if (empty($this->output)) { + return NULL; + } + return array_pop($this->output); + } + + /** + * Pushes an element onto the front of the queue. + */ + public function push($x) { + array_push($this->input, $x); + } + + /** + * Checks if it's empty. + */ + public function isEmpty() { + return empty($this->input) && empty($this->output); + } +} diff --git a/library/HTMLPurifier/Strategy.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php similarity index 66% rename from library/HTMLPurifier/Strategy.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php index 2462865210..e1ff3b72df 100644 --- a/library/HTMLPurifier/Strategy.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy.php @@ -15,12 +15,12 @@ abstract class HTMLPurifier_Strategy /** * Executes the strategy on the tokens. * - * @param $tokens Array of HTMLPurifier_Token objects to be operated on. - * @param $config Configuration options - * @returns Processed array of token objects. + * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token objects to be operated on. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] Processed array of token objects. */ abstract public function execute($tokens, $config, $context); - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Strategy/Composite.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php similarity index 61% rename from library/HTMLPurifier/Strategy/Composite.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php index 816490b799..d7d35ce7d1 100644 --- a/library/HTMLPurifier/Strategy/Composite.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Composite.php @@ -8,18 +8,23 @@ abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy /** * List of strategies to run tokens through. + * @type HTMLPurifier_Strategy[] */ protected $strategies = array(); - abstract public function __construct(); - - public function execute($tokens, $config, $context) { + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { foreach ($this->strategies as $strategy) { $tokens = $strategy->execute($tokens, $config, $context); } return $tokens; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Strategy/Core.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php similarity index 92% rename from library/HTMLPurifier/Strategy/Core.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php index d90e158606..4414c17d6e 100644 --- a/library/HTMLPurifier/Strategy/Core.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/Core.php @@ -5,14 +5,13 @@ */ class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite { - - public function __construct() { + public function __construct() + { $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php new file mode 100644 index 0000000000..6fa673db9a --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/FixNesting.php @@ -0,0 +1,181 @@ +getHTMLDefinition(); + + $excludes_enabled = !$config->get('Core.DisableExcludes'); + + // 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 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($definition->info_parent_def->excludes); + + // variable that contains the start token while we are processing + // nodes. This enables error reporting to do its job + $node = $top_node; + // dummy token + list($token, $d) = $node->toTokenPair(); + $context->register('CurrentNode', $node); + $context->register('CurrentToken', $token); + + //####################################################################// + // Loop + + // We need to implement a post-order traversal iteratively, to + // avoid running into stack space limits. This is pretty tricky + // to reason about, so we just manually stack-ify the recursive + // variant: + // + // function f($node) { + // foreach ($node->children as $child) { + // f($child); + // } + // validate($node); + // } + // + // Thus, we will represent a stack frame as array($node, + // $is_inline, stack of children) + // e.g. array_reverse($node->children) - already processed + // children. + + $parent_def = $definition->info_parent_def; + $stack = array( + array($top_node, + $parent_def->descendants_are_inline, + $parent_def->excludes, // exclusions + 0) + ); + + while (!empty($stack)) { + list($node, $is_inline, $excludes, $ix) = array_pop($stack); + // recursive call + $go = false; + $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name]; + while (isset($node->children[$ix])) { + $child = $node->children[$ix++]; + if ($child instanceof HTMLPurifier_Node_Element) { + $go = true; + $stack[] = array($node, $is_inline, $excludes, $ix); + $stack[] = array($child, + // ToDo: I don't think it matters if it's def or + // child_def, but double check this... + $is_inline || $def->descendants_are_inline, + empty($def->excludes) ? $excludes + : array_merge($excludes, $def->excludes), + 0); + break; + } + }; + if ($go) continue; + list($token, $d) = $node->toTokenPair(); + // base case + if ($excludes_enabled && isset($excludes[$node->name])) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); + } else { + // XXX I suppose it would be slightly more efficient to + // avoid the allocation here and have children + // strategies handle it + $children = array(); + foreach ($node->children as $child) { + if (!$child->dead) $children[] = $child; + } + $result = $def->child->validateChildren($children, $config, $context); + if ($result === true) { + // nop + $node->children = $children; + } elseif ($result === false) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); + } else { + $node->children = $result; + if ($e) { + // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators + if (empty($result) && !empty($children)) { + $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); + } else if ($result != $children) { + $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); + } + } + } + } + } + + //####################################################################// + // Post-processing + + // remove context variables + $context->destroy('IsInline'); + $context->destroy('CurrentNode'); + $context->destroy('CurrentToken'); + + //####################################################################// + // Return + + return HTMLPurifier_Arborize::flatten($node, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Strategy/MakeWellFormed.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php similarity index 52% rename from library/HTMLPurifier/Strategy/MakeWellFormed.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php index c736584007..e389e00116 100644 --- a/library/HTMLPurifier/Strategy/MakeWellFormed.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/MakeWellFormed.php @@ -2,66 +2,97 @@ /** * Takes tokens makes them well-formed (balance end tags, etc.) + * + * Specification of the armor attributes this strategy uses: + * + * - MakeWellFormed_TagClosedError: This armor field is used to + * suppress tag closed errors for certain tokens [TagClosedSuppress], + * in particular, if a tag was generated automatically by HTML + * Purifier, we may rely on our infrastructure to close it for us + * and shouldn't report an error to the user [TagClosedAuto]. */ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy { /** * Array stream of tokens being processed. + * @type HTMLPurifier_Token[] */ protected $tokens; /** - * Current index in $tokens. + * Current token. + * @type HTMLPurifier_Token */ - protected $t; + protected $token; + + /** + * Zipper managing the true state. + * @type HTMLPurifier_Zipper + */ + protected $zipper; /** * Current nesting of elements. + * @type array */ protected $stack; /** * Injectors active in this stream processing. + * @type HTMLPurifier_Injector[] */ protected $injectors; /** * Current instance of HTMLPurifier_Config. + * @type HTMLPurifier_Config */ protected $config; /** * Current instance of HTMLPurifier_Context. + * @type HTMLPurifier_Context */ protected $context; - public function execute($tokens, $config, $context) { - + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + * @throws HTMLPurifier_Exception + */ + public function execute($tokens, $config, $context) + { $definition = $config->getHTMLDefinition(); // local variables $generator = new HTMLPurifier_Generator($config, $context); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); + // used for autoclose early abortion + $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config); $e = $context->get('ErrorCollector', true); - $t = false; // token index $i = false; // injector index - $token = false; // the current token - $reprocess = false; // whether or not to reprocess the same token + list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens); + if ($token === NULL) { + return array(); + } + $reprocess = false; // whether or not to reprocess the same token $stack = array(); // member variables - $this->stack =& $stack; - $this->t =& $t; - $this->tokens =& $tokens; - $this->config = $config; + $this->stack =& $stack; + $this->tokens =& $tokens; + $this->token =& $token; + $this->zipper =& $zipper; + $this->config = $config; $this->context = $context; // context variables $context->register('CurrentNesting', $stack); - $context->register('InputIndex', $t); - $context->register('InputTokens', $tokens); - $context->register('CurrentToken', $token); + $context->register('InputZipper', $zipper); + $context->register('CurrentToken', $token); // -- begin INJECTOR -- @@ -73,9 +104,13 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy unset($injectors['Custom']); // special case foreach ($injectors as $injector => $b) { // XXX: Fix with a legitimate lookup table of enabled filters - if (strpos($injector, '.') !== false) continue; + if (strpos($injector, '.') !== false) { + continue; + } $injector = "HTMLPurifier_Injector_$injector"; - if (!$b) continue; + if (!$b) { + continue; + } $this->injectors[] = new $injector; } foreach ($def_injectors as $injector) { @@ -83,7 +118,9 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $this->injectors[] = $injector; } foreach ($custom_injectors as $injector) { - if (!$injector) continue; + if (!$injector) { + continue; + } if (is_string($injector)) { $injector = "HTMLPurifier_Injector_$injector"; $injector = new $injector; @@ -95,14 +132,16 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // variables for performance reasons foreach ($this->injectors as $ix => $injector) { $error = $injector->prepare($config, $context); - if (!$error) continue; + if (!$error) { + continue; + } array_splice($this->injectors, $ix, 1); // rm the injector trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); } // -- end INJECTOR -- - // a note on punting: + // a note on reprocessing: // In order to reduce code duplication, whenever some code needs // to make HTML changes in order to make things "correct", the // new HTML gets sent through the purifier, regardless of its @@ -111,70 +150,75 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // punt ($reprocess = true; continue;) and it does that for us. // isset is in loop because $tokens size changes during loop exec - for ( - $t = 0; - $t == 0 || isset($tokens[$t - 1]); - // only increment if we don't need to reprocess - $reprocess ? $reprocess = false : $t++ - ) { + for (;; + // only increment if we don't need to reprocess + $reprocess ? $reprocess = false : $token = $zipper->next($token)) { // check for a rewind - if (is_int($i) && $i >= 0) { + if (is_int($i)) { // possibility: disable rewinding if the current token has a // rewind set on it already. This would offer protection from // infinite loop, but might hinder some advanced rewinding. - $rewind_to = $this->injectors[$i]->getRewind(); - if (is_int($rewind_to) && $rewind_to < $t) { - if ($rewind_to < 0) $rewind_to = 0; - while ($t > $rewind_to) { - $t--; - $prev = $tokens[$t]; + $rewind_offset = $this->injectors[$i]->getRewindOffset(); + if (is_int($rewind_offset)) { + for ($j = 0; $j < $rewind_offset; $j++) { + if (empty($zipper->front)) break; + $token = $zipper->prev($token); // indicate that other injectors should not process this token, // but we need to reprocess it - unset($prev->skip[$i]); - $prev->rewind = $i; - if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); - elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; + unset($token->skip[$i]); + $token->rewind = $i; + if ($token instanceof HTMLPurifier_Token_Start) { + array_pop($this->stack); + } elseif ($token instanceof HTMLPurifier_Token_End) { + $this->stack[] = $token->start; + } } } $i = false; } // handle case of document end - if (!isset($tokens[$t])) { + if ($token === NULL) { // kill processing if stack is empty - if (empty($this->stack)) break; + if (empty($this->stack)) { + break; + } // peek $top_nesting = array_pop($this->stack); $this->stack[] = $top_nesting; - // send error + // send error [TagClosedSuppress] if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); } // append, don't splice, since this is the end - $tokens[] = new HTMLPurifier_Token_End($top_nesting->name); + $token = new HTMLPurifier_Token_End($top_nesting->name); // punt! $reprocess = true; continue; } - $token = $tokens[$t]; - - //echo '
        '; printTokens($tokens, $t); printTokens($this->stack); + //echo '
        '; printZipper($zipper, $token);//printTokens($this->stack); //flush(); // quick-check: if it's not a tag, no need to process if (empty($token->is_tag)) { if ($token instanceof HTMLPurifier_Token_Text) { foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleText($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + // XXX fuckup + $r = $token; + $injector->handleText($r); + $token = $this->processToken($r, $i); $reprocess = true; break; } @@ -193,12 +237,22 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $ok = false; if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { // claims to be a start tag but is empty - $token = new HTMLPurifier_Token_Empty($token->name, $token->attr); + $token = new HTMLPurifier_Token_Empty( + $token->name, + $token->attr, + $token->line, + $token->col, + $token->armor + ); $ok = true; } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { // claims to be empty but really is a start tag - $this->swap(new HTMLPurifier_Token_End($token->name)); - $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr)); + // NB: this assignment is required + $old_token = $token; + $token = new HTMLPurifier_Token_End($token->name); + $token = $this->insertBefore( + new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor) + ); // punt (since we had to modify the input stream in a non-trivial way) $reprocess = true; continue; @@ -211,56 +265,97 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // ...unless they also have to close their parent if (!empty($this->stack)) { + // Performance note: you might think that it's rather + // inefficient, recalculating the autoclose information + // for every tag that a token closes (since when we + // do an autoclose, we push a new token into the + // stream and then /process/ that, before + // re-processing this token.) But this is + // necessary, because an injector can make an + // arbitrary transformations to the autoclosing + // tokens we introduce, so things may have changed + // in the meantime. Also, doing the inefficient thing is + // "easy" to reason about (for certain perverse definitions + // of "easy") + $parent = array_pop($this->stack); $this->stack[] = $parent; + $parent_def = null; + $parent_elements = null; + $autoclose = false; if (isset($definition->info[$parent->name])) { - $elements = $definition->info[$parent->name]->child->getAllowedElements($config); - $autoclose = !isset($elements[$token->name]); - } else { - $autoclose = false; + $parent_def = $definition->info[$parent->name]; + $parent_elements = $parent_def->child->getAllowedElements($config); + $autoclose = !isset($parent_elements[$token->name]); } if ($autoclose && $definition->info[$token->name]->wrap) { - // Check if an element can be wrapped by another - // element to make it valid in a context (for + // Check if an element can be wrapped by another + // element to make it valid in a context (for // example,
            needs a
          • in between) $wrapname = $definition->info[$token->name]->wrap; $wrapdef = $definition->info[$wrapname]; $elements = $wrapdef->child->getAllowedElements($config); - $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config); if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { $newtoken = new HTMLPurifier_Token_Start($wrapname); - $this->insertBefore($newtoken); + $token = $this->insertBefore($newtoken); $reprocess = true; continue; } } $carryover = false; - if ($autoclose && $definition->info[$parent->name]->formatting) { + if ($autoclose && $parent_def->formatting) { $carryover = true; } if ($autoclose) { - // errors need to be updated - $new_token = new HTMLPurifier_Token_End($parent->name); - $new_token->start = $parent; - if ($carryover) { - $element = clone $parent; - $element->armor['MakeWellFormed_TagClosedError'] = true; - $element->carryover = true; - $this->processToken(array($new_token, $token, $element)); - } else { - $this->insertBefore($new_token); - } - if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { - if (!$carryover) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); - } else { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); + // check if this autoclose is doomed to fail + // (this rechecks $parent, which his harmless) + $autoclose_ok = isset($global_parent_allowed_elements[$token->name]); + if (!$autoclose_ok) { + foreach ($this->stack as $ancestor) { + $elements = $definition->info[$ancestor->name]->child->getAllowedElements($config); + if (isset($elements[$token->name])) { + $autoclose_ok = true; + break; + } + if ($definition->info[$token->name]->wrap) { + $wrapname = $definition->info[$token->name]->wrap; + $wrapdef = $definition->info[$wrapname]; + $wrap_elements = $wrapdef->child->getAllowedElements($config); + if (isset($wrap_elements[$token->name]) && isset($elements[$wrapname])) { + $autoclose_ok = true; + break; + } + } } } + if ($autoclose_ok) { + // errors need to be updated + $new_token = new HTMLPurifier_Token_End($parent->name); + $new_token->start = $parent; + // [TagClosedSuppress] + if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { + if (!$carryover) { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); + } else { + $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); + } + } + if ($carryover) { + $element = clone $parent; + // [TagClosedAuto] + $element->armor['MakeWellFormed_TagClosedError'] = true; + $element->carryover = true; + $token = $this->processToken(array($new_token, $token, $element)); + } else { + $token = $this->insertBefore($new_token); + } + } else { + $token = $this->remove(); + } $reprocess = true; continue; } @@ -271,20 +366,26 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy if ($ok) { foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleElement($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleElement($r); + $token = $this->processToken($r, $i); $reprocess = true; break; } if (!$reprocess) { // ah, nothing interesting happened; do normal processing - $this->swap($token); if ($token instanceof HTMLPurifier_Token_Start) { $this->stack[] = $token; } elseif ($token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); + throw new HTMLPurifier_Exception( + 'Improper handling of end tag in start code; possible error in MakeWellFormed' + ); } } continue; @@ -298,13 +399,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // make sure that we have something open if (empty($this->stack)) { if ($escape_invalid_tags) { - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + } + $token = $this->remove(); } $reprocess = true; continue; @@ -318,10 +421,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy if ($current_parent->name == $token->name) { $token->start = $current_parent; foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleEnd($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleEnd($r); + $token = $this->processToken($r, $i); $this->stack[] = $current_parent; $reprocess = true; break; @@ -349,13 +457,15 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy // we didn't find the tag, so remove if ($skipped_tags === false) { if ($escape_invalid_tags) { - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + } + $token = $this->remove(); } $reprocess = true; continue; @@ -366,7 +476,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy if ($e) { for ($j = $c - 1; $j > 0; $j--) { // notice we exclude $j == 0, i.e. the current ending tag, from - // the errors... + // the errors... [TagClosedSuppress] if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); } @@ -381,24 +491,24 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $new_token->start = $skipped_tags[$j]; array_unshift($replace, $new_token); if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { + // [TagClosedAuto] $element = clone $skipped_tags[$j]; $element->carryover = true; $element->armor['MakeWellFormed_TagClosedError'] = true; $replace[] = $element; } } - $this->processToken($replace); + $token = $this->processToken($replace); $reprocess = true; continue; } - $context->destroy('CurrentNesting'); - $context->destroy('InputTokens'); - $context->destroy('InputIndex'); $context->destroy('CurrentToken'); + $context->destroy('CurrentNesting'); + $context->destroy('InputZipper'); - unset($this->injectors, $this->stack, $this->tokens, $this->t); - return $tokens; + unset($this->injectors, $this->stack, $this->tokens); + return $zipper->toArray($token); } /** @@ -417,25 +527,38 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy * If $token is an integer, that number of tokens (with the first token * being the current one) will be deleted. * - * @param $token Token substitution value - * @param $injector Injector that performed the substitution; default is if + * @param HTMLPurifier_Token|array|int|bool $token Token substitution value + * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if * this is not an injector related operation. + * @throws HTMLPurifier_Exception */ - protected function processToken($token, $injector = -1) { - + protected function processToken($token, $injector = -1) + { // normalize forms of token - if (is_object($token)) $token = array(1, $token); - if (is_int($token)) $token = array($token); - if ($token === false) $token = array(1); - if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); - if (!is_int($token[0])) array_unshift($token, 1); - if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + if (is_object($token)) { + $token = array(1, $token); + } + if (is_int($token)) { + $token = array($token); + } + if ($token === false) { + $token = array(1); + } + if (!is_array($token)) { + throw new HTMLPurifier_Exception('Invalid token type from injector'); + } + if (!is_int($token[0])) { + array_unshift($token, 1); + } + if ($token[0] === 0) { + throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + } // $token is now an array with the following form: // array(number nodes to delete, new node 1, new node 2, ...) $delete = array_shift($token); - $old = array_splice($this->tokens, $this->t, $delete, $token); + list($old, $r) = $this->zipper->splice($this->token, $delete, $token); if ($injector > -1) { // determine appropriate skips @@ -446,30 +569,32 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy } } + return $r; + } /** - * Inserts a token before the current token. Cursor now points to this token + * Inserts a token before the current token. Cursor now points to + * this token. You must reprocess after this. + * @param HTMLPurifier_Token $token */ - private function insertBefore($token) { - array_splice($this->tokens, $this->t, 0, array($token)); + private function insertBefore($token) + { + // NB not $this->zipper->insertBefore(), due to positioning + // differences + $splice = $this->zipper->splice($this->token, 0, array($token)); + + return $splice[1]; } /** * Removes current token. Cursor now points to new token occupying previously - * occupied space. + * occupied space. You must reprocess after this. */ - private function remove() { - array_splice($this->tokens, $this->t, 1); + private function remove() + { + return $this->zipper->delete(); } - - /** - * Swap current token with new token. Cursor points to new token (no change). - */ - private function swap($token) { - $this->tokens[$this->t] = $token; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Strategy/RemoveForeignElements.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php similarity index 64% rename from library/HTMLPurifier/Strategy/RemoveForeignElements.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php index cf3a33e406..1a8149eccb 100644 --- a/library/HTMLPurifier/Strategy/RemoveForeignElements.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/RemoveForeignElements.php @@ -11,19 +11,29 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy { - public function execute($tokens, $config, $context) { + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array|HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { $definition = $config->getHTMLDefinition(); $generator = new HTMLPurifier_Generator($config, $context); $result = array(); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); + $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); // currently only used to determine if comments should be kept $trusted = $config->get('HTML.Trusted'); + $comment_lookup = $config->get('HTML.AllowedComments'); + $comment_regexp = $config->get('HTML.AllowedCommentsRegexp'); + $check_comments = $comment_lookup !== array() || $comment_regexp !== null; $remove_script_contents = $config->get('Core.RemoveScriptContents'); - $hidden_elements = $config->get('Core.HiddenElements'); + $hidden_elements = $config->get('Core.HiddenElements'); // remove script contents compatibility if ($remove_script_contents === true) { @@ -48,34 +58,31 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy $e =& $context->get('ErrorCollector'); } - foreach($tokens as $token) { + foreach ($tokens as $token) { if ($remove_until) { if (empty($token->is_tag) || $token->name !== $remove_until) { continue; } } - if (!empty( $token->is_tag )) { + if (!empty($token->is_tag)) { // DEFINITION CALL // before any processing, try to transform the element - if ( - isset($definition->info_tag_transform[$token->name]) - ) { + if (isset($definition->info_tag_transform[$token->name])) { $original_name = $token->name; // there is a transformation for this tag // DEFINITION CALL $token = $definition-> - info_tag_transform[$token->name]-> - transform($token, $config, $context); - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + info_tag_transform[$token->name]->transform($token, $config, $context); + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + } } if (isset($definition->info[$token->name])) { - // mostly everything's good, but // we need to make sure required attributes are in order - if ( - ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && + if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && $definition->info[$token->name]->required_attr && ($token->name != 'img' || $remove_invalid_img) // ensure config option still works ) { @@ -88,7 +95,13 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy } } if (!$ok) { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name); + if ($e) { + $e->send( + E_ERROR, + 'Strategy_RemoveForeignElements: Missing required attribute', + $name + ); + } continue; } $token->armor['ValidateAttributes'] = true; @@ -102,7 +115,9 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy } elseif ($escape_invalid_tags) { // invalid tag, generate HTML representation and insert in - if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + if ($e) { + $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + } $token = new HTMLPurifier_Token_Text( $generator->generateFromToken($token) ); @@ -117,9 +132,13 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy } else { $remove_until = false; } - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + } } else { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + } } continue; } @@ -128,26 +147,46 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy if ($textify_comments !== false) { $data = $token->data; $token = new HTMLPurifier_Token_Text($data); - } elseif ($trusted) { - // keep, but perform comment cleaning + } elseif ($trusted || $check_comments) { + // always cleanup comments + $trailing_hyphen = false; if ($e) { // perform check whether or not there's a trailing hyphen if (substr($token->data, -1) == '-') { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'); + $trailing_hyphen = true; } } $token->data = rtrim($token->data, '-'); $found_double_hyphen = false; while (strpos($token->data, '--') !== false) { - if ($e && !$found_double_hyphen) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); - } - $found_double_hyphen = true; // prevent double-erroring + $found_double_hyphen = true; $token->data = str_replace('--', '-', $token->data); } + if ($trusted || !empty($comment_lookup[trim($token->data)]) || + ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) { + // OK good + if ($e) { + if ($trailing_hyphen) { + $e->send( + E_NOTICE, + 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' + ); + } + if ($found_double_hyphen) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); + } + } + } else { + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + } + continue; + } } else { // strip comments - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + } continue; } } elseif ($token instanceof HTMLPurifier_Token_Text) { @@ -160,12 +199,9 @@ class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy // we removed tokens until the end, throw error $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); } - $context->destroy('CurrentToken'); - return $result; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Strategy/ValidateAttributes.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php similarity index 65% rename from library/HTMLPurifier/Strategy/ValidateAttributes.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php index c3328a9d44..fbb3d27c81 100644 --- a/library/HTMLPurifier/Strategy/ValidateAttributes.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Strategy/ValidateAttributes.php @@ -7,8 +7,14 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy { - public function execute($tokens, $config, $context) { - + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { // setup validator $validator = new HTMLPurifier_AttrValidator(); @@ -19,21 +25,21 @@ class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy // only process tokens that have attributes, // namely start and empty tags - if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue; + if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) { + continue; + } // skip tokens that are armored - if (!empty($token->armor['ValidateAttributes'])) continue; + if (!empty($token->armor['ValidateAttributes'])) { + continue; + } // note that we have no facilities here for removing tokens $validator->validateToken($token, $config, $context); - - $tokens[$key] = $token; // for PHP 4 } $context->destroy('CurrentToken'); - return $tokens; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/StringHash.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php similarity index 75% rename from library/HTMLPurifier/StringHash.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php index 62085c5c2f..c07370197a 100644 --- a/library/HTMLPurifier/StringHash.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHash.php @@ -10,28 +10,36 @@ */ class HTMLPurifier_StringHash extends ArrayObject { + /** + * @type array + */ protected $accessed = array(); /** * Retrieves a value, and logs the access. + * @param mixed $index + * @return mixed */ - public function offsetGet($index) { + public function offsetGet($index) + { $this->accessed[$index] = true; return parent::offsetGet($index); } /** * Returns a lookup array of all array indexes that have been accessed. - * @return Array in form array($index => true). + * @return array in form array($index => true). */ - public function getAccessed() { + public function getAccessed() + { return $this->accessed; } /** * Resets the access array. */ - public function resetAccessed() { + public function resetAccessed() + { $this->accessed = array(); } } diff --git a/library/HTMLPurifier/StringHashParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php similarity index 73% rename from library/HTMLPurifier/StringHashParser.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php index f3e70c712f..7c73f80835 100644 --- a/library/HTMLPurifier/StringHashParser.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/StringHashParser.php @@ -28,15 +28,25 @@ class HTMLPurifier_StringHashParser { + /** + * @type string + */ public $default = 'ID'; /** * Parses a file that contains a single string-hash. + * @param string $file + * @return array */ - public function parseFile($file) { - if (!file_exists($file)) return false; + public function parseFile($file) + { + if (!file_exists($file)) { + return false; + } $fh = fopen($file, 'r'); - if (!$fh) return false; + if (!$fh) { + return false; + } $ret = $this->parseHandle($fh); fclose($fh); return $ret; @@ -44,12 +54,19 @@ class HTMLPurifier_StringHashParser /** * Parses a file that contains multiple string-hashes delimited by '----' + * @param string $file + * @return array */ - public function parseMultiFile($file) { - if (!file_exists($file)) return false; + public function parseMultiFile($file) + { + if (!file_exists($file)) { + return false; + } $ret = array(); $fh = fopen($file, 'r'); - if (!$fh) return false; + if (!$fh) { + return false; + } while (!feof($fh)) { $ret[] = $this->parseHandle($fh); } @@ -62,26 +79,36 @@ class HTMLPurifier_StringHashParser * @note While it's possible to simulate in-memory parsing by using * custom stream wrappers, if such a use-case arises we should * factor out the file handle into its own class. - * @param $fh File handle with pointer at start of valid string-hash + * @param resource $fh File handle with pointer at start of valid string-hash * block. + * @return array */ - protected function parseHandle($fh) { + protected function parseHandle($fh) + { $state = false; $single = false; $ret = array(); do { $line = fgets($fh); - if ($line === false) break; + if ($line === false) { + break; + } $line = rtrim($line, "\n\r"); - if (!$state && $line === '') continue; - if ($line === '----') break; + if (!$state && $line === '') { + continue; + } + if ($line === '----') { + break; + } if (strncmp('--#', $line, 3) === 0) { // Comment continue; } elseif (strncmp('--', $line, 2) === 0) { // Multiline declaration $state = trim($line, '- '); - if (!isset($ret[$state])) $ret[$state] = ''; + if (!isset($ret[$state])) { + $ret[$state] = ''; + } continue; } elseif (!$state) { $single = true; @@ -104,7 +131,6 @@ class HTMLPurifier_StringHashParser } while (!feof($fh)); return $ret; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/TagTransform.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php similarity index 62% rename from library/HTMLPurifier/TagTransform.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php index 210a447217..7b8d833433 100644 --- a/library/HTMLPurifier/TagTransform.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform.php @@ -8,14 +8,15 @@ abstract class HTMLPurifier_TagTransform /** * Tag name to transform the tag to. + * @type string */ public $transform_to; /** * Transforms the obsolete tag into the valid tag. - * @param $tag Tag to be transformed. - * @param $config Mandatory HTMLPurifier_Config object - * @param $context Mandatory HTMLPurifier_Context object + * @param HTMLPurifier_Token_Tag $tag Tag to be transformed. + * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object + * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object */ abstract public function transform($tag, $config, $context); @@ -23,14 +24,14 @@ abstract class HTMLPurifier_TagTransform * Prepends CSS properties to the style attribute, creating the * attribute if it doesn't exist. * @warning Copied over from AttrTransform, be sure to keep in sync - * @param $attr Attribute array to process (passed by reference) - * @param $css CSS to prepend + * @param array $attr Attribute array to process (passed by reference) + * @param string $css CSS to prepend */ - protected function prependCSS(&$attr, $css) { + protected function prependCSS(&$attr, $css) + { $attr['style'] = isset($attr['style']) ? $attr['style'] : ''; $attr['style'] = $css . $attr['style']; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/TagTransform/Font.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php similarity index 71% rename from library/HTMLPurifier/TagTransform/Font.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php index ed2463786d..7853d90bc6 100644 --- a/library/HTMLPurifier/TagTransform/Font.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Font.php @@ -17,9 +17,14 @@ */ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform { - + /** + * @type string + */ public $transform_to = 'span'; + /** + * @type array + */ protected $_size_lookup = array( '0' => 'xx-small', '1' => 'xx-small', @@ -37,8 +42,14 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform '+4' => '300%' ); - public function transform($tag, $config, $context) { - + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token_End|string + */ + public function transform($tag, $config, $context) + { if ($tag instanceof HTMLPurifier_Token_End) { $new_tag = clone $tag; $new_tag->name = $this->transform_to; @@ -63,17 +74,25 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform // handle size transform if (isset($attr['size'])) { // normalize large numbers - if ($attr['size']{0} == '+' || $attr['size']{0} == '-') { - $size = (int) $attr['size']; - if ($size < -2) $attr['size'] = '-2'; - if ($size > 4) $attr['size'] = '+4'; - } else { - $size = (int) $attr['size']; - if ($size > 7) $attr['size'] = '7'; + if ($attr['size'] !== '') { + if ($attr['size']{0} == '+' || $attr['size']{0} == '-') { + $size = (int)$attr['size']; + if ($size < -2) { + $attr['size'] = '-2'; + } + if ($size > 4) { + $attr['size'] = '+4'; + } + } else { + $size = (int)$attr['size']; + if ($size > 7) { + $attr['size'] = '7'; + } + } } if (isset($this->_size_lookup[$attr['size']])) { $prepend_style .= 'font-size:' . - $this->_size_lookup[$attr['size']] . ';'; + $this->_size_lookup[$attr['size']] . ';'; } unset($attr['size']); } @@ -89,7 +108,6 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform $new_tag->attr = $attr; return $new_tag; - } } diff --git a/library/HTMLPurifier/TagTransform/Simple.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php similarity index 61% rename from library/HTMLPurifier/TagTransform/Simple.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php index 0e36130f25..71bf10b91f 100644 --- a/library/HTMLPurifier/TagTransform/Simple.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/TagTransform/Simple.php @@ -7,19 +7,29 @@ */ class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform { - + /** + * @type string + */ protected $style; /** - * @param $transform_to Tag name to transform to. - * @param $style CSS style to add to the tag + * @param string $transform_to Tag name to transform to. + * @param string $style CSS style to add to the tag */ - public function __construct($transform_to, $style = null) { + public function __construct($transform_to, $style = null) + { $this->transform_to = $transform_to; $this->style = $style; } - public function transform($tag, $config, $context) { + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function transform($tag, $config, $context) + { $new_tag = clone $tag; $new_tag->name = $this->transform_to; if (!is_null($this->style) && @@ -29,7 +39,6 @@ class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform } return $new_tag; } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php new file mode 100644 index 0000000000..85b85e072d --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token.php @@ -0,0 +1,100 @@ +line = $l; + $this->col = $c; + } + + /** + * Convenience function for DirectLex settings line/col position. + * @param int $l + * @param int $c + */ + public function rawPosition($l, $c) + { + if ($c === -1) { + $l++; + } + $this->line = $l; + $this->col = $c; + } + + /** + * Converts a token into its corresponding node. + */ + abstract public function toNode(); +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php new file mode 100644 index 0000000000..23453c7052 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Comment.php @@ -0,0 +1,38 @@ +data = $data; + $this->line = $line; + $this->col = $col; + } + + public function toNode() { + return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Token/Empty.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php similarity index 54% rename from library/HTMLPurifier/Token/Empty.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php index 2a82b47ad1..78a95f5555 100644 --- a/library/HTMLPurifier/Token/Empty.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Empty.php @@ -5,7 +5,11 @@ */ class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag { - + public function toNode() { + $n = parent::toNode(); + $n->empty = true; + return $n; + } } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Token/End.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php similarity index 58% rename from library/HTMLPurifier/Token/End.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php index 353e79daf7..59b38fdc57 100644 --- a/library/HTMLPurifier/Token/End.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/End.php @@ -10,10 +10,15 @@ class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag { /** - * Token that started this node. Added by MakeWellFormed. Please - * do not edit this! + * Token that started this node. + * Added by MakeWellFormed. Please do not edit this! + * @type HTMLPurifier_Token */ public $start; + + public function toNode() { + throw new Exception("HTMLPurifier_Token_End->toNode not supported!"); + } } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Token/Start.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php similarity index 99% rename from library/HTMLPurifier/Token/Start.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php index e0e14fc624..019f317ada 100644 --- a/library/HTMLPurifier/Token/Start.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Start.php @@ -5,7 +5,6 @@ */ class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag { - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/Token/Tag.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php similarity index 72% rename from library/HTMLPurifier/Token/Tag.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php index 798be028ee..d643fa64e2 100644 --- a/library/HTMLPurifier/Token/Tag.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Tag.php @@ -3,13 +3,14 @@ /** * Abstract class of a tag token (start, end or empty), and its behavior. */ -class HTMLPurifier_Token_Tag extends HTMLPurifier_Token +abstract class HTMLPurifier_Token_Tag extends HTMLPurifier_Token { /** * Static bool marker that indicates the class is a tag. * * This allows us to check objects with !empty($obj->is_tag) * without having to use a function call is_a(). + * @type bool */ public $is_tag = true; @@ -19,21 +20,27 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token * @note Strictly speaking, XML tags are case sensitive, so we shouldn't * be lower-casing them, but these tokens cater to HTML tags, which are * insensitive. + * @type string */ public $name; /** * Associative array of the tag's attributes. + * @type array */ public $attr = array(); /** * Non-overloaded constructor, which lower-cases passed tag name. * - * @param $name String name. - * @param $attr Associative array of attributes. + * @param string $name String name. + * @param array $attr Associative array of attributes. + * @param int $line + * @param int $col + * @param array $armor */ - public function __construct($name, $attr = array(), $line = null, $col = null) { + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) + { $this->name = ctype_lower($name) ? $name : strtolower($name); foreach ($attr as $key => $value) { // normalization only necessary when key is not lowercase @@ -49,7 +56,12 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token } $this->attr = $attr; $this->line = $line; - $this->col = $col; + $this->col = $col; + $this->armor = $armor; + } + + public function toNode() { + return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor); } } diff --git a/library/HTMLPurifier/Token/Text.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php similarity index 54% rename from library/HTMLPurifier/Token/Text.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php index 82efd823d6..f26a1c211b 100644 --- a/library/HTMLPurifier/Token/Text.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Token/Text.php @@ -12,22 +12,42 @@ class HTMLPurifier_Token_Text extends HTMLPurifier_Token { - public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */ - public $data; /**< Parsed character data of text. */ - public $is_whitespace; /**< Bool indicating if node is whitespace. */ + /** + * @type string + */ + public $name = '#PCDATA'; + /**< PCDATA tag name compatible with DTD. */ + + /** + * @type string + */ + public $data; + /**< Parsed character data of text. */ + + /** + * @type bool + */ + public $is_whitespace; + + /**< Bool indicating if node is whitespace. */ /** * Constructor, accepts data and determines if it is whitespace. - * - * @param $data String parsed character data. + * @param string $data String parsed character data. + * @param int $line + * @param int $col */ - public function __construct($data, $line = null, $col = null) { + public function __construct($data, $line = null, $col = null) + { $this->data = $data; $this->is_whitespace = ctype_space($data); $this->line = $line; - $this->col = $col; + $this->col = $col; } + public function toNode() { + return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col); + } } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php new file mode 100644 index 0000000000..dea2446b93 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/TokenFactory.php @@ -0,0 +1,118 @@ +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 string $name Tag name + * @param array $attr Associative array of attributes + * @return HTMLPurifier_Token_Start 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 string $name Tag name + * @return HTMLPurifier_Token_End Generated HTMLPurifier_Token_End + */ + public function createEnd($name) + { + $p = clone $this->p_end; + $p->__construct($name); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Empty. + * @param string $name Tag name + * @param array $attr Associative array of attributes + * @return HTMLPurifier_Token_Empty 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 string $data Data of text token + * @return HTMLPurifier_Token_Text Generated HTMLPurifier_Token_Text + */ + public function createText($data) + { + $p = clone $this->p_text; + $p->__construct($data); + return $p; + } + + /** + * Creates a HTMLPurifier_Token_Comment. + * @param string $data Data of comment token + * @return HTMLPurifier_Token_Comment Generated HTMLPurifier_Token_Comment + */ + public function createComment($data) + { + $p = clone $this->p_comment; + $p->__construct($data); + return $p; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php new file mode 100644 index 0000000000..a5e7ae2984 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URI.php @@ -0,0 +1,314 @@ +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 HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_URIScheme 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 = $def->getDefaultScheme($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 HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool 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 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 scheme + // NOTE: It's not appropriate to check whether or not this + // scheme is in our registry, since a URIFilter may convert a + // URI that we don't allow into one we do. So instead, we just + // check if the scheme can be dropped because there is no host + // and it is our default scheme. + if (!is_null($this->scheme) && is_null($this->host) || $this->host === '') { + // support for relative paths is pretty abysmal when the + // scheme is present, so axe it when possible + $def = $config->getDefinition('URI'); + if ($def->defaultScheme === $this->scheme) { + $this->scheme = 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 + $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/'); + if (!is_null($this->host)) { // this catches $this->host === '' + // path-abempty (hier and relative) + // http://www.example.com/my/path + // //www.example.com/my/path (looks odd, but works, and + // recognized by most browsers) + // (this set is valid or invalid on a scheme by scheme + // basis, so we'll deal with it later) + // file:///my/path + // ///my/path + $this->path = $segments_encoder->encode($this->path); + } elseif ($this->path !== '') { + if ($this->path[0] === '/') { + // path-absolute (hier and relative) + // http:/my/path + // /my/path + if (strlen($this->path) >= 2 && $this->path[1] === '/') { + // This could happen if both the host gets stripped + // out + // http://my/path + // //my/path + $this->path = ''; + } else { + $this->path = $segments_encoder->encode($this->path); + } + } elseif (!is_null($this->scheme)) { + // path-rootless (hier) + // http:my/path + // Short circuit evaluation means we don't need to check nz + $this->path = $segments_encoder->encode($this->path); + } else { + // path-noscheme (relative) + // my/path + // (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; + // there is a rendering difference between a null authority + // (http:foo-bar) and an empty string authority + // (http:///foo-bar). + 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 + // One might wonder about parsing quirks from browsers after + // this reconstruction. Unfortunately, parsing behavior depends + // on what *scheme* was employed (file:///foo is handled *very* + // differently than http:///foo), so unfortunately we have to + // defer to the schemes to do the right thing. + $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; + } + + /** + * Returns true if this URL might be considered a 'local' URL given + * the current context. This is true when the host is null, or + * when it matches the host supplied to the configuration. + * + * Note that this does not do any scheme checking, so it is mostly + * only appropriate for metadata that doesn't care about protocol + * security. isBenign is probably what you actually want. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function isLocal($config, $context) + { + if ($this->host === null) { + return true; + } + $uri_def = $config->getDefinition('URI'); + if ($uri_def->host === $this->host) { + return true; + } + return false; + } + + /** + * Returns true if this URL should be considered a 'benign' URL, + * that is: + * + * - It is a local URL (isLocal), and + * - It has a equal or better level of security + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function isBenign($config, $context) + { + if (!$this->isLocal($config, $context)) { + return false; + } + + $scheme_obj = $this->getSchemeObj($config, $context); + if (!$scheme_obj) { + return false; + } // conservative approach + + $current_scheme_obj = $config->getDefinition('URI')->getDefaultScheme($config, $context); + if ($current_scheme_obj->secure) { + if (!$scheme_obj->secure) { + return false; + } + } + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIDefinition.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php similarity index 70% rename from library/HTMLPurifier/URIDefinition.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php index ea2b8fe245..e0bd8bcca8 100644 --- a/library/HTMLPurifier/URIDefinition.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIDefinition.php @@ -23,19 +23,24 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition */ public $defaultScheme; - public function __construct() { + public function __construct() + { $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); + $this->registerFilter(new HTMLPurifier_URIFilter_DisableResources()); $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); + $this->registerFilter(new HTMLPurifier_URIFilter_SafeIframe()); $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); } - public function registerFilter($filter) { + public function registerFilter($filter) + { $this->registeredFilters[$filter->name] = $filter; } - public function addFilter($filter, $config) { + public function addFilter($filter, $config) + { $r = $filter->prepare($config); if ($r === false) return; // null is ok, for backwards compat if ($filter->post) { @@ -45,22 +50,29 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition } } - protected function doSetup($config) { + protected function doSetup($config) + { $this->setupMemberVariables($config); $this->setupFilters($config); } - protected function setupFilters($config) { + protected function setupFilters($config) + { foreach ($this->registeredFilters as $name => $filter) { - $conf = $config->get('URI.' . $name); - if ($conf !== false && $conf !== null) { + if ($filter->always_load) { $this->addFilter($filter, $config); + } else { + $conf = $config->get('URI.' . $name); + if ($conf !== false && $conf !== null) { + $this->addFilter($filter, $config); + } } } unset($this->registeredFilters); } - protected function setupMemberVariables($config) { + protected function setupMemberVariables($config) + { $this->host = $config->get('URI.Host'); $base_uri = $config->get('URI.Base'); if (!is_null($base_uri)) { @@ -72,7 +84,13 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); } - public function filter(&$uri, $config, $context) { + public function getDefaultScheme($config, $context) + { + return HTMLPurifier_URISchemeRegistry::instance()->getScheme($this->defaultScheme, $config, $context); + } + + public function filter(&$uri, $config, $context) + { foreach ($this->filters as $name => $f) { $result = $f->filter($uri, $config, $context); if (!$result) return false; @@ -80,7 +98,8 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition return true; } - public function postFilter(&$uri, $config, $context) { + public function postFilter(&$uri, $config, $context) + { foreach ($this->postFilters as $name => $f) { $result = $f->filter($uri, $config, $context); if (!$result) return false; diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php new file mode 100644 index 0000000000..09724e9f41 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter.php @@ -0,0 +1,74 @@ +getDefinition('URI')->host; + if ($our_host !== null) { + $this->ourHostParts = array_reverse(explode('.', $our_host)); + } + } + + /** + * @param HTMLPurifier_URI $uri Reference + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + 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 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php new file mode 100644 index 0000000000..c6562169e0 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableExternalResources.php @@ -0,0 +1,25 @@ +get('EmbeddedURI', true)) { + return true; + } + return parent::filter($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php new file mode 100644 index 0000000000..d5c412c444 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/DisableResources.php @@ -0,0 +1,22 @@ +get('EmbeddedURI', true); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php new file mode 100644 index 0000000000..a6645c17ee --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/HostBlacklist.php @@ -0,0 +1,46 @@ +blacklist = $config->get('URI.HostBlacklist'); + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + 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 diff --git a/library/HTMLPurifier/URIFilter/MakeAbsolute.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php similarity index 71% rename from library/HTMLPurifier/URIFilter/MakeAbsolute.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php index f46ab2630d..c507bbff8e 100644 --- a/library/HTMLPurifier/URIFilter/MakeAbsolute.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/MakeAbsolute.php @@ -4,14 +4,35 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'MakeAbsolute'; + + /** + * @type + */ protected $base; + + /** + * @type array + */ protected $basePathStack = array(); - public function prepare($config) { + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function prepare($config) + { $def = $config->getDefinition('URI'); $this->base = $def->base; if (is_null($this->base)) { - trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING); + trigger_error( + 'URI.MakeAbsolute is being ignored due to lack of ' . + 'value for URI.Base configuration', + E_USER_WARNING + ); return false; } $this->base->fragment = null; // fragment is invalid for base URI @@ -21,19 +42,29 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter $this->basePathStack = $stack; return true; } - public function filter(&$uri, $config, $context) { - if (is_null($this->base)) return true; // abort early - if ( - $uri->path === '' && is_null($uri->scheme) && - is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment) - ) { + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (is_null($this->base)) { + return true; + } // abort early + if ($uri->path === '' && is_null($uri->scheme) && + is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) { // reference to current document $uri = clone $this->base; return true; } if (!is_null($uri->scheme)) { // absolute URI already: don't change - if (!is_null($uri->host)) return true; + if (!is_null($uri->host)) { + return true; + } $scheme_obj = $uri->getSchemeObj($config, $context); if (!$scheme_obj) { // scheme not recognized @@ -66,22 +97,33 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter } // re-combine $uri->scheme = $this->base->scheme; - if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo; - if (is_null($uri->host)) $uri->host = $this->base->host; - if (is_null($uri->port)) $uri->port = $this->base->port; + if (is_null($uri->userinfo)) { + $uri->userinfo = $this->base->userinfo; + } + if (is_null($uri->host)) { + $uri->host = $this->base->host; + } + if (is_null($uri->port)) { + $uri->port = $this->base->port; + } return true; } /** * Resolve dots and double-dots in a path stack + * @param array $stack + * @return array */ - private function _collapseStack($stack) { + private function _collapseStack($stack) + { $result = array(); $is_folder = false; for ($i = 0; isset($stack[$i]); $i++) { $is_folder = false; // absorb an internally duplicated slash - if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue; + if ($stack[$i] == '' && $i && isset($stack[$i + 1])) { + continue; + } if ($stack[$i] == '..') { if (!empty($result)) { $segment = array_pop($result); @@ -106,7 +148,9 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter } $result[] = $stack[$i]; } - if ($is_folder) $result[] = ''; + if ($is_folder) { + $result[] = ''; + } return $result; } } diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php new file mode 100644 index 0000000000..6e03315a17 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/Munge.php @@ -0,0 +1,115 @@ +target = $config->get('URI.' . $this->name); + $this->parser = new HTMLPurifier_URIParser(); + $this->doEmbed = $config->get('URI.MungeResources'); + $this->secretKey = $config->get('URI.MungeSecretKey'); + if ($this->secretKey && !function_exists('hash_hmac')) { + throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support."); + } + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + 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 (!$scheme_obj->browsable) { + return true; + } // ignore non-browseable schemes, since we can't munge those in a reasonable way + if ($uri->isBenign($config, $context)) { + return true; + } // don't redirect if a benign URL + + $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; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + */ + 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'] = hash_hmac("sha256", $string, $this->secretKey); + } + } +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php new file mode 100644 index 0000000000..f609c47a34 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIFilter/SafeIframe.php @@ -0,0 +1,68 @@ +regexp = $config->get('URI.SafeIframeRegexp'); + return true; + } + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + // check if filter not applicable + if (!$config->get('HTML.SafeIframe')) { + return true; + } + // check if the filter should actually trigger + if (!$context->get('EmbeddedURI', true)) { + return true; + } + $token = $context->get('CurrentToken', true); + if (!($token && $token->name == 'iframe')) { + return true; + } + // check if we actually have some whitelists enabled + if ($this->regexp === null) { + return false; + } + // actually check the whitelists + return preg_match($this->regexp, $uri->toString()); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php similarity index 94% rename from library/HTMLPurifier/URIParser.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php index 7179e4ab89..0e7381a07b 100644 --- a/library/HTMLPurifier/URIParser.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIParser.php @@ -12,7 +12,8 @@ class HTMLPurifier_URIParser */ protected $percentEncoder; - public function __construct() { + public function __construct() + { $this->percentEncoder = new HTMLPurifier_PercentEncoder(); } @@ -22,15 +23,15 @@ class HTMLPurifier_URIParser * @return HTMLPurifier_URI representation of URI. This representation has * not been validated yet and may not conform to RFC. */ - public function parse($uri) { - + public function parse($uri) + { $uri = $this->percentEncoder->normalize($uri); // Regexp is as per Appendix B. // Note that ["<>] are an addition to the RFC's recommended // characters, because they represent external delimeters. $r_URI = '!'. - '(([^:/?#"<>]+):)?'. // 2. Scheme + '(([a-zA-Z0-9\.\+\-]+):)?'. // 2. Scheme '(//([^/?#"<>]*))?'. // 4. Authority '([^?#"<>]*)'. // 5. Path '(\?([^#"<>]*))?'. // 7. Query diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php new file mode 100644 index 0000000000..fe9e82cf26 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme.php @@ -0,0 +1,102 @@ +, resolves edge cases + * with making relative URIs absolute + * @type bool + */ + public $hierarchical = false; + + /** + * Whether or not the URI may omit a hostname when the scheme is + * explicitly specified, ala file:///path/to/file. As of writing, + * 'file' is the only scheme that browsers support his properly. + * @type bool + */ + public $may_omit_host = false; + + /** + * Validates the components of a URI for a specific scheme. + * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool success or failure + */ + abstract public function doValidate(&$uri, $config, $context); + + /** + * Public interface for validating components of a URI. Performs a + * bunch of default actions. Don't overload this method. + * @param HTMLPurifier_URI $uri Reference to a HTMLPurifier_URI object + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool success or failure + */ + public function validate(&$uri, $config, $context) + { + if ($this->default_port == $uri->port) { + $uri->port = null; + } + // kludge: browsers do funny things when the scheme but not the + // authority is set + if (!$this->may_omit_host && + // if the scheme is present, a missing host is always in error + (!is_null($uri->scheme) && ($uri->host === '' || is_null($uri->host))) || + // if the scheme is not present, a *blank* host is in error, + // since this translates into '///path' which most browsers + // interpret as being 'http://path'. + (is_null($uri->scheme) && $uri->host === '') + ) { + do { + if (is_null($uri->scheme)) { + if (substr($uri->path, 0, 2) != '//') { + $uri->host = null; + break; + } + // URI is '////path', so we cannot nullify the + // host to preserve semantics. Try expanding the + // hostname instead (fall through) + } + // first see if we can manually insert a hostname + $host = $config->get('URI.Host'); + if (!is_null($host)) { + $uri->host = $host; + } else { + // we can't do anything sensible, reject the URL. + return false; + } + } while (false); + } + return $this->doValidate($uri, $config, $context); + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/data.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php similarity index 74% rename from library/HTMLPurifier/URIScheme/data.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php index b7f1989cbf..6ebca49848 100644 --- a/library/HTMLPurifier/URIScheme/data.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/data.php @@ -3,18 +3,38 @@ /** * Implements data: URI for base64 encoded images supported by GD. */ -class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme +{ + /** + * @type bool + */ public $browsable = true; + + /** + * @type array + */ public $allowed_types = array( // you better write validation code for other types if you // decide to allow them 'image/jpeg' => true, 'image/gif' => true, 'image/png' => true, - ); + ); + // this is actually irrelevant since we only write out the path + // component + /** + * @type bool + */ + public $may_omit_host = true; - public function validate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $result = explode(',', $uri->path, 2); $is_base64 = false; $charset = null; @@ -23,7 +43,7 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { list($metadata, $data) = $result; // do some legwork on the metadata $metas = explode(';', $metadata); - while(!empty($metas)) { + while (!empty($metas)) { $cur = array_shift($metas); if ($cur == 'base64') { $is_base64 = true; @@ -32,10 +52,14 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { if (substr($cur, 0, 8) == 'charset=') { // doesn't match if there are arbitrary spaces, but // whatever dude - if ($charset !== null) continue; // garbage + if ($charset !== null) { + continue; + } // garbage $charset = substr($cur, 8); // not used } else { - if ($content_type !== null) continue; // garbage + if ($content_type !== null) { + continue; + } // garbage $content_type = $cur; } } @@ -61,11 +85,15 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { file_put_contents($file, $raw_data); if (function_exists('exif_imagetype')) { $image_code = exif_imagetype($file); + unlink($file); } elseif (function_exists('getimagesize')) { set_error_handler(array($this, 'muteErrorHandler')); $info = getimagesize($file); restore_error_handler(); - if ($info == false) return false; + unlink($file); + if ($info == false) { + return false; + } $image_code = $info[2]; } else { trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR); @@ -74,7 +102,9 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { if ($real_content_type != $content_type) { // we're nice guys; if the content type is something else we // support, change it over - if (empty($this->allowed_types[$real_content_type])) return false; + if (empty($this->allowed_types[$real_content_type])) { + return false; + } $content_type = $real_content_type; } // ok, it's kosher, rewrite what we need @@ -87,7 +117,11 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { return true; } - public function muteErrorHandler($errno, $errstr) {} - + /** + * @param int $errno + * @param string $errstr + */ + public function muteErrorHandler($errno, $errstr) + { + } } - diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php new file mode 100644 index 0000000000..215be4ba80 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/file.php @@ -0,0 +1,44 @@ +userinfo = null; + // file:// makes no provisions for accessing the resource + $uri->port = null; + // While it seems to work on Firefox, the querystring has + // no possible effect and is thus stripped. + $uri->query = null; + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/ftp.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php similarity index 74% rename from library/HTMLPurifier/URIScheme/ftp.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php index 5849bf7ff0..1eb43ee5c4 100644 --- a/library/HTMLPurifier/URIScheme/ftp.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/ftp.php @@ -3,15 +3,32 @@ /** * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738. */ -class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme +{ + /** + * @type int + */ public $default_port = 21; + + /** + * @type bool + */ public $browsable = true; // usually + + /** + * @type bool + */ public $hierarchical = true; - public function validate(&$uri, $config, $context) { - parent::validate($uri, $config, $context); - $uri->query = null; + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { + $uri->query = null; // typecode check $semicolon_pos = strrpos($uri->path, ';'); // reverse @@ -34,10 +51,8 @@ class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme { $uri->path = str_replace(';', '%3B', $uri->path); $uri->path .= $type_ret; } - return true; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/http.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php similarity index 50% rename from library/HTMLPurifier/URIScheme/http.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php index b097a31d6a..ce69ec438a 100644 --- a/library/HTMLPurifier/URIScheme/http.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/http.php @@ -3,18 +3,34 @@ /** * Validates http (HyperText Transfer Protocol) as defined by RFC 2616 */ -class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme +{ + /** + * @type int + */ public $default_port = 80; + + /** + * @type bool + */ public $browsable = true; + + /** + * @type bool + */ public $hierarchical = true; - public function validate(&$uri, $config, $context) { - parent::validate($uri, $config, $context); + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; return true; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/https.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php similarity index 65% rename from library/HTMLPurifier/URIScheme/https.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php index 29e380919f..0e96882db9 100644 --- a/library/HTMLPurifier/URIScheme/https.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/https.php @@ -3,10 +3,16 @@ /** * Validates https (Secure HTTP) according to http scheme. */ -class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http { - +class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http +{ + /** + * @type int + */ public $default_port = 443; - + /** + * @type bool + */ + public $secure = true; } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URIScheme/mailto.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php similarity index 63% rename from library/HTMLPurifier/URIScheme/mailto.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php index c1e2cd5aae..c3a6b602a7 100644 --- a/library/HTMLPurifier/URIScheme/mailto.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php @@ -9,19 +9,32 @@ * @todo Filter allowed query parameters */ -class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme +{ + /** + * @type bool + */ public $browsable = false; - public function validate(&$uri, $config, $context) { - parent::validate($uri, $config, $context); + /** + * @type bool + */ + public $may_omit_host = true; + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; $uri->host = null; $uri->port = null; // we need to validate path against RFC 2368's addr-spec return true; } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php new file mode 100644 index 0000000000..7490927d63 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php @@ -0,0 +1,35 @@ +userinfo = null; + $uri->host = null; + $uri->port = null; + $uri->query = null; + // typecode check needed on path + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php new file mode 100644 index 0000000000..f211d715e9 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php @@ -0,0 +1,32 @@ +userinfo = null; + $uri->query = null; + return true; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/URISchemeRegistry.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php similarity index 58% rename from library/HTMLPurifier/URISchemeRegistry.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php index 576bf7b6d1..4ac8a0b76e 100644 --- a/library/HTMLPurifier/URISchemeRegistry.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/URISchemeRegistry.php @@ -8,12 +8,14 @@ class HTMLPurifier_URISchemeRegistry /** * Retrieve sole instance of the registry. - * @param $prototype Optional prototype to overload sole instance with, + * @param HTMLPurifier_URISchemeRegistry $prototype Optional prototype to overload sole instance with, * or bool true to reset to default registry. + * @return HTMLPurifier_URISchemeRegistry * @note Pass a registry object $prototype with a compatible interface and * the function will copy it and return it all further times. */ - public static function instance($prototype = null) { + public static function instance($prototype = null) + { static $instance = null; if ($prototype !== null) { $instance = $prototype; @@ -25,17 +27,22 @@ class HTMLPurifier_URISchemeRegistry /** * Cache of retrieved schemes. + * @type HTMLPurifier_URIScheme[] */ protected $schemes = array(); /** * Retrieves a scheme validator object - * @param $scheme String scheme name like http or mailto - * @param $config HTMLPurifier_Config object - * @param $config HTMLPurifier_Context object + * @param string $scheme String scheme name like http or mailto + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_URIScheme */ - public function getScheme($scheme, $config, $context) { - if (!$config) $config = HTMLPurifier_Config::createDefault(); + public function getScheme($scheme, $config, $context) + { + if (!$config) { + $config = HTMLPurifier_Config::createDefault(); + } // important, otherwise attacker could include arbitrary file $allowed_schemes = $config->get('URI.AllowedSchemes'); @@ -45,24 +52,30 @@ class HTMLPurifier_URISchemeRegistry return; } - if (isset($this->schemes[$scheme])) return $this->schemes[$scheme]; - if (!isset($allowed_schemes[$scheme])) return; + if (isset($this->schemes[$scheme])) { + return $this->schemes[$scheme]; + } + if (!isset($allowed_schemes[$scheme])) { + return; + } $class = 'HTMLPurifier_URIScheme_' . $scheme; - if (!class_exists($class)) return; + if (!class_exists($class)) { + return; + } $this->schemes[$scheme] = new $class(); return $this->schemes[$scheme]; } /** * Registers a custom scheme to the cache, bypassing reflection. - * @param $scheme Scheme name - * @param $scheme_obj HTMLPurifier_URIScheme object + * @param string $scheme Scheme name + * @param HTMLPurifier_URIScheme $scheme_obj */ - public function register($scheme, $scheme_obj) { + public function register($scheme, $scheme_obj) + { $this->schemes[$scheme] = $scheme_obj; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/UnitConverter.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php similarity index 75% rename from library/HTMLPurifier/UnitConverter.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php index 545d426220..166f3bf306 100644 --- a/library/HTMLPurifier/UnitConverter.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php @@ -37,20 +37,24 @@ class HTMLPurifier_UnitConverter /** * Minimum bcmath precision for output. + * @type int */ protected $outputPrecision; /** * Bcmath precision for internal calculations. + * @type int */ protected $internalPrecision; /** - * Whether or not BCMath is available + * Whether or not BCMath is available. + * @type bool */ private $bcmath; - public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) { + public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) + { $this->outputPrecision = $output_precision; $this->internalPrecision = $internal_precision; $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); @@ -63,6 +67,7 @@ class HTMLPurifier_UnitConverter * it before passing it here! * @param string $to_unit * Unit to convert to. + * @return HTMLPurifier_Length|bool * @note * About precision: This conversion function pays very special * attention to the incoming precision of values and attempts @@ -74,11 +79,13 @@ class HTMLPurifier_UnitConverter * and this causes some decimals to be excluded, those * decimals will be added on. */ - public function convert($length, $to_unit) { + public function convert($length, $to_unit) + { + if (!$length->isValid()) { + return false; + } - if (!$length->isValid()) return false; - - $n = $length->getN(); + $n = $length->getN(); $unit = $length->getUnit(); if ($n === '0' || $unit === false) { @@ -87,21 +94,29 @@ class HTMLPurifier_UnitConverter $state = $dest_state = false; foreach (self::$units as $k => $x) { - if (isset($x[$unit])) $state = $k; - if (isset($x[$to_unit])) $dest_state = $k; + if (isset($x[$unit])) { + $state = $k; + } + if (isset($x[$to_unit])) { + $dest_state = $k; + } + } + if (!$state || !$dest_state) { + return false; } - if (!$state || !$dest_state) return false; // Some calculations about the initial precision of the number; // this will be useful when we need to do final rounding. $sigfigs = $this->getSigFigs($n); - if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision; + if ($sigfigs < $this->outputPrecision) { + $sigfigs = $this->outputPrecision; + } // BCMath's internal precision deals only with decimals. Use // our default if the initial number has no decimals, or increase // it by how ever many decimals, thus, the number of guard digits // will always be greater than or equal to internalPrecision. - $log = (int) floor(log(abs($n), 10)); + $log = (int)floor(log(abs($n), 10)); $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision for ($i = 0; $i < 2; $i++) { @@ -152,14 +167,18 @@ class HTMLPurifier_UnitConverter } // Post-condition: $unit == $to_unit - if ($unit !== $to_unit) return false; + if ($unit !== $to_unit) { + return false; + } // Useful for debugging: //echo "
            n";
                     //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n
            \n"; $n = $this->round($n, $sigfigs); - if (strpos($n, '.') !== false) $n = rtrim($n, '0'); + if (strpos($n, '.') !== false) { + $n = rtrim($n, '0'); + } $n = rtrim($n, '.'); return new HTMLPurifier_Length($n, $unit); @@ -170,53 +189,84 @@ class HTMLPurifier_UnitConverter * @param string $n Decimal number * @return int number of sigfigs */ - public function getSigFigs($n) { + public function getSigFigs($n) + { $n = ltrim($n, '0+-'); $dp = strpos($n, '.'); // decimal position if ($dp === false) { $sigfigs = strlen(rtrim($n, '0')); } else { $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character - if ($dp !== 0) $sigfigs--; + if ($dp !== 0) { + $sigfigs--; + } } return $sigfigs; } /** * Adds two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string */ - private function add($s1, $s2, $scale) { - if ($this->bcmath) return bcadd($s1, $s2, $scale); - else return $this->scale($s1 + $s2, $scale); + private function add($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcadd($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 + (float)$s2, $scale); + } } /** * Multiples two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string */ - private function mul($s1, $s2, $scale) { - if ($this->bcmath) return bcmul($s1, $s2, $scale); - else return $this->scale($s1 * $s2, $scale); + private function mul($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcmul($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 * (float)$s2, $scale); + } } /** * Divides two numbers, using arbitrary precision when available. + * @param string $s1 + * @param string $s2 + * @param int $scale + * @return string */ - private function div($s1, $s2, $scale) { - if ($this->bcmath) return bcdiv($s1, $s2, $scale); - else return $this->scale($s1 / $s2, $scale); + private function div($s1, $s2, $scale) + { + if ($this->bcmath) { + return bcdiv($s1, $s2, $scale); + } else { + return $this->scale((float)$s1 / (float)$s2, $scale); + } } /** * Rounds a number according to the number of sigfigs it should have, * using arbitrary precision when available. + * @param float $n + * @param int $sigfigs + * @return string */ - private function round($n, $sigfigs) { - $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1 + private function round($n, $sigfigs) + { + $new_log = (int)floor(log(abs($n), 10)); // Number of digits left of decimal - 1 $rp = $sigfigs - $new_log - 1; // Number of decimal places needed $neg = $n < 0 ? '-' : ''; // Negative sign if ($this->bcmath) { if ($rp >= 0) { - $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); + $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); $n = bcdiv($n, '1', $rp); } else { // This algorithm partially depends on the standardized @@ -232,23 +282,26 @@ class HTMLPurifier_UnitConverter /** * Scales a float to $scale digits right of decimal point, like BCMath. + * @param float $r + * @param int $scale + * @return string */ - private function scale($r, $scale) { + private function scale($r, $scale) + { if ($scale < 0) { // The f sprintf type doesn't support negative numbers, so we // need to cludge things manually. First get the string. - $r = sprintf('%.0f', (float) $r); + $r = sprintf('%.0f', (float)$r); // Due to floating point precision loss, $r will more than likely // look something like 4652999999999.9234. We grab one more digit // than we need to precise from $r and then use that to round // appropriately. - $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1); + $precise = (string)round(substr($r, 0, strlen($r) + $scale), -1); // Now we return it, truncating the zero that was rounded off. return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); } - return sprintf('%.' . $scale . 'f', (float) $r); + return sprintf('%.' . $scale . 'f', (float)$r); } - } // vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php new file mode 100644 index 0000000000..50cba6910d --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php @@ -0,0 +1,198 @@ + self::STRING, + 'istring' => self::ISTRING, + 'text' => self::TEXT, + 'itext' => self::ITEXT, + 'int' => self::INT, + 'float' => self::FLOAT, + 'bool' => self::BOOL, + 'lookup' => self::LOOKUP, + 'list' => self::ALIST, + 'hash' => self::HASH, + 'mixed' => self::MIXED + ); + + /** + * Lookup table of types that are string, and can have aliases or + * allowed value lists. + */ + public static $stringTypes = array( + self::STRING => true, + self::ISTRING => true, + self::TEXT => true, + self::ITEXT => true, + ); + + /** + * Validate a variable according to type. + * It may return NULL as a valid type if $allow_null is true. + * + * @param mixed $var Variable to validate + * @param int $type Type of variable, see HTMLPurifier_VarParser->types + * @param bool $allow_null Whether or not to permit null as a value + * @return string Validated and type-coerced variable + * @throws HTMLPurifier_VarParserException + */ + final public function parse($var, $type, $allow_null = false) + { + if (is_string($type)) { + if (!isset(HTMLPurifier_VarParser::$types[$type])) { + throw new HTMLPurifier_VarParserException("Invalid type '$type'"); + } else { + $type = HTMLPurifier_VarParser::$types[$type]; + } + } + $var = $this->parseImplementation($var, $type, $allow_null); + if ($allow_null && $var === null) { + return null; + } + // These are basic checks, to make sure nothing horribly wrong + // happened in our implementations. + switch ($type) { + case (self::STRING): + case (self::ISTRING): + case (self::TEXT): + case (self::ITEXT): + if (!is_string($var)) { + break; + } + if ($type == self::ISTRING || $type == self::ITEXT) { + $var = strtolower($var); + } + return $var; + case (self::INT): + if (!is_int($var)) { + break; + } + return $var; + case (self::FLOAT): + if (!is_float($var)) { + break; + } + return $var; + case (self::BOOL): + if (!is_bool($var)) { + break; + } + return $var; + case (self::LOOKUP): + case (self::ALIST): + case (self::HASH): + if (!is_array($var)) { + break; + } + if ($type === self::LOOKUP) { + foreach ($var as $k) { + if ($k !== true) { + $this->error('Lookup table contains value other than true'); + } + } + } elseif ($type === self::ALIST) { + $keys = array_keys($var); + if (array_keys($keys) !== $keys) { + $this->error('Indices for list are not uniform'); + } + } + return $var; + case (self::MIXED): + return $var; + default: + $this->errorInconsistent(get_class($this), $type); + } + $this->errorGeneric($var, $type); + } + + /** + * Actually implements the parsing. Base implementation does not + * do anything to $var. Subclasses should overload this! + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return string + */ + protected function parseImplementation($var, $type, $allow_null) + { + return $var; + } + + /** + * Throws an exception. + * @throws HTMLPurifier_VarParserException + */ + protected function error($msg) + { + throw new HTMLPurifier_VarParserException($msg); + } + + /** + * Throws an inconsistency exception. + * @note This should not ever be called. It would be called if we + * extend the allowed values of HTMLPurifier_VarParser without + * updating subclasses. + * @param string $class + * @param int $type + * @throws HTMLPurifier_Exception + */ + protected function errorInconsistent($class, $type) + { + throw new HTMLPurifier_Exception( + "Inconsistency in $class: " . HTMLPurifier_VarParser::getTypeName($type) . + " not implemented" + ); + } + + /** + * Generic error for if a type didn't work. + * @param mixed $var + * @param int $type + */ + protected function errorGeneric($var, $type) + { + $vtype = gettype($var); + $this->error("Expected type " . HTMLPurifier_VarParser::getTypeName($type) . ", got $vtype"); + } + + /** + * @param int $type + * @return string + */ + public static function getTypeName($type) + { + static $lookup; + if (!$lookup) { + // Lazy load the alternative lookup table + $lookup = array_flip(HTMLPurifier_VarParser::$types); + } + if (!isset($lookup[$type])) { + return 'unknown'; + } + return $lookup[$type]; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/VarParser/Flexible.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php similarity index 56% rename from library/HTMLPurifier/VarParser/Flexible.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php index c954250e9f..b15016c5b2 100644 --- a/library/HTMLPurifier/VarParser/Flexible.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php @@ -7,28 +7,41 @@ */ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser { - - protected function parseImplementation($var, $type, $allow_null) { - if ($allow_null && $var === null) return null; + /** + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return array|bool|float|int|mixed|null|string + * @throws HTMLPurifier_VarParserException + */ + protected function parseImplementation($var, $type, $allow_null) + { + if ($allow_null && $var === null) { + return null; + } switch ($type) { // Note: if code "breaks" from the switch, it triggers a generic // exception to be thrown. Specific errors can be specifically // done here. - case self::MIXED : - case self::ISTRING : - case self::STRING : - case self::TEXT : - case self::ITEXT : + case self::MIXED: + case self::ISTRING: + case self::STRING: + case self::TEXT: + case self::ITEXT: return $var; - case self::INT : - if (is_string($var) && ctype_digit($var)) $var = (int) $var; + case self::INT: + if (is_string($var) && ctype_digit($var)) { + $var = (int)$var; + } return $var; - case self::FLOAT : - if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var; + case self::FLOAT: + if ((is_string($var) && is_numeric($var)) || is_int($var)) { + $var = (float)$var; + } return $var; - case self::BOOL : + case self::BOOL: if (is_int($var) && ($var === 0 || $var === 1)) { - $var = (bool) $var; + $var = (bool)$var; } elseif (is_string($var)) { if ($var == 'on' || $var == 'true' || $var == '1') { $var = true; @@ -39,48 +52,70 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser } } return $var; - case self::ALIST : - case self::HASH : - case self::LOOKUP : + case self::ALIST: + case self::HASH: + case self::LOOKUP: if (is_string($var)) { // special case: technically, this is an array with // a single empty string item, but having an empty // array is more intuitive - if ($var == '') return array(); + if ($var == '') { + return array(); + } if (strpos($var, "\n") === false && strpos($var, "\r") === false) { // simplistic string to array method that only works // for simple lists of tag names or alphanumeric characters - $var = explode(',',$var); + $var = explode(',', $var); } else { $var = preg_split('/(,|[\n\r]+)/', $var); } // remove spaces - foreach ($var as $i => $j) $var[$i] = trim($j); + foreach ($var as $i => $j) { + $var[$i] = trim($j); + } if ($type === self::HASH) { // key:value,key2:value2 $nvar = array(); foreach ($var as $keypair) { $c = explode(':', $keypair, 2); - if (!isset($c[1])) continue; - $nvar[$c[0]] = $c[1]; + if (!isset($c[1])) { + continue; + } + $nvar[trim($c[0])] = trim($c[1]); } $var = $nvar; } } - if (!is_array($var)) break; + if (!is_array($var)) { + break; + } $keys = array_keys($var); if ($keys === array_keys($keys)) { - if ($type == self::ALIST) return $var; - elseif ($type == self::LOOKUP) { + if ($type == self::ALIST) { + return $var; + } elseif ($type == self::LOOKUP) { $new = array(); foreach ($var as $key) { $new[$key] = true; } return $new; - } else break; + } else { + break; + } + } + if ($type === self::ALIST) { + trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); + return array_values($var); } if ($type === self::LOOKUP) { foreach ($var as $key => $value) { + if ($value !== true) { + trigger_error( + "Lookup array has non-true value at key '$key'; " . + "maybe your input array was not indexed numerically", + E_USER_WARNING + ); + } $var[$key] = true; } } @@ -90,7 +125,6 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser } $this->errorGeneric($var, $type); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/VarParser/Native.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php similarity index 67% rename from library/HTMLPurifier/VarParser/Native.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php index b02a6de54c..f11c318efb 100644 --- a/library/HTMLPurifier/VarParser/Native.php +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php @@ -8,11 +8,24 @@ class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser { - protected function parseImplementation($var, $type, $allow_null) { + /** + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return null|string + */ + protected function parseImplementation($var, $type, $allow_null) + { return $this->evalExpression($var); } - protected function evalExpression($expr) { + /** + * @param string $expr + * @return mixed + * @throws HTMLPurifier_VarParserException + */ + protected function evalExpression($expr) + { $var = null; $result = eval("\$var = $expr;"); if ($result === false) { @@ -20,7 +33,6 @@ class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser } return $var; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/VarParserException.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php similarity index 100% rename from library/HTMLPurifier/VarParserException.php rename to library/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php diff --git a/library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php b/library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php new file mode 100644 index 0000000000..6e21ea0703 --- /dev/null +++ b/library/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php @@ -0,0 +1,157 @@ +front = $front; + $this->back = $back; + } + + /** + * Creates a zipper from an array, with a hole in the + * 0-index position. + * @param Array to zipper-ify. + * @return Tuple of zipper and element of first position. + */ + static public function fromArray($array) { + $z = new self(array(), array_reverse($array)); + $t = $z->delete(); // delete the "dummy hole" + return array($z, $t); + } + + /** + * Convert zipper back into a normal array, optionally filling in + * the hole with a value. (Usually you should supply a $t, unless you + * are at the end of the array.) + */ + public function toArray($t = NULL) { + $a = $this->front; + if ($t !== NULL) $a[] = $t; + for ($i = count($this->back)-1; $i >= 0; $i--) { + $a[] = $this->back[$i]; + } + return $a; + } + + /** + * Move hole to the next element. + * @param $t Element to fill hole with + * @return Original contents of new hole. + */ + public function next($t) { + if ($t !== NULL) array_push($this->front, $t); + return empty($this->back) ? NULL : array_pop($this->back); + } + + /** + * Iterated hole advancement. + * @param $t Element to fill hole with + * @param $i How many forward to advance hole + * @return Original contents of new hole, i away + */ + public function advance($t, $n) { + for ($i = 0; $i < $n; $i++) { + $t = $this->next($t); + } + return $t; + } + + /** + * Move hole to the previous element + * @param $t Element to fill hole with + * @return Original contents of new hole. + */ + public function prev($t) { + if ($t !== NULL) array_push($this->back, $t); + return empty($this->front) ? NULL : array_pop($this->front); + } + + /** + * Delete contents of current hole, shifting hole to + * next element. + * @return Original contents of new hole. + */ + public function delete() { + return empty($this->back) ? NULL : array_pop($this->back); + } + + /** + * Returns true if we are at the end of the list. + * @return bool + */ + public function done() { + return empty($this->back); + } + + /** + * Insert element before hole. + * @param Element to insert + */ + public function insertBefore($t) { + if ($t !== NULL) array_push($this->front, $t); + } + + /** + * Insert element after hole. + * @param Element to insert + */ + public function insertAfter($t) { + if ($t !== NULL) array_push($this->back, $t); + } + + /** + * Splice in multiple elements at hole. Functional specification + * in terms of array_splice: + * + * $arr1 = $arr; + * $old1 = array_splice($arr1, $i, $delete, $replacement); + * + * list($z, $t) = HTMLPurifier_Zipper::fromArray($arr); + * $t = $z->advance($t, $i); + * list($old2, $t) = $z->splice($t, $delete, $replacement); + * $arr2 = $z->toArray($t); + * + * assert($old1 === $old2); + * assert($arr1 === $arr2); + * + * NB: the absolute index location after this operation is + * *unchanged!* + * + * @param Current contents of hole. + */ + public function splice($t, $delete, $replacement) { + // delete + $old = array(); + $r = $t; + for ($i = $delete; $i > 0; $i--) { + $old[] = $r; + $r = $this->delete(); + } + // insert + for ($i = count($replacement)-1; $i >= 0; $i--) { + $this->insertAfter($r); + $r = $replacement[$i]; + } + return array($old, $r); + } +} diff --git a/library/ezyang/htmlpurifier/package.php b/library/ezyang/htmlpurifier/package.php new file mode 100644 index 0000000000..bfef93622d --- /dev/null +++ b/library/ezyang/htmlpurifier/package.php @@ -0,0 +1,61 @@ +setOptions( + array( + 'baseinstalldir' => '/', + 'packagefile' => 'package.xml', + 'packagedirectory' => realpath(dirname(__FILE__) . '/library'), + 'filelistgenerator' => 'file', + 'include' => array('*'), + 'dir_roles' => array('/' => 'php'), // hack to put *.ser files in the right place + 'ignore' => array( + 'HTMLPurifier.standalone.php', + 'HTMLPurifier.path.php', + '*.tar.gz', + '*.tgz', + 'standalone/' + ), + ) +); + +$pkg->setPackage('HTMLPurifier'); +$pkg->setLicense('LGPL', 'http://www.gnu.org/licenses/lgpl.html'); +$pkg->setSummary('Standards-compliant HTML filter'); +$pkg->setDescription( + 'HTML Purifier is an HTML filter that will remove all malicious code + (better known as XSS) with a thoroughly audited, secure yet permissive + whitelist and will also make sure your documents are standards + compliant.' +); + +$pkg->addMaintainer('lead', 'ezyang', 'Edward Z. Yang', 'admin@htmlpurifier.org', 'yes'); + +$version = trim(file_get_contents('VERSION')); +$api_version = substr($version, 0, strrpos($version, '.')); + +$pkg->setChannel('htmlpurifier.org'); +$pkg->setAPIVersion($api_version); +$pkg->setAPIStability('stable'); +$pkg->setReleaseVersion($version); +$pkg->setReleaseStability('stable'); + +$pkg->addRelease(); + +$pkg->setNotes(file_get_contents('WHATSNEW')); +$pkg->setPackageType('php'); + +$pkg->setPhpDep('5.0.0'); +$pkg->setPearinstallerDep('1.4.3'); + +$pkg->generateContents(); + +$pkg->writePackageFile(); + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/phpdoc.ini b/library/ezyang/htmlpurifier/phpdoc.ini new file mode 100644 index 0000000000..c4c3723538 --- /dev/null +++ b/library/ezyang/htmlpurifier/phpdoc.ini @@ -0,0 +1,102 @@ +;; phpDocumentor parse configuration file +;; +;; This file is designed to cut down on repetitive typing on the command-line or web interface +;; You can copy this file to create a number of configuration files that can be used with the +;; command-line switch -c, as in phpdoc -c default.ini or phpdoc -c myini.ini. The web +;; interface will automatically generate a list of .ini files that can be used. +;; +;; default.ini is used to generate the online manual at http://www.phpdoc.org/docs +;; +;; ALL .ini files must be in the user subdirectory of phpDocumentor with an extension of .ini +;; +;; Copyright 2002, Greg Beaver +;; +;; WARNING: do not change the name of any command-line parameters, phpDocumentor will ignore them + +[Parse Data] +;; title of all the documentation +;; legal values: any string +title = HTML Purifier API Documentation + +;; parse files that start with a . like .bash_profile +;; legal values: true, false +hidden = false + +;; show elements marked @access private in documentation by setting this to on +;; legal values: on, off +parseprivate = off + +;; parse with javadoc-like description (first sentence is always the short description) +;; legal values: on, off +javadocdesc = on + +;; add any custom @tags separated by commas here +;; legal values: any legal tagname separated by commas. +;customtags = mytag1,mytag2 + +;; This is only used by the XML:DocBook/peardoc2 converter +defaultcategoryname = Documentation + +;; what is the main package? +;; legal values: alphanumeric string plus - and _ +defaultpackagename = HTMLPurifier + +;; output any parsing information? set to on for cron jobs +;; legal values: on +;quiet = on + +;; parse a PEAR-style repository. Do not turn this on if your project does +;; not have a parent directory named "pear" +;; legal values: on/off +;pear = on + +;; where should the documentation be written? +;; legal values: a legal path +target = docs/phpdoc + +;; Which files should be parsed out as special documentation files, such as README, +;; INSTALL and CHANGELOG? This overrides the default files found in +;; phpDocumentor.ini (this file is not a user .ini file, but the global file) +readmeinstallchangelog = README, INSTALL, NEWS, WYSIWYG, SLOW, LICENSE, CREDITS + +;; limit output to the specified packages, even if others are parsed +;; legal values: package names separated by commas +;packageoutput = package1,package2 + +;; comma-separated list of files to parse +;; legal values: paths separated by commas +;filename = /path/to/file1,/path/to/file2,fileincurrentdirectory + +;; comma-separated list of directories to parse +;; legal values: directory paths separated by commas +;directory = /path1,/path2,.,..,subdirectory +;directory = /home/jeichorn/cvs/pear +directory = . + +;; template base directory (the equivalent directory of /phpDocumentor) +;templatebase = /path/to/my/templates + +;; directory to find any example files in through @example and {@example} tags +;examplesdir = /path/to/my/templates + +;; comma-separated list of files, directories or wildcards ? and * (any wildcard) to ignore +;; legal values: any wildcard strings separated by commas +;ignore = /path/to/ignore*,*list.php,myfile.php,subdirectory/ +ignore = *tests*,*benchmarks*,*docs*,*test-settings.php,*configdoc*,*maintenance*,*smoketests*,*standalone*,*.svn*,*conf* + +sourcecode = on + +;; comma-separated list of Converters to use in outputformat:Convertername:templatedirectory format +;; legal values: HTML:frames:default,HTML:frames:l0l33t,HTML:frames:phpdoc.de,HTML:frames:phphtmllib, +;; HTML:frames:earthli, +;; HTML:frames:DOM/default,HTML:frames:DOM/l0l33t,HTML:frames:DOM/phpdoc.de, +;; HTML:frames:DOM/phphtmllib,HTML:frames:DOM/earthli +;; HTML:Smarty:default,HTML:Smarty:PHP,HTML:Smarty:HandS +;; PDF:default:default,CHM:default:default,XML:DocBook/peardoc2:default +output=HTML:frames:default + +;; turn this option on if you want highlighted source code for every file +;; legal values: on/off +sourcecode = on + +; vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/modx.txt b/library/ezyang/htmlpurifier/plugins/modx.txt new file mode 100644 index 0000000000..0763821b50 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/modx.txt @@ -0,0 +1,112 @@ + +MODx Plugin + +MODx is an open source PHP application framework. +I first came across them in my referrer logs when tillda asked if anyone +could implement an HTML Purifier plugin. This forum thread + eventually resulted +in the fruition of this plugin that davidm says, "is on top of my favorite +list." HTML Purifier goes great with WYSIWYG editors! + + + +1. Credits + +PaulGregory wrote the overall structure of the code. I added the +slashes hack. + + + +2. Install + +First, you need to place HTML Purifier library somewhere. The code here +assumes that you've placed in MODx's assets/plugins/htmlpurifier (no version +number). + +Log into the manager, and navigate: + +Resources > Manage Resources > Plugins tab > New Plugin + +Type in a name (probably HTML Purifier), and copy paste this code into the +textarea: + +-------------------------------------------------------------------------------- +$e = &$modx->Event; +if ($e->name == 'OnBeforeDocFormSave') { + global $content; + + include_once '../assets/plugins/htmlpurifier/library/HTMLPurifier.auto.php'; + $purifier = new HTMLPurifier(); + + static $magic_quotes = null; + if ($magic_quotes === null) { + // this is an ugly hack because this hook hasn't + // had the backslashes removed yet when magic_quotes_gpc is on, + // but HTMLPurifier must not have the quotes slashed. + $magic_quotes = get_magic_quotes_gpc(); + } + + if ($magic_quotes) $content = stripslashes($content); + $content = $purifier->purify($content); + if ($magic_quotes) $content = addslashes($content); +} +-------------------------------------------------------------------------------- + +Then navigate to the System Events tab and check "OnBeforeDocFormSave". +Save the plugin. HTML Purifier now is integrated! + + + +3. Making sure it works + +You can test HTML Purifier by deliberately putting in crappy HTML and seeing +whether or not it gets fixed. A better way is to put in something like this: + +

            Il est bon

            + +...and seeing whether or not the content comes out as: + +

            Il est bon

            + +(lang to xml:lang synchronization is one of the many features HTML Purifier +has). + + + +4. Caveat Emptor + +This code does not intercept save requests from the QuickEdit plugin, this may +be added in a later version. It also modifies things on save, so there's a +slight chance that HTML Purifier may make a boo-boo and accidently mess things +up (the original version is not saved). + +Finally, make sure that MODx is using UTF-8. If you are using, say, a French +localisation, you may be using Latin-1, if that's the case, configure +HTML Purifier properly like this: + +$config = HTMLPurifier_Config::createDefault(); +$config->set('Core', 'Encoding', 'ISO-8859-1'); // or whatever encoding +$purifier = new HTMLPurifier($config); + + + +5. Known Bugs + +'rn' characters sometimes mysteriously appear after purification. We are +currently investigating this issue. See: + + + +6. See Also + +A modified version of Jot 1.1.3 is available, which integrates with HTML +Purifier. You can check it out here: + + +X. Changelog + +2008-06-16 +- Updated code to work with 3.1.0 and later +- Add Known Bugs and See Also section + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/.gitignore b/library/ezyang/htmlpurifier/plugins/phorum/.gitignore new file mode 100644 index 0000000000..8325e09020 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/.gitignore @@ -0,0 +1,2 @@ +migrate.php +htmlpurifier/* diff --git a/library/ezyang/htmlpurifier/plugins/phorum/Changelog b/library/ezyang/htmlpurifier/plugins/phorum/Changelog new file mode 100644 index 0000000000..9f939e54ae --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/Changelog @@ -0,0 +1,27 @@ +Changelog HTMLPurifier : Phorum Mod +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + += KEY ==================== + # Breaks back-compat + ! Feature + - Bugfix + + Sub-comment + . Internal change +========================== + +Version 4.0.0 for Phorum 5.2, released July 9, 2009 +# Works only with HTML Purifier 4.0.0 +! Better installation documentation +- Fixed double encoded quotes +- Fixed fatal error when migrate.php is blank + +Version 3.0.0 for Phorum 5.2, released January 12, 2008 +# WYSIWYG and suppress_message options are now configurable via web + interface. +- Module now compatible with Phorum 5.2, primary bugs were in migration + code as well as signature and edit message handling. This module is NOT + compatible with Phorum 5.1. +- Buggy WYSIWYG mode refined +. AutoFormatParam added to list of default configuration namespaces + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/INSTALL b/library/ezyang/htmlpurifier/plugins/phorum/INSTALL new file mode 100644 index 0000000000..23c76fc5c6 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/INSTALL @@ -0,0 +1,84 @@ + +Install + How to install the Phorum HTML Purifier plugin + +0. PREREQUISITES +---------------- +This Phorum module only works on PHP5 and with HTML Purifier 4.0.0 +or later. + +1. UNZIP +-------- +Unzip phorum-htmlpurifier-x.y.z, producing an htmlpurifier folder. +You've already done this step if you're reading this! + +2. MOVE +------- +Move the htmlpurifier folder to the mods/ folder of your Phorum +installation, so the directory structure looks like: + +phorum/ + mods/ + htmlpurifier/ + INSTALL - this install file + info.txt, ... - the module files + htmlpurifier/ + +3. INSTALL HTML PURIFIER +------------------------ +Download and unzip HTML Purifier . Place the contents of +the library/ folder in the htmlpurifier/htmlpurifier folder. Your directory +structure will look like: + +phorum/ + mods/ + htmlpurifier/ + htmlpurifier/ + HTMLPurifier.auto.php + ... - other files + HTMLPurifier/ + +Advanced users: + If you have HTML Purifier installed elsewhere on your server, + all you need is an HTMLPurifier.auto.php file in the library folder which + includes the HTMLPurifier.auto.php file in your install. + +4. MIGRATE +---------- +If you're setting up a new Phorum installation, all you need to do is create +a blank migrate.php file in the htmlpurifier module folder (NOT the library +folder. + +If you have an old Phorum installation and was using BBCode, +copy migrate.bbcode.php to migrate.php. If you were using a different input +format, follow the instructions in migrate.bbcode.php to create your own custom +migrate.php file. + +Your directory structure should now look like this: + +phorum/ + mods/ + htmlpurifier/ + migrate.php + +5. ENABLE +--------- +Navigate to your Phorum admin panel at http://example.com/phorum/admin.php, +click on Global Settings > Modules, scroll to "HTML Purifier Phorum Mod" and +turn it On. + +6. MIGRATE SIGNATURES +--------------------- +If you're setting up a new Phorum installation, skip this step. + +If you allowed your users to make signatures, navigate to the module settings +page of HTML Purifier (Global Settings > Modules > HTML Purifier Phorum Mod > +Configure), type in "yes" in the "Confirm" box, and press "Migrate." + +ONLY DO THIS ONCE! BE SURE TO BACK UP YOUR DATABASE! + +7. CONFIGURE +------------ +Configure using Edit settings. See that page for more information. + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/README b/library/ezyang/htmlpurifier/plugins/phorum/README new file mode 100644 index 0000000000..0524ed39db --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/README @@ -0,0 +1,45 @@ + +HTML Purifier Phorum Mod - Filter your HTML the Standards-Compliant Way! + +This Phorum mod enables HTML posting on Phorum. Under normal circumstances, +this would cause a huge security risk, but because we are running +HTML through HTML Purifier, output is guaranteed to be XSS free and +standards-compliant. + +This mod requires HTML input, and previous markup languages need to be +converted accordingly. Thus, it is vital that you create a 'migrate.php' +file that works with your installation. If you're using the built-in +BBCode formatting, simply move migrate.bbcode.php to that place; for +other markup languages, consult said file for instructions on how +to adapt it to your needs. + + -- NOTE ------------------------------------------------- + You can also run this module in parallel with another + formatting module; this module attempts to place itself + at the end of the filtering chain. However, if any + previous modules produce insecure HTML (for instance, + a JavaScript email obfuscator) they will get cleaned. + +This module will not work if 'migrate.php' is not created, and an improperly +made migration file may *CORRUPT* Phorum, so please take your time to +do this correctly. It should go without saying to *BACKUP YOUR DATABASE* +before attempting anything here. If no migration is necessary, you can +simply create a blank migrate.php file. HTML Purifier is smart and will +not re-migrate already processed messages. However, the original code +is irretrievably lost (we may change this in the future.) + +This module will not automatically migrate user signatures, because this +process may take a long time. After installing the HTML Purifier module and +then configuring 'migrate.php', navigate to Settings and click 'Migrate +Signatures' to migrate all user signatures to HTML. + +All of HTML Purifier's usual functions are configurable via the mod settings +page. If you require custom configuration, create config.php file in +the mod directory that edits a $config variable. Be sure, also, to +set $PHORUM['mod_htmlpurifier']['wysiwyg'] to TRUE if you are using a +WYSIWYG editor (you can do this through a common hook or the web +configuration form). + +Visit HTML Purifier at . + + vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/config.default.php b/library/ezyang/htmlpurifier/plugins/phorum/config.default.php new file mode 100644 index 0000000000..e047c0b423 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/config.default.php @@ -0,0 +1,57 @@ +set('HTML.Allowed', + // alphabetically sorted +'a[href|title] +abbr[title] +acronym[title] +b +blockquote[cite] +br +caption +cite +code +dd +del +dfn +div +dl +dt +em +i +img[src|alt|title|class] +ins +kbd +li +ol +p +pre +s +strike +strong +sub +sup +table +tbody +td +tfoot +th +thead +tr +tt +u +ul +var'); +$config->set('AutoFormat.AutoParagraph', true); +$config->set('AutoFormat.Linkify', true); +$config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); +$config->set('Core.AggressivelyFixLt', true); +$config->set('Core.Encoding', $GLOBALS['PHORUM']['DATA']['CHARSET']); // we'll change this eventually +if (strtolower($GLOBALS['PHORUM']['DATA']['CHARSET']) !== 'utf-8') { + $config->set('Core.EscapeNonASCIICharacters', true); +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php b/library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php new file mode 100644 index 0000000000..f66d8c36cd --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/htmlpurifier.php @@ -0,0 +1,316 @@ + $message){ + if(isset($message['body'])) { + + if ($message_id) { + // we're dealing with a real message, not a fake, so + // there a number of shortcuts that can be taken + + if (isset($message['meta']['htmlpurifier_light'])) { + // format hook was called outside of Phorum's normal + // functions, do the abridged purification + $data[$message_id]['body'] = $purifier->purify($message['body']); + continue; + } + + if (!empty($PHORUM['args']['purge'])) { + // purge the cache, must be below the following if + unset($message['meta']['body_cache']); + } + + if ( + isset($message['meta']['body_cache']) && + isset($message['meta']['body_cache_serial']) && + $message['meta']['body_cache_serial'] == $cache_serial + ) { + // cached version is present, bail out early + $data[$message_id]['body'] = base64_decode($message['meta']['body_cache']); + continue; + } + } + + // migration might edit this array, that's why it's defined + // so early + $updated_message = array(); + + // create the $body variable + if ( + $message_id && // message must be real to migrate + !isset($message['meta']['body_cache_serial']) + ) { + // perform migration + $fake_data = array(); + list($signature, $edit_message) = phorum_htmlpurifier_remove_sig_and_editmessage($message); + $fake_data[$message_id] = $message; + $fake_data = phorum_htmlpurifier_migrate($fake_data); + $body = $fake_data[$message_id]['body']; + $body = str_replace("\n", "\n", $body); + $updated_message['body'] = $body; // save it in + $body .= $signature . $edit_message; // add it back in + } else { + // reverse Phorum's pre-processing + $body = $message['body']; + // order is important + $body = str_replace("\n", "\n", $body); + $body = str_replace(array('<','>','&', '"'), array('<','>','&','"'), $body); + if (!$message_id && defined('PHORUM_CONTROL_CENTER')) { + // we're in control.php, so it was double-escaped + $body = str_replace(array('<','>','&', '"'), array('<','>','&','"'), $body); + } + } + + $body = $purifier->purify($body); + + // dynamically update the cache (MUST BE DONE HERE!) + // this is inefficient because it's one db call per + // cache miss, but once the cache is in place things are + // a lot zippier. + + if ($message_id) { // make sure it's not a fake id + $updated_message['meta'] = $message['meta']; + $updated_message['meta']['body_cache'] = base64_encode($body); + $updated_message['meta']['body_cache_serial'] = $cache_serial; + phorum_db_update_message($message_id, $updated_message); + } + + // must not get overloaded until after we cache it, otherwise + // we'll inadvertently change the original text + $data[$message_id]['body'] = $body; + + } + } + + return $data; +} + +// ----------------------------------------------------------------------- +// This is fragile code, copied from read.php:596 (Phorum 5.2.6). Please +// keep this code in-sync with Phorum + +/** + * Generates a signature based on a message array + */ +function phorum_htmlpurifier_generate_sig($row) +{ + $phorum_sig = ''; + if(isset($row["user"]["signature"]) + && isset($row['meta']['show_signature']) && $row['meta']['show_signature']==1){ + $phorum_sig=trim($row["user"]["signature"]); + if(!empty($phorum_sig)){ + $phorum_sig="\n\n$phorum_sig"; + } + } + return $phorum_sig; +} + +/** + * Generates an edit message based on a message array + */ +function phorum_htmlpurifier_generate_editmessage($row) +{ + $PHORUM = $GLOBALS['PHORUM']; + $editmessage = ''; + if(isset($row['meta']['edit_count']) && $row['meta']['edit_count'] > 0) { + $editmessage = str_replace ("%count%", $row['meta']['edit_count'], $PHORUM["DATA"]["LANG"]["EditedMessage"]); + $editmessage = str_replace ("%lastedit%", phorum_date($PHORUM["short_date_time"],$row['meta']['edit_date']), $editmessage); + $editmessage = str_replace ("%lastuser%", $row['meta']['edit_username'], $editmessage); + $editmessage = "\n\n\n\n$editmessage"; + } + return $editmessage; +} + +// End fragile code +// ----------------------------------------------------------------------- + +/** + * Removes the signature and edit message from a message + * @param $row Message passed by reference + */ +function phorum_htmlpurifier_remove_sig_and_editmessage(&$row) +{ + $signature = phorum_htmlpurifier_generate_sig($row); + $editmessage = phorum_htmlpurifier_generate_editmessage($row); + $replacements = array(); + // we need to remove add as that is the form these + // extra bits are in. + if ($signature) $replacements[str_replace("\n", "\n", $signature)] = ''; + if ($editmessage) $replacements[str_replace("\n", "\n", $editmessage)] = ''; + $row['body'] = strtr($row['body'], $replacements); + return array($signature, $editmessage); +} + +/** + * Indicate that data is fully HTML and not from migration, invalidate + * previous caches + * @note This function could generate the actual cache entries, but + * since there's data missing that must be deferred to the first read + */ +function phorum_htmlpurifier_posting($message) +{ + $PHORUM = $GLOBALS["PHORUM"]; + unset($message['meta']['body_cache']); // invalidate the cache + $message['meta']['body_cache_serial'] = $PHORUM['mod_htmlpurifier']['body_cache_serial']; + return $message; +} + +/** + * Overload quoting mechanism to prevent default, mail-style quote from happening + */ +function phorum_htmlpurifier_quote($array) +{ + $PHORUM = $GLOBALS["PHORUM"]; + $purifier =& HTMLPurifier::getInstance(); + $text = $purifier->purify($array[1]); + $source = htmlspecialchars($array[0]); + return "
            \n$text\n
            "; +} + +/** + * Ensure that our format hook is processed last. Also, loads the library. + * @credits + */ +function phorum_htmlpurifier_common() +{ + require_once(dirname(__FILE__).'/htmlpurifier/HTMLPurifier.auto.php'); + require(dirname(__FILE__).'/init-config.php'); + + $config = phorum_htmlpurifier_get_config(); + HTMLPurifier::getInstance($config); + + // increment revision.txt if you want to invalidate the cache + $GLOBALS['PHORUM']['mod_htmlpurifier']['body_cache_serial'] = $config->getSerial(); + + // load migration + if (file_exists(dirname(__FILE__) . '/migrate.php')) { + include(dirname(__FILE__) . '/migrate.php'); + } else { + echo 'Error: No migration path specified for HTML Purifier, please check + modes/htmlpurifier/migrate.bbcode.php for instructions on + how to migrate from your previous markup language.'; + exit; + } + + if (!function_exists('phorum_htmlpurifier_migrate')) { + // Dummy function + function phorum_htmlpurifier_migrate($data) {return $data;} + } + +} + +/** + * Pre-emptively performs purification if it looks like a WYSIWYG editor + * is being used + */ +function phorum_htmlpurifier_before_editor($message) +{ + if (!empty($GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg'])) { + if (!empty($message['body'])) { + $body = $message['body']; + // de-entity-ize contents + $body = str_replace(array('<','>','&'), array('<','>','&'), $body); + $purifier =& HTMLPurifier::getInstance(); + $body = $purifier->purify($body); + // re-entity-ize contents + $body = htmlspecialchars($body, ENT_QUOTES, $GLOBALS['PHORUM']['DATA']['CHARSET']); + $message['body'] = $body; + } + } + return $message; +} + +function phorum_htmlpurifier_editor_after_subject() +{ + // don't show this message if it's a WYSIWYG editor, since it will + // then be handled automatically + if (!empty($GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg'])) { + $i = $GLOBALS['PHORUM']['DATA']['MODE']; + if ($i == 'quote' || $i == 'edit' || $i == 'moderation') { + ?> +
            +

            + Notice: HTML has been scrubbed for your safety. + If you would like to see the original, turn off WYSIWYG mode + (consult your administrator for details.) +

            +
            +
            +

            + HTML input is enabled. Make sure you escape all HTML and + angled brackets with &lt; and &gt;. +

            config; + if ($config->get('AutoFormat.AutoParagraph')) { + ?>

            + Auto-paragraphing is enabled. Double + newlines will be converted to paragraphs; for single + newlines, use the pre tag. +

            getDefinition('HTML'); + $allowed = array(); + foreach ($html_definition->info as $name => $x) $allowed[] = "$name"; + sort($allowed); + $allowed_text = implode(', ', $allowed); + ?>

            Allowed tags: .

            +

            +

            + For inputting literal code such as HTML and PHP for display, use + CDATA tags to auto-escape your angled brackets, and pre + to preserve newlines: +

            +
            <pre><![CDATA[
            +Place code here
            +]]></pre>
            +

            + Power users, you can hide this notice with: +

            .htmlpurifier-help {display:none;}
            +

            +
            '; +phorum_htmlpurifier_show_form(); + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/form.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/form.php new file mode 100644 index 0000000000..9b6ad5f39e --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/settings/form.php @@ -0,0 +1,95 @@ +hidden("module", "modsettings"); + $frm->hidden("mod", "htmlpurifier"); // this is the directory name that the Settings file lives in + + if (!empty($error)){ + echo "$error
            "; + } + + $frm->addbreak("Edit settings for the HTML Purifier module"); + + $frm->addMessage('

            The box below sets $PHORUM[\'mod_htmlpurifier\'][\'wysiwyg\']. + When checked, contents sent for edit are now purified and the + informative message is disabled. If your WYSIWYG editor is disabled for + admin edits, you can safely keep this unchecked.

            '); + $frm->addRow('Use WYSIWYG?', $frm->checkbox('wysiwyg', '1', '', $PHORUM['mod_htmlpurifier']['wysiwyg'])); + + $frm->addMessage('

            The box below sets $PHORUM[\'mod_htmlpurifier\'][\'suppress_message\'], + which removes the big how-to use + HTML Purifier message.

            '); + $frm->addRow('Suppress information?', $frm->checkbox('suppress_message', '1', '', $PHORUM['mod_htmlpurifier']['suppress_message'])); + + $frm->addMessage('

            Click on directive links to read what each option does + (links do not open in new windows).

            +

            For more flexibility (for instance, you want to edit the full + range of configuration directives), you can create a config.php + file in your mods/htmlpurifier/ directory. Doing so will, + however, make the web configuration interface unavailable.

            '); + + require_once 'HTMLPurifier/Printer/ConfigForm.php'; + $htmlpurifier_form = new HTMLPurifier_Printer_ConfigForm('config', 'http://htmlpurifier.org/live/configdoc/plain.html#%s'); + $htmlpurifier_form->setTextareaDimensions(23, 7); // widen a little, since we have space + + $frm->addMessage($htmlpurifier_form->render( + $config, $PHORUM['mod_htmlpurifier']['directives'], false)); + + $frm->addMessage("Warning: Changing HTML Purifier's configuration will invalidate + the cache. Expect to see a flurry of database activity after you change + any of these settings."); + + $frm->addrow('Reset to defaults:', $frm->checkbox("reset", "1", "", false)); + + // hack to include extra styling + echo ''; + $js = $htmlpurifier_form->getJavaScript(); + echo ''; + + $frm->show(); +} + +function phorum_htmlpurifier_show_config_info() +{ + global $PHORUM; + + // update mod_htmlpurifier for housekeeping + phorum_htmlpurifier_commit_settings(); + + // politely tell user how to edit settings manually +?> +
            How to edit settings for HTML Purifier module
            +

            + A config.php file exists in your mods/htmlpurifier/ + directory. This file contains your custom configuration: in order to + change it, please navigate to that file and edit it accordingly. + You can also set $GLOBALS['PHORUM']['mod_htmlpurifier']['wysiwyg'] + or $GLOBALS['PHORUM']['mod_htmlpurifier']['suppress_message'] +

            +

            + To use the web interface, delete config.php (or rename it to + config.php.bak). +

            +

            + Warning: Changing HTML Purifier's configuration will invalidate + the cache. Expect to see a flurry of database activity after you change + any of these settings. +

            +hidden("module", "modsettings"); + $frm->hidden("mod", "htmlpurifier"); + $frm->hidden("migrate-sigs", "1"); + $frm->addbreak("Migrate user signatures to HTML"); + $frm->addMessage('This operation will migrate your users signatures + to HTML. This process is irreversible and must only be performed once. + Type in yes in the confirmation field to migrate.'); + if (!file_exists(dirname(__FILE__) . '/../migrate.php')) { + $frm->addMessage('Migration file does not exist, cannot migrate signatures. + Please check migrate.bbcode.php on how to create an appropriate file.'); + } else { + $frm->addrow('Confirm:', $frm->text_box("confirmation", "")); + } + $frm->show(); +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php new file mode 100644 index 0000000000..5ea9cd0b81 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/settings/migrate-sigs.php @@ -0,0 +1,79 @@ +$PHORUM["mod_htmlpurifier"])); + $offset = 1; + } elseif (!empty($_GET['migrate-sigs']) && $PHORUM['mod_htmlpurifier']['migrate-sigs']) { + $offset = (int) $_GET['migrate-sigs']; + } + return $offset; +} + +function phorum_htmlpurifier_migrate_sigs($offset) +{ + global $PHORUM; + + if(!$offset) return; // bail out quick if $offset == 0 + + // theoretically, we could get rid of this multi-request + // doo-hickery if safe mode is off + @set_time_limit(0); // attempt to let this run + $increment = $PHORUM['mod_htmlpurifier']['migrate-sigs-increment']; + + require_once(dirname(__FILE__) . '/../migrate.php'); + // migrate signatures + // do this in batches so we don't run out of time/space + $end = $offset + $increment; + $user_ids = array(); + for ($i = $offset; $i < $end; $i++) { + $user_ids[] = $i; + } + $userinfos = phorum_db_user_get_fields($user_ids, 'signature'); + foreach ($userinfos as $i => $user) { + if (empty($user['signature'])) continue; + $sig = $user['signature']; + // perform standard Phorum processing on the sig + $sig = str_replace(array("&","<",">"), array("&","<",">"), $sig); + $sig = preg_replace("/<((http|https|ftp):\/\/[a-z0-9;\/\?:@=\&\$\-_\.\+!*'\(\),~%]+?)>/i", "$1", $sig); + // prepare fake data to pass to migration function + $fake_data = array(array("author"=>"", "email"=>"", "subject"=>"", 'body' => $sig)); + list($fake_message) = phorum_htmlpurifier_migrate($fake_data); + $user['signature'] = $fake_message['body']; + if (!phorum_api_user_save($user)) { + exit('Error while saving user data'); + } + } + unset($userinfos); // free up memory + + // query for highest ID in database + $type = $PHORUM['DBCONFIG']['type']; + $sql = "select MAX(user_id) from {$PHORUM['user_table']}"; + $row = phorum_db_interact(DB_RETURN_ROW, $sql); + $top_id = (int) $row[0]; + + $offset += $increment; + if ($offset > $top_id) { // test for end condition + echo 'Migration finished'; + $PHORUM['mod_htmlpurifier']['migrate-sigs'] = false; + phorum_htmlpurifier_commit_settings(); + return true; + } + $host = $_SERVER['HTTP_HOST']; + $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\'); + $extra = 'admin.php?module=modsettings&mod=htmlpurifier&migrate-sigs=' . $offset; + // relies on output buffering to work + header("Location: http://$host$uri/$extra"); + exit; + +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/plugins/phorum/settings/save.php b/library/ezyang/htmlpurifier/plugins/phorum/settings/save.php new file mode 100644 index 0000000000..2aefaf83a0 --- /dev/null +++ b/library/ezyang/htmlpurifier/plugins/phorum/settings/save.php @@ -0,0 +1,29 @@ +mods/htmlpurifier/config.php already exists. To change + settings, edit that file. To use the web form, delete that file.
            "; + } else { + $config = phorum_htmlpurifier_get_config(true); + if (!isset($_POST['reset'])) $config->mergeArrayFromForm($_POST, 'config', $PHORUM['mod_htmlpurifier']['directives']); + $PHORUM['mod_htmlpurifier']['config'] = $config->getAll(); + } + $PHORUM['mod_htmlpurifier']['wysiwyg'] = !empty($_POST['wysiwyg']); + $PHORUM['mod_htmlpurifier']['suppress_message'] = !empty($_POST['suppress_message']); + if(!phorum_htmlpurifier_commit_settings()){ + $error="Database error while updating settings."; + } else { + echo "Settings Updated
            "; + } +} + +function phorum_htmlpurifier_commit_settings() +{ + global $PHORUM; + return phorum_db_update_settings(array("mod_htmlpurifier"=>$PHORUM["mod_htmlpurifier"])); +} + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/release1-update.php b/library/ezyang/htmlpurifier/release1-update.php new file mode 100644 index 0000000000..834d385676 --- /dev/null +++ b/library/ezyang/htmlpurifier/release1-update.php @@ -0,0 +1,110 @@ + 1) { + echo 'More than one release declaration in NEWS replaced' . PHP_EOL; + exit; + } + file_put_contents('NEWS', $news_c); +} + +// ...in Doxyfile +$doxyfile_c = preg_replace( + '/(?<=PROJECT_NUMBER {9}= )[^\s]+/m', // brittle + $version, + file_get_contents('Doxyfile'), + 1, $c +); +if (!$c) { + echo 'Could not update Doxyfile, missing PROJECT_NUMBER.' . PHP_EOL; + exit; +} +file_put_contents('Doxyfile', $doxyfile_c); + +// ...in HTMLPurifier.php +$htmlpurifier_c = file_get_contents('library/HTMLPurifier.php'); +$htmlpurifier_c = preg_replace( + '/HTML Purifier .+? - /', + "HTML Purifier $version - ", + $htmlpurifier_c, + 1, $c +); +if (!$c) { + echo 'Could not update HTMLPurifier.php, missing HTML Purifier [version] header.' . PHP_EOL; + exit; +} +$htmlpurifier_c = preg_replace( + '/public \$version = \'.+?\';/', + "public \$version = '$version';", + $htmlpurifier_c, + 1, $c +); +if (!$c) { + echo 'Could not update HTMLPurifier.php, missing public $version.' . PHP_EOL; + exit; +} +$htmlpurifier_c = preg_replace( + '/const VERSION = \'.+?\';/', + "const VERSION = '$version';", + $htmlpurifier_c, + 1, $c +); +if (!$c) { + echo 'Could not update HTMLPurifier.php, missing const $version.' . PHP_EOL; + exit; +} +file_put_contents('library/HTMLPurifier.php', $htmlpurifier_c); + +$config_c = file_get_contents('library/HTMLPurifier/Config.php'); +$config_c = preg_replace( + '/public \$version = \'.+?\';/', + "public \$version = '$version';", + $config_c, + 1, $c +); +if (!$c) { + echo 'Could not update Config.php, missing public $version.' . PHP_EOL; + exit; +} +file_put_contents('library/HTMLPurifier/Config.php', $config_c); + +passthru('php maintenance/flush.php'); + +if ($is_dev) echo "Review changes, write something in WHATSNEW and FOCUS, and then commit with log 'Release $version.'" . PHP_EOL; +else echo "Numbers updated to dev, no other modifications necessary!"; + +// vim: et sw=4 sts=4 diff --git a/library/ezyang/htmlpurifier/release2-tag.php b/library/ezyang/htmlpurifier/release2-tag.php new file mode 100644 index 0000000000..25e5300d81 --- /dev/null +++ b/library/ezyang/htmlpurifier/release2-tag.php @@ -0,0 +1,22 @@ + .ps-scrollbar-x-rail, .ps-container.ps-active-y > .ps-scrollbar-y-rail { - display: block; } + display: block; + background-color: transparent; } .ps-container.ps-in-scrolling { pointer-events: none; } .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { @@ -23,13 +32,12 @@ /* please don't change 'position' */ -webkit-border-radius: 4px; -moz-border-radius: 4px; - -ms-border-radius: 4px; border-radius: 4px; opacity: 0; - -webkit-transition: background-color 0.2s linear, opacity 0.2s linear; - -moz-transition: background-color 0.2s linear, opacity 0.2s linear; - -o-transition: background-color 0.2s linear, opacity 0.2s linear; - transition: background-color 0.2s linear, opacity 0.2s linear; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; bottom: 3px; /* there must be 'bottom' for ps-scrollbar-x-rail */ height: 8px; } @@ -39,12 +47,11 @@ background-color: #aaa; -webkit-border-radius: 4px; -moz-border-radius: 4px; - -ms-border-radius: 4px; border-radius: 4px; - -webkit-transition: background-color 0.2s linear; - -moz-transition: background-color 0.2s linear; - -o-transition: background-color 0.2s linear; - transition: background-color 0.2s linear; + -webkit-transition: background-color .2s linear; + -moz-transition: background-color .2s linear; + -o-transition: background-color .2s linear; + transition: background-color .2s linear; bottom: 0; /* there must be 'bottom' for ps-scrollbar-x */ height: 8px; } @@ -54,13 +61,12 @@ /* please don't change 'position' */ -webkit-border-radius: 4px; -moz-border-radius: 4px; - -ms-border-radius: 4px; border-radius: 4px; opacity: 0; - -webkit-transition: background-color 0.2s linear, opacity 0.2s linear; - -moz-transition: background-color 0.2s linear, opacity 0.2s linear; - -o-transition: background-color 0.2s linear, opacity 0.2s linear; - transition: background-color 0.2s linear, opacity 0.2s linear; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; right: 3px; /* there must be 'right' for ps-scrollbar-y-rail */ width: 8px; } @@ -70,12 +76,11 @@ background-color: #aaa; -webkit-border-radius: 4px; -moz-border-radius: 4px; - -ms-border-radius: 4px; border-radius: 4px; - -webkit-transition: background-color 0.2s linear; - -moz-transition: background-color 0.2s linear; - -o-transition: background-color 0.2s linear; - transition: background-color 0.2s linear; + -webkit-transition: background-color .2s linear; + -moz-transition: background-color .2s linear; + -o-transition: background-color .2s linear; + transition: background-color .2s linear; right: 0; /* there must be 'right' for ps-scrollbar-y */ width: 8px; } diff --git a/library/perfect-scrollbar/perfect-scrollbar.jquery.js b/library/perfect-scrollbar/perfect-scrollbar.jquery.js index 2bc3b2f939..d94ed91bcd 100644 --- a/library/perfect-scrollbar/perfect-scrollbar.jquery.js +++ b/library/perfect-scrollbar/perfect-scrollbar.jquery.js @@ -1,10 +1,12 @@ -/* perfect-scrollbar v0.6.8 */ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= i.contentHeight - i.containerHeight) { - element.scrollTop = i.contentHeight - i.containerHeight; + element.scrollTop = value = i.contentHeight - i.containerHeight; // don't allow scroll past container element.dispatchEvent(yEndEvent); - return; // don't allow scroll past container } if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { - element.scrollLeft = i.contentWidth - i.containerWidth; + element.scrollLeft = value = i.contentWidth - i.containerWidth; // don't allow scroll past container element.dispatchEvent(xEndEvent); - return; // don't allow scroll past container } if (!lastTop) { @@ -1575,9 +1535,6 @@ module.exports = function (element, axis, value) { }; },{"./instances":18}],21:[function(require,module,exports){ -/* Copyright (c) 2015 Hyunje Alex Jun and other contributors - * Licensed under the MIT License - */ 'use strict'; var d = require('../lib/dom') diff --git a/library/perfect-scrollbar/perfect-scrollbar.min.css b/library/perfect-scrollbar/perfect-scrollbar.min.css index 288671cfcc..e8d2f37d9c 100644 --- a/library/perfect-scrollbar/perfect-scrollbar.min.css +++ b/library/perfect-scrollbar/perfect-scrollbar.min.css @@ -1,2 +1,2 @@ -/* perfect-scrollbar v0.6.8 */ -.ps-container{-ms-touch-action:none;overflow:hidden !important}.ps-container.ps-active-x>.ps-scrollbar-x-rail,.ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block}.ps-container.ps-in-scrolling{pointer-events:none}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:0.9}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:0.9}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}.ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;opacity:0;-webkit-transition:background-color 0.2s linear,opacity 0.2s linear;-moz-transition:background-color 0.2s linear,opacity 0.2s linear;-o-transition:background-color 0.2s linear,opacity 0.2s linear;transition:background-color 0.2s linear,opacity 0.2s linear;bottom:3px;height:8px}.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;-webkit-transition:background-color 0.2s linear;-moz-transition:background-color 0.2s linear;-o-transition:background-color 0.2s linear;transition:background-color 0.2s linear;bottom:0;height:8px}.ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;opacity:0;-webkit-transition:background-color 0.2s linear,opacity 0.2s linear;-moz-transition:background-color 0.2s linear,opacity 0.2s linear;-o-transition:background-color 0.2s linear,opacity 0.2s linear;transition:background-color 0.2s linear,opacity 0.2s linear;right:3px;width:8px}.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:4px;-moz-border-radius:4px;-ms-border-radius:4px;border-radius:4px;-webkit-transition:background-color 0.2s linear;-moz-transition:background-color 0.2s linear;-o-transition:background-color 0.2s linear;transition:background-color 0.2s linear;right:0;width:8px}.ps-container:hover.ps-in-scrolling{pointer-events:none}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:0.9}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:0.9}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}.ps-container:hover>.ps-scrollbar-x-rail,.ps-container:hover>.ps-scrollbar-y-rail{opacity:0.6}.ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:0.9}.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}.ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:0.9}.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999} +/* perfect-scrollbar v0.6.10 */ +.ps-container{-ms-touch-action:none;touch-action:none;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){.ps-container{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){.ps-container{overflow:auto !important}}.ps-container.ps-active-x>.ps-scrollbar-x-rail,.ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block;background-color:transparent}.ps-container.ps-in-scrolling{pointer-events:none}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:0.9}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:0.9}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}.ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;bottom:3px;height:8px}.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-transition:background-color .2s linear;-moz-transition:background-color .2s linear;-o-transition:background-color .2s linear;transition:background-color .2s linear;bottom:0;height:8px}.ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;right:3px;width:8px}.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-transition:background-color .2s linear;-moz-transition:background-color .2s linear;-o-transition:background-color .2s linear;transition:background-color .2s linear;right:0;width:8px}.ps-container:hover.ps-in-scrolling{pointer-events:none}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:0.9}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:0.9}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999}.ps-container:hover>.ps-scrollbar-x-rail,.ps-container:hover>.ps-scrollbar-y-rail{opacity:0.6}.ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:0.9}.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}.ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:0.9}.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999} diff --git a/library/simplepie/LICENSE.txt b/library/simplepie/LICENSE.txt deleted file mode 100644 index a822a4bd98..0000000000 --- a/library/simplepie/LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are -permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. - - * Neither the name of the SimplePie Team nor the names of its contributors may be used - to endorse or promote products derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS -AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/library/simplepie/README.markdown b/library/simplepie/README.markdown deleted file mode 100644 index e5ca021ced..0000000000 --- a/library/simplepie/README.markdown +++ /dev/null @@ -1,53 +0,0 @@ -# SimplePie - -## Authors and contributors - -* [Ryan Parman](http://ryanparman.com) -* [Geoffrey Sneddon](http://gsnedders.com) -* [Ryan McCue](http://ryanmccue.info) -* [Michael Shipley](http://michaelpshipley.com) -* [Steve Minutillo](http://minutillo.com/steve/) - - -## License - -[New BSD license](http://www.opensource.org/licenses/bsd-license.php) - - -## Project status - -SimplePie is currently maintained by Ryan McCue. - -At the moment, there isn't a lot of active development happening. If the community decides that SimplePie is a valuable tool, then the community will come together to maintain it into the future. - -If you're interested in getting involved with SimplePie, please get in touch with Ryan McCue. - - -## What comes in the package? - -1. `simplepie.inc` - The SimplePie library. This is all that's required for your pages. -2. `README.markdown` - This document. -3. `LICENSE.txt` - A copy of the BSD license. -4. `compatibility_test/` - The SimplePie compatibility test that checks your server for required settings. -5. `demo/` - A basic feed reader demo that shows off some of SimplePie's more noticable features. -6. `idn/` - A third-party library that SimplePie can optionally use to understand Internationalized Domain Names (IDNs). -7. `test/` - SimplePie's unit test suite. - - -## To start the demo - -1. Upload this package to your webserver. -2. Make sure that the cache folder inside of the demo folder is server-writable. -3. Navigate your browser to the demo folder. - - -## Need support? - -For further setup and install documentation, function references, etc., visit: -[http://simplepie.org/wiki/](http://simplepie.org/wiki/) - -For bug reports and feature requests, visit: -[http://github.com/rmccue/simplepie/issues](http://github.com/rmccue/simplepie/issues) - -Support mailing list -- powered by users, for users. -[http://tech.groups.yahoo.com/group/simplepie-support/](http://tech.groups.yahoo.com/group/simplepie-support/) diff --git a/library/simplepie/compatibility_test/COMPATIBILITY README.txt b/library/simplepie/compatibility_test/COMPATIBILITY README.txt deleted file mode 100644 index 5b24989927..0000000000 --- a/library/simplepie/compatibility_test/COMPATIBILITY README.txt +++ /dev/null @@ -1,7 +0,0 @@ -SIMPLEPIE COMPATIBILITY TEST - -1) Upload sp_compatibility_test.php to the web-accessible root of your website. -For example, if your website is www.example.com, upload it so that you can get -to it at www.example.com/sp_compatibility_test.php - -2) Open your web browser and go to the page you just uploaded. \ No newline at end of file diff --git a/library/simplepie/compatibility_test/sp_compatibility_test.php b/library/simplepie/compatibility_test/sp_compatibility_test.php deleted file mode 100644 index a7a7f5fde2..0000000000 --- a/library/simplepie/compatibility_test/sp_compatibility_test.php +++ /dev/null @@ -1,330 +0,0 @@ -=')); -$pcre_ok = extension_loaded('pcre'); -$curl_ok = function_exists('curl_exec'); -$zlib_ok = extension_loaded('zlib'); -$mbstring_ok = extension_loaded('mbstring'); -$iconv_ok = extension_loaded('iconv'); -if (extension_loaded('xmlreader')) -{ - $xml_ok = true; -} -elseif (extension_loaded('xml')) -{ - $parser_check = xml_parser_create(); - xml_parse_into_struct($parser_check, '&', $values); - xml_parser_free($parser_check); - $xml_ok = isset($values[0]['value']); -} -else -{ - $xml_ok = false; -} - -header('Content-type: text/html; charset=UTF-8'); - -?> - - - -SimplePie: Server Compatibility Test 1.2 - - - - - - - - - -
            -
            - -
            -

            SimplePie Compatibility Test

            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            TestShould BeWhat You Have
            PHP¹4.3.0 or higher
            XMLEnabled
            PCRE²Enabled
            cURLEnabled
            ZlibEnabled
            mbstringEnabled
            iconvEnabled
            -
            - -
            -

            What does this mean?

            -
              - -
            1. You have everything you need to run SimplePie properly! Congratulations!
            2. - - -
            3. PHP: You are running a supported version of PHP. No problems here.
            4. - -
            5. XML: You have XMLReader support or a version of XML support that isn't broken installed. No problems here.
            6. - -
            7. PCRE: You have PCRE support installed. No problems here.
            8. - -
            9. cURL: You have cURL support installed. No problems here.
            10. - -
            11. cURL: The cURL extension is not available. SimplePie will use fsockopen() instead.
            12. - - - -
            13. Zlib: You have Zlib enabled. This allows SimplePie to support GZIP-encoded feeds. No problems here.
            14. - -
            15. Zlib: The Zlib extension is not available. SimplePie will ignore any GZIP-encoding, and instead handle feeds as uncompressed text.
            16. - - - -
            17. mbstring and iconv: You have both mbstring and iconv installed! This will allow SimplePie to handle the greatest number of languages. Check the Supported Character Encodings chart to see what's supported on your webhost.
            18. - -
            19. mbstring: mbstring is installed, but iconv is not. Check the Supported Character Encodings chart to see what's supported on your webhost.
            20. - -
            21. iconv: iconv is installed, but mbstring is not. Check the Supported Character Encodings chart to see what's supported on your webhost.
            22. - -
            23. mbstring and iconv: You do not have either of the extensions installed. This will significantly impair your ability to read non-English feeds, as well as even some English ones. Check the Supported Character Encodings chart to see what's supported on your webhost.
            24. - - -
            25. PCRE: Your PHP installation doesn't support Perl-Compatible Regular Expressions. SimplePie is a no-go at the moment.
            26. - - -
            27. XML: Your PHP installation doesn't support XML parsing. SimplePie is a no-go at the moment.
            28. - - -
            29. PHP: You are running an unsupported version of PHP. SimplePie is a no-go at the moment.
            30. - - -
            -
            - -
            - -

            Bottom Line: Yes, you can!

            -

            Your webhost has its act together!

            -

            You can download the latest version of SimplePie from SimplePie.org and install it by following the instructions. You can find example uses with SimplePie Ideas.

            -

            Take the time to read Requirements and Getting Started to make sure you're prepared to use SimplePie. No seriously, read them.

            -

            Note: Passing this test does not guarantee that SimplePie will run on your webhost — it only ensures that the basic requirements have been addressed.

            - -

            Bottom Line: Yes, you can!

            -

            For most feeds, it'll run with no problems. There are certain languages that you might have a hard time with though.

            -

            You can download the latest version of SimplePie from SimplePie.org and install it by following the instructions. You can find example uses with SimplePie Ideas.

            -

            Take the time to read Requirements and Getting Started to make sure you're prepared to use SimplePie. No seriously, read them.

            -

            Note: Passing this test does not guarantee that SimplePie will run on your webhost — it only ensures that the basic requirements have been addressed.

            - -

            Bottom Line: We're sorry…

            -

            Your webhost does not support the minimum requirements for SimplePie. It may be a good idea to contact your webhost, and ask them to install a more recent version of PHP as well as the xmlreader, xml, mbstring, iconv, curl, and zlib extensions.

            - -
            - -
            -

            ¹ — SimplePie 2 will not support PHP 4.x. The core PHP team has discontinued PHP 4.x patches and support. Read the announcement.

            -

            ² — Some recent versions of the PCRE (PERL-Compatible Regular Expression) engine compiled into PHP have been buggy, and are the source of PHP segmentation faults (e.g. crashes) which cause random things like blank, white screens. Check the Support Forums for the latest information on patches and ongoing fixes.

            -
            - -
            - -
            - - - \ No newline at end of file diff --git a/library/simplepie/create.php b/library/simplepie/create.php deleted file mode 100644 index 908ed182bd..0000000000 --- a/library/simplepie/create.php +++ /dev/null @@ -1,178 +0,0 @@ -success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) - { - return false; - } - else - { - $data = explode("\n", $file->body); - unset($file); - - foreach ($data as $line) - { - // New character set - if (substr($line, 0, 5) === 'Name:') - { - // If we already have one, push it on to the array - if (isset($aliases)) - { - for ($i = 0, $count = count($aliases); $i < $count; $i++) - { - $aliases[$i] = normalize_character_set($aliases[$i]); - } - $charsets[$preferred] = array_unique($aliases); - natsort($charsets[$preferred]); - } - - $start = 5 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 5); - $chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start); - $aliases = array(substr($line, $start, $chars)); - $preferred = end($aliases); - } - // Another alias - elseif(substr($line, 0, 6) === 'Alias:') - { - $start = 7 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 7); - $chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start); - $aliases[] = substr($line, $start, $chars); - - if (end($aliases) === 'None') - { - array_pop($aliases); - } - elseif (substr($line, 7 + $chars + 1, 21) === '(preferred MIME name)') - { - $preferred = end($aliases); - } - } - } - - // Compatibility replacements - $compat = array( - 'EUC-KR' => 'windows-949', - 'GB2312' => 'GBK', - 'GB_2312-80' => 'GBK', - 'ISO-8859-1' => 'windows-1252', - 'ISO-8859-9' => 'windows-1254', - 'ISO-8859-11' => 'windows-874', - 'KS_C_5601-1987' => 'windows-949', - 'TIS-620' => 'windows-874', - //'US-ASCII' => 'windows-1252', - 'x-x-big5' => 'Big5', - ); - - foreach ($compat as $real => $replace) - { - if (isset($charsets[$real]) && isset($charsets[$replace])) - { - $charsets[$replace] = array_merge($charsets[$replace], $charsets[$real]); - unset($charsets[$real]); - } - elseif (isset($charsets[$real])) - { - $charsets[$replace] = $charsets[$real]; - $charsets[$replace][] = normalize_character_set($replace); - unset($charsets[$real]); - } - else - { - $charsets[$replace][] = normalize_character_set($real); - } - $charsets[$replace] = array_unique($charsets[$replace]); - natsort($charsets[$replace]); - } - - // Sort it - uksort($charsets, 'strnatcasecmp'); - - // Check that nothing matches more than one - $all = call_user_func_array('array_merge', $charsets); - $all_count = array_count_values($all); - if (max($all_count) > 1) - { - echo "Duplicated charsets:\n"; - foreach ($all_count as $charset => $count) - { - if ($count > 1) - { - echo "$charset\n"; - } - } - } - - // And we're done! - return $charsets; - } -} - -function charset($charset) -{ - $normalized_charset = normalize_character_set($charset); - if ($charsets = build_character_set_list()) - { - foreach ($charsets as $preferred => $aliases) - { - if (in_array($normalized_charset, $aliases)) - { - return $preferred; - } - } - return $charset; - } - else - { - return false; - } -} - -function build_function() -{ - if ($charsets = build_character_set_list()) - { - $return = << $aliases) - { - foreach ($aliases as $alias) - { - $return .= "\t\tcase " . var_export($alias, true) . ":\n"; - } - $return .= "\t\t\treturn " . var_export($preferred, true) . ";\n\n"; - } - $return .= << \ No newline at end of file diff --git a/library/simplepie/db.sql b/library/simplepie/db.sql deleted file mode 100644 index 13f504c214..0000000000 --- a/library/simplepie/db.sql +++ /dev/null @@ -1,38 +0,0 @@ -/* SQLite */ -CREATE TABLE cache_data ( - id TEXT NOT NULL, - items SMALLINT NOT NULL DEFAULT 0, - data BLOB NOT NULL, - mtime INTEGER UNSIGNED NOT NULL -); -CREATE UNIQUE INDEX id ON cache_data(id); - -CREATE TABLE items ( - feed_id TEXT NOT NULL, - id TEXT NOT NULL, - data TEXT NOT NULL, - posted INTEGER UNSIGNED NOT NULL -); -CREATE INDEX feed_id ON items(feed_id); - - -/* MySQL */ -CREATE TABLE `cache_data` ( - `id` TEXT CHARACTER SET utf8 NOT NULL, - `items` SMALLINT NOT NULL DEFAULT 0, - `data` BLOB NOT NULL, - `mtime` INT UNSIGNED NOT NULL, - UNIQUE ( - `id`(125) - ) -); - -CREATE TABLE `items` ( - `feed_id` TEXT CHARACTER SET utf8 NOT NULL, - `id` TEXT CHARACTER SET utf8 NOT NULL, - `data` TEXT CHARACTER SET utf8 NOT NULL, - `posted` INT UNSIGNED NOT NULL, - INDEX `feed_id` ( - `feed_id`(125) - ) -); \ No newline at end of file diff --git a/library/simplepie/demo/cli_test.php b/library/simplepie/demo/cli_test.php deleted file mode 100644 index ec933c5ad7..0000000000 --- a/library/simplepie/demo/cli_test.php +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/php -set_feed_url($argv[1]); - $feed->enable_cache(false); - $feed->init(); -} - -$items = $feed->get_items(); - -foreach ($items as $item) -{ - echo $item->get_title() . "\n"; -} - -var_dump($feed->get_item_quantity()); - -?> \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/alternate_favicon.png b/library/simplepie/demo/for_the_demo/alternate_favicon.png deleted file mode 100644 index 063fb28054..0000000000 Binary files a/library/simplepie/demo/for_the_demo/alternate_favicon.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/background_blockquote.png b/library/simplepie/demo/for_the_demo/background_blockquote.png deleted file mode 100644 index 8267e23a23..0000000000 Binary files a/library/simplepie/demo/for_the_demo/background_blockquote.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/background_menuitem.gif b/library/simplepie/demo/for_the_demo/background_menuitem.gif deleted file mode 100644 index fa765d670e..0000000000 Binary files a/library/simplepie/demo/for_the_demo/background_menuitem.gif and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/background_menuitem_off.gif b/library/simplepie/demo/for_the_demo/background_menuitem_off.gif deleted file mode 100644 index 236cf406dc..0000000000 Binary files a/library/simplepie/demo/for_the_demo/background_menuitem_off.gif and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif b/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif deleted file mode 100644 index 95cfb820d4..0000000000 Binary files a/library/simplepie/demo/for_the_demo/background_menuitem_shadow.gif and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/alternate.png b/library/simplepie/demo/for_the_demo/favicons/alternate.png deleted file mode 100644 index 063fb28054..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/alternate.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/blinklist.png b/library/simplepie/demo/for_the_demo/favicons/blinklist.png deleted file mode 100644 index 53200b3c62..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/blinklist.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/blogmarks.png b/library/simplepie/demo/for_the_demo/favicons/blogmarks.png deleted file mode 100644 index c5372614a4..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/blogmarks.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/delicious.png b/library/simplepie/demo/for_the_demo/favicons/delicious.png deleted file mode 100644 index 2e6021d26e..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/delicious.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/digg.png b/library/simplepie/demo/for_the_demo/favicons/digg.png deleted file mode 100644 index 3aa96770e8..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/digg.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/magnolia.png b/library/simplepie/demo/for_the_demo/favicons/magnolia.png deleted file mode 100644 index da519f5ab9..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/magnolia.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/myweb2.png b/library/simplepie/demo/for_the_demo/favicons/myweb2.png deleted file mode 100644 index 2a12968d5e..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/myweb2.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/newsvine.png b/library/simplepie/demo/for_the_demo/favicons/newsvine.png deleted file mode 100644 index 5cdbb31c69..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/newsvine.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/reddit.png b/library/simplepie/demo/for_the_demo/favicons/reddit.png deleted file mode 100644 index 65c38867cd..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/reddit.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/segnalo.png b/library/simplepie/demo/for_the_demo/favicons/segnalo.png deleted file mode 100644 index 748149b371..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/segnalo.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/simpy.png b/library/simplepie/demo/for_the_demo/favicons/simpy.png deleted file mode 100644 index 30b23c1a55..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/simpy.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/spurl.png b/library/simplepie/demo/for_the_demo/favicons/spurl.png deleted file mode 100644 index f5be3963dc..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/spurl.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/technorati.png b/library/simplepie/demo/for_the_demo/favicons/technorati.png deleted file mode 100644 index 0f19e824e6..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/technorati.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/favicons/wists.png b/library/simplepie/demo/for_the_demo/favicons/wists.png deleted file mode 100644 index 2e2d294d1e..0000000000 Binary files a/library/simplepie/demo/for_the_demo/favicons/wists.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/feed.png b/library/simplepie/demo/for_the_demo/feed.png deleted file mode 100644 index e23c50c85b..0000000000 Binary files a/library/simplepie/demo/for_the_demo/feed.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png b/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png deleted file mode 100644 index eda2d868b5..0000000000 Binary files a/library/simplepie/demo/for_the_demo/logo_simplepie_demo.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf b/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf deleted file mode 100644 index 0a41e15eb8..0000000000 Binary files a/library/simplepie/demo/for_the_demo/lucida-grande-bold.swf and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/mediaplayer.swf b/library/simplepie/demo/for_the_demo/mediaplayer.swf deleted file mode 100644 index bf78fd919a..0000000000 Binary files a/library/simplepie/demo/for_the_demo/mediaplayer.swf and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/mediaplayer_readme.htm b/library/simplepie/demo/for_the_demo/mediaplayer_readme.htm deleted file mode 100644 index 56e12c309e..0000000000 --- a/library/simplepie/demo/for_the_demo/mediaplayer_readme.htm +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/mini_podcast.png b/library/simplepie/demo/for_the_demo/mini_podcast.png deleted file mode 100644 index fd6faf2a31..0000000000 Binary files a/library/simplepie/demo/for_the_demo/mini_podcast.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/place_audio.png b/library/simplepie/demo/for_the_demo/place_audio.png deleted file mode 100644 index 560ea00393..0000000000 Binary files a/library/simplepie/demo/for_the_demo/place_audio.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/place_video.png b/library/simplepie/demo/for_the_demo/place_video.png deleted file mode 100644 index be5ec8219e..0000000000 Binary files a/library/simplepie/demo/for_the_demo/place_video.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/sIFR-print.css b/library/simplepie/demo/for_the_demo/sIFR-print.css deleted file mode 100644 index ec89b1961e..0000000000 --- a/library/simplepie/demo/for_the_demo/sIFR-print.css +++ /dev/null @@ -1,35 +0,0 @@ -/*=:project - scalable Inman Flash Replacement (sIFR) version 3. - - =:file - Copyright: 2006 Mark Wubben. - Author: Mark Wubben, - - =:history - * IFR: Shaun Inman - * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin - * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben - - =:license - This software is licensed and provided under the CC-GNU LGPL. - See -*/ - - -/* This is the print stylesheet to hide the Flash headlines from the browser... regular browser text headlines will now print as normal */ - -.sIFR-flash { - display: none !important; - height: 0; - width: 0; - position: absolute; - overflow: hidden; -} - -.sIFR-alternate { - visibility: visible !important; - display: block !important; - position: static !important; - left: auto !important; - top: auto !important; -} \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/sIFR-screen.css b/library/simplepie/demo/for_the_demo/sIFR-screen.css deleted file mode 100644 index 778e09d2b6..0000000000 --- a/library/simplepie/demo/for_the_demo/sIFR-screen.css +++ /dev/null @@ -1,39 +0,0 @@ -/*=:project - scalable Inman Flash Replacement (sIFR) version 3. - - =:file - Copyright: 2006 Mark Wubben. - Author: Mark Wubben, - - =:history - * IFR: Shaun Inman - * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin - * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben - - =:license - This software is licensed and provided under the CC-GNU LGPL. - See -*/ - -/*---- sIFR ---*/ -.sIFR-flash { - visibility: visible !important; - margin: 0; - padding: 0; -} - -.sIFR-replaced { - visibility: visible !important; -} - -.sIFR-alternate { - position: absolute; - left: 0; - top: 0; - width: 0; - height: 0; - display: block; - overflow: hidden; -} - -/*---- Header styling ---*/ diff --git a/library/simplepie/demo/for_the_demo/sifr-config.js b/library/simplepie/demo/for_the_demo/sifr-config.js deleted file mode 100644 index e7066b3613..0000000000 --- a/library/simplepie/demo/for_the_demo/sifr-config.js +++ /dev/null @@ -1,40 +0,0 @@ -var yanone_kaffeesatz = { - src: './for_the_demo/yanone-kaffeesatz-bold.swf' -}; - -var lucida_grande = { - src: './for_the_demo/lucida-grande-bold.swf' -}; - -sIFR.activate(yanone_kaffeesatz); -//sIFR.activate(lucida_grande); - -sIFR.replace(yanone_kaffeesatz, { -//sIFR.replace(lucida_grande, { - - selector: 'h3.header', - wmode: 'transparent', - css: { - '.sIFR-root': { - 'text-align': 'center', - 'color': '#000000', - 'font-weight': 'bold', - 'background-color': '#EEFFEE', - - 'font-size': '50px', // For Yanone Kaffeesatz - //'font-size': '40px', // For Lucida Grande - - 'letter-spacing': '0' // For Yanone Kaffeesatz - //'letter-spacing': '-4' // For Lucida Grande - - }, - 'a': { - 'text-decoration': 'none', - 'color': '#000000' - }, - 'a:hover': { - 'text-decoration': 'none', - 'color': '#666666' - } - } -}); diff --git a/library/simplepie/demo/for_the_demo/sifr.js b/library/simplepie/demo/for_the_demo/sifr.js deleted file mode 100644 index 0a8b1b6dc0..0000000000 --- a/library/simplepie/demo/for_the_demo/sifr.js +++ /dev/null @@ -1,19 +0,0 @@ -/*=:project - scalable Inman Flash Replacement (sIFR) version 3, revision 245 - - =:file - Copyright: 2006 Mark Wubben. - Author: Mark Wubben, - - =:history - * IFR: Shaun Inman - * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin - * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben - - =:license - This software is licensed and provided under the CC-GNU LGPL. - See -*/ - -var parseSelector=(function(){var _1=/\s*,\s*/;var _2=/\s*([\s>+~(),]|^|$)\s*/g;var _3=/([\s>+~,]|[^(]\+|^)([#.:@])/g;var _4=/^[^\s>+~]/;var _5=/[\s#.:>+~()@]|[^\s#.:>+~()@]+/g;function parseSelector(_6,_7){_7=_7||document.documentElement;var _8=_6.split(_1),_9=[];for(var i=0;i<_8.length;i++){var _b=[_7],_c=toStream(_8[i]);for(var j=0;j<_c.length;){var _e=_c[j++],_f=_c[j++],_10="";if(_c[j]=="("){while(_c[j++]!=")"&&j<_c.length){_10+=_c[j]}_10=_10.slice(0,-1)}_b=select(_b,_e,_f,_10)}_9=_9.concat(_b)}return _9}function toStream(_11){var _12=_11.replace(_2,"$1").replace(_3,"$1*$2");if(_4.test(_12)){_12=" "+_12}return _12.match(_5)||[]}function select(_13,_14,_15,_16){return (_17[_14])?_17[_14](_13,_15,_16):[]}var _18={toArray:function(_19){var a=[];for(var i=0;i<_19.length;i++){a.push(_19[i])}return a}};var dom={isTag:function(_1d,tag){return (tag=="*")||(tag.toLowerCase()==_1d.nodeName.toLowerCase())},previousSiblingElement:function(_1f){do{_1f=_1f.previousSibling}while(_1f&&_1f.nodeType!=1);return _1f},nextSiblingElement:function(_20){do{_20=_20.nextSibling}while(_20&&_20.nodeType!=1);return _20},hasClass:function(_21,_22){return (_22.className||"").match("(^|\\s)"+_21+"(\\s|$)")},getByTag:function(tag,_24){return _24.getElementsByTagName(tag)}};var _17={"#":function(_25,_26){for(var i=0;i<_25.length;i++){if(_25[i].getAttribute("id")==_26){return [_25[i]]}}return []}," ":function(_28,_29){var _2a=[];for(var i=0;i<_28.length;i++){_2a=_2a.concat(_18.toArray(dom.getByTag(_29,_28[i])))}return _2a},">":function(_2c,_2d){var _2e=[];for(var i=0,_30;i<_2c.length;i++){_30=_2c[i];for(var j=0,_32;j<_30.childNodes.length;j++){_32=_30.childNodes[j];if(_32.nodeType==1&&dom.isTag(_32,_2d)){_2e.push(_32)}}}return _2e},".":function(_33,_34){var _35=[];for(var i=0,_37;i<_33.length;i++){_37=_33[i];if(dom.hasClass([_34],_37)){_35.push(_37)}}return _35},":":function(_38,_39,_3a){return (pseudoClasses[_39])?pseudoClasses[_39](_38,_3a):[]}};parseSelector.selectors=_17;parseSelector.pseudoClasses={};parseSelector.util=_18;parseSelector.dom=dom;return parseSelector})(); -var sIFR=new function(){var _3b=this;var _3c="sIFR-active";var _3d="sIFR-replaced";var _3e="sIFR-replacing";var _3f="sIFR-flash";var _40="sIFR-ignore";var _41="sIFR-alternate";var _42="sIFR-class";var _43="sIFR-layout";var _44=6;var _45=126;var _46=8;var _47="SIFR-PREFETCHED";var _48=[10,1.55,19,1.45,32,1.35,71,1.3,1.25];var _49=5;this.isActive=false;this.isEnabled=true;this.hideElements=true;this.preserveSingleWhitespace=false;this.fixWrap=true;this.fixHover=true;this.registerEvents=true;this.setPrefetchCookie=true;this.cookiePath="/";this.domains=[];this.fromLocal=true;this.forceClear=false;this.forceWidth=false;this.fitExactly=false;this.forceTextTransform=true;this.useDomContentLoaded=true;this.debugMode=false;this.hasFlashClassSet=false;this.delayCss=false;this.callbacks=[];var _4a=0;var _4b=false,_4c=false;var dom=new function(){var _4e="http://www.w3.org/1999/xhtml";this.getBody=function(){var _4f=document.getElementsByTagName("body");if(_4f.length==1){return _4f[0]}return null};this.addClass=function(_50,_51){if(_51){_51.className=((_51.className||"")==""?"":_51.className+" ")+_50}};this.removeClass=function(_52,_53){if(_53){_53.className=_53.className.replace(new RegExp("(^|\\s)"+_52+"(\\s|$)"),"").replace(/^\s+|(\s)\s+/g,"$1")}};this.hasClass=function(_54,_55){return new RegExp("(^|\\s)"+_54+"(\\s|$)").test(_55.className)};this.hasOneOfClassses=function(_56,_57){for(var i=0;i<_56.length;i++){if(this.hasClass(_56[i],_57)){return true}}return false};this.create=function(_59){if(document.createElementNS){return document.createElementNS(_4e,_59)}return document.createElement(_59)};this.setInnerHtml=function(_5a,_5b){if(ua.innerHtmlSupport){_5a.innerHTML=_5b}else{if(ua.xhtmlSupport){_5b=["",_5b,""].join("");var xml=(new DOMParser()).parseFromString(_5b,"text/xml");xml=document.importNode(xml.documentElement,true);while(_5a.firstChild){_5a.removeChild(_5a.firstChild)}while(xml.firstChild){_5a.appendChild(xml.firstChild)}}}};this.nodeFromHtml=function(_5d){var _5e=this.create("div");_5e.innerHTML=_5d;return _5e.firstChild};this.getComputedStyle=function(_5f,_60){var _61;if(document.defaultView&&document.defaultView.getComputedStyle){_61=document.defaultView.getComputedStyle(_5f,null)[_60]}else{if(_5f.currentStyle){_61=_5f.currentStyle[_60]}}return _61||""};this.getStyleAsInt=function(_62,_63,_64){var _65=this.getComputedStyle(_62,_63);if(_64&&!/px$/.test(_65)){return 0}_65=parseInt(_65);return isNaN(_65)?0:_65};this.getZoom=function(){return _66.zoom.getLatest()}};this.dom=dom;var ua=new function(){var ua=navigator.userAgent.toLowerCase();var _69=(navigator.product||"").toLowerCase();this.macintosh=ua.indexOf("mac")>-1;this.windows=ua.indexOf("windows")>-1;this.quicktime=false;this.opera=ua.indexOf("opera")>-1;this.konqueror=_69.indexOf("konqueror")>-1;this.ie=false/*@cc_on || true @*/;this.ieSupported=this.ie&&!/ppc|smartphone|iemobile|msie\s5\.5/.test(ua)/*@cc_on && @_jscript_version >= 5.5 @*/;this.ieWin=this.ie&&this.windows/*@cc_on && @_jscript_version >= 5.1 @*/;this.windows=this.windows&&(!this.ie||this.ieWin);this.ieMac=this.ie&&this.macintosh/*@cc_on && @_jscript_version < 5.1 @*/;this.macintosh=this.macintosh&&(!this.ie||this.ieMac);this.safari=ua.indexOf("safari")>-1;this.webkit=ua.indexOf("applewebkit")>-1&&!this.konqueror;this.khtml=this.webkit||this.konqueror;this.gecko=!this.webkit&&_69=="gecko";this.operaVersion=this.opera&&/.*opera(\s|\/)(\d+\.\d+)/.exec(ua)?parseInt(RegExp.$2):0;this.webkitVersion=this.webkit&&/.*applewebkit\/(\d+).*/.exec(ua)?parseInt(RegExp.$1):0;this.geckoBuildDate=this.gecko&&/.*gecko\/(\d{8}).*/.exec(ua)?parseInt(RegExp.$1):0;this.konquerorVersion=this.konqueror&&/.*konqueror\/(\d\.\d).*/.exec(ua)?parseInt(RegExp.$1):0;this.flashVersion=0;if(this.ieWin){var axo;var _6b=false;try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")}catch(e){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");this.flashVersion=6;axo.AllowScriptAccess="always"}catch(e){_6b=this.flashVersion==6}if(!_6b){try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash")}catch(e){}}}if(!_6b&&axo){this.flashVersion=parseFloat(/([\d,?]+)/.exec(axo.GetVariable("$version"))[1].replace(/,/g,"."))}}else{if(navigator.plugins&&navigator.plugins["Shockwave Flash"]){var _6c=navigator.plugins["Shockwave Flash"];this.flashVersion=parseFloat(/(\d+\.?\d*)/.exec(_6c.description)[1]);var i=0;while(this.flashVersion>=_46&&i-1){this.flashVersion=0;this.quicktime=true}i++}}}this.flash=this.flashVersion>=_46;this.transparencySupport=this.macintosh||this.windows;this.computedStyleSupport=this.ie||document.defaultView&&document.defaultView.getComputedStyle&&(!this.gecko||this.geckoBuildDate>=20030624);this.css=true;if(this.computedStyleSupport){try{var _6f=document.getElementsByTagName("head")[0];_6f.style.backgroundColor="#FF0000";var _70=dom.getComputedStyle(_6f,"backgroundColor");this.css=!_70||/\#F{2}0{4}|rgb\(255,\s?0,\s?0\)/i.test(_70);_6f.style.backgroundColor="";_6f=null}catch(e){}}this.xhtmlSupport=!!window.DOMParser&&!!document.importNode;try{var n=dom.create("span");if(!this.ieMac){n.innerHTML="x"}this.innerHtmlSupport=n.innerHTML=="x"}catch(e){this.innerHtmlSupport=false}this.zoomSupport=!!(this.opera&&document.documentElement);this.geckoXml=this.gecko&&(document.contentType||"").indexOf("xml")>-1;this.requiresPrefetch=this.ieWin||this.khtml;this.verifiedKonqueror=false;this.supported=this.flash&&this.css&&(!this.ie||this.ieSupported)&&(!this.opera||this.operaVersion>=8)&&(!this.webkit||this.webkitVersion>=412)&&(!this.konqueror||this.konquerorVersion>3.5)&&this.computedStyleSupport&&(this.innerHtmlSupport||!this.khtml&&this.xhtmlSupport)};this.ua=ua;var _72=new function(){var _73={leading:true,"margin-left":true,"margin-right":true,"text-indent":true};var _74=" ";function capitalize($){return $.toUpperCase()}this.normalize=function(str){if(_3b.preserveSingleWhitespace){return str.replace(/\s/g,_74)}return str.replace(/(\s)\s+/g,"$1").replace(/\xA0/,_74)};this.textTransform=function(_77,str){switch(_77){case "uppercase":str=str.toUpperCase();break;case "lowercase":str=str.toLowerCase();break;case "capitalize":var _79=str;str=str.replace(/^\w|\s\w/g,capitalize);if(str.indexOf("function capitalize")!=-1){var _7a=_79.replace(/(^|\s)(\w)/g,"$1$1$2$2").split(/^\w|\s\w/g);str="";for(var i=0;i<_7a.length;i++){str+=_7a[i].charAt(0).toUpperCase()+_7a[i].substring(1)}}break}return str};this.toHexString=function(str){if(typeof (str)!="string"||!str.charAt(0)=="#"||str.length!=4&&str.length!=7){return str}str=str.replace(/#/,"");if(str.length==3){str=str.replace(/(.)(.)(.)/,"$1$1$2$2$3$3")}return "0x"+str};this.toJson=function(obj){var _7e="";switch(typeof (obj)){case "string":_7e="\""+obj+"\"";break;case "number":case "boolean":_7e=obj.toString();break;case "object":_7e=[];for(var _7f in obj){if(obj[_7f]==Object.prototype[_7f]){continue}_7e.push("\""+_7f+"\":"+_72.toJson(obj[_7f]))}_7e="{"+_7e.join(",")+"}";break}return _7e};this.convertCssArg=function(arg){if(!arg){return {}}if(typeof (arg)=="object"){if(arg.constructor==Array){arg=arg.join("")}else{return arg}}var obj={};var _82=arg.split("}");for(var i=0;i<_82.length;i++){var $=_82[i].match(/([^\s{]+)\s*\{(.+)\s*;?\s*/);if(!$||$.length!=3){continue}if(!obj[$[1]]){obj[$[1]]={}}var _85=$[2].split(";");for(var j=0;j<_85.length;j++){var $2=_85[j].match(/\s*([^:\s]+)\s*\:\s*([^\s;]+)/);if(!$2||$2.length!=3){continue}obj[$[1]][$2[1]]=$2[2]}}return obj};this.extractFromCss=function(css,_89,_8a,_8b){var _8c=null;if(css&&css[_89]&&css[_89][_8a]){_8c=css[_89][_8a];if(_8b){delete css[_89][_8a]}}return _8c};this.cssToString=function(arg){var css=[];for(var _8f in arg){var _90=arg[_8f];if(_90==Object.prototype[_8f]){continue}css.push(_8f,"{");for(var _91 in _90){if(_90[_91]==Object.prototype[_91]){continue}var _92=_90[_91];if(_73[_91]){_92=parseInt(_92,10)}css.push(_91,":",_92,";")}css.push("}")}return escape(css.join(""))};this.bind=function(_93,_94){return function(){_93[_94].apply(_93,arguments)}}};this.util=_72;var _66={};_66.fragmentIdentifier=new function(){this.fix=true;var _95;this.cache=function(){_95=document.title};function doFix(){document.title=_95}this.restore=function(){if(this.fix){setTimeout(doFix,0)}}};_66.synchronizer=new function(){this.isBlocked=false;this.block=function(){this.isBlocked=true};this.unblock=function(){this.isBlocked=false;_96.replaceAll()}};_66.zoom=new function(){var _97=100;this.getLatest=function(){return _97};if(ua.zoomSupport&&ua.opera){var _98=document.createElement("div");_98.style.position="fixed";_98.style.left="-65536px";_98.style.top="0";_98.style.height="100%";_98.style.width="1px";_98.style.zIndex="-32";document.documentElement.appendChild(_98);function updateZoom(){if(!_98){return}var _99=window.innerHeight/_98.offsetHeight;var _9a=Math.round(_99*100)%10;if(_9a>5){_99=Math.round(_99*100)+10-_9a}else{_99=Math.round(_99*100)-_9a}_97=isNaN(_99)?100:_99;_66.synchronizer.unblock();document.documentElement.removeChild(_98);_98=null}_66.synchronizer.block();setTimeout(updateZoom,54)}};this.hacks=_66;var _9b={kwargs:[],replaceAll:function(){for(var i=0;i-1){_9f=_9f.substr(_a0+1);var _a1=_9d.lastIndexOf(_9f);if(_a1>-1&&(_a1+_9f.length)==_9d.length){return true}}}return false}this.activate=function(){if(!ua.supported||!this.isEnabled||this.isActive||!isValidDomain()){return}if(arguments.length>0){this.prefetch.apply(this,arguments)}this.isActive=true;if(this.hideElements){this.setFlashClass()}if(ua.ieWin&&_66.fragmentIdentifier.fix&&window.location.hash!=""){_66.fragmentIdentifier.cache()}else{_66.fragmentIdentifier.fix=false}if(!this.registerEvents){return}function handler(evt){_3b.initialize();if(evt&&evt.type=="load"){if(document.removeEventListener){document.removeEventListener("DOMContentLoaded",handler,false)}if(window.removeEventListener){window.removeEventListener("load",handler,false)}}}if(window.addEventListener){if(_3b.useDomContentLoaded&&ua.gecko){document.addEventListener("DOMContentLoaded",handler,false)}window.addEventListener("load",handler,false)}else{if(ua.ieWin){if(_3b.useDomContentLoaded){document.write("");document.getElementById("__sifr_ie_onload").onreadystatechange=function(){if(this.readyState=="complete"){handler();this.removeNode()}}}window.attachEvent("onload",handler)}}};this.setFlashClass=function(){if(this.hasFlashClassSet){return}dom.addClass(_3c,dom.getBody()||document.documentElement);this.hasFlashClassSet=true};this.removeFlashClass=function(){if(!this.hasFlashClassSet){return}dom.removeClass(_3c,dom.getBody());dom.removeClass(_3c,document.documentElement);this.hasFlashClassSet=false};this.initialize=function(){if(_4c||!this.isActive||!this.isEnabled){return}_4c=true;_9b.replaceAll();clearPrefetch()};function getSource(src){if(typeof (src)!="string"){if(src.src){src=src.src}if(typeof (src)!="string"){var _a4=[];for(var _a5 in src){if(src[_a5]!=Object.prototype[_a5]){_a4.push(_a5)}}_a4.sort().reverse();var _a6="";var i=-1;while(!_a6&&++i<_a4.length){if(parseFloat(_a4[i])<=ua.flashVersion){_a6=src[_a4[i]]}}src=_a6}}if(!src&&_3b.debugMode){throw new Error("sIFR: Could not determine appropriate source")}if(ua.ie&&src.charAt(0)=="/"){src=window.location.toString().replace(/([^:]+)(:\/?\/?)([^\/]+).*/,"$1$2$3")+src}return src}this.prefetch=function(){if(!ua.requiresPrefetch||!ua.supported||!this.isEnabled||!isValidDomain()){return}if(this.setPrefetchCookie&&new RegExp(";?"+_47+"=true;?").test(document.cookie)){return}try{_4b=true;if(ua.ieWin){prefetchIexplore(arguments)}else{prefetchLight(arguments)}if(this.setPrefetchCookie){document.cookie=_47+"=true;path="+this.cookiePath}}catch(e){if(_3b.debugMode){throw e}}};function prefetchIexplore(_a8){for(var i=0;i<_a8.length;i++){document.write("")}}function prefetchLight(_aa){for(var i=0;i<_aa.length;i++){new Image().src=getSource(_aa[i])}}function clearPrefetch(){if(!ua.ieWin||!_4b){return}try{var _ac=document.getElementsByTagName("script");for(var i=_ac.length-1;i>=0;i--){var _ae=_ac[i];if(_ae.type=="sifr/prefetch"){_ae.parentNode.removeChild(_ae)}}}catch(e){}}function getRatio(_af,_b0){for(var i=0;i<_b0.length;i+=2){if(_af<=_b0[i]){return _b0[i+1]}}return _b0[_b0.length-1]}function getFilters(obj){var _b3=[];for(var _b4 in obj){if(obj[_b4]==Object.prototype[_b4]){continue}var _b5=obj[_b4];_b4=[_b4.replace(/filter/i,"")+"Filter"];for(var _b6 in _b5){if(_b5[_b6]==Object.prototype[_b6]){continue}_b4.push(_b6+":"+escape(_72.toJson(_72.toHexString(_b5[_b6]))))}_b3.push(_b4.join(","))}return _b3.join(";")}function calculate(_b7){var _b8,_b9;if(!ua.ie){_b8=dom.getStyleAsInt(_b7,"lineHeight");_b9=Math.floor(dom.getStyleAsInt(_b7,"height")/_b8)}else{if(ua.ie){var _ba=_b7.innerHTML;_b7.style.visibility="visible";_b7.style.overflow="visible";_b7.style.position="static";_b7.style.zoom="normal";_b7.style.writingMode="lr-tb";_b7.style.width=_b7.style.height="auto";_b7.style.maxWidth=_b7.style.maxHeight=_b7.style.styleFloat="none";var _bb=_b7;var _bc=_b7.currentStyle.hasLayout;if(_bc){dom.setInnerHtml(_b7,"
            X
            X
            X
            ");_bb=_b7.firstChild}else{dom.setInnerHtml(_b7,"X
            X
            X")}var _bd=_bb.getClientRects();_b8=_bd[1].bottom-_bd[1].top;_b8=Math.ceil(_b8*0.8);if(_bc){dom.setInnerHtml(_b7,"
            "+_ba+"
            ");_bb=_b7.firstChild}else{dom.setInnerHtml(_b7,_ba)}_bd=_bb.getClientRects();_b9=_bd.length;if(_bc){dom.setInnerHtml(_b7,_ba)}_b7.style.visibility=_b7.style.width=_b7.style.height=_b7.style.maxWidth=_b7.style.maxHeight=_b7.style.overflow=_b7.style.styleFloat=_b7.style.position=_b7.style.zoom=_b7.style.writingMode=""}}return {lineHeight:_b8,lines:_b9}}this.replace=function(_be,_bf){if(!ua.supported){return}if(_bf){for(var _c0 in _be){if(typeof (_bf[_c0])=="undefined"){_bf[_c0]=_be[_c0]}}_be=_bf}if(!_4c){return _9b.kwargs.push(_be)}if(_66.synchronizer.isBlocked){return _96.kwargs.push(_be)}var _c1=_be.elements;if(!_c1&&parseSelector){_c1=parseSelector(_be.selector)}if(_c1.length==0){return}this.setFlashClass();var src=getSource(_be.src);var css=_72.convertCssArg(_be.css);var _c4=getFilters(_be.filters);var _c5=(_be.forceClear==null)?_3b.forceClear:_be.forceClear;var _c6=(_be.fitExactly==null)?_3b.fitExactly:_be.fitExactly;var _c7=_c6||(_be.forceWidth==null?_3b.forceWidth:_be.forceWidth);var _c8=parseInt(_72.extractFromCss(css,".sIFR-root","leading"))||0;var _c9=_72.extractFromCss(css,".sIFR-root","font-size",true)||0;var _ca=_72.extractFromCss(css,".sIFR-root","background-color",true)||"#FFFFFF";var _cb=_72.extractFromCss(css,".sIFR-root","kerning",true)||"";var _cc=_be.gridFitType||_72.extractFromCss(css,".sIFR-root","text-align")=="right"?"subpixel":"pixel";var _cd=_3b.forceTextTransform?_72.extractFromCss(css,".sIFR-root","text-transform",true)||"none":"none";var _ce=_72.extractFromCss(css,".sIFR-root","opacity",true)||"100";var _cf=_be.pixelFont||false;var _d0=_be.ratios||_48;if(parseInt(_c9).toString()!=_c9&&_c9.indexOf("px")==-1){_c9=0}else{_c9=parseInt(_c9)}if(parseFloat(_ce)<1){_ce=100*parseFloat(_ce)}var _d1=null;var _d2="";if(_c6){_72.extractFromCss(css,".sIFR-root","text-align",true)}if(!_be.modifyCss){_d2=_72.cssToString(css);_d1=_3b.fixHover&&_d2.indexOf("%3Ahover")>-1}var _d3=!ua.opera&&_3b.delayCss;var _d4=_be.wmode||"";if(!_d4){if(_be.transparent){_d4="transparent"}else{if(_be.opaque){_d4="opaque"}}}if(_d4=="transparent"){if(!ua.transparencySupport){_d4="opaque"}else{_ca="transparent"}}for(var i=0;i<_c1.length;i++){var _d6=_c1[i];if(!ua.verifiedKonqueror){if(dom.getComputedStyle(_d6,"lineHeight").match(/e\+08px/)){ua.supported=_3b.isEnabled=false;this.removeFlashClass();return}ua.verifiedKonqueror=true}if(dom.hasOneOfClassses([_3d,_3e,_40,_41],_d6)){continue}var _d7=_d6.offsetHeight;var _d8=_d6.offsetWidth;var _d9=dom.getComputedStyle(_d6,"display");if(!_d7||!_d8||_d9==null||_d9=="none"){continue}if(_c5&&ua.gecko){_d6.style.clear="both"}var _da=null;if(_3b.fixWrap&&ua.ie&&_d9=="block"){_da=_d6.innerHTML;dom.setInnerHtml(_d6,"X")}_d8=dom.getStyleAsInt(_d6,"width",ua.ie);if(_d8==0){var _db=dom.getStyleAsInt(_d6,"paddingRight",true);var _dc=dom.getStyleAsInt(_d6,"paddingLeft",true);var _dd=dom.getStyleAsInt(_d6,"borderRightWidth",true);var _de=dom.getStyleAsInt(_d6,"borderLeftWidth",true);_d8=_d6.offsetWidth-_dc-_db-_de-_dd}if(_da&&_3b.fixWrap&&ua.ie){dom.setInnerHtml(_d6,_da)}var _df,_e0;if(!_c9){var _e1=calculate(_d6);_df=Math.min(_45,Math.max(_44,_e1.lineHeight));if(_cf){_df=Math.max(8,8*Math.round(_df/8))}_e0=_e1.lines;if(isNaN(_e0)||!isFinite(_e0)||_e0==0){_e0=1}if(_e0>1&&_c8){_d7+=Math.round((_e0-1)*_c8)}}else{_df=_c9;_e0=1}_d7=Math.round(_e0*_df);if(_c5&&ua.gecko){_d6.style.clear=""}var _e2=dom.create("span");_e2.className=_41;var _e3=_d6.cloneNode(true);for(var j=0,l=_e3.childNodes.length;j-1}var _e6=handleContent(_e3,_cd);if(_be.modifyContentString){_e6=_be.modifyContentString(_e6,_be.selector)}if(_e6==""){continue}var _e7=["content="+_e6,"width="+_d8,"height="+_d7,"fitexactly="+(_c6?"true":""),"tunewidth="+(_be.tuneWidth||""),"tuneheight="+(_be.tuneHeight||""),"offsetleft="+(_be.offsetLeft||""),"offsettop="+(_be.offsetTop||""),"thickness="+(_be.thickness||""),"sharpness="+(_be.sharpness||""),"kerning="+_cb,"gridfittype="+_cc,"zoomsupport="+ua.zoomSupport,"flashfilters="+_c4,"opacity="+_ce,"blendmode="+(_be.blendMode||""),"size="+_df,"zoom="+dom.getZoom(),"css="+_d2,"selectable="+(_be.selectable==null?"true":_be.selectable),"lines="+_e0];var _e8=encodeURI(_e7.join("&"));var _e9="sIFR_callback_"+_4a++;var _ea=new CallbackInfo(_e9,_e7,_be.onReplacement,_d1);window[_e9+"_DoFSCommand"]=(function(_eb){return function(_ec,arg){_eb.handle(_ec,arg)}})(_ea);_d7=Math.round(_e0*getRatio(_df,_d0)*_df)+_49;var _ee=_c7?_d8:"100%";var _ef;if(ua.ie){_ef=["","","","","","","","","","",_e9,"_DoFSCommand(info, args);",""].join("")}else{_ef=[""].join("")}dom.setInnerHtml(_d6,_ef);_ea.flashNode=_d6.firstChild;_ea.html=_ef;_3b.callbacks.push(_ea);if(_be.selector){if(!_3b.callbacks[_be.selector]){_3b.callbacks[_be.selector]=[_ea]}else{_3b.callbacks[_be.selector].push(_ea)}}_d6.appendChild(_e2);dom.addClass(_d3?_3e:_3d,_d6);_ea.setupFixHover()}_66.fragmentIdentifier.restore()};this.getCallbackByFlashElement=function(_f0){for(var i=0;i<_3b.callbacks.length;i++){if(_3b.callbacks[i].id==_f0.getAttribute("id")){return _3b.callbacks[i]}}};function handleContent(_f2,_f3){var _f4=[],_f5=[];var _f6=_f2.childNodes;var i=0;while(i<_f6.length){var _f8=_f6[i];if(_f8.nodeType==3){var _f9=_72.normalize(_f8.nodeValue);_f9=_72.textTransform(_f3,_f9);_f5.push(_f9.replace(/\%/g,"%25").replace(/\&/g,"%26").replace(/\,/g,"%2C").replace(/\+/g,"%2B"))}if(_f8.nodeType==1){var _fa=[];var _fb=_f8.nodeName.toLowerCase();var _fc=_f8.className||"";if(/\s+/.test(_fc)){if(_fc.indexOf(_42)>-1){_fc=_fc.match("(\\s|^)"+_42+"-([^\\s$]*)(\\s|$)")[2]}else{_fc=_fc.match(/^([^\s]+)/)[1]}}if(_fc!=""){_fa.push("class=\""+_fc+"\"")}if(_fb=="a"){var _fd=_f8.getAttribute("href")||"";var _fe=_f8.getAttribute("target")||"";_fa.push("href=\""+_fd+"\"","target=\""+_fe+"\"")}_f5.push("<"+_fb+(_fa.length>0?" ":"")+escape(_fa.join(" "))+">");if(_f8.hasChildNodes()){_f4.push(i);i=0;_f6=_f8.childNodes;continue}else{if(!/^(br|img)$/i.test(_f8.nodeName)){_f5.push("")}}}if(_f4.length>0&&!_f8.nextSibling){do{i=_f4.pop();_f6=_f8.parentNode.parentNode.childNodes;_f8=_f6[i];if(_f8){_f5.push("")}}while(i==_f6.length-1&&_f4.length>0)}i++}return _f5.join("").replace(/\n|\r/g,"")}function CallbackInfo(id,vars,_101,_102){this.id=id;this.vars=vars;this._replacementHandler=_101;this._firedReplacementEvent=!(this._replacementHandler!=null);this._fixHover=_102;this._setClasses=!_3b.delayCss;this.html="";this._pings=0}CallbackInfo.prototype.getFlashElement=function(){return document.getElementById(this.id)};CallbackInfo.prototype.handle=function(info,arg){if(/(FSCommand\:)?resize/.test(info)){var _105=this.getFlashElement();var $=arg.split(/\:|,/);_105.setAttribute($[0],$[1]);if($.length>2){_105.setAttribute($[2],$[3])}if(!this._setClasses){if(!ua.ie&&!ua.opera){dom.addClass(_3f,_105)}dom.removeClass(_3e,_105.parentNode);dom.addClass(_3d,_105.parentNode);this._setClasses=true}if(ua.khtml){var _107=_105.offsetHeight}if(!this._firedReplacementEvent){this._replacementHandler(this);this._firedReplacementEvent=true}}else{if(/(FSCommand\:)?resetmovie/.test(info)){this.resetMovie()}else{if(/(FSCommand\:)?ping/.test(info)){if(this._pings>0){this.setupFixHover()}this._pings++}else{if(this.debugHandler&&/(FSCommand\:)?debug/.test(info)){this.debugHandler(info,arg)}}}}};CallbackInfo.prototype.call=function(type,_109){var _10a=this.getFlashElement();if(!_10a){return}_10a.SetVariable("callbackType",type);_10a.SetVariable("callbackValue",_109);_10a.SetVariable("callbackTrigger",true)};CallbackInfo.prototype.replaceText=function(_10b){_10b=escape(_10b);this.call("replacetext",_10b);this.vars[0]="content="+_10b;this.html=this.html.replace(/(flashvars(=|\"\svalue=)\")[^\"]+/,"$1"+encodeURI(this.vars.join("&")))};CallbackInfo.prototype.resetMovie=function(){var _10c=this.getFlashElement();var node=_10c.parentNode;node.replaceChild(dom.nodeFromHtml(this.html),_10c);this.setupFixHover()};CallbackInfo.prototype.setupFixHover=function(){var _10e=this.getFlashElement();if(!this._fixHover||!_10e){return}var node=_10e.parentNode;if(node.addEventListener){node.addEventListener("mouseout",_72.bind(this,"fixHover"),false)}else{if(node.attachEvent){node.attachEvent("onmouseout",_72.bind(this,"fixHover"))}}};CallbackInfo.prototype.fixHover=function(){this.call("resettext")}}; \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/simplepie.css b/library/simplepie/demo/for_the_demo/simplepie.css deleted file mode 100644 index 3753cb96de..0000000000 --- a/library/simplepie/demo/for_the_demo/simplepie.css +++ /dev/null @@ -1,397 +0,0 @@ -/* -Theme Name: SimplePie -Theme URI: http://simplepie.org -Description: A simple, yet beautiful theme inspired by several cleanly designed websites. -Version: 1.4 -Author: Ryan Parman -Author URI: http://skyzyx.com -Updated: 21 June 2007 -*/ - - -/********************************************* -HYPERLINK STYLES -*********************************************/ -a { - color:#369; - text-decoration:underline; - padding:0 1px; -} - -a:hover { - color:#fff !important; - background-color:#333; - text-decoration:none; - padding:0 1px; -} - -a.nohover { - text-decoration:none; - border:none; -} - -a.nohover:hover { - background-color:transparent; - border:none; -} - -a.namelink { - padding:0; - margin:0; - overflow:hidden; - height:1px; -} - -h4 a, -.sample_feeds a { - color:#000; -} - - -/********************************************* -GENERAL STYLES -*********************************************/ -body { - /*font:12px/18px Verdana, sans-serif;*/ - font:14px/1.5em "Lucida Grande", Tahoma, sans-serif; - letter-spacing:0px; - color:#333; - background-color:#fff; - margin:0; - padding:0; -} - -div#site { - width:600px; - margin:50px auto 0 auto; -} - -h1#logo { - margin:0; - padding:0; - text-align:center; -} - -h1#logo a, -h1#logo a:hover { - background-color:transparent; - text-decoration:none; - padding:0; -} - -h2.image { - margin:0; - padding:0; - text-align:center; -} - -h3 { - margin:20px 0 0 0; - padding:0; - font-size:1.5em; -} - -h4 { - margin:20px 0 0 0; - padding:0; - font-size:1.2em; - letter-spacing:-1px; -} - -h5 { - margin:10px 0 0 0; - padding:0; - font-size:1em; - font-weight:bold; -} - -em { - font-style:normal; - background-color:#ffc; -} - -p { - margin:0; - padding:5px 0; -} - -ul, ol { - margin:10px 0 10px 20px; - padding:0 0 0 15px; -} - -ul li, ol li { - margin:0 0 7px 0; - padding:0 0 0 3px; -} - -form { - margin:0; - padding:0; -} - -code { - font-size:1em; - background-color:#f3f3ff; - color:#000; -} - -div#site pre { - background-color:#f3f3ff; - color:#000080; - border:1px dotted #000080; - overflow:auto; - padding:3px 5px; -} - -blockquote { - font-size:1em; - color:#666; - border-left:4px solid #666; - margin:10px 0 10px 30px; - padding:0 5px 0 10px; - background:#f3f3f3 url(background_blockquote.png) repeat top left; -} - -input, select, textarea { - font-size:12px; - line-height:1.2em; - padding:2px; -} - -input[type=text], select, textarea { - background-color:#e9f5ff; - border:1px solid #333; -} - -input[type=text]:focus, select:focus, textarea:focus { - background-color:#ffe; -} - -.clearLeft {clear:left;} -.clearRight {clear:right;} -.clearBoth {clear:both;} -.hide {display:none;} - - -/********************************************* -NAVIGATION STYLES -*********************************************/ -div#header { - background:#fff url(top_gradient.gif) repeat-x top left; - margin:0; - padding:0; -} - -div#header form { - margin:0; - padding:0; -} - -div#header div#headerInner { - margin:0; - padding:0; -} - -div#header div#headerInner div#logoContainer {} - -div#header div#headerInner div#logoContainerInner { - width:550px; - margin:0 auto; - padding:20px; -} - -div#header div#headerInner div#logoContainer div#logo { - float:left; - width:200px; -} - -div#header div#headerInner div#logoContainer div#logo a, -div#header div#headerInner div#logoContainer div#logo a:hover { - border:none; - background:none; -} - -div#header div#headerInner div#logoContainer div#feed { - float:right; - width:300px; - text-align:right; - padding:10px 0 0 0; -} - -div#header div#headerInner div#logoContainer div#feed input.text { - width:60%; -} - -div#header div#headerInner div#menu { - background:#eee url(background_menuitem_shadow.gif) repeat-x top left; - border-top:2px solid #ccc; - border-bottom:1px solid #ddd; - text-align:center; -} - -div#header div#headerInner div#menu table { - width:auto; - margin:0 auto; -} - -div#header div#headerInner div#menu ul { - display:block; - width:100%; - margin:0 auto; - padding:0; - font-size:12px; -} - -div#header div#headerInner div#menu ul li { - display:block; - float:left; -} - -div#header div#headerInner div#menu ul li a { - display:block; - margin:-2px 0 0 0; - padding:5px 7px 8px 7px; - text-decoration:none; - color:#666 !important; - background-color:transparent; -} - -div#header div#headerInner div#menu ul li a:hover { - display:block; - margin:-2px 0 0 0; - padding:5px 7px 8px 7px; - text-decoration:none; - color:#666; - background:#fff url(background_menuitem_off.gif) no-repeat bottom right; -} - -body#bodydemo div#header div#headerInner div#menu ul li#demo a { - display:block; - margin:-2px 0 0 0; - padding:5px 7px 8px 7px; - text-decoration:none; - color:#333; - font-weight:bold; - background:#fff url(background_menuitem.gif) no-repeat bottom right; -} - - -/********************************************* -CONTENT STYLES -*********************************************/ -div.chunk { - margin:20px 0 0 0; - padding:0 0 10px 0; - border-bottom:1px solid #ccc; -} - -div.topchunk { - margin:0 !important; -} - -.footnote, -.footnote a { - font-size:12px; - line-height:1.3em; - color:#aaa; -} - -.footnote em { - background-color:transparent; - font-style:italic; -} - -.footnote code { - background-color:transparent; - font:11px/14px monospace; - color:#aaa; -} - -p.subscribe { - background-color:#f3f3f3; - font-size:12px; - text-align:center; -} - -p.highlight { - background-color:#ffc; - font-size:12px; - text-align:center; -} - -p.sample_feeds { - font-size:12px; - line-height:1.2em; -} - -div.sp_errors { - background-color:#eee; - padding:5px; - text-align:center; - font-size:12px; -} - -.noborder { - border:none !important; -} - -img.favicon { - margin:0 4px -2px 0; - width:16px; - height:16px; -} - -p.favicons a, -p.favicons a:hover { - border:none; - background-color:transparent; -} - -p.favicons img { - border:none; -} - - -/********************************************* -DEMO STYLES -*********************************************/ -div#sp_input { - background-color:#ffc; - border:2px solid #f90; - padding:5px; - text-align:center; -} - -div#sp_input input.text { - border:1px solid #999; - background:#e9f5ff url(feed.png) no-repeat 4px 50%; - width:75%; - padding:2px 2px 2px 28px; - font:18px/22px "Lucida Grande", Verdana, sans-serif; - font-weight:bold; - letter-spacing:-1px; -} - -form#sp_form { - margin:15px 0; -} - -div.focus { - margin:0; - padding:10px 20px; - background-color:#efe; -} - -p.sample_feeds { - text-align:justify; -} - - -/********************************************* -SIFR STYLES -*********************************************/ -.sIFR-active h3.header { - visibility:hidden; - line-height:1em; -} diff --git a/library/simplepie/demo/for_the_demo/sleight.js b/library/simplepie/demo/for_the_demo/sleight.js deleted file mode 100644 index 4b5058e9a6..0000000000 --- a/library/simplepie/demo/for_the_demo/sleight.js +++ /dev/null @@ -1,31 +0,0 @@ -/********************************************************** -Sleight -(c) 2001, Aaron Boodman -http://www.youngpup.net -**********************************************************/ - -if (navigator.platform == "Win32" && navigator.appName == "Microsoft Internet Explorer" && window.attachEvent) -{ - document.writeln(''); - window.attachEvent("onload", fnLoadPngs); -} - -function fnLoadPngs() -{ - var rslt = navigator.appVersion.match(/MSIE (\d+\.\d+)/, ''); - var itsAllGood = (rslt != null && Number(rslt[1]) >= 5.5); - - for (var i = document.images.length - 1, img = null; (img = document.images[i]); i--) - { - if (itsAllGood && img.src.match(/\.png$/i) != null) - { - var src = img.src; - var div = document.createElement("DIV"); - div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizing='scale')" - div.style.width = img.width + "px"; - div.style.height = img.height + "px"; - img.replaceNode(div); - } - img.style.visibility = "visible"; - } -} diff --git a/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png b/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png deleted file mode 100644 index 2bfd87d0ce..0000000000 Binary files a/library/simplepie/demo/for_the_demo/source_files/place_audio_fireworksfile.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png b/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png deleted file mode 100644 index d0629769cf..0000000000 Binary files a/library/simplepie/demo/for_the_demo/source_files/place_video_fireworksfile.png and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as deleted file mode 100644 index 6a98ca5522..0000000000 --- a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/SifrStyleSheet.as +++ /dev/null @@ -1,71 +0,0 @@ -/*=:project - scalable Inman Flash Replacement (sIFR) version 3. - - =:file - Copyright: 2006 Mark Wubben. - Author: Mark Wubben, - - =:history - * IFR: Shaun Inman - * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin - * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben - - =:license - This software is licensed and provided under the CC-GNU LGPL. - See -*/ - -import TextField.StyleSheet; - -class SifrStyleSheet extends TextField.StyleSheet { - public var fontSize; - public var latestLeading = 0; - - public function parseCSS(cssText:String) { - var native = new TextField.StyleSheet(); - var parsed = native.parseCSS(cssText); - - if(!parsed) return false; - - var selectors = native.getStyleNames(); - for(var i = selectors.length - 1; i >= 0; i--) { - var selector = selectors[i]; - var nativeStyle = native.getStyle(selector); - var style = this.getStyle(selector) || nativeStyle; - if(style != nativeStyle) { - for(var property in nativeStyle) style[property] = nativeStyle[property]; - } - this.setStyle(selector, style); - } - - return true; - } - - // Apply leading to the textFormat. Much thanks to . - private function applyLeading(format, leading) { - this.latestLeading = leading; - - if(leading >= 0) { - format.leading = leading; - return format; - } - - // Workaround for negative leading, which is ignored otherwise. - var newFormat = new TextFormat(null, null, null, null, null, null, null, null, null, null, null, null, leading); - for(var property in format) if(property != 'leading') newFormat[property] = format[property]; - - return newFormat; - } - - public function transform(style) { - var format = super.transform(style); - if(style.leading) format = applyLeading(format, style.leading); - if(style.letterSpacing) format.letterSpacing = style.letterSpacing; - // Support font sizes relative to the size of .sIFR-root. - if(this.fontSize && style.fontSize && style.fontSize.indexOf('%')) { - format.size = this.fontSize * parseInt(style.fontSize) / 100; - } - format.kerning = _root.kerning == 'true' || !(_root.kerning == 'false') || sIFR.defaultKerning; - return format; - } -} \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt deleted file mode 100644 index 2b9d32d202..0000000000 --- a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/_README_.txt +++ /dev/null @@ -1,12 +0,0 @@ -This is a pre-release nightly of sIFR 3 (r245 to be exact). We (the SimplePie team) will be updating the -sIFR code and font files from time to time as new releases of sIFR 3 are made available. - -In this folder you'll find a few Flash 8 files. The only one of you might want to mess with is sifr.fla. - * Open it up - * Double-click the rectangle in the middle - * Select all - * Change the font - -More information about sIFR 3 can be found here: - * http://dev.novemberborn.net/sifr3/ - * http://wiki.novemberborn.net/sifr3/ \ No newline at end of file diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as deleted file mode 100644 index 4d371954bc..0000000000 --- a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/options.as +++ /dev/null @@ -1,12 +0,0 @@ -// MTASC only parses as-files with class definitions, so here goes... -class Options { - public static function apply() { - sIFR.fromLocal = true; - sIFR.domains = ['*']; - - // Parsing `p.foo` might not work, see: - // Appearantly you have to use hex color codes as well, names are not supported! - - sIFR.styles.parseCSS('.foo { text-decoration: underline; }'); - } -} diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as deleted file mode 100644 index 4902e003f3..0000000000 --- a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sIFR.as +++ /dev/null @@ -1,359 +0,0 @@ -/*=:project - scalable Inman Flash Replacement (sIFR) version 3. - - =:file - Copyright: 2006 Mark Wubben. - Author: Mark Wubben, - - =:history - * IFR: Shaun Inman - * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin - * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben - - =:license - This software is licensed and provided under the CC-GNU LGPL. - See -*/ - -import SifrStyleSheet; - -class sIFR { - public static var DEFAULT_TEXT = 'Rendered with sIFR 3, revision 245'; - public static var CSS_ROOT_CLASS = 'sIFR-root'; - public static var DEFAULT_WIDTH = 300; - public static var DEFAULT_HEIGHT = 100; - public static var DEFAULT_ANTI_ALIAS_TYPE = 'advanced'; - public static var MARGIN_LEFT = -3; - public static var PADDING_BOTTOM = 5; // Extra padding to make sure the movie is high enough in most cases. - public static var LEADING_REMAINDER = 2; // Flash uses the specified leading minus 2 as the applied leading. - - public static var MAX_FONT_SIZE = 126; - public static var ALIASING_MAX_FONT_SIZE = 48; - - //= Holds CSS properties and other rendering properties for the Flash movie. - // *Don't overwrite!* - public static var styles:SifrStyleSheet = new SifrStyleSheet(); - //= Allow sIFR to be run from localhost - public static var fromLocal:Boolean = true; - //= Array containing domains for which sIFR may render text. Used to prevent - // hotlinking. Use `*` to allow all domains. - public static var domains:Array = []; - //= Whether kerning is enabled by default. This can be overriden from the client side. - // See also . - public static var defaultKerning:Boolean = true; - //= Default value which can be overriden from the client side. - // See also . - public static var defaultSharpness:Number = 0; - //= Default value which can be overriden from the client side. - // See also . - public static var defaultThickness:Number = 0; - //= Default value which can be overriden from the client side. - // See also . - public static var defaultOpacity:Number = -1; // Use client settings - //= Default value which can be overriden from the client side. - // See also . - public static var defaultBlendMode:Number = -1; // Use cliest settings - //= Overrides the grid fit type as defined on the client side. - // See also . - public static var enforcedGridFitType:String = null; - //= If `true` sIFR won't override the anti aliasing set in the Flash IDE when exporting. - // Thickness and sharpness won't be affected either. - public static var preserveAntiAlias:Boolean = false; - //= If `true` sIFR will disable anti-aliasing if the font size is larger than `ALIASING_MAX_FONT_SIZE`. - // This setting is *independent* from `preserveAntiAlias`. - public static var conditionalAntiAlias:Boolean = true; - //= Sets the anti alias type. By default it's `DEFAULT_ANTI_ALIAS_TYPE`. - // See also . - public static var antiAliasType:String = null; - //= Flash filters can be added to this array and will be applied to the text field. - public static var filters:Array = []; - //= A mapping from the names of the filters to their actual objecs, used when transforming - // filters defined on the client. You can add additional filters here so they'll be supported - // when defined on the client. - public static var filterMap:Object = { - DisplacementMapFilter : flash.filters.DisplacementMapFilter, - ColorMatrixFilter : flash.filters.ColorMatrixFilter, - ConvolutionFilter : flash.filters.ConvolutionFilter, - GradientBevelFilter : flash.filters.GradientBevelFilter, - GradientGlowFilter : flash.filters.GradientGlowFilter, - BevelFilter : flash.filters.BevelFilter, - GlowFilter : flash.filters.GlowFilter, - BlurFilter : flash.filters.BlurFilter, - DropShadowFilter : flash.filters.DropShadowFilter - }; - - private static var instance; - - private var textField; - private var content; - private var realHeight; - private var originalHeight; - private var currentHeight; - private var fontSize; - private var tuneWidth; - private var tuneHeight; - - - - //= Sets the default styles for `sIFR.styles`. This method is called - // directly in `sifr.fla`, before options are applied. - public static function setDefaultStyles() { - sIFR.styles.parseCSS([ - '.', CSS_ROOT_CLASS, ' { color: #000000; }', - 'strong { display: inline; font-weight: bold; } ', - 'em { display: inline; font-style: italic; }', - 'a { color: #0000FF; text-decoration: underline; }', - 'a:hover { color: #0000FF; text-decoration: none; }' - ].join('')); - } - - //= Validates the domain sIFR is being used on. - // Returns `true` if the domain is valid, `false` otherwise. - public static function checkDomain():Boolean { - if(sIFR.domains.length == 0) return true; - - var domain = (new LocalConnection()).domain(); - if(sIFR.fromLocal) sIFR.domains.push('localhost'); - - for(var i = 0; i < sIFR.domains.length; i++) { - var match = sIFR.domains[i]; - if(match == '*' || match == domain) return true; - - var wildcard = match.lastIndexOf('*'); - if(wildcard > -1) { - match = match.substr(wildcard + 1); - var matchPosition = domain.lastIndexOf(match); - if(matchPosition > -1 && (matchPosition + match.length) == domain.length) return true; - } - } - - return false; - } - - //= Runs sIFR. Called automatically. - public static function run() { - var holder = _root.holder; - var content = checkDomain() ? unescape(_root.content) : DEFAULT_TEXT - if(content == 'undefined' || content == '') { - content = DEFAULT_TEXT; - fscommand('resetmovie', ''); - } else fscommand('ping', ''); - - // Sets stage parameters - Stage.scaleMode = 'noscale'; - Stage.align = 'TL'; - Stage.showMenu = false; - - // Other parameters - var opacity = parseInt(_root.opacity); - if(!isNaN(opacity)) holder._alpha = sIFR.defaultOpacity == -1 ? opacity : sIFR.defaultOpacity; - else holder._alpha = 100; - _root.blendMode = sIFR.defaultBlendMode == -1 ? _root.blendmode : sIFR.defaultBlendMode; - - sIFR.instance = new sIFR(holder.txtF, content); - // This should ignore resizes from the callback. Disabled for now. -/* if(_root.zoomsupport == 'true') Stage.addListener({onResize: function() { sIFR.instance.scale() }});*/ - - // Setup callbacks - _root.watch('callbackTrigger', function() { - sIFR.callback(); - return false; - }); - } - - private static function eval(str) { - var as; - - if(str.charAt(0) == '{') { // Ah, we need to create an object - as = {}; - str = str.substring(1, str.length - 1); - var $ = str.split(','); - for(var i = 0; i < $.length; i++) { - var $1 = $[i].split(':'); - as[$1[0]] = sIFR.eval($1[1]); - } - } else if(str.charAt(0) == '"') { // String - as = str.substring(1, str.length - 1); - } else if(str == 'true' || str == 'false') { // Boolean - as = str == 'true'; - } else { // Float - as = parseFloat(str); - } - - return as; - } - - private function applyFilters() { - var $filters = this.textField.filters; - $filters = $filters.concat(sIFR.filters); - - var $ = _root.flashfilters.split(';'); // name,prop:value,...; - for(var i = 0; i < $.length; i++) { - var $1 = $[i].split(','); - - var newFilter = new sIFR.filterMap[$1[0]](); - for(var j = 1; j < $1.length; j++) { - var $2 = $1[j].split(':'); - newFilter[$2[0]] = sIFR.eval(unescape($2[1])); - } - - $filters.push(newFilter); - } - - this.textField.filters = $filters; - } - - private function sIFR(textField, content) { - this.textField = textField; - this.content = content; - - var offsetLeft = parseInt(_root.offsetleft); - textField._x = MARGIN_LEFT + (isNaN(offsetLeft) ? 0 : offsetLeft); - var offsetTop = parseInt(_root.offsettop); - if(!isNaN(offsetTop)) textField._y += offsetTop; - - tuneWidth = parseInt(_root.tunewidth); - if(isNaN(tuneWidth)) tuneWidth = 0; - tuneHeight = parseInt(_root.tuneheight); - if(isNaN(tuneHeight)) tuneHeight = 0; - - textField._width = tuneWidth + (isNaN(parseInt(_root.width)) ? DEFAULT_WIDTH : parseInt(_root.width)); - textField._height = tuneHeight + (isNaN(parseInt(_root.height)) ? DEFAULT_HEIGHT : parseInt(_root.height)); - textField.wordWrap = true; - textField.selectable = _root.selectable == 'true'; - textField.gridFitType = sIFR.enforcedGridFitType || _root.gridfittype; - this.applyFilters(); - - // Determine font-size and the number of lines - this.fontSize = parseInt(_root.size); - if(isNaN(this.fontSize)) this.fontSize = 26; - styles.fontSize = this.fontSize; - - if(!sIFR.preserveAntiAlias && (sIFR.conditionalAntiAlias && this.fontSize < ALIASING_MAX_FONT_SIZE - || !sIFR.conditionalAntiAlias)) { - textField.antiAliasType = sIFR.antiAliasType || DEFAULT_ANTI_ALIAS_TYPE; - } - - if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.sharpness))) { - textField.sharpness = parseInt(_root.sharpness); - } - if(isNaN(textField.sharpness)) textField.sharpness = sIFR.defaultSharpness; - - if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.thickness))) { - textField.thickness = parseInt(_root.thickness); - } - if(isNaN(textField.thickness)) textField.thickness = sIFR.defaultThickness; - - // Set font-size and other styles - sIFR.styles.parseCSS(unescape(_root.css)); - - var rootStyle = styles.getStyle('.sIFR-root') || {}; - rootStyle.fontSize = this.fontSize; // won't go higher than 126! - styles.setStyle('.sIFR-root', rootStyle); - textField.styleSheet = styles; - - this.write(content); - this.repaint(); - } - - private function repaint() { - var leadingFix = this.isSingleLine() ? sIFR.styles.latestLeading : 0; - if(leadingFix > 0) leadingFix -= LEADING_REMAINDER; - - // Flash wants to scroll the movie by one line, by adding the fontSize to the - // textField height this is no longer happens. We also add the absolute tuneHeight, - // to prevent a negative value from triggering the bug. We won't send the fake - // value to the JavaScript side, though. - textField._height = textField.textHeight + PADDING_BOTTOM + this.fontSize + Math.abs(tuneHeight) + tuneHeight - leadingFix; - this.realHeight = textField._height - this.fontSize - Math.abs(tuneHeight); - var arg = 'height:' + this.realHeight; - if(_root.fitexactly == 'true') arg += ',width:' + (textField.textWidth + tuneWidth); - fscommand('resize', arg); - - this.originalHeight = textField._height; - this.currentHeight = Stage.height; - - textField._xscale = textField._yscale = parseInt(_root.zoom); - } - - private function write(content) { - this.textField.htmlText = ['

            ', - content, '

            ' - ].join(''); - } - - private function isSingleLine() { - return Math.round((this.textField.textHeight - sIFR.styles.latestLeading) / this.fontSize) == 1; - } - - //= Scales the text field to the new scale of the Flash movie itself. - public function scale() { - this.currentHeight = Stage.height; - var scale = 100 * Math.round(this.currentHeight / this.originalHeight); - textField._xscale = textField._yscale = scale; - } - - private function calculateRatios() { - var strings = ['X', 'X
            X', 'X
            X
            X', 'X
            X
            X
            X']; - var results = {}; - - for(var i = 1; i <= strings.length; i++) { - var size = 6; - - this.write(strings[i - 1]); - while(size < MAX_FONT_SIZE) { - var rootStyle = sIFR.styles.getStyle('.sIFR-root') || {}; - rootStyle.fontSize = size; - sIFR.styles.setStyle('.sIFR-root', rootStyle); - this.textField.styleSheet = sIFR.styles; - this.repaint(); - var ratio = (this.realHeight - PADDING_BOTTOM) / i / size; - if(!results[size]) results[size] = ratio; - else results[size] = ((i - 1) * results[size] + ratio) / i; - size++; - } - } - - var sizes = [], ratios = []; - var ratiosToSizes = {}, sizesToRatios = {}; - - for(var size in results) { - if(results[size] == Object.prototype[size]) continue; - var ratio = results[size]; - ratiosToSizes[ratio] = Math.max(ratio, parseInt(size)); - } - - for(var ratio in ratiosToSizes) { - if(ratiosToSizes[ratio] == Object.prototype[ratio]) continue; - sizesToRatios[ratiosToSizes[ratio]] = roundDecimals(ratio, 2); - sizes.push(ratiosToSizes[ratio]); - } - - sizes.sort(function(a, b) { return a - b; }); - for(var j = 0; j < sizes.length - 1; j++) ratios.push(sizes[j], sizesToRatios[sizes[j]]); - ratios.push(sizesToRatios[sizes[sizes.length - 1]]); - - fscommand('debug:ratios', '[' + ratios.join(',') + ']'); - } - - private function roundDecimals(value, decimals) { - return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals); - } - - public static function callback() { - switch(_root.callbackType) { - case 'replacetext': - sIFR.instance.content = _root.callbackValue; - sIFR.instance.write(_root.callbackValue); - sIFR.instance.repaint(); - break; - case 'resettext': - sIFR.instance.write(''); - sIFR.instance.write(sIFR.instance.content); - break; - case 'ratios': - sIFR.instance.calculateRatios(); - break; - } - } -} diff --git a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla b/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla deleted file mode 100644 index 2aa3f647f4..0000000000 Binary files a/library/simplepie/demo/for_the_demo/source_files/sIFR-r245/sifr.fla and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/top_gradient.gif b/library/simplepie/demo/for_the_demo/top_gradient.gif deleted file mode 100644 index f77bd38f97..0000000000 Binary files a/library/simplepie/demo/for_the_demo/top_gradient.gif and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/verdana.swf b/library/simplepie/demo/for_the_demo/verdana.swf deleted file mode 100644 index baf0350473..0000000000 Binary files a/library/simplepie/demo/for_the_demo/verdana.swf and /dev/null differ diff --git a/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf b/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf deleted file mode 100644 index c812a79dcf..0000000000 Binary files a/library/simplepie/demo/for_the_demo/yanone-kaffeesatz-bold.swf and /dev/null differ diff --git a/library/simplepie/demo/handler_image.php b/library/simplepie/demo/handler_image.php deleted file mode 100644 index 49c3ec89b2..0000000000 --- a/library/simplepie/demo/handler_image.php +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/library/simplepie/demo/index.php b/library/simplepie/demo/index.php deleted file mode 100644 index 1481ba9172..0000000000 --- a/library/simplepie/demo/index.php +++ /dev/null @@ -1,295 +0,0 @@ -force_fsockopen(true); - -// Make sure that page is getting passed a URL -if (isset($_GET['feed']) && $_GET['feed'] !== '') -{ - // Strip slashes if magic quotes is enabled (which automatically escapes certain characters) - if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) - { - $_GET['feed'] = stripslashes($_GET['feed']); - } - - // Use the URL that was passed to the page in SimplePie - $feed->set_feed_url($_GET['feed']); - - // XML dump - $feed->enable_xml_dump(isset($_GET['xmldump']) ? true : false); -} - -// Allow us to change the input encoding from the URL string if we want to. (optional) -if (!empty($_GET['input'])) -{ - $feed->set_input_encoding($_GET['input']); -} - -// Allow us to choose to not re-order the items by date. (optional) -if (!empty($_GET['orderbydate']) && $_GET['orderbydate'] == 'false') -{ - $feed->enable_order_by_date(false); -} - -// Allow us to cache images in feeds. This will also bypass any hotlink blocking put in place by the website. -if (!empty($_GET['image']) && $_GET['image'] == 'true') -{ - $feed->set_image_handler('./handler_image.php'); -} - -// We'll enable the discovering and caching of favicons. -$feed->set_favicon_handler('./handler_image.php'); - -// Initialize the whole SimplePie object. Read the feed, process it, parse it, cache it, and -// all that other good stuff. The feed's information will not be available to SimplePie before -// this is called. -$success = $feed->init(); - -// We'll make sure that the right content type and character encoding gets set automatically. -// This function will grab the proper character encoding, as well as set the content type to text/html. -$feed->handle_content_type(); - -// When we end our PHP block, we want to make sure our DOCTYPE is on the top line to make -// sure that the browser snaps into Standards Mode. -?> - - - -SimplePie: Demo - - - - - - - - - - - - - - - -
            - -
            - -
            -
            -
            - - - -

             

            - - -
            -
            - - - error()) - { - // If so, start a
            element with a classname so we can style it. - echo '
            ' . "\r\n"; - - // ... and display it. - echo '

            ' . htmlspecialchars($feed->error()) . "

            \r\n"; - - // Close the
            element we opened. - echo '
            ' . "\r\n"; - } - ?> - - -

            Or try one of the following: - 詹姆斯.com, - adult swim, - Afterdawn, - Ajaxian, - Andy Budd, - Ask a Ninja, - AtomEnabled.org, - BBC News, - BBC Arabic, - BBC China, - BBC Russia, - Brent Simmons, - Channel Frederator, - CNN, - Digg, - Diggnation, - Flickr, - Google News, - Google Video, - Harvard Law, - Hebrew Language, - InfoWorld, - iTunes, - Japanese Language, - Korean Language, - mir.aculo.us, - Movie Trailers, - Newspond, - Nick Bradbury, - OK/Cancel, - OS News, - Phil Ringnalda, - Photoshop Videocast, - Romanian Language, - Russian Language, - Traditional Chinese Language, - Technorati, - Tim Bray, - TUAW, - TVgasm, - UNEASYsilence, - Web 2.0 Show, - Windows Vista Blog, - XKCD, - Yahoo! News, - You Tube, - Zeldman

            - -
            - -
            - - - -
            - - -

            get_link()) echo ''; echo $feed->get_title(); if ($feed->get_link()) echo ''; ?>

            - - - get_description(); ?> - -
            - - - - - - - get_items() as $item): ?> -
            - - get_favicon()) - { - $favicon = './for_the_demo/favicons/alternate.png'; - } - ?> - - -

            Faviconget_permalink()) echo ''; echo $item->get_title(); if ($item->get_permalink()) echo ''; ?> get_date('j M Y, g:i a'); ?>

            - - - get_content(); ?> - - get_enclosure(0)) - { - // Use the embed() method to embed the enclosure into the page inline. - echo '
            '; - echo '

            ' . $enclosure->embed(array( - 'audio' => './for_the_demo/place_audio.png', - 'video' => './for_the_demo/place_video.png', - 'mediaplayer' => './for_the_demo/mediaplayer.swf', - 'altclass' => 'download' - )) . '

            '; - - if ($enclosure->get_link() && $enclosure->get_type()) - { - echo '

            (' . $enclosure->get_type(); - if ($enclosure->get_size()) - { - echo '; ' . $enclosure->get_size() . ' MB'; - } - echo ')

            '; - } - if ($enclosure->get_thumbnail()) - { - echo '
            '; - } - echo '
            '; - } - ?> - - -

            - Blinklist - Blogmarks - del.icio.us - Digg - Ma.gnolia - My Web 2.0 - Newsvine - Reddit - Segnalo - Simpy - Spurl - Wists - Technorati -

            - -
            - - - - - - - -
            - -
            - -

            Page processed in seconds.

            - - -

            Powered by . Run the SimplePie Compatibility Test. SimplePie is © 2004–, Ryan Parman and Geoffrey Sneddon, and licensed under the BSD License.

            -
            - -
            - -
            - - - diff --git a/library/simplepie/demo/minimalistic.php b/library/simplepie/demo/minimalistic.php deleted file mode 100644 index 56509c00cb..0000000000 --- a/library/simplepie/demo/minimalistic.php +++ /dev/null @@ -1,137 +0,0 @@ -=')) - { - return microtime(true); - } - else - { - list($usec, $sec) = explode(' ', microtime()); - return ((float) $usec + (float) $sec); - } -} - -$start = microtime_float(); - -include('../simplepie.inc'); - -// Parse it -$feed = new SimplePie(); -if (!empty($_GET['feed'])) -{ - if (get_magic_quotes_gpc()) - { - $_GET['feed'] = stripslashes($_GET['feed']); - } - $feed->set_feed_url($_GET['feed']); - $feed->init(); -} -$feed->handle_content_type(); - -?> - - - -<?php echo (empty($_GET['feed'])) ? 'SimplePie' : 'SimplePie: ' . $feed->get_title(); ?> - - - - - - - - - - -

            get_title(); ?>

            - -
            -

             

            -
            - -
            - data): ?> - get_items(); ?> -

            Displaying get_item_quantity(); ?> most recent entries.

            - -
            -

            get_title(); ?> get_date('j M Y'); ?>

            - get_content(); ?> - get_enclosure(0)) - echo '

            Podcast

            '; - ?> -
            - -
            - -
            - - - - diff --git a/library/simplepie/demo/multifeeds.php b/library/simplepie/demo/multifeeds.php deleted file mode 100644 index b23d792a2f..0000000000 --- a/library/simplepie/demo/multifeeds.php +++ /dev/null @@ -1,108 +0,0 @@ -set_feed_url(array( - 'http://rss.news.yahoo.com/rss/topstories', - 'http://news.google.com/?output=atom', - 'http://rss.cnn.com/rss/cnn_topstories.rss' -)); - -// When we set these, we need to make sure that the handler_image.php file is also trying to read from the same cache directory that we are. -$feed->set_favicon_handler('./handler_image.php'); -$feed->set_image_handler('./handler_image.php'); - -// Initialize the feed. -$feed->init(); - -// Make sure the page is being served with the UTF-8 headers. -$feed->handle_content_type(); - -// Begin the (X)HTML page. -?> - - - Multifeeds Test page - - - - -
            - - error): ?> -

            error()?>

            - - -
            -

            Quick-n-Dirty Multifeeds Demo

            -
            - - get_items() as $item): - - // Let's give ourselves a reference to the parent $feed object for this particular item. - $feed = $item->get_feed(); - ?> - -
            -

            get_title(), ENT_QUOTES, 'UTF-8'); ?>

            - - - get_content(); ?> - - get_enclosure()): ?> -
            - native_embed(array( - // New 'mediaplayer' attribute shows off Flash-based MP3 and FLV playback. - 'mediaplayer' => '../demo/for_the_demo/mediaplayer.swf' - )); ?> -
            - - -

            Source: get_title(); ?> | get_date('j M Y | g:i a'); ?>

            -
            - - - -

            This is a test of the emergency broadcast system. This is only a test… beeeeeeeeeeeeeeeeeeeeeeeeeep!

            - -
            - - \ No newline at end of file diff --git a/library/simplepie/demo/test.php b/library/simplepie/demo/test.php deleted file mode 100644 index 5b9943abbc..0000000000 --- a/library/simplepie/demo/test.php +++ /dev/null @@ -1,62 +0,0 @@ -set_feed_url($_GET['feed']); - $feed->enable_cache(false); - $starttime = explode(' ', microtime()); - $starttime = $starttime[1] + $starttime[0]; - $feed->init(); - $endtime = explode(' ', microtime()); - $endtime = $endtime[1] + $endtime[0]; - $time = $endtime - $starttime; -} -else -{ - $time = 'null'; -} - -$feed->handle_content_type(); - -?> - -SimplePie Test -
            -
            -
            \ No newline at end of file diff --git a/library/simplepie/idn/ReadMe.txt b/library/simplepie/idn/ReadMe.txt deleted file mode 100644 index 7ca8c7e6de..0000000000 --- a/library/simplepie/idn/ReadMe.txt +++ /dev/null @@ -1,123 +0,0 @@ -******************************************************************************* -* * -* IDNA Convert (idna_convert.class.php) * -* * -* http://idnaconv.phlymail.de mailto:phlymail@phlylabs.de * -******************************************************************************* -* (c) 2004-2007 phlyLabs, Berlin * -* This file is encoded in UTF-8 * -******************************************************************************* - -Introduction ------------- - -The class idna_convert allows to convert internationalized domain names -(see RFC 3490, 3491, 3492 and 3454 for detials) as they can be used with various -registries worldwide to be translated between their original (localized) form -and their encoded form as it will be used in the DNS (Domain Name System). - -The class provides two public methods, encode() and decode(), which do exactly -what you would expect them to do. You are allowed to use complete domain names, -simple strings and complete email addresses as well. That means, that you might -use any of the following notations: - -- www.nörgler.com -- xn--nrgler-wxa -- xn--brse-5qa.xn--knrz-1ra.info - -Errors, incorrectly encoded or invalid strings will lead to either a FALSE -response (when in strict mode) or to only partially converted strings. -You can query the occured error by calling the method get_last_error(). - -Unicode strings are expected to be either UTF-8 strings, UCS-4 strings or UCS-4 -arrays. The default format is UTF-8. For setting different encodings, you can -call the method setParams() - please see the inline documentation for details. -ACE strings (the Punycode form) are always 7bit ASCII strings. - -ATTENTION: We no longer supply the PHP5 version of the class. It is not -necessary for achieving a successfull conversion, since the supplied PHP code is -compatible with both PHP4 and PHP5. We expect to see no compatibility issues -with the upcoming PHP6, too. - - -Files ------ - -idna_convert.class.php - The actual class -idna_convert.create.npdata.php - Useful for (re)creating the NPData file -npdata.ser - Serialized data for NamePrep -example.php - An example web page for converting -ReadMe.txt - This file -LICENCE - The LGPL licence file - -The class is contained in idna_convert.class.php. -MAKE SURE to copy the npdata.ser file into the same folder as the class file -itself! - - -Examples --------- - -1. Say we wish to encode the domain name nörgler.com: - -// Include the class -include_once('idna_convert.class.php'); -// Instantiate it * -$IDN = new idna_convert(); -// The input string, if input is not UTF-8 or UCS-4, it must be converted before -$input = utf8_encode('nörgler.com'); -// Encode it to its punycode presentation -$output = $IDN->encode($input); -// Output, what we got now -echo $output; // This will read: xn--nrgler-wxa.com - - -2. We received an email from a punycoded domain and are willing to learn, how - the domain name reads originally - -// Include the class -include_once('idna_convert.class.php'); -// Instantiate it (depending on the version you are using) with -$IDN = new idna_convert(); -// The input string -$input = 'andre@xn--brse-5qa.xn--knrz-1ra.info'; -// Encode it to its punycode presentation -$output = $IDN->decode($input); -// Output, what we got now, if output should be in a format different to UTF-8 -// or UCS-4, you will have to convert it before outputting it -echo utf8_decode($output); // This will read: andre@börse.knörz.info - - -3. The input is read from a UCS-4 coded file and encoded line by line. By - appending the optional second parameter we tell enode() about the input - format to be used - -// Include the class -include_once('idna_convert.class.php'); -// Instantiate it -$IDN = new dinca_convert(); -// Iterate through the input file line by line -foreach (file('ucs4-domains.txt') as $line) { - echo $IDN->encode(trim($line), 'ucs4_string'); - echo "\n"; -} - - -NPData ------- - -Should you need to recreate the npdata.ser file, which holds all necessary translation -tables in a serialized format, you can run the file idna_convert.create.npdata.php, which -creates the file for you and stores it in the same folder, where it is placed. -Should you need to do changes to the tables you can do so, but beware of the consequences. - - -Contact us ----------- - -In case of errors, bugs, questions, wishes, please don't hesitate to contact us -under the email address above. - -The team of phlyLabs -http://phlylabs.de -mailto:phlymail@phlylabs.de \ No newline at end of file diff --git a/library/simplepie/idn/idna_convert.class.php b/library/simplepie/idn/idna_convert.class.php deleted file mode 100644 index ed2bae26db..0000000000 --- a/library/simplepie/idn/idna_convert.class.php +++ /dev/null @@ -1,969 +0,0 @@ - - * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de - * @version 0.5.1 - * - */ -class idna_convert -{ - /** - * Holds all relevant mapping tables, loaded from a seperate file on construct - * See RFC3454 for details - * - * @var array - * @access private - */ - var $NP = array(); - - // Internal settings, do not mess with them - var $_punycode_prefix = 'xn--'; - var $_invalid_ucs = 0x80000000; - var $_max_ucs = 0x10FFFF; - var $_base = 36; - var $_tmin = 1; - var $_tmax = 26; - var $_skew = 38; - var $_damp = 700; - var $_initial_bias = 72; - var $_initial_n = 0x80; - var $_sbase = 0xAC00; - var $_lbase = 0x1100; - var $_vbase = 0x1161; - var $_tbase = 0x11A7; - var $_lcount = 19; - var $_vcount = 21; - var $_tcount = 28; - var $_ncount = 588; // _vcount * _tcount - var $_scount = 11172; // _lcount * _tcount * _vcount - var $_error = false; - - // See {@link set_paramter()} for details of how to change the following - // settings from within your script / application - var $_api_encoding = 'utf8'; // Default input charset is UTF-8 - var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden - var $_strict_mode = false; // Behave strict or not - - // The constructor - function idna_convert($options = false) - { - $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; - if (function_exists('file_get_contents')) { - $this->NP = unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser')); - } else { - $this->NP = unserialize(join('', file(dirname(__FILE__).'/npdata.ser'))); - } - // If parameters are given, pass these to the respective method - if (is_array($options)) { - return $this->set_parameter($options); - } - return true; - } - - /** - * Sets a new option value. Available options and values: - * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, - * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead - * - * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string Value to use (if parameter 1 is a string) - * @return boolean true on success, false otherwise - * @access public - */ - function set_parameter($option, $value = false) - { - if (!is_array($option)) { - $option = array($option => $value); - } - foreach ($option as $k => $v) { - switch ($k) { - case 'encoding': - switch ($v) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - $this->_api_encoding = $v; - break; - default: - $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k); - return false; - } - break; - case 'overlong': - $this->_allow_overlong = ($v) ? true : false; - break; - case 'strict': - $this->_strict_mode = ($v) ? true : false; - break; - default: - $this->_error('Set Parameter: Unknown option '.$k); - return false; - } - } - return true; - } - - /** - * Decode a given ACE domain name - * @param string Domain name (ACE string) - * [@param string Desired output encoding, see {@link set_parameter}] - * @return string Decoded Domain name (UTF-8 or UCS-4) - * @access public - */ - function decode($input, $one_time_encoding = false) - { - // Optionally set - if ($one_time_encoding) { - switch ($one_time_encoding) { - case 'utf8': - case 'ucs4_string': - case 'ucs4_array': - break; - default: - $this->_error('Unknown encoding '.$one_time_encoding); - return false; - } - } - // Make sure to drop any newline characters around - $input = trim($input); - - // Negotiate input and try to determine, whether it is a plain string, - // an email address or something like a complete URL - if (strpos($input, '@')) { // Maybe it is an email address - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - list ($email_pref, $input) = explode('@', $input, 2); - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - } - $input = join('.', $arr); - $arr = explode('.', $email_pref); - foreach ($arr as $k => $v) { - if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - } - $email_pref = join('.', $arr); - $return = $email_pref . '@' . $input; - } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) - // No no in strict mode - if ($this->_strict_mode) { - $this->_error('Only simple domain name parts can be handled in strict mode'); - return false; - } - $parsed = parse_url($input); - if (isset($parsed['host'])) { - $arr = explode('.', $parsed['host']); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - if ($conv) $arr[$k] = $conv; - } - $parsed['host'] = join('.', $arr); - $return = - (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')) - .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@') - .$parsed['host'] - .(empty($parsed['port']) ? '' : ':'.$parsed['port']) - .(empty($parsed['path']) ? '' : $parsed['path']) - .(empty($parsed['query']) ? '' : '?'.$parsed['query']) - .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']); - } else { // parse_url seems to have failed, try without it - $arr = explode('.', $input); - foreach ($arr as $k => $v) { - $conv = $this->_decode($v); - $arr[$k] = ($conv) ? $conv : $v; - } - $return = join('.', $arr); - } - } else { // Otherwise we consider it being a pure domain name string - $return = $this->_decode($input); - if (!$return) $return = $input; - } - // The output is UTF-8 by default, other output formats need conversion here - // If one time encoding is given, use this, else the objects property - switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - return $return; - break; - case 'ucs4_string': - return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); - break; - case 'ucs4_array': - return $this->_utf8_to_ucs4($return); - break; - default: - $this->_error('Unsupported output format'); - return false; - } - } - - /** - * Encode a given UTF-8 domain name - * @param string Domain name (UTF-8 or UCS-4) - * [@param string Desired input encoding, see {@link set_parameter}] - * @return string Encoded Domain name (ACE string) - * @access public - */ - function encode($decoded, $one_time_encoding = false) - { - // Forcing conversion of input to UCS4 array - // If one time encoding is given, use this, else the objects property - switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { - case 'utf8': - $decoded = $this->_utf8_to_ucs4($decoded); - break; - case 'ucs4_string': - $decoded = $this->_ucs4_string_to_ucs4($decoded); - case 'ucs4_array': - break; - default: - $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); - return false; - } - - // No input, no output, what else did you expect? - if (empty($decoded)) return ''; - - // Anchors for iteration - $last_begin = 0; - // Output string - $output = ''; - foreach ($decoded as $k => $v) { - // Make sure to use just the plain dot - switch($v) { - case 0x3002: - case 0xFF0E: - case 0xFF61: - $decoded[$k] = 0x2E; - // Right, no break here, the above are converted to dots anyway - // Stumbling across an anchoring character - case 0x2E: - case 0x2F: - case 0x3A: - case 0x3F: - case 0x40: - // Neither email addresses nor URLs allowed in strict mode - if ($this->_strict_mode) { - $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); - return false; - } else { - // Skip first char - if ($k) { - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); - } - $output .= chr($decoded[$k]); - } - $last_begin = $k + 1; - } - } - } - // Catch the rest of the string - if ($last_begin) { - $inp_len = sizeof($decoded); - $encoded = ''; - $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - if ($encoded) { - $output .= $encoded; - } else { - $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); - } - return $output; - } else { - if ($output = $this->_encode($decoded)) { - return $output; - } else { - return $this->_ucs4_to_utf8($decoded); - } - } - } - - /** - * Use this method to get the last error ocurred - * @param void - * @return string The last error, that occured - * @access public - */ - function get_last_error() - { - return $this->_error; - } - - /** - * The actual decoding algorithm - * @access private - */ - function _decode($encoded) - { - // We do need to find the Punycode prefix - if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) { - $this->_error('This is not a punycode string'); - return false; - } - $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded); - // If nothing left after removing the prefix, it is hopeless - if (!$encode_test) { - $this->_error('The given encoded string was empty'); - return false; - } - // Find last occurence of the delimiter - $delim_pos = strrpos($encoded, '-'); - if ($delim_pos > strlen($this->_punycode_prefix)) { - for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { - $decoded[] = ord($encoded{$k}); - } - } else { - $decoded = array(); - } - $deco_len = count($decoded); - $enco_len = strlen($encoded); - - // Wandering through the strings; init - $is_first = true; - $bias = $this->_initial_bias; - $idx = 0; - $char = $this->_initial_n; - - for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { - for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { - $digit = $this->_decode_digit($encoded{$enco_idx++}); - $idx += $digit * $w; - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); - if ($digit < $t) break; - $w = (int) ($w * ($this->_base - $t)); - } - $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); - $is_first = false; - $char += (int) ($idx / ($deco_len + 1)); - $idx %= ($deco_len + 1); - if ($deco_len > 0) { - // Make room for the decoded char - for ($i = $deco_len; $i > $idx; $i--) { - $decoded[$i] = $decoded[($i - 1)]; - } - } - $decoded[$idx++] = $char; - } - return $this->_ucs4_to_utf8($decoded); - } - - /** - * The actual encoding algorithm - * @access private - */ - function _encode($decoded) - { - // We cannot encode a domain name containing the Punycode prefix - $extract = strlen($this->_punycode_prefix); - $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); - $check_deco = array_slice($decoded, 0, $extract); - - if ($check_pref == $check_deco) { - $this->_error('This is already a punycode string'); - return false; - } - // We will not try to encode strings consisting of basic code points only - $encodable = false; - foreach ($decoded as $k => $v) { - if ($v > 0x7a) { - $encodable = true; - break; - } - } - if (!$encodable) { - $this->_error('The given string does not contain encodable chars'); - return false; - } - - // Do NAMEPREP - $decoded = $this->_nameprep($decoded); - if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed - - $deco_len = count($decoded); - if (!$deco_len) return false; // Empty array - - $codecount = 0; // How many chars have been consumed - - $encoded = ''; - // Copy all basic code points to output - for ($i = 0; $i < $deco_len; ++$i) { - $test = $decoded[$i]; - // Will match [-0-9a-zA-Z] - if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) - || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { - $encoded .= chr($decoded[$i]); - $codecount++; - } - } - if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones - - // Start with the prefix; copy it to output - $encoded = $this->_punycode_prefix.$encoded; - - // If we have basic code points in output, add an hyphen to the end - if ($codecount) $encoded .= '-'; - - // Now find and encode all non-basic code points - $is_first = true; - $cur_code = $this->_initial_n; - $bias = $this->_initial_bias; - $delta = 0; - while ($codecount < $deco_len) { - // Find the smallest code point >= the current code point and - // remember the last ouccrence of it in the input - for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { - if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { - $next_code = $decoded[$i]; - } - } - - $delta += ($next_code - $cur_code) * ($codecount + 1); - $cur_code = $next_code; - - // Scan input again and encode all characters whose code point is $cur_code - for ($i = 0; $i < $deco_len; $i++) { - if ($decoded[$i] < $cur_code) { - $delta++; - } elseif ($decoded[$i] == $cur_code) { - for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { - $t = ($k <= $bias) ? $this->_tmin : - (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); - if ($q < $t) break; - $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() - $q = (int) (($q - $t) / ($this->_base - $t)); - } - $encoded .= $this->_encode_digit($q); - $bias = $this->_adapt($delta, $codecount+1, $is_first); - $codecount++; - $delta = 0; - $is_first = false; - } - } - $delta++; - $cur_code++; - } - return $encoded; - } - - /** - * Adapt the bias according to the current code point and position - * @access private - */ - function _adapt($delta, $npoints, $is_first) - { - $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); - $delta += intval($delta / $npoints); - for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { - $delta = intval($delta / ($this->_base - $this->_tmin)); - } - return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); - } - - /** - * Encoding a certain digit - * @access private - */ - function _encode_digit($d) - { - return chr($d + 22 + 75 * ($d < 26)); - } - - /** - * Decode a certain digit - * @access private - */ - function _decode_digit($cp) - { - $cp = ord($cp); - return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); - } - - /** - * Internal error handling method - * @access private - */ - function _error($error = '') - { - $this->_error = $error; - } - - /** - * Do Nameprep according to RFC3491 and RFC3454 - * @param array Unicode Characters - * @return string Unicode Characters, Nameprep'd - * @access private - */ - function _nameprep($input) - { - $output = array(); - $error = false; - // - // Mapping - // Walking through the input array, performing the required steps on each of - // the input chars and putting the result into the output array - // While mapping required chars we apply the cannonical ordering - foreach ($input as $v) { - // Map to nothing == skip that code point - if (in_array($v, $this->NP['map_nothing'])) continue; - - // Try to find prohibited input - if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) { - $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); - return false; - } - foreach ($this->NP['prohibit_ranges'] as $range) { - if ($range[0] <= $v && $v <= $range[1]) { - $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); - return false; - } - } - // - // Hangul syllable decomposition - if (0xAC00 <= $v && $v <= 0xD7AF) { - foreach ($this->_hangul_decompose($v) as $out) { - $output[] = (int) $out; - } - // There's a decomposition mapping for that code point - } elseif (isset($this->NP['replacemaps'][$v])) { - foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) { - $output[] = (int) $out; - } - } else { - $output[] = (int) $v; - } - } - // Before applying any Combining, try to rearrange any Hangul syllables - $output = $this->_hangul_compose($output); - // - // Combine code points - // - $last_class = 0; - $last_starter = 0; - $out_len = count($output); - for ($i = 0; $i < $out_len; ++$i) { - $class = $this->_get_combining_class($output[$i]); - if ((!$last_class || $last_class > $class) && $class) { - // Try to match - $seq_len = $i - $last_starter; - $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); - // On match: Replace the last starter with the composed character and remove - // the now redundant non-starter(s) - if ($out) { - $output[$last_starter] = $out; - if (count($out) != $seq_len) { - for ($j = $i+1; $j < $out_len; ++$j) { - $output[$j-1] = $output[$j]; - } - unset($output[$out_len]); - } - // Rewind the for loop by one, since there can be more possible compositions - $i--; - $out_len--; - $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]); - continue; - } - } - // The current class is 0 - if (!$class) $last_starter = $i; - $last_class = $class; - } - return $output; - } - - /** - * Decomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param integer 32bit UCS4 code point - * @return array Either Hangul Syllable decomposed or original 32bit value as one value array - * @access private - */ - function _hangul_decompose($char) - { - $sindex = (int) $char - $this->_sbase; - if ($sindex < 0 || $sindex >= $this->_scount) { - return array($char); - } - $result = array(); - $result[] = (int) $this->_lbase + $sindex / $this->_ncount; - $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; - $T = intval($this->_tbase + $sindex % $this->_tcount); - if ($T != $this->_tbase) $result[] = $T; - return $result; - } - /** - * Ccomposes a Hangul syllable - * (see http://www.unicode.org/unicode/reports/tr15/#Hangul - * @param array Decomposed UCS4 sequence - * @return array UCS4 sequence with syllables composed - * @access private - */ - function _hangul_compose($input) - { - $inp_len = count($input); - if (!$inp_len) return array(); - $result = array(); - $last = (int) $input[0]; - $result[] = $last; // copy first char from input to output - - for ($i = 1; $i < $inp_len; ++$i) { - $char = (int) $input[$i]; - $sindex = $last - $this->_sbase; - $lindex = $last - $this->_lbase; - $vindex = $char - $this->_vbase; - $tindex = $char - $this->_tbase; - // Find out, whether two current characters are LV and T - if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) - && 0 <= $tindex && $tindex <= $this->_tcount) { - // create syllable of form LVT - $last += $tindex; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // Find out, whether two current characters form L and V - if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { - // create syllable of form LV - $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; - $result[(count($result) - 1)] = $last; // reset last - continue; // discard char - } - // if neither case was true, just add the character - $last = $char; - $result[] = $char; - } - return $result; - } - - /** - * Returns the combining class of a certain wide char - * @param integer Wide char to check (32bit integer) - * @return integer Combining class if found, else 0 - * @access private - */ - function _get_combining_class($char) - { - return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0; - } - - /** - * Apllies the cannonical ordering of a decomposed UCS4 sequence - * @param array Decomposed UCS4 sequence - * @return array Ordered USC4 sequence - * @access private - */ - function _apply_cannonical_ordering($input) - { - $swap = true; - $size = count($input); - while ($swap) { - $swap = false; - $last = $this->_get_combining_class(intval($input[0])); - for ($i = 0; $i < $size-1; ++$i) { - $next = $this->_get_combining_class(intval($input[$i+1])); - if ($next != 0 && $last > $next) { - // Move item leftward until it fits - for ($j = $i + 1; $j > 0; --$j) { - if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break; - $t = intval($input[$j]); - $input[$j] = intval($input[$j-1]); - $input[$j-1] = $t; - $swap = true; - } - // Reentering the loop looking at the old character again - $next = $last; - } - $last = $next; - } - } - return $input; - } - - /** - * Do composition of a sequence of starter and non-starter - * @param array UCS4 Decomposed sequence - * @return array Ordered USC4 sequence - * @access private - */ - function _combine($input) - { - $inp_len = count($input); - foreach ($this->NP['replacemaps'] as $np_src => $np_target) { - if ($np_target[0] != $input[0]) continue; - if (count($np_target) != $inp_len) continue; - $hit = false; - foreach ($input as $k2 => $v2) { - if ($v2 == $np_target[$k2]) { - $hit = true; - } else { - $hit = false; - break; - } - } - if ($hit) return $np_src; - } - return false; - } - - /** - * This converts an UTF-8 encoded string to its UCS-4 representation - * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing - * each of the "chars". This is due to PHP not being able to handle strings with - * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. - * The following UTF-8 encodings are supported: - * bytes bits representation - * 1 7 0xxxxxxx - * 2 11 110xxxxx 10xxxxxx - * 3 16 1110xxxx 10xxxxxx 10xxxxxx - * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * Each x represents a bit that can be used to store character data. - * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 - * @access private - */ - function _utf8_to_ucs4($input) - { - $output = array(); - $out_len = 0; - $inp_len = strlen($input); - $mode = 'next'; - $test = 'none'; - for ($k = 0; $k < $inp_len; ++$k) { - $v = ord($input{$k}); // Extract byte from input string - - if ($v < 128) { // We found an ASCII char - put into stirng as is - $output[$out_len] = $v; - ++$out_len; - if ('add' == $mode) { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - return false; - } - continue; - } - if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char - $start_byte = $v; - $mode = 'add'; - $test = 'range'; - if ($v >> 5 == 6) { // &110xxxxx 10xxxxx - $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left - $v = ($v - 192) << 6; - } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx - $next_byte = 1; - $v = ($v - 224) << 12; - } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 2; - $v = ($v - 240) << 18; - } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 3; - $v = ($v - 248) << 24; - } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - $next_byte = 4; - $v = ($v - 252) << 30; - } else { - $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k); - return false; - } - if ('add' == $mode) { - $output[$out_len] = (int) $v; - ++$out_len; - continue; - } - } - if ('add' == $mode) { - if (!$this->_allow_overlong && $test == 'range') { - $test = 'none'; - if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { - $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k); - return false; - } - } - if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx - $v = ($v - 128) << ($next_byte * 6); - $output[($out_len - 1)] += $v; - --$next_byte; - } else { - $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); - return false; - } - if ($next_byte < 0) { - $mode = 'next'; - } - } - } // for - return $output; - } - - /** - * Convert UCS-4 string into UTF-8 string - * See _utf8_to_ucs4() for details - * @access private - */ - function _ucs4_to_utf8($input) - { - $output = ''; - $k = 0; - foreach ($input as $v) { - ++$k; - // $v = ord($v); - if ($v < 128) { // 7bit are transferred literally - $output .= chr($v); - } elseif ($v < (1 << 11)) { // 2 bytes - $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 16)) { // 3 bytes - $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 21)) { // 4 bytes - $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } elseif ($v < (1 << 26)) { // 5 bytes - $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63)) - . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) - . chr(128 + ($v & 63)); - } elseif ($v < (1 << 31)) { // 6 bytes - $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63)) - . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) - . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); - } else { - $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k); - return false; - } - } - return $output; - } - - /** - * Convert UCS-4 array into UCS-4 string - * - * @access private - */ - function _ucs4_to_ucs4_string($input) - { - $output = ''; - // Take array values and split output to 4 bytes per value - // The bit mask is 255, which reads &11111111 - foreach ($input as $v) { - $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255); - } - return $output; - } - - /** - * Convert UCS-4 strin into UCS-4 garray - * - * @access private - */ - function _ucs4_string_to_ucs4($input) - { - $output = array(); - $inp_len = strlen($input); - // Input length must be dividable by 4 - if ($inp_len % 4) { - $this->_error('Input UCS4 string is broken'); - return false; - } - // Empty input - return empty output - if (!$inp_len) return $output; - for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { - // Increment output position every 4 input bytes - if (!($i % 4)) { - $out_len++; - $output[$out_len] = 0; - } - $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); - } - return $output; - } -} - -/** -* Adapter class for aligning the API of idna_convert with that of Net_IDNA -* @author Matthias Sommerfeld -*/ -class Net_IDNA_php4 extends idna_convert -{ - /** - * Sets a new option value. Available options and values: - * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, - * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] - * [overlong - Unicode does not allow unnecessarily long encodings of chars, - * to allow this, set this parameter to true, else to false; - * default is false.] - * [strict - true: strict mode, good for registration purposes - Causes errors - * on failures; false: loose mode, ideal for "wildlife" applications - * by silently ignoring errors and returning the original input instead - * - * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) - * @param string Value to use (if parameter 1 is a string) - * @return boolean true on success, false otherwise - * @access public - */ - function setParams($option, $param = false) - { - return $this->IC->set_parameters($option, $param); - } -} - -?> \ No newline at end of file diff --git a/library/simplepie/idn/npdata.ser b/library/simplepie/idn/npdata.ser deleted file mode 100644 index d7ce6d03f2..0000000000 --- a/library/simplepie/idn/npdata.ser +++ /dev/null @@ -1 +0,0 @@ -a:6:{s:11:"map_nothing";a:27:{i:0;i:173;i:1;i:847;i:2;i:6150;i:3;i:6155;i:4;i:6156;i:5;i:6157;i:6;i:8203;i:7;i:8204;i:8;i:8205;i:9;i:8288;i:10;i:65024;i:11;i:65025;i:12;i:65026;i:13;i:65027;i:14;i:65028;i:15;i:65029;i:16;i:65030;i:17;i:65031;i:18;i:65032;i:19;i:65033;i:20;i:65034;i:21;i:65035;i:22;i:65036;i:23;i:65037;i:24;i:65038;i:25;i:65039;i:26;i:65279;}s:18:"general_prohibited";a:64:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;i:40;i:40;i:41;i:41;i:42;i:42;i:43;i:43;i:44;i:44;i:45;i:47;i:46;i:59;i:47;i:60;i:48;i:61;i:49;i:62;i:50;i:63;i:51;i:64;i:52;i:91;i:53;i:92;i:54;i:93;i:55;i:94;i:56;i:95;i:57;i:96;i:58;i:123;i:59;i:124;i:60;i:125;i:61;i:126;i:62;i:127;i:63;i:12290;}s:8:"prohibit";a:84:{i:0;i:160;i:1;i:5760;i:2;i:8192;i:3;i:8193;i:4;i:8194;i:5;i:8195;i:6;i:8196;i:7;i:8197;i:8;i:8198;i:9;i:8199;i:10;i:8200;i:11;i:8201;i:12;i:8202;i:13;i:8203;i:14;i:8239;i:15;i:8287;i:16;i:12288;i:17;i:1757;i:18;i:1807;i:19;i:6158;i:20;i:8204;i:21;i:8205;i:22;i:8232;i:23;i:8233;i:24;i:65279;i:25;i:65529;i:26;i:65530;i:27;i:65531;i:28;i:65532;i:29;i:65534;i:30;i:65535;i:31;i:131070;i:32;i:131071;i:33;i:196606;i:34;i:196607;i:35;i:262142;i:36;i:262143;i:37;i:327678;i:38;i:327679;i:39;i:393214;i:40;i:393215;i:41;i:458750;i:42;i:458751;i:43;i:524286;i:44;i:524287;i:45;i:589822;i:46;i:589823;i:47;i:655358;i:48;i:655359;i:49;i:720894;i:50;i:720895;i:51;i:786430;i:52;i:786431;i:53;i:851966;i:54;i:851967;i:55;i:917502;i:56;i:917503;i:57;i:983038;i:58;i:983039;i:59;i:1048574;i:60;i:1048575;i:61;i:1114110;i:62;i:1114111;i:63;i:65529;i:64;i:65530;i:65;i:65531;i:66;i:65532;i:67;i:65533;i:68;i:832;i:69;i:833;i:70;i:8206;i:71;i:8207;i:72;i:8234;i:73;i:8235;i:74;i:8236;i:75;i:8237;i:76;i:8238;i:77;i:8298;i:78;i:8299;i:79;i:8300;i:80;i:8301;i:81;i:8302;i:82;i:8303;i:83;i:917505;}s:15:"prohibit_ranges";a:10:{i:0;a:2:{i:0;i:128;i:1;i:159;}i:1;a:2:{i:0;i:8288;i:1;i:8303;}i:2;a:2:{i:0;i:119155;i:1;i:119162;}i:3;a:2:{i:0;i:57344;i:1;i:63743;}i:4;a:2:{i:0;i:983040;i:1;i:1048573;}i:5;a:2:{i:0;i:1048576;i:1;i:1114109;}i:6;a:2:{i:0;i:64976;i:1;i:65007;}i:7;a:2:{i:0;i:55296;i:1;i:57343;}i:8;a:2:{i:0;i:12272;i:1;i:12283;}i:9;a:2:{i:0;i:917536;i:1;i:917631;}}s:11:"replacemaps";a:1401:{i:65;a:1:{i:0;i:97;}i:66;a:1:{i:0;i:98;}i:67;a:1:{i:0;i:99;}i:68;a:1:{i:0;i:100;}i:69;a:1:{i:0;i:101;}i:70;a:1:{i:0;i:102;}i:71;a:1:{i:0;i:103;}i:72;a:1:{i:0;i:104;}i:73;a:1:{i:0;i:105;}i:74;a:1:{i:0;i:106;}i:75;a:1:{i:0;i:107;}i:76;a:1:{i:0;i:108;}i:77;a:1:{i:0;i:109;}i:78;a:1:{i:0;i:110;}i:79;a:1:{i:0;i:111;}i:80;a:1:{i:0;i:112;}i:81;a:1:{i:0;i:113;}i:82;a:1:{i:0;i:114;}i:83;a:1:{i:0;i:115;}i:84;a:1:{i:0;i:116;}i:85;a:1:{i:0;i:117;}i:86;a:1:{i:0;i:118;}i:87;a:1:{i:0;i:119;}i:88;a:1:{i:0;i:120;}i:89;a:1:{i:0;i:121;}i:90;a:1:{i:0;i:122;}i:181;a:1:{i:0;i:956;}i:192;a:1:{i:0;i:224;}i:193;a:1:{i:0;i:225;}i:194;a:1:{i:0;i:226;}i:195;a:1:{i:0;i:227;}i:196;a:1:{i:0;i:228;}i:197;a:1:{i:0;i:229;}i:198;a:1:{i:0;i:230;}i:199;a:1:{i:0;i:231;}i:200;a:1:{i:0;i:232;}i:201;a:1:{i:0;i:233;}i:202;a:1:{i:0;i:234;}i:203;a:1:{i:0;i:235;}i:204;a:1:{i:0;i:236;}i:205;a:1:{i:0;i:237;}i:206;a:1:{i:0;i:238;}i:207;a:1:{i:0;i:239;}i:208;a:1:{i:0;i:240;}i:209;a:1:{i:0;i:241;}i:210;a:1:{i:0;i:242;}i:211;a:1:{i:0;i:243;}i:212;a:1:{i:0;i:244;}i:213;a:1:{i:0;i:245;}i:214;a:1:{i:0;i:246;}i:216;a:1:{i:0;i:248;}i:217;a:1:{i:0;i:249;}i:218;a:1:{i:0;i:250;}i:219;a:1:{i:0;i:251;}i:220;a:1:{i:0;i:252;}i:221;a:1:{i:0;i:253;}i:222;a:1:{i:0;i:254;}i:223;a:2:{i:0;i:115;i:1;i:115;}i:256;a:1:{i:0;i:257;}i:258;a:1:{i:0;i:259;}i:260;a:1:{i:0;i:261;}i:262;a:1:{i:0;i:263;}i:264;a:1:{i:0;i:265;}i:266;a:1:{i:0;i:267;}i:268;a:1:{i:0;i:269;}i:270;a:1:{i:0;i:271;}i:272;a:1:{i:0;i:273;}i:274;a:1:{i:0;i:275;}i:276;a:1:{i:0;i:277;}i:278;a:1:{i:0;i:279;}i:280;a:1:{i:0;i:281;}i:282;a:1:{i:0;i:283;}i:284;a:1:{i:0;i:285;}i:286;a:1:{i:0;i:287;}i:288;a:1:{i:0;i:289;}i:290;a:1:{i:0;i:291;}i:292;a:1:{i:0;i:293;}i:294;a:1:{i:0;i:295;}i:296;a:1:{i:0;i:297;}i:298;a:1:{i:0;i:299;}i:300;a:1:{i:0;i:301;}i:302;a:1:{i:0;i:303;}i:304;a:2:{i:0;i:105;i:1;i:775;}i:306;a:1:{i:0;i:307;}i:308;a:1:{i:0;i:309;}i:310;a:1:{i:0;i:311;}i:313;a:1:{i:0;i:314;}i:315;a:1:{i:0;i:316;}i:317;a:1:{i:0;i:318;}i:319;a:1:{i:0;i:320;}i:321;a:1:{i:0;i:322;}i:323;a:1:{i:0;i:324;}i:325;a:1:{i:0;i:326;}i:327;a:1:{i:0;i:328;}i:329;a:2:{i:0;i:700;i:1;i:110;}i:330;a:1:{i:0;i:331;}i:332;a:1:{i:0;i:333;}i:334;a:1:{i:0;i:335;}i:336;a:1:{i:0;i:337;}i:338;a:1:{i:0;i:339;}i:340;a:1:{i:0;i:341;}i:342;a:1:{i:0;i:343;}i:344;a:1:{i:0;i:345;}i:346;a:1:{i:0;i:347;}i:348;a:1:{i:0;i:349;}i:350;a:1:{i:0;i:351;}i:352;a:1:{i:0;i:353;}i:354;a:1:{i:0;i:355;}i:356;a:1:{i:0;i:357;}i:358;a:1:{i:0;i:359;}i:360;a:1:{i:0;i:361;}i:362;a:1:{i:0;i:363;}i:364;a:1:{i:0;i:365;}i:366;a:1:{i:0;i:367;}i:368;a:1:{i:0;i:369;}i:370;a:1:{i:0;i:371;}i:372;a:1:{i:0;i:373;}i:374;a:1:{i:0;i:375;}i:376;a:1:{i:0;i:255;}i:377;a:1:{i:0;i:378;}i:379;a:1:{i:0;i:380;}i:381;a:1:{i:0;i:382;}i:383;a:1:{i:0;i:115;}i:385;a:1:{i:0;i:595;}i:386;a:1:{i:0;i:387;}i:388;a:1:{i:0;i:389;}i:390;a:1:{i:0;i:596;}i:391;a:1:{i:0;i:392;}i:393;a:1:{i:0;i:598;}i:394;a:1:{i:0;i:599;}i:395;a:1:{i:0;i:396;}i:398;a:1:{i:0;i:477;}i:399;a:1:{i:0;i:601;}i:400;a:1:{i:0;i:603;}i:401;a:1:{i:0;i:402;}i:403;a:1:{i:0;i:608;}i:404;a:1:{i:0;i:611;}i:406;a:1:{i:0;i:617;}i:407;a:1:{i:0;i:616;}i:408;a:1:{i:0;i:409;}i:412;a:1:{i:0;i:623;}i:413;a:1:{i:0;i:626;}i:415;a:1:{i:0;i:629;}i:416;a:1:{i:0;i:417;}i:418;a:1:{i:0;i:419;}i:420;a:1:{i:0;i:421;}i:422;a:1:{i:0;i:640;}i:423;a:1:{i:0;i:424;}i:425;a:1:{i:0;i:643;}i:428;a:1:{i:0;i:429;}i:430;a:1:{i:0;i:648;}i:431;a:1:{i:0;i:432;}i:433;a:1:{i:0;i:650;}i:434;a:1:{i:0;i:651;}i:435;a:1:{i:0;i:436;}i:437;a:1:{i:0;i:438;}i:439;a:1:{i:0;i:658;}i:440;a:1:{i:0;i:441;}i:444;a:1:{i:0;i:445;}i:452;a:1:{i:0;i:454;}i:453;a:1:{i:0;i:454;}i:455;a:1:{i:0;i:457;}i:456;a:1:{i:0;i:457;}i:458;a:1:{i:0;i:460;}i:459;a:1:{i:0;i:460;}i:461;a:1:{i:0;i:462;}i:463;a:1:{i:0;i:464;}i:465;a:1:{i:0;i:466;}i:467;a:1:{i:0;i:468;}i:469;a:1:{i:0;i:470;}i:471;a:1:{i:0;i:472;}i:473;a:1:{i:0;i:474;}i:475;a:1:{i:0;i:476;}i:478;a:1:{i:0;i:479;}i:480;a:1:{i:0;i:481;}i:482;a:1:{i:0;i:483;}i:484;a:1:{i:0;i:485;}i:486;a:1:{i:0;i:487;}i:488;a:1:{i:0;i:489;}i:490;a:1:{i:0;i:491;}i:492;a:1:{i:0;i:493;}i:494;a:1:{i:0;i:495;}i:496;a:2:{i:0;i:106;i:1;i:780;}i:497;a:1:{i:0;i:499;}i:498;a:1:{i:0;i:499;}i:500;a:1:{i:0;i:501;}i:502;a:1:{i:0;i:405;}i:503;a:1:{i:0;i:447;}i:504;a:1:{i:0;i:505;}i:506;a:1:{i:0;i:507;}i:508;a:1:{i:0;i:509;}i:510;a:1:{i:0;i:511;}i:512;a:1:{i:0;i:513;}i:514;a:1:{i:0;i:515;}i:516;a:1:{i:0;i:517;}i:518;a:1:{i:0;i:519;}i:520;a:1:{i:0;i:521;}i:522;a:1:{i:0;i:523;}i:524;a:1:{i:0;i:525;}i:526;a:1:{i:0;i:527;}i:528;a:1:{i:0;i:529;}i:530;a:1:{i:0;i:531;}i:532;a:1:{i:0;i:533;}i:534;a:1:{i:0;i:535;}i:536;a:1:{i:0;i:537;}i:538;a:1:{i:0;i:539;}i:540;a:1:{i:0;i:541;}i:542;a:1:{i:0;i:543;}i:544;a:1:{i:0;i:414;}i:546;a:1:{i:0;i:547;}i:548;a:1:{i:0;i:549;}i:550;a:1:{i:0;i:551;}i:552;a:1:{i:0;i:553;}i:554;a:1:{i:0;i:555;}i:556;a:1:{i:0;i:557;}i:558;a:1:{i:0;i:559;}i:560;a:1:{i:0;i:561;}i:562;a:1:{i:0;i:563;}i:837;a:1:{i:0;i:953;}i:890;a:2:{i:0;i:32;i:1;i:953;}i:902;a:1:{i:0;i:940;}i:904;a:1:{i:0;i:941;}i:905;a:1:{i:0;i:942;}i:906;a:1:{i:0;i:943;}i:908;a:1:{i:0;i:972;}i:910;a:1:{i:0;i:973;}i:911;a:1:{i:0;i:974;}i:912;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:913;a:1:{i:0;i:945;}i:914;a:1:{i:0;i:946;}i:915;a:1:{i:0;i:947;}i:916;a:1:{i:0;i:948;}i:917;a:1:{i:0;i:949;}i:918;a:1:{i:0;i:950;}i:919;a:1:{i:0;i:951;}i:920;a:1:{i:0;i:952;}i:921;a:1:{i:0;i:953;}i:922;a:1:{i:0;i:954;}i:923;a:1:{i:0;i:955;}i:924;a:1:{i:0;i:956;}i:925;a:1:{i:0;i:957;}i:926;a:1:{i:0;i:958;}i:927;a:1:{i:0;i:959;}i:928;a:1:{i:0;i:960;}i:929;a:1:{i:0;i:961;}i:931;a:1:{i:0;i:963;}i:932;a:1:{i:0;i:964;}i:933;a:1:{i:0;i:965;}i:934;a:1:{i:0;i:966;}i:935;a:1:{i:0;i:967;}i:936;a:1:{i:0;i:968;}i:937;a:1:{i:0;i:969;}i:938;a:1:{i:0;i:970;}i:939;a:1:{i:0;i:971;}i:944;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:962;a:1:{i:0;i:963;}i:976;a:1:{i:0;i:946;}i:977;a:1:{i:0;i:952;}i:978;a:1:{i:0;i:965;}i:979;a:1:{i:0;i:973;}i:980;a:1:{i:0;i:971;}i:981;a:1:{i:0;i:966;}i:982;a:1:{i:0;i:960;}i:984;a:1:{i:0;i:985;}i:986;a:1:{i:0;i:987;}i:988;a:1:{i:0;i:989;}i:990;a:1:{i:0;i:991;}i:992;a:1:{i:0;i:993;}i:994;a:1:{i:0;i:995;}i:996;a:1:{i:0;i:997;}i:998;a:1:{i:0;i:999;}i:1000;a:1:{i:0;i:1001;}i:1002;a:1:{i:0;i:1003;}i:1004;a:1:{i:0;i:1005;}i:1006;a:1:{i:0;i:1007;}i:1008;a:1:{i:0;i:954;}i:1009;a:1:{i:0;i:961;}i:1010;a:1:{i:0;i:963;}i:1012;a:1:{i:0;i:952;}i:1013;a:1:{i:0;i:949;}i:1024;a:1:{i:0;i:1104;}i:1025;a:1:{i:0;i:1105;}i:1026;a:1:{i:0;i:1106;}i:1027;a:1:{i:0;i:1107;}i:1028;a:1:{i:0;i:1108;}i:1029;a:1:{i:0;i:1109;}i:1030;a:1:{i:0;i:1110;}i:1031;a:1:{i:0;i:1111;}i:1032;a:1:{i:0;i:1112;}i:1033;a:1:{i:0;i:1113;}i:1034;a:1:{i:0;i:1114;}i:1035;a:1:{i:0;i:1115;}i:1036;a:1:{i:0;i:1116;}i:1037;a:1:{i:0;i:1117;}i:1038;a:1:{i:0;i:1118;}i:1039;a:1:{i:0;i:1119;}i:1040;a:1:{i:0;i:1072;}i:1041;a:1:{i:0;i:1073;}i:1042;a:1:{i:0;i:1074;}i:1043;a:1:{i:0;i:1075;}i:1044;a:1:{i:0;i:1076;}i:1045;a:1:{i:0;i:1077;}i:1046;a:1:{i:0;i:1078;}i:1047;a:1:{i:0;i:1079;}i:1048;a:1:{i:0;i:1080;}i:1049;a:1:{i:0;i:1081;}i:1050;a:1:{i:0;i:1082;}i:1051;a:1:{i:0;i:1083;}i:1052;a:1:{i:0;i:1084;}i:1053;a:1:{i:0;i:1085;}i:1054;a:1:{i:0;i:1086;}i:1055;a:1:{i:0;i:1087;}i:1056;a:1:{i:0;i:1088;}i:1057;a:1:{i:0;i:1089;}i:1058;a:1:{i:0;i:1090;}i:1059;a:1:{i:0;i:1091;}i:1060;a:1:{i:0;i:1092;}i:1061;a:1:{i:0;i:1093;}i:1062;a:1:{i:0;i:1094;}i:1063;a:1:{i:0;i:1095;}i:1064;a:1:{i:0;i:1096;}i:1065;a:1:{i:0;i:1097;}i:1066;a:1:{i:0;i:1098;}i:1067;a:1:{i:0;i:1099;}i:1068;a:1:{i:0;i:1100;}i:1069;a:1:{i:0;i:1101;}i:1070;a:1:{i:0;i:1102;}i:1071;a:1:{i:0;i:1103;}i:1120;a:1:{i:0;i:1121;}i:1122;a:1:{i:0;i:1123;}i:1124;a:1:{i:0;i:1125;}i:1126;a:1:{i:0;i:1127;}i:1128;a:1:{i:0;i:1129;}i:1130;a:1:{i:0;i:1131;}i:1132;a:1:{i:0;i:1133;}i:1134;a:1:{i:0;i:1135;}i:1136;a:1:{i:0;i:1137;}i:1138;a:1:{i:0;i:1139;}i:1140;a:1:{i:0;i:1141;}i:1142;a:1:{i:0;i:1143;}i:1144;a:1:{i:0;i:1145;}i:1146;a:1:{i:0;i:1147;}i:1148;a:1:{i:0;i:1149;}i:1150;a:1:{i:0;i:1151;}i:1152;a:1:{i:0;i:1153;}i:1162;a:1:{i:0;i:1163;}i:1164;a:1:{i:0;i:1165;}i:1166;a:1:{i:0;i:1167;}i:1168;a:1:{i:0;i:1169;}i:1170;a:1:{i:0;i:1171;}i:1172;a:1:{i:0;i:1173;}i:1174;a:1:{i:0;i:1175;}i:1176;a:1:{i:0;i:1177;}i:1178;a:1:{i:0;i:1179;}i:1180;a:1:{i:0;i:1181;}i:1182;a:1:{i:0;i:1183;}i:1184;a:1:{i:0;i:1185;}i:1186;a:1:{i:0;i:1187;}i:1188;a:1:{i:0;i:1189;}i:1190;a:1:{i:0;i:1191;}i:1192;a:1:{i:0;i:1193;}i:1194;a:1:{i:0;i:1195;}i:1196;a:1:{i:0;i:1197;}i:1198;a:1:{i:0;i:1199;}i:1200;a:1:{i:0;i:1201;}i:1202;a:1:{i:0;i:1203;}i:1204;a:1:{i:0;i:1205;}i:1206;a:1:{i:0;i:1207;}i:1208;a:1:{i:0;i:1209;}i:1210;a:1:{i:0;i:1211;}i:1212;a:1:{i:0;i:1213;}i:1214;a:1:{i:0;i:1215;}i:1217;a:1:{i:0;i:1218;}i:1219;a:1:{i:0;i:1220;}i:1221;a:1:{i:0;i:1222;}i:1223;a:1:{i:0;i:1224;}i:1225;a:1:{i:0;i:1226;}i:1227;a:1:{i:0;i:1228;}i:1229;a:1:{i:0;i:1230;}i:1232;a:1:{i:0;i:1233;}i:1234;a:1:{i:0;i:1235;}i:1236;a:1:{i:0;i:1237;}i:1238;a:1:{i:0;i:1239;}i:1240;a:1:{i:0;i:1241;}i:1242;a:1:{i:0;i:1243;}i:1244;a:1:{i:0;i:1245;}i:1246;a:1:{i:0;i:1247;}i:1248;a:1:{i:0;i:1249;}i:1250;a:1:{i:0;i:1251;}i:1252;a:1:{i:0;i:1253;}i:1254;a:1:{i:0;i:1255;}i:1256;a:1:{i:0;i:1257;}i:1258;a:1:{i:0;i:1259;}i:1260;a:1:{i:0;i:1261;}i:1262;a:1:{i:0;i:1263;}i:1264;a:1:{i:0;i:1265;}i:1266;a:1:{i:0;i:1267;}i:1268;a:1:{i:0;i:1269;}i:1272;a:1:{i:0;i:1273;}i:1280;a:1:{i:0;i:1281;}i:1282;a:1:{i:0;i:1283;}i:1284;a:1:{i:0;i:1285;}i:1286;a:1:{i:0;i:1287;}i:1288;a:1:{i:0;i:1289;}i:1290;a:1:{i:0;i:1291;}i:1292;a:1:{i:0;i:1293;}i:1294;a:1:{i:0;i:1295;}i:1329;a:1:{i:0;i:1377;}i:1330;a:1:{i:0;i:1378;}i:1331;a:1:{i:0;i:1379;}i:1332;a:1:{i:0;i:1380;}i:1333;a:1:{i:0;i:1381;}i:1334;a:1:{i:0;i:1382;}i:1335;a:1:{i:0;i:1383;}i:1336;a:1:{i:0;i:1384;}i:1337;a:1:{i:0;i:1385;}i:1338;a:1:{i:0;i:1386;}i:1339;a:1:{i:0;i:1387;}i:1340;a:1:{i:0;i:1388;}i:1341;a:1:{i:0;i:1389;}i:1342;a:1:{i:0;i:1390;}i:1343;a:1:{i:0;i:1391;}i:1344;a:1:{i:0;i:1392;}i:1345;a:1:{i:0;i:1393;}i:1346;a:1:{i:0;i:1394;}i:1347;a:1:{i:0;i:1395;}i:1348;a:1:{i:0;i:1396;}i:1349;a:1:{i:0;i:1397;}i:1350;a:1:{i:0;i:1398;}i:1351;a:1:{i:0;i:1399;}i:1352;a:1:{i:0;i:1400;}i:1353;a:1:{i:0;i:1401;}i:1354;a:1:{i:0;i:1402;}i:1355;a:1:{i:0;i:1403;}i:1356;a:1:{i:0;i:1404;}i:1357;a:1:{i:0;i:1405;}i:1358;a:1:{i:0;i:1406;}i:1359;a:1:{i:0;i:1407;}i:1360;a:1:{i:0;i:1408;}i:1361;a:1:{i:0;i:1409;}i:1362;a:1:{i:0;i:1410;}i:1363;a:1:{i:0;i:1411;}i:1364;a:1:{i:0;i:1412;}i:1365;a:1:{i:0;i:1413;}i:1366;a:1:{i:0;i:1414;}i:1415;a:2:{i:0;i:1381;i:1;i:1410;}i:7680;a:1:{i:0;i:7681;}i:7682;a:1:{i:0;i:7683;}i:7684;a:1:{i:0;i:7685;}i:7686;a:1:{i:0;i:7687;}i:7688;a:1:{i:0;i:7689;}i:7690;a:1:{i:0;i:7691;}i:7692;a:1:{i:0;i:7693;}i:7694;a:1:{i:0;i:7695;}i:7696;a:1:{i:0;i:7697;}i:7698;a:1:{i:0;i:7699;}i:7700;a:1:{i:0;i:7701;}i:7702;a:1:{i:0;i:7703;}i:7704;a:1:{i:0;i:7705;}i:7706;a:1:{i:0;i:7707;}i:7708;a:1:{i:0;i:7709;}i:7710;a:1:{i:0;i:7711;}i:7712;a:1:{i:0;i:7713;}i:7714;a:1:{i:0;i:7715;}i:7716;a:1:{i:0;i:7717;}i:7718;a:1:{i:0;i:7719;}i:7720;a:1:{i:0;i:7721;}i:7722;a:1:{i:0;i:7723;}i:7724;a:1:{i:0;i:7725;}i:7726;a:1:{i:0;i:7727;}i:7728;a:1:{i:0;i:7729;}i:7730;a:1:{i:0;i:7731;}i:7732;a:1:{i:0;i:7733;}i:7734;a:1:{i:0;i:7735;}i:7736;a:1:{i:0;i:7737;}i:7738;a:1:{i:0;i:7739;}i:7740;a:1:{i:0;i:7741;}i:7742;a:1:{i:0;i:7743;}i:7744;a:1:{i:0;i:7745;}i:7746;a:1:{i:0;i:7747;}i:7748;a:1:{i:0;i:7749;}i:7750;a:1:{i:0;i:7751;}i:7752;a:1:{i:0;i:7753;}i:7754;a:1:{i:0;i:7755;}i:7756;a:1:{i:0;i:7757;}i:7758;a:1:{i:0;i:7759;}i:7760;a:1:{i:0;i:7761;}i:7762;a:1:{i:0;i:7763;}i:7764;a:1:{i:0;i:7765;}i:7766;a:1:{i:0;i:7767;}i:7768;a:1:{i:0;i:7769;}i:7770;a:1:{i:0;i:7771;}i:7772;a:1:{i:0;i:7773;}i:7774;a:1:{i:0;i:7775;}i:7776;a:1:{i:0;i:7777;}i:7778;a:1:{i:0;i:7779;}i:7780;a:1:{i:0;i:7781;}i:7782;a:1:{i:0;i:7783;}i:7784;a:1:{i:0;i:7785;}i:7786;a:1:{i:0;i:7787;}i:7788;a:1:{i:0;i:7789;}i:7790;a:1:{i:0;i:7791;}i:7792;a:1:{i:0;i:7793;}i:7794;a:1:{i:0;i:7795;}i:7796;a:1:{i:0;i:7797;}i:7798;a:1:{i:0;i:7799;}i:7800;a:1:{i:0;i:7801;}i:7802;a:1:{i:0;i:7803;}i:7804;a:1:{i:0;i:7805;}i:7806;a:1:{i:0;i:7807;}i:7808;a:1:{i:0;i:7809;}i:7810;a:1:{i:0;i:7811;}i:7812;a:1:{i:0;i:7813;}i:7814;a:1:{i:0;i:7815;}i:7816;a:1:{i:0;i:7817;}i:7818;a:1:{i:0;i:7819;}i:7820;a:1:{i:0;i:7821;}i:7822;a:1:{i:0;i:7823;}i:7824;a:1:{i:0;i:7825;}i:7826;a:1:{i:0;i:7827;}i:7828;a:1:{i:0;i:7829;}i:7830;a:2:{i:0;i:104;i:1;i:817;}i:7831;a:2:{i:0;i:116;i:1;i:776;}i:7832;a:2:{i:0;i:119;i:1;i:778;}i:7833;a:2:{i:0;i:121;i:1;i:778;}i:7834;a:2:{i:0;i:97;i:1;i:702;}i:7835;a:1:{i:0;i:7777;}i:7840;a:1:{i:0;i:7841;}i:7842;a:1:{i:0;i:7843;}i:7844;a:1:{i:0;i:7845;}i:7846;a:1:{i:0;i:7847;}i:7848;a:1:{i:0;i:7849;}i:7850;a:1:{i:0;i:7851;}i:7852;a:1:{i:0;i:7853;}i:7854;a:1:{i:0;i:7855;}i:7856;a:1:{i:0;i:7857;}i:7858;a:1:{i:0;i:7859;}i:7860;a:1:{i:0;i:7861;}i:7862;a:1:{i:0;i:7863;}i:7864;a:1:{i:0;i:7865;}i:7866;a:1:{i:0;i:7867;}i:7868;a:1:{i:0;i:7869;}i:7870;a:1:{i:0;i:7871;}i:7872;a:1:{i:0;i:7873;}i:7874;a:1:{i:0;i:7875;}i:7876;a:1:{i:0;i:7877;}i:7878;a:1:{i:0;i:7879;}i:7880;a:1:{i:0;i:7881;}i:7882;a:1:{i:0;i:7883;}i:7884;a:1:{i:0;i:7885;}i:7886;a:1:{i:0;i:7887;}i:7888;a:1:{i:0;i:7889;}i:7890;a:1:{i:0;i:7891;}i:7892;a:1:{i:0;i:7893;}i:7894;a:1:{i:0;i:7895;}i:7896;a:1:{i:0;i:7897;}i:7898;a:1:{i:0;i:7899;}i:7900;a:1:{i:0;i:7901;}i:7902;a:1:{i:0;i:7903;}i:7904;a:1:{i:0;i:7905;}i:7906;a:1:{i:0;i:7907;}i:7908;a:1:{i:0;i:7909;}i:7910;a:1:{i:0;i:7911;}i:7912;a:1:{i:0;i:7913;}i:7914;a:1:{i:0;i:7915;}i:7916;a:1:{i:0;i:7917;}i:7918;a:1:{i:0;i:7919;}i:7920;a:1:{i:0;i:7921;}i:7922;a:1:{i:0;i:7923;}i:7924;a:1:{i:0;i:7925;}i:7926;a:1:{i:0;i:7927;}i:7928;a:1:{i:0;i:7929;}i:7944;a:1:{i:0;i:7936;}i:7945;a:1:{i:0;i:7937;}i:7946;a:1:{i:0;i:7938;}i:7947;a:1:{i:0;i:7939;}i:7948;a:1:{i:0;i:7940;}i:7949;a:1:{i:0;i:7941;}i:7950;a:1:{i:0;i:7942;}i:7951;a:1:{i:0;i:7943;}i:7960;a:1:{i:0;i:7952;}i:7961;a:1:{i:0;i:7953;}i:7962;a:1:{i:0;i:7954;}i:7963;a:1:{i:0;i:7955;}i:7964;a:1:{i:0;i:7956;}i:7965;a:1:{i:0;i:7957;}i:7976;a:1:{i:0;i:7968;}i:7977;a:1:{i:0;i:7969;}i:7978;a:1:{i:0;i:7970;}i:7979;a:1:{i:0;i:7971;}i:7980;a:1:{i:0;i:7972;}i:7981;a:1:{i:0;i:7973;}i:7982;a:1:{i:0;i:7974;}i:7983;a:1:{i:0;i:7975;}i:7992;a:1:{i:0;i:7984;}i:7993;a:1:{i:0;i:7985;}i:7994;a:1:{i:0;i:7986;}i:7995;a:1:{i:0;i:7987;}i:7996;a:1:{i:0;i:7988;}i:7997;a:1:{i:0;i:7989;}i:7998;a:1:{i:0;i:7990;}i:7999;a:1:{i:0;i:7991;}i:8008;a:1:{i:0;i:8000;}i:8009;a:1:{i:0;i:8001;}i:8010;a:1:{i:0;i:8002;}i:8011;a:1:{i:0;i:8003;}i:8012;a:1:{i:0;i:8004;}i:8013;a:1:{i:0;i:8005;}i:8016;a:2:{i:0;i:965;i:1;i:787;}i:8018;a:3:{i:0;i:965;i:1;i:787;i:2;i:768;}i:8020;a:3:{i:0;i:965;i:1;i:787;i:2;i:769;}i:8022;a:3:{i:0;i:965;i:1;i:787;i:2;i:834;}i:8025;a:1:{i:0;i:8017;}i:8027;a:1:{i:0;i:8019;}i:8029;a:1:{i:0;i:8021;}i:8031;a:1:{i:0;i:8023;}i:8040;a:1:{i:0;i:8032;}i:8041;a:1:{i:0;i:8033;}i:8042;a:1:{i:0;i:8034;}i:8043;a:1:{i:0;i:8035;}i:8044;a:1:{i:0;i:8036;}i:8045;a:1:{i:0;i:8037;}i:8046;a:1:{i:0;i:8038;}i:8047;a:1:{i:0;i:8039;}i:8064;a:2:{i:0;i:7936;i:1;i:953;}i:8065;a:2:{i:0;i:7937;i:1;i:953;}i:8066;a:2:{i:0;i:7938;i:1;i:953;}i:8067;a:2:{i:0;i:7939;i:1;i:953;}i:8068;a:2:{i:0;i:7940;i:1;i:953;}i:8069;a:2:{i:0;i:7941;i:1;i:953;}i:8070;a:2:{i:0;i:7942;i:1;i:953;}i:8071;a:2:{i:0;i:7943;i:1;i:953;}i:8072;a:2:{i:0;i:7936;i:1;i:953;}i:8073;a:2:{i:0;i:7937;i:1;i:953;}i:8074;a:2:{i:0;i:7938;i:1;i:953;}i:8075;a:2:{i:0;i:7939;i:1;i:953;}i:8076;a:2:{i:0;i:7940;i:1;i:953;}i:8077;a:2:{i:0;i:7941;i:1;i:953;}i:8078;a:2:{i:0;i:7942;i:1;i:953;}i:8079;a:2:{i:0;i:7943;i:1;i:953;}i:8080;a:2:{i:0;i:7968;i:1;i:953;}i:8081;a:2:{i:0;i:7969;i:1;i:953;}i:8082;a:2:{i:0;i:7970;i:1;i:953;}i:8083;a:2:{i:0;i:7971;i:1;i:953;}i:8084;a:2:{i:0;i:7972;i:1;i:953;}i:8085;a:2:{i:0;i:7973;i:1;i:953;}i:8086;a:2:{i:0;i:7974;i:1;i:953;}i:8087;a:2:{i:0;i:7975;i:1;i:953;}i:8088;a:2:{i:0;i:7968;i:1;i:953;}i:8089;a:2:{i:0;i:7969;i:1;i:953;}i:8090;a:2:{i:0;i:7970;i:1;i:953;}i:8091;a:2:{i:0;i:7971;i:1;i:953;}i:8092;a:2:{i:0;i:7972;i:1;i:953;}i:8093;a:2:{i:0;i:7973;i:1;i:953;}i:8094;a:2:{i:0;i:7974;i:1;i:953;}i:8095;a:2:{i:0;i:7975;i:1;i:953;}i:8096;a:2:{i:0;i:8032;i:1;i:953;}i:8097;a:2:{i:0;i:8033;i:1;i:953;}i:8098;a:2:{i:0;i:8034;i:1;i:953;}i:8099;a:2:{i:0;i:8035;i:1;i:953;}i:8100;a:2:{i:0;i:8036;i:1;i:953;}i:8101;a:2:{i:0;i:8037;i:1;i:953;}i:8102;a:2:{i:0;i:8038;i:1;i:953;}i:8103;a:2:{i:0;i:8039;i:1;i:953;}i:8104;a:2:{i:0;i:8032;i:1;i:953;}i:8105;a:2:{i:0;i:8033;i:1;i:953;}i:8106;a:2:{i:0;i:8034;i:1;i:953;}i:8107;a:2:{i:0;i:8035;i:1;i:953;}i:8108;a:2:{i:0;i:8036;i:1;i:953;}i:8109;a:2:{i:0;i:8037;i:1;i:953;}i:8110;a:2:{i:0;i:8038;i:1;i:953;}i:8111;a:2:{i:0;i:8039;i:1;i:953;}i:8114;a:2:{i:0;i:8048;i:1;i:953;}i:8115;a:2:{i:0;i:945;i:1;i:953;}i:8116;a:2:{i:0;i:940;i:1;i:953;}i:8118;a:2:{i:0;i:945;i:1;i:834;}i:8119;a:3:{i:0;i:945;i:1;i:834;i:2;i:953;}i:8120;a:1:{i:0;i:8112;}i:8121;a:1:{i:0;i:8113;}i:8122;a:1:{i:0;i:8048;}i:8123;a:1:{i:0;i:8049;}i:8124;a:2:{i:0;i:945;i:1;i:953;}i:8126;a:1:{i:0;i:953;}i:8130;a:2:{i:0;i:8052;i:1;i:953;}i:8131;a:2:{i:0;i:951;i:1;i:953;}i:8132;a:2:{i:0;i:942;i:1;i:953;}i:8134;a:2:{i:0;i:951;i:1;i:834;}i:8135;a:3:{i:0;i:951;i:1;i:834;i:2;i:953;}i:8136;a:1:{i:0;i:8050;}i:8137;a:1:{i:0;i:8051;}i:8138;a:1:{i:0;i:8052;}i:8139;a:1:{i:0;i:8053;}i:8140;a:2:{i:0;i:951;i:1;i:953;}i:8146;a:3:{i:0;i:953;i:1;i:776;i:2;i:768;}i:8147;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:8150;a:2:{i:0;i:953;i:1;i:834;}i:8151;a:3:{i:0;i:953;i:1;i:776;i:2;i:834;}i:8152;a:1:{i:0;i:8144;}i:8153;a:1:{i:0;i:8145;}i:8154;a:1:{i:0;i:8054;}i:8155;a:1:{i:0;i:8055;}i:8162;a:3:{i:0;i:965;i:1;i:776;i:2;i:768;}i:8163;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:8164;a:2:{i:0;i:961;i:1;i:787;}i:8166;a:2:{i:0;i:965;i:1;i:834;}i:8167;a:3:{i:0;i:965;i:1;i:776;i:2;i:834;}i:8168;a:1:{i:0;i:8160;}i:8169;a:1:{i:0;i:8161;}i:8170;a:1:{i:0;i:8058;}i:8171;a:1:{i:0;i:8059;}i:8172;a:1:{i:0;i:8165;}i:8178;a:2:{i:0;i:8060;i:1;i:953;}i:8179;a:2:{i:0;i:969;i:1;i:953;}i:8180;a:2:{i:0;i:974;i:1;i:953;}i:8182;a:2:{i:0;i:969;i:1;i:834;}i:8183;a:3:{i:0;i:969;i:1;i:834;i:2;i:953;}i:8184;a:1:{i:0;i:8056;}i:8185;a:1:{i:0;i:8057;}i:8186;a:1:{i:0;i:8060;}i:8187;a:1:{i:0;i:8061;}i:8188;a:2:{i:0;i:969;i:1;i:953;}i:8360;a:2:{i:0;i:114;i:1;i:115;}i:8450;a:1:{i:0;i:99;}i:8451;a:2:{i:0;i:176;i:1;i:99;}i:8455;a:1:{i:0;i:603;}i:8457;a:2:{i:0;i:176;i:1;i:102;}i:8459;a:1:{i:0;i:104;}i:8460;a:1:{i:0;i:104;}i:8461;a:1:{i:0;i:104;}i:8464;a:1:{i:0;i:105;}i:8465;a:1:{i:0;i:105;}i:8466;a:1:{i:0;i:108;}i:8469;a:1:{i:0;i:110;}i:8470;a:2:{i:0;i:110;i:1;i:111;}i:8473;a:1:{i:0;i:112;}i:8474;a:1:{i:0;i:113;}i:8475;a:1:{i:0;i:114;}i:8476;a:1:{i:0;i:114;}i:8477;a:1:{i:0;i:114;}i:8480;a:2:{i:0;i:115;i:1;i:109;}i:8481;a:3:{i:0;i:116;i:1;i:101;i:2;i:108;}i:8482;a:2:{i:0;i:116;i:1;i:109;}i:8484;a:1:{i:0;i:122;}i:8486;a:1:{i:0;i:969;}i:8488;a:1:{i:0;i:122;}i:8490;a:1:{i:0;i:107;}i:8491;a:1:{i:0;i:229;}i:8492;a:1:{i:0;i:98;}i:8493;a:1:{i:0;i:99;}i:8496;a:1:{i:0;i:101;}i:8497;a:1:{i:0;i:102;}i:8499;a:1:{i:0;i:109;}i:8510;a:1:{i:0;i:947;}i:8511;a:1:{i:0;i:960;}i:8517;a:1:{i:0;i:100;}i:8544;a:1:{i:0;i:8560;}i:8545;a:1:{i:0;i:8561;}i:8546;a:1:{i:0;i:8562;}i:8547;a:1:{i:0;i:8563;}i:8548;a:1:{i:0;i:8564;}i:8549;a:1:{i:0;i:8565;}i:8550;a:1:{i:0;i:8566;}i:8551;a:1:{i:0;i:8567;}i:8552;a:1:{i:0;i:8568;}i:8553;a:1:{i:0;i:8569;}i:8554;a:1:{i:0;i:8570;}i:8555;a:1:{i:0;i:8571;}i:8556;a:1:{i:0;i:8572;}i:8557;a:1:{i:0;i:8573;}i:8558;a:1:{i:0;i:8574;}i:8559;a:1:{i:0;i:8575;}i:9398;a:1:{i:0;i:9424;}i:9399;a:1:{i:0;i:9425;}i:9400;a:1:{i:0;i:9426;}i:9401;a:1:{i:0;i:9427;}i:9402;a:1:{i:0;i:9428;}i:9403;a:1:{i:0;i:9429;}i:9404;a:1:{i:0;i:9430;}i:9405;a:1:{i:0;i:9431;}i:9406;a:1:{i:0;i:9432;}i:9407;a:1:{i:0;i:9433;}i:9408;a:1:{i:0;i:9434;}i:9409;a:1:{i:0;i:9435;}i:9410;a:1:{i:0;i:9436;}i:9411;a:1:{i:0;i:9437;}i:9412;a:1:{i:0;i:9438;}i:9413;a:1:{i:0;i:9439;}i:9414;a:1:{i:0;i:9440;}i:9415;a:1:{i:0;i:9441;}i:9416;a:1:{i:0;i:9442;}i:9417;a:1:{i:0;i:9443;}i:9418;a:1:{i:0;i:9444;}i:9419;a:1:{i:0;i:9445;}i:9420;a:1:{i:0;i:9446;}i:9421;a:1:{i:0;i:9447;}i:9422;a:1:{i:0;i:9448;}i:9423;a:1:{i:0;i:9449;}i:13169;a:3:{i:0;i:104;i:1;i:112;i:2;i:97;}i:13171;a:2:{i:0;i:97;i:1;i:117;}i:13173;a:2:{i:0;i:111;i:1;i:118;}i:13184;a:2:{i:0;i:112;i:1;i:97;}i:13185;a:2:{i:0;i:110;i:1;i:97;}i:13186;a:2:{i:0;i:956;i:1;i:97;}i:13187;a:2:{i:0;i:109;i:1;i:97;}i:13188;a:2:{i:0;i:107;i:1;i:97;}i:13189;a:2:{i:0;i:107;i:1;i:98;}i:13190;a:2:{i:0;i:109;i:1;i:98;}i:13191;a:2:{i:0;i:103;i:1;i:98;}i:13194;a:2:{i:0;i:112;i:1;i:102;}i:13195;a:2:{i:0;i:110;i:1;i:102;}i:13196;a:2:{i:0;i:956;i:1;i:102;}i:13200;a:2:{i:0;i:104;i:1;i:122;}i:13201;a:3:{i:0;i:107;i:1;i:104;i:2;i:122;}i:13202;a:3:{i:0;i:109;i:1;i:104;i:2;i:122;}i:13203;a:3:{i:0;i:103;i:1;i:104;i:2;i:122;}i:13204;a:3:{i:0;i:116;i:1;i:104;i:2;i:122;}i:13225;a:2:{i:0;i:112;i:1;i:97;}i:13226;a:3:{i:0;i:107;i:1;i:112;i:2;i:97;}i:13227;a:3:{i:0;i:109;i:1;i:112;i:2;i:97;}i:13228;a:3:{i:0;i:103;i:1;i:112;i:2;i:97;}i:13236;a:2:{i:0;i:112;i:1;i:118;}i:13237;a:2:{i:0;i:110;i:1;i:118;}i:13238;a:2:{i:0;i:956;i:1;i:118;}i:13239;a:2:{i:0;i:109;i:1;i:118;}i:13240;a:2:{i:0;i:107;i:1;i:118;}i:13241;a:2:{i:0;i:109;i:1;i:118;}i:13242;a:2:{i:0;i:112;i:1;i:119;}i:13243;a:2:{i:0;i:110;i:1;i:119;}i:13244;a:2:{i:0;i:956;i:1;i:119;}i:13245;a:2:{i:0;i:109;i:1;i:119;}i:13246;a:2:{i:0;i:107;i:1;i:119;}i:13247;a:2:{i:0;i:109;i:1;i:119;}i:13248;a:2:{i:0;i:107;i:1;i:969;}i:13249;a:2:{i:0;i:109;i:1;i:969;}i:13251;a:2:{i:0;i:98;i:1;i:113;}i:13254;a:4:{i:0;i:99;i:1;i:8725;i:2;i:107;i:3;i:103;}i:13255;a:3:{i:0;i:99;i:1;i:111;i:2;i:46;}i:13256;a:2:{i:0;i:100;i:1;i:98;}i:13257;a:2:{i:0;i:103;i:1;i:121;}i:13259;a:2:{i:0;i:104;i:1;i:112;}i:13261;a:2:{i:0;i:107;i:1;i:107;}i:13262;a:2:{i:0;i:107;i:1;i:109;}i:13271;a:2:{i:0;i:112;i:1;i:104;}i:13273;a:3:{i:0;i:112;i:1;i:112;i:2;i:109;}i:13274;a:2:{i:0;i:112;i:1;i:114;}i:13276;a:2:{i:0;i:115;i:1;i:118;}i:13277;a:2:{i:0;i:119;i:1;i:98;}i:64256;a:2:{i:0;i:102;i:1;i:102;}i:64257;a:2:{i:0;i:102;i:1;i:105;}i:64258;a:2:{i:0;i:102;i:1;i:108;}i:64259;a:3:{i:0;i:102;i:1;i:102;i:2;i:105;}i:64260;a:3:{i:0;i:102;i:1;i:102;i:2;i:108;}i:64261;a:2:{i:0;i:115;i:1;i:116;}i:64262;a:2:{i:0;i:115;i:1;i:116;}i:64275;a:2:{i:0;i:1396;i:1;i:1398;}i:64276;a:2:{i:0;i:1396;i:1;i:1381;}i:64277;a:2:{i:0;i:1396;i:1;i:1387;}i:64278;a:2:{i:0;i:1406;i:1;i:1398;}i:64279;a:2:{i:0;i:1396;i:1;i:1389;}i:65313;a:1:{i:0;i:65345;}i:65314;a:1:{i:0;i:65346;}i:65315;a:1:{i:0;i:65347;}i:65316;a:1:{i:0;i:65348;}i:65317;a:1:{i:0;i:65349;}i:65318;a:1:{i:0;i:65350;}i:65319;a:1:{i:0;i:65351;}i:65320;a:1:{i:0;i:65352;}i:65321;a:1:{i:0;i:65353;}i:65322;a:1:{i:0;i:65354;}i:65323;a:1:{i:0;i:65355;}i:65324;a:1:{i:0;i:65356;}i:65325;a:1:{i:0;i:65357;}i:65326;a:1:{i:0;i:65358;}i:65327;a:1:{i:0;i:65359;}i:65328;a:1:{i:0;i:65360;}i:65329;a:1:{i:0;i:65361;}i:65330;a:1:{i:0;i:65362;}i:65331;a:1:{i:0;i:65363;}i:65332;a:1:{i:0;i:65364;}i:65333;a:1:{i:0;i:65365;}i:65334;a:1:{i:0;i:65366;}i:65335;a:1:{i:0;i:65367;}i:65336;a:1:{i:0;i:65368;}i:65337;a:1:{i:0;i:65369;}i:65338;a:1:{i:0;i:65370;}i:66560;a:1:{i:0;i:66600;}i:66561;a:1:{i:0;i:66601;}i:66562;a:1:{i:0;i:66602;}i:66563;a:1:{i:0;i:66603;}i:66564;a:1:{i:0;i:66604;}i:66565;a:1:{i:0;i:66605;}i:66566;a:1:{i:0;i:66606;}i:66567;a:1:{i:0;i:66607;}i:66568;a:1:{i:0;i:66608;}i:66569;a:1:{i:0;i:66609;}i:66570;a:1:{i:0;i:66610;}i:66571;a:1:{i:0;i:66611;}i:66572;a:1:{i:0;i:66612;}i:66573;a:1:{i:0;i:66613;}i:66574;a:1:{i:0;i:66614;}i:66575;a:1:{i:0;i:66615;}i:66576;a:1:{i:0;i:66616;}i:66577;a:1:{i:0;i:66617;}i:66578;a:1:{i:0;i:66618;}i:66579;a:1:{i:0;i:66619;}i:66580;a:1:{i:0;i:66620;}i:66581;a:1:{i:0;i:66621;}i:66582;a:1:{i:0;i:66622;}i:66583;a:1:{i:0;i:66623;}i:66584;a:1:{i:0;i:66624;}i:66585;a:1:{i:0;i:66625;}i:66586;a:1:{i:0;i:66626;}i:66587;a:1:{i:0;i:66627;}i:66588;a:1:{i:0;i:66628;}i:66589;a:1:{i:0;i:66629;}i:66590;a:1:{i:0;i:66630;}i:66591;a:1:{i:0;i:66631;}i:66592;a:1:{i:0;i:66632;}i:66593;a:1:{i:0;i:66633;}i:66594;a:1:{i:0;i:66634;}i:66595;a:1:{i:0;i:66635;}i:66596;a:1:{i:0;i:66636;}i:66597;a:1:{i:0;i:66637;}i:119808;a:1:{i:0;i:97;}i:119809;a:1:{i:0;i:98;}i:119810;a:1:{i:0;i:99;}i:119811;a:1:{i:0;i:100;}i:119812;a:1:{i:0;i:101;}i:119813;a:1:{i:0;i:102;}i:119814;a:1:{i:0;i:103;}i:119815;a:1:{i:0;i:104;}i:119816;a:1:{i:0;i:105;}i:119817;a:1:{i:0;i:106;}i:119818;a:1:{i:0;i:107;}i:119819;a:1:{i:0;i:108;}i:119820;a:1:{i:0;i:109;}i:119821;a:1:{i:0;i:110;}i:119822;a:1:{i:0;i:111;}i:119823;a:1:{i:0;i:112;}i:119824;a:1:{i:0;i:113;}i:119825;a:1:{i:0;i:114;}i:119826;a:1:{i:0;i:115;}i:119827;a:1:{i:0;i:116;}i:119828;a:1:{i:0;i:117;}i:119829;a:1:{i:0;i:118;}i:119830;a:1:{i:0;i:119;}i:119831;a:1:{i:0;i:120;}i:119832;a:1:{i:0;i:121;}i:119833;a:1:{i:0;i:122;}i:119860;a:1:{i:0;i:97;}i:119861;a:1:{i:0;i:98;}i:119862;a:1:{i:0;i:99;}i:119863;a:1:{i:0;i:100;}i:119864;a:1:{i:0;i:101;}i:119865;a:1:{i:0;i:102;}i:119866;a:1:{i:0;i:103;}i:119867;a:1:{i:0;i:104;}i:119868;a:1:{i:0;i:105;}i:119869;a:1:{i:0;i:106;}i:119870;a:1:{i:0;i:107;}i:119871;a:1:{i:0;i:108;}i:119872;a:1:{i:0;i:109;}i:119873;a:1:{i:0;i:110;}i:119874;a:1:{i:0;i:111;}i:119875;a:1:{i:0;i:112;}i:119876;a:1:{i:0;i:113;}i:119877;a:1:{i:0;i:114;}i:119878;a:1:{i:0;i:115;}i:119879;a:1:{i:0;i:116;}i:119880;a:1:{i:0;i:117;}i:119881;a:1:{i:0;i:118;}i:119882;a:1:{i:0;i:119;}i:119883;a:1:{i:0;i:120;}i:119884;a:1:{i:0;i:121;}i:119885;a:1:{i:0;i:122;}i:119912;a:1:{i:0;i:97;}i:119913;a:1:{i:0;i:98;}i:119914;a:1:{i:0;i:99;}i:119915;a:1:{i:0;i:100;}i:119916;a:1:{i:0;i:101;}i:119917;a:1:{i:0;i:102;}i:119918;a:1:{i:0;i:103;}i:119919;a:1:{i:0;i:104;}i:119920;a:1:{i:0;i:105;}i:119921;a:1:{i:0;i:106;}i:119922;a:1:{i:0;i:107;}i:119923;a:1:{i:0;i:108;}i:119924;a:1:{i:0;i:109;}i:119925;a:1:{i:0;i:110;}i:119926;a:1:{i:0;i:111;}i:119927;a:1:{i:0;i:112;}i:119928;a:1:{i:0;i:113;}i:119929;a:1:{i:0;i:114;}i:119930;a:1:{i:0;i:115;}i:119931;a:1:{i:0;i:116;}i:119932;a:1:{i:0;i:117;}i:119933;a:1:{i:0;i:118;}i:119934;a:1:{i:0;i:119;}i:119935;a:1:{i:0;i:120;}i:119936;a:1:{i:0;i:121;}i:119937;a:1:{i:0;i:122;}i:119964;a:1:{i:0;i:97;}i:119966;a:1:{i:0;i:99;}i:119967;a:1:{i:0;i:100;}i:119970;a:1:{i:0;i:103;}i:119973;a:1:{i:0;i:106;}i:119974;a:1:{i:0;i:107;}i:119977;a:1:{i:0;i:110;}i:119978;a:1:{i:0;i:111;}i:119979;a:1:{i:0;i:112;}i:119980;a:1:{i:0;i:113;}i:119982;a:1:{i:0;i:115;}i:119983;a:1:{i:0;i:116;}i:119984;a:1:{i:0;i:117;}i:119985;a:1:{i:0;i:118;}i:119986;a:1:{i:0;i:119;}i:119987;a:1:{i:0;i:120;}i:119988;a:1:{i:0;i:121;}i:119989;a:1:{i:0;i:122;}i:120016;a:1:{i:0;i:97;}i:120017;a:1:{i:0;i:98;}i:120018;a:1:{i:0;i:99;}i:120019;a:1:{i:0;i:100;}i:120020;a:1:{i:0;i:101;}i:120021;a:1:{i:0;i:102;}i:120022;a:1:{i:0;i:103;}i:120023;a:1:{i:0;i:104;}i:120024;a:1:{i:0;i:105;}i:120025;a:1:{i:0;i:106;}i:120026;a:1:{i:0;i:107;}i:120027;a:1:{i:0;i:108;}i:120028;a:1:{i:0;i:109;}i:120029;a:1:{i:0;i:110;}i:120030;a:1:{i:0;i:111;}i:120031;a:1:{i:0;i:112;}i:120032;a:1:{i:0;i:113;}i:120033;a:1:{i:0;i:114;}i:120034;a:1:{i:0;i:115;}i:120035;a:1:{i:0;i:116;}i:120036;a:1:{i:0;i:117;}i:120037;a:1:{i:0;i:118;}i:120038;a:1:{i:0;i:119;}i:120039;a:1:{i:0;i:120;}i:120040;a:1:{i:0;i:121;}i:120041;a:1:{i:0;i:122;}i:120068;a:1:{i:0;i:97;}i:120069;a:1:{i:0;i:98;}i:120071;a:1:{i:0;i:100;}i:120072;a:1:{i:0;i:101;}i:120073;a:1:{i:0;i:102;}i:120074;a:1:{i:0;i:103;}i:120077;a:1:{i:0;i:106;}i:120078;a:1:{i:0;i:107;}i:120079;a:1:{i:0;i:108;}i:120080;a:1:{i:0;i:109;}i:120081;a:1:{i:0;i:110;}i:120082;a:1:{i:0;i:111;}i:120083;a:1:{i:0;i:112;}i:120084;a:1:{i:0;i:113;}i:120086;a:1:{i:0;i:115;}i:120087;a:1:{i:0;i:116;}i:120088;a:1:{i:0;i:117;}i:120089;a:1:{i:0;i:118;}i:120090;a:1:{i:0;i:119;}i:120091;a:1:{i:0;i:120;}i:120092;a:1:{i:0;i:121;}i:120120;a:1:{i:0;i:97;}i:120121;a:1:{i:0;i:98;}i:120123;a:1:{i:0;i:100;}i:120124;a:1:{i:0;i:101;}i:120125;a:1:{i:0;i:102;}i:120126;a:1:{i:0;i:103;}i:120128;a:1:{i:0;i:105;}i:120129;a:1:{i:0;i:106;}i:120130;a:1:{i:0;i:107;}i:120131;a:1:{i:0;i:108;}i:120132;a:1:{i:0;i:109;}i:120134;a:1:{i:0;i:111;}i:120138;a:1:{i:0;i:115;}i:120139;a:1:{i:0;i:116;}i:120140;a:1:{i:0;i:117;}i:120141;a:1:{i:0;i:118;}i:120142;a:1:{i:0;i:119;}i:120143;a:1:{i:0;i:120;}i:120144;a:1:{i:0;i:121;}i:120172;a:1:{i:0;i:97;}i:120173;a:1:{i:0;i:98;}i:120174;a:1:{i:0;i:99;}i:120175;a:1:{i:0;i:100;}i:120176;a:1:{i:0;i:101;}i:120177;a:1:{i:0;i:102;}i:120178;a:1:{i:0;i:103;}i:120179;a:1:{i:0;i:104;}i:120180;a:1:{i:0;i:105;}i:120181;a:1:{i:0;i:106;}i:120182;a:1:{i:0;i:107;}i:120183;a:1:{i:0;i:108;}i:120184;a:1:{i:0;i:109;}i:120185;a:1:{i:0;i:110;}i:120186;a:1:{i:0;i:111;}i:120187;a:1:{i:0;i:112;}i:120188;a:1:{i:0;i:113;}i:120189;a:1:{i:0;i:114;}i:120190;a:1:{i:0;i:115;}i:120191;a:1:{i:0;i:116;}i:120192;a:1:{i:0;i:117;}i:120193;a:1:{i:0;i:118;}i:120194;a:1:{i:0;i:119;}i:120195;a:1:{i:0;i:120;}i:120196;a:1:{i:0;i:121;}i:120197;a:1:{i:0;i:122;}i:120224;a:1:{i:0;i:97;}i:120225;a:1:{i:0;i:98;}i:120226;a:1:{i:0;i:99;}i:120227;a:1:{i:0;i:100;}i:120228;a:1:{i:0;i:101;}i:120229;a:1:{i:0;i:102;}i:120230;a:1:{i:0;i:103;}i:120231;a:1:{i:0;i:104;}i:120232;a:1:{i:0;i:105;}i:120233;a:1:{i:0;i:106;}i:120234;a:1:{i:0;i:107;}i:120235;a:1:{i:0;i:108;}i:120236;a:1:{i:0;i:109;}i:120237;a:1:{i:0;i:110;}i:120238;a:1:{i:0;i:111;}i:120239;a:1:{i:0;i:112;}i:120240;a:1:{i:0;i:113;}i:120241;a:1:{i:0;i:114;}i:120242;a:1:{i:0;i:115;}i:120243;a:1:{i:0;i:116;}i:120244;a:1:{i:0;i:117;}i:120245;a:1:{i:0;i:118;}i:120246;a:1:{i:0;i:119;}i:120247;a:1:{i:0;i:120;}i:120248;a:1:{i:0;i:121;}i:120249;a:1:{i:0;i:122;}i:120276;a:1:{i:0;i:97;}i:120277;a:1:{i:0;i:98;}i:120278;a:1:{i:0;i:99;}i:120279;a:1:{i:0;i:100;}i:120280;a:1:{i:0;i:101;}i:120281;a:1:{i:0;i:102;}i:120282;a:1:{i:0;i:103;}i:120283;a:1:{i:0;i:104;}i:120284;a:1:{i:0;i:105;}i:120285;a:1:{i:0;i:106;}i:120286;a:1:{i:0;i:107;}i:120287;a:1:{i:0;i:108;}i:120288;a:1:{i:0;i:109;}i:120289;a:1:{i:0;i:110;}i:120290;a:1:{i:0;i:111;}i:120291;a:1:{i:0;i:112;}i:120292;a:1:{i:0;i:113;}i:120293;a:1:{i:0;i:114;}i:120294;a:1:{i:0;i:115;}i:120295;a:1:{i:0;i:116;}i:120296;a:1:{i:0;i:117;}i:120297;a:1:{i:0;i:118;}i:120298;a:1:{i:0;i:119;}i:120299;a:1:{i:0;i:120;}i:120300;a:1:{i:0;i:121;}i:120301;a:1:{i:0;i:122;}i:120328;a:1:{i:0;i:97;}i:120329;a:1:{i:0;i:98;}i:120330;a:1:{i:0;i:99;}i:120331;a:1:{i:0;i:100;}i:120332;a:1:{i:0;i:101;}i:120333;a:1:{i:0;i:102;}i:120334;a:1:{i:0;i:103;}i:120335;a:1:{i:0;i:104;}i:120336;a:1:{i:0;i:105;}i:120337;a:1:{i:0;i:106;}i:120338;a:1:{i:0;i:107;}i:120339;a:1:{i:0;i:108;}i:120340;a:1:{i:0;i:109;}i:120341;a:1:{i:0;i:110;}i:120342;a:1:{i:0;i:111;}i:120343;a:1:{i:0;i:112;}i:120344;a:1:{i:0;i:113;}i:120345;a:1:{i:0;i:114;}i:120346;a:1:{i:0;i:115;}i:120347;a:1:{i:0;i:116;}i:120348;a:1:{i:0;i:117;}i:120349;a:1:{i:0;i:118;}i:120350;a:1:{i:0;i:119;}i:120351;a:1:{i:0;i:120;}i:120352;a:1:{i:0;i:121;}i:120353;a:1:{i:0;i:122;}i:120380;a:1:{i:0;i:97;}i:120381;a:1:{i:0;i:98;}i:120382;a:1:{i:0;i:99;}i:120383;a:1:{i:0;i:100;}i:120384;a:1:{i:0;i:101;}i:120385;a:1:{i:0;i:102;}i:120386;a:1:{i:0;i:103;}i:120387;a:1:{i:0;i:104;}i:120388;a:1:{i:0;i:105;}i:120389;a:1:{i:0;i:106;}i:120390;a:1:{i:0;i:107;}i:120391;a:1:{i:0;i:108;}i:120392;a:1:{i:0;i:109;}i:120393;a:1:{i:0;i:110;}i:120394;a:1:{i:0;i:111;}i:120395;a:1:{i:0;i:112;}i:120396;a:1:{i:0;i:113;}i:120397;a:1:{i:0;i:114;}i:120398;a:1:{i:0;i:115;}i:120399;a:1:{i:0;i:116;}i:120400;a:1:{i:0;i:117;}i:120401;a:1:{i:0;i:118;}i:120402;a:1:{i:0;i:119;}i:120403;a:1:{i:0;i:120;}i:120404;a:1:{i:0;i:121;}i:120405;a:1:{i:0;i:122;}i:120432;a:1:{i:0;i:97;}i:120433;a:1:{i:0;i:98;}i:120434;a:1:{i:0;i:99;}i:120435;a:1:{i:0;i:100;}i:120436;a:1:{i:0;i:101;}i:120437;a:1:{i:0;i:102;}i:120438;a:1:{i:0;i:103;}i:120439;a:1:{i:0;i:104;}i:120440;a:1:{i:0;i:105;}i:120441;a:1:{i:0;i:106;}i:120442;a:1:{i:0;i:107;}i:120443;a:1:{i:0;i:108;}i:120444;a:1:{i:0;i:109;}i:120445;a:1:{i:0;i:110;}i:120446;a:1:{i:0;i:111;}i:120447;a:1:{i:0;i:112;}i:120448;a:1:{i:0;i:113;}i:120449;a:1:{i:0;i:114;}i:120450;a:1:{i:0;i:115;}i:120451;a:1:{i:0;i:116;}i:120452;a:1:{i:0;i:117;}i:120453;a:1:{i:0;i:118;}i:120454;a:1:{i:0;i:119;}i:120455;a:1:{i:0;i:120;}i:120456;a:1:{i:0;i:121;}i:120457;a:1:{i:0;i:122;}i:120488;a:1:{i:0;i:945;}i:120489;a:1:{i:0;i:946;}i:120490;a:1:{i:0;i:947;}i:120491;a:1:{i:0;i:948;}i:120492;a:1:{i:0;i:949;}i:120493;a:1:{i:0;i:950;}i:120494;a:1:{i:0;i:951;}i:120495;a:1:{i:0;i:952;}i:120496;a:1:{i:0;i:953;}i:120497;a:1:{i:0;i:954;}i:120498;a:1:{i:0;i:955;}i:120499;a:1:{i:0;i:956;}i:120500;a:1:{i:0;i:957;}i:120501;a:1:{i:0;i:958;}i:120502;a:1:{i:0;i:959;}i:120503;a:1:{i:0;i:960;}i:120504;a:1:{i:0;i:961;}i:120505;a:1:{i:0;i:952;}i:120506;a:1:{i:0;i:963;}i:120507;a:1:{i:0;i:964;}i:120508;a:1:{i:0;i:965;}i:120509;a:1:{i:0;i:966;}i:120510;a:1:{i:0;i:967;}i:120511;a:1:{i:0;i:968;}i:120512;a:1:{i:0;i:969;}i:120531;a:1:{i:0;i:963;}i:120546;a:1:{i:0;i:945;}i:120547;a:1:{i:0;i:946;}i:120548;a:1:{i:0;i:947;}i:120549;a:1:{i:0;i:948;}i:120550;a:1:{i:0;i:949;}i:120551;a:1:{i:0;i:950;}i:120552;a:1:{i:0;i:951;}i:120553;a:1:{i:0;i:952;}i:120554;a:1:{i:0;i:953;}i:120555;a:1:{i:0;i:954;}i:120556;a:1:{i:0;i:955;}i:120557;a:1:{i:0;i:956;}i:120558;a:1:{i:0;i:957;}i:120559;a:1:{i:0;i:958;}i:120560;a:1:{i:0;i:959;}i:120561;a:1:{i:0;i:960;}i:120562;a:1:{i:0;i:961;}i:120563;a:1:{i:0;i:952;}i:120564;a:1:{i:0;i:963;}i:120565;a:1:{i:0;i:964;}i:120566;a:1:{i:0;i:965;}i:120567;a:1:{i:0;i:966;}i:120568;a:1:{i:0;i:967;}i:120569;a:1:{i:0;i:968;}i:120570;a:1:{i:0;i:969;}i:120589;a:1:{i:0;i:963;}i:120604;a:1:{i:0;i:945;}i:120605;a:1:{i:0;i:946;}i:120606;a:1:{i:0;i:947;}i:120607;a:1:{i:0;i:948;}i:120608;a:1:{i:0;i:949;}i:120609;a:1:{i:0;i:950;}i:120610;a:1:{i:0;i:951;}i:120611;a:1:{i:0;i:952;}i:120612;a:1:{i:0;i:953;}i:120613;a:1:{i:0;i:954;}i:120614;a:1:{i:0;i:955;}i:120615;a:1:{i:0;i:956;}i:120616;a:1:{i:0;i:957;}i:120617;a:1:{i:0;i:958;}i:120618;a:1:{i:0;i:959;}i:120619;a:1:{i:0;i:960;}i:120620;a:1:{i:0;i:961;}i:120621;a:1:{i:0;i:952;}i:120622;a:1:{i:0;i:963;}i:120623;a:1:{i:0;i:964;}i:120624;a:1:{i:0;i:965;}i:120625;a:1:{i:0;i:966;}i:120626;a:1:{i:0;i:967;}i:120627;a:1:{i:0;i:968;}i:120628;a:1:{i:0;i:969;}i:120647;a:1:{i:0;i:963;}i:120662;a:1:{i:0;i:945;}i:120663;a:1:{i:0;i:946;}i:120664;a:1:{i:0;i:947;}i:120665;a:1:{i:0;i:948;}i:120666;a:1:{i:0;i:949;}i:120667;a:1:{i:0;i:950;}i:120668;a:1:{i:0;i:951;}i:120669;a:1:{i:0;i:952;}i:120670;a:1:{i:0;i:953;}i:120671;a:1:{i:0;i:954;}i:120672;a:1:{i:0;i:955;}i:120673;a:1:{i:0;i:956;}i:120674;a:1:{i:0;i:957;}i:120675;a:1:{i:0;i:958;}i:120676;a:1:{i:0;i:959;}i:120677;a:1:{i:0;i:960;}i:120678;a:1:{i:0;i:961;}i:120679;a:1:{i:0;i:952;}i:120680;a:1:{i:0;i:963;}i:120681;a:1:{i:0;i:964;}i:120682;a:1:{i:0;i:965;}i:120683;a:1:{i:0;i:966;}i:120684;a:1:{i:0;i:967;}i:120685;a:1:{i:0;i:968;}i:120686;a:1:{i:0;i:969;}i:120705;a:1:{i:0;i:963;}i:120720;a:1:{i:0;i:945;}i:120721;a:1:{i:0;i:946;}i:120722;a:1:{i:0;i:947;}i:120723;a:1:{i:0;i:948;}i:120724;a:1:{i:0;i:949;}i:120725;a:1:{i:0;i:950;}i:120726;a:1:{i:0;i:951;}i:120727;a:1:{i:0;i:952;}i:120728;a:1:{i:0;i:953;}i:120729;a:1:{i:0;i:954;}i:120730;a:1:{i:0;i:955;}i:120731;a:1:{i:0;i:956;}i:120732;a:1:{i:0;i:957;}i:120733;a:1:{i:0;i:958;}i:120734;a:1:{i:0;i:959;}i:120735;a:1:{i:0;i:960;}i:120736;a:1:{i:0;i:961;}i:120737;a:1:{i:0;i:952;}i:120738;a:1:{i:0;i:963;}i:120739;a:1:{i:0;i:964;}i:120740;a:1:{i:0;i:965;}i:120741;a:1:{i:0;i:966;}i:120742;a:1:{i:0;i:967;}i:120743;a:1:{i:0;i:968;}i:120744;a:1:{i:0;i:969;}i:120763;a:1:{i:0;i:963;}i:1017;a:1:{i:0;i:963;}i:7468;a:1:{i:0;i:97;}i:7469;a:1:{i:0;i:230;}i:7470;a:1:{i:0;i:98;}i:7472;a:1:{i:0;i:100;}i:7473;a:1:{i:0;i:101;}i:7474;a:1:{i:0;i:477;}i:7475;a:1:{i:0;i:103;}i:7476;a:1:{i:0;i:104;}i:7477;a:1:{i:0;i:105;}i:7478;a:1:{i:0;i:106;}i:7479;a:1:{i:0;i:107;}i:7480;a:1:{i:0;i:108;}i:7481;a:1:{i:0;i:109;}i:7482;a:1:{i:0;i:110;}i:7484;a:1:{i:0;i:111;}i:7485;a:1:{i:0;i:547;}i:7486;a:1:{i:0;i:112;}i:7487;a:1:{i:0;i:114;}i:7488;a:1:{i:0;i:116;}i:7489;a:1:{i:0;i:117;}i:7490;a:1:{i:0;i:119;}i:8507;a:3:{i:0;i:102;i:1;i:97;i:2;i:120;}i:12880;a:3:{i:0;i:112;i:1;i:116;i:2;i:101;}i:13004;a:2:{i:0;i:104;i:1;i:103;}i:13006;a:2:{i:0;i:101;i:1;i:118;}i:13007;a:3:{i:0;i:108;i:1;i:116;i:2;i:100;}i:13178;a:2:{i:0;i:105;i:1;i:117;}i:13278;a:3:{i:0;i:118;i:1;i:8725;i:2;i:109;}i:13279;a:3:{i:0;i:97;i:1;i:8725;i:2;i:109;}}s:12:"norm_combcls";a:341:{i:820;i:1;i:821;i:1;i:822;i:1;i:823;i:1;i:824;i:1;i:2364;i:7;i:2492;i:7;i:2620;i:7;i:2748;i:7;i:2876;i:7;i:3260;i:7;i:4151;i:7;i:12441;i:8;i:12442;i:8;i:2381;i:9;i:2509;i:9;i:2637;i:9;i:2765;i:9;i:2893;i:9;i:3021;i:9;i:3149;i:9;i:3277;i:9;i:3405;i:9;i:3530;i:9;i:3642;i:9;i:3972;i:9;i:4153;i:9;i:5908;i:9;i:5940;i:9;i:6098;i:9;i:1456;i:10;i:1457;i:11;i:1458;i:12;i:1459;i:13;i:1460;i:14;i:1461;i:15;i:1462;i:16;i:1463;i:17;i:1464;i:18;i:1465;i:19;i:1467;i:20;i:1468;i:21;i:1469;i:22;i:1471;i:23;i:1473;i:24;i:1474;i:25;i:64286;i:26;i:1611;i:27;i:1612;i:28;i:1613;i:29;i:1614;i:30;i:1615;i:31;i:1616;i:32;i:1617;i:33;i:1618;i:34;i:1648;i:35;i:1809;i:36;i:3157;i:84;i:3158;i:91;i:3640;i:103;i:3641;i:103;i:3656;i:107;i:3657;i:107;i:3658;i:107;i:3659;i:107;i:3768;i:118;i:3769;i:118;i:3784;i:122;i:3785;i:122;i:3786;i:122;i:3787;i:122;i:3953;i:129;i:3954;i:130;i:3962;i:130;i:3963;i:130;i:3964;i:130;i:3965;i:130;i:3968;i:130;i:3956;i:132;i:801;i:202;i:802;i:202;i:807;i:202;i:808;i:202;i:795;i:216;i:3897;i:216;i:119141;i:216;i:119142;i:216;i:119150;i:216;i:119151;i:216;i:119152;i:216;i:119153;i:216;i:119154;i:216;i:12330;i:218;i:790;i:220;i:791;i:220;i:792;i:220;i:793;i:220;i:796;i:220;i:797;i:220;i:798;i:220;i:799;i:220;i:800;i:220;i:803;i:220;i:804;i:220;i:805;i:220;i:806;i:220;i:809;i:220;i:810;i:220;i:811;i:220;i:812;i:220;i:813;i:220;i:814;i:220;i:815;i:220;i:816;i:220;i:817;i:220;i:818;i:220;i:819;i:220;i:825;i:220;i:826;i:220;i:827;i:220;i:828;i:220;i:839;i:220;i:840;i:220;i:841;i:220;i:845;i:220;i:846;i:220;i:851;i:220;i:852;i:220;i:853;i:220;i:854;i:220;i:1425;i:220;i:1430;i:220;i:1435;i:220;i:1443;i:220;i:1444;i:220;i:1445;i:220;i:1446;i:220;i:1447;i:220;i:1450;i:220;i:1621;i:220;i:1622;i:220;i:1763;i:220;i:1770;i:220;i:1773;i:220;i:1841;i:220;i:1844;i:220;i:1847;i:220;i:1848;i:220;i:1849;i:220;i:1851;i:220;i:1852;i:220;i:1854;i:220;i:1858;i:220;i:1860;i:220;i:1862;i:220;i:1864;i:220;i:2386;i:220;i:3864;i:220;i:3865;i:220;i:3893;i:220;i:3895;i:220;i:4038;i:220;i:6459;i:220;i:8424;i:220;i:119163;i:220;i:119164;i:220;i:119165;i:220;i:119166;i:220;i:119167;i:220;i:119168;i:220;i:119169;i:220;i:119170;i:220;i:119178;i:220;i:119179;i:220;i:1434;i:222;i:1453;i:222;i:6441;i:222;i:12333;i:222;i:12334;i:224;i:12335;i:224;i:119149;i:226;i:1454;i:228;i:6313;i:228;i:12331;i:228;i:768;i:230;i:769;i:230;i:770;i:230;i:771;i:230;i:772;i:230;i:773;i:230;i:774;i:230;i:775;i:230;i:776;i:230;i:777;i:230;i:778;i:230;i:779;i:230;i:780;i:230;i:781;i:230;i:782;i:230;i:783;i:230;i:784;i:230;i:785;i:230;i:786;i:230;i:787;i:230;i:788;i:230;i:829;i:230;i:830;i:230;i:831;i:230;i:832;i:230;i:833;i:230;i:834;i:230;i:835;i:230;i:836;i:230;i:838;i:230;i:842;i:230;i:843;i:230;i:844;i:230;i:848;i:230;i:849;i:230;i:850;i:230;i:855;i:230;i:867;i:230;i:868;i:230;i:869;i:230;i:870;i:230;i:871;i:230;i:872;i:230;i:873;i:230;i:874;i:230;i:875;i:230;i:876;i:230;i:877;i:230;i:878;i:230;i:879;i:230;i:1155;i:230;i:1156;i:230;i:1157;i:230;i:1158;i:230;i:1426;i:230;i:1427;i:230;i:1428;i:230;i:1429;i:230;i:1431;i:230;i:1432;i:230;i:1433;i:230;i:1436;i:230;i:1437;i:230;i:1438;i:230;i:1439;i:230;i:1440;i:230;i:1441;i:230;i:1448;i:230;i:1449;i:230;i:1451;i:230;i:1452;i:230;i:1455;i:230;i:1476;i:230;i:1552;i:230;i:1553;i:230;i:1554;i:230;i:1555;i:230;i:1556;i:230;i:1557;i:230;i:1619;i:230;i:1620;i:230;i:1623;i:230;i:1624;i:230;i:1750;i:230;i:1751;i:230;i:1752;i:230;i:1753;i:230;i:1754;i:230;i:1755;i:230;i:1756;i:230;i:1759;i:230;i:1760;i:230;i:1761;i:230;i:1762;i:230;i:1764;i:230;i:1767;i:230;i:1768;i:230;i:1771;i:230;i:1772;i:230;i:1840;i:230;i:1842;i:230;i:1843;i:230;i:1845;i:230;i:1846;i:230;i:1850;i:230;i:1853;i:230;i:1855;i:230;i:1856;i:230;i:1857;i:230;i:1859;i:230;i:1861;i:230;i:1863;i:230;i:1865;i:230;i:1866;i:230;i:2385;i:230;i:2387;i:230;i:2388;i:230;i:3970;i:230;i:3971;i:230;i:3974;i:230;i:3975;i:230;i:5901;i:230;i:6458;i:230;i:8400;i:230;i:8401;i:230;i:8404;i:230;i:8405;i:230;i:8406;i:230;i:8407;i:230;i:8411;i:230;i:8412;i:230;i:8417;i:230;i:8423;i:230;i:8425;i:230;i:65056;i:230;i:65057;i:230;i:65058;i:230;i:65059;i:230;i:119173;i:230;i:119174;i:230;i:119175;i:230;i:119177;i:230;i:119176;i:230;i:119210;i:230;i:119211;i:230;i:119212;i:230;i:119213;i:230;i:789;i:232;i:794;i:232;i:12332;i:232;i:863;i:233;i:866;i:233;i:861;i:234;i:862;i:234;i:864;i:234;i:865;i:234;i:837;i:240;}} \ No newline at end of file diff --git a/library/simplepie/simplepie.inc b/library/simplepie/simplepie.inc deleted file mode 100644 index 96ad06678e..0000000000 --- a/library/simplepie/simplepie.inc +++ /dev/null @@ -1,15150 +0,0 @@ -' . SIMPLEPIE_NAME . ''); - -/** - * No Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_NONE', 0); - -/** - * Feed Link Element Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); - -/** - * Local Feed Extension Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); - -/** - * Local Feed Body Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); - -/** - * Remote Feed Extension Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); - -/** - * Remote Feed Body Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); - -/** - * All Feed Autodiscovery - * @see SimplePie::set_autodiscovery_level() - */ -define('SIMPLEPIE_LOCATOR_ALL', 31); - -/** - * No known feed type - */ -define('SIMPLEPIE_TYPE_NONE', 0); - -/** - * RSS 0.90 - */ -define('SIMPLEPIE_TYPE_RSS_090', 1); - -/** - * RSS 0.91 (Netscape) - */ -define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); - -/** - * RSS 0.91 (Userland) - */ -define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); - -/** - * RSS 0.91 (both Netscape and Userland) - */ -define('SIMPLEPIE_TYPE_RSS_091', 6); - -/** - * RSS 0.92 - */ -define('SIMPLEPIE_TYPE_RSS_092', 8); - -/** - * RSS 0.93 - */ -define('SIMPLEPIE_TYPE_RSS_093', 16); - -/** - * RSS 0.94 - */ -define('SIMPLEPIE_TYPE_RSS_094', 32); - -/** - * RSS 1.0 - */ -define('SIMPLEPIE_TYPE_RSS_10', 64); - -/** - * RSS 2.0 - */ -define('SIMPLEPIE_TYPE_RSS_20', 128); - -/** - * RDF-based RSS - */ -define('SIMPLEPIE_TYPE_RSS_RDF', 65); - -/** - * Non-RDF-based RSS (truly intended as syndication format) - */ -define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); - -/** - * All RSS - */ -define('SIMPLEPIE_TYPE_RSS_ALL', 255); - -/** - * Atom 0.3 - */ -define('SIMPLEPIE_TYPE_ATOM_03', 256); - -/** - * Atom 1.0 - */ -define('SIMPLEPIE_TYPE_ATOM_10', 512); - -/** - * All Atom - */ -define('SIMPLEPIE_TYPE_ATOM_ALL', 768); - -/** - * All feed types - */ -define('SIMPLEPIE_TYPE_ALL', 1023); - -/** - * No construct - */ -define('SIMPLEPIE_CONSTRUCT_NONE', 0); - -/** - * Text construct - */ -define('SIMPLEPIE_CONSTRUCT_TEXT', 1); - -/** - * HTML construct - */ -define('SIMPLEPIE_CONSTRUCT_HTML', 2); - -/** - * XHTML construct - */ -define('SIMPLEPIE_CONSTRUCT_XHTML', 4); - -/** - * base64-encoded construct - */ -define('SIMPLEPIE_CONSTRUCT_BASE64', 8); - -/** - * IRI construct - */ -define('SIMPLEPIE_CONSTRUCT_IRI', 16); - -/** - * A construct that might be HTML - */ -define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); - -/** - * All constructs - */ -define('SIMPLEPIE_CONSTRUCT_ALL', 63); - -/** - * Don't change case - */ -define('SIMPLEPIE_SAME_CASE', 1); - -/** - * Change to lowercase - */ -define('SIMPLEPIE_LOWERCASE', 2); - -/** - * Change to uppercase - */ -define('SIMPLEPIE_UPPERCASE', 4); - -/** - * PCRE for HTML attributes - */ -define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); - -/** - * PCRE for XML attributes - */ -define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); - -/** - * XML Namespace - */ -define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); - -/** - * Atom 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); - -/** - * Atom 0.3 Namespace - */ -define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); - -/** - * RDF Namespace - */ -define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); - -/** - * RSS 0.90 Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); - -/** - * RSS 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); - -/** - * RSS 1.0 Content Module Namespace - */ -define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); - -/** - * RSS 2.0 Namespace - * (Stupid, I know, but I'm certain it will confuse people less with support.) - */ -define('SIMPLEPIE_NAMESPACE_RSS_20', ''); - -/** - * DC 1.0 Namespace - */ -define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); - -/** - * DC 1.1 Namespace - */ -define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); - -/** - * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace - */ -define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); - -/** - * GeoRSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); - -/** - * Media RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); - -/** - * Wrong Media RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); - -/** - * iTunes RSS Namespace - */ -define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); - -/** - * XHTML Namespace - */ -define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); - -/** - * IANA Link Relations Registry - */ -define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); - -/** - * Whether we're running on PHP5 - */ -define('SIMPLEPIE_PHP5', version_compare(PHP_VERSION, '5.0.0', '>=')); - -/** - * No file source - */ -define('SIMPLEPIE_FILE_SOURCE_NONE', 0); - -/** - * Remote file source - */ -define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); - -/** - * Local file source - */ -define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); - -/** - * fsockopen() file source - */ -define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); - -/** - * cURL file source - */ -define('SIMPLEPIE_FILE_SOURCE_CURL', 8); - -/** - * file_get_contents() file source - */ -define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); - -/** - * SimplePie - * - * @package SimplePie - */ -class SimplePie -{ - /** - * @var array Raw data - * @access private - */ - var $data = array(); - - /** - * @var mixed Error string - * @access private - */ - var $error; - - /** - * @var object Instance of SimplePie_Sanitize (or other class) - * @see SimplePie::set_sanitize_class() - * @access private - */ - var $sanitize; - - /** - * @var string SimplePie Useragent - * @see SimplePie::set_useragent() - * @access private - */ - var $useragent = SIMPLEPIE_USERAGENT; - - /** - * @var string Feed URL - * @see SimplePie::set_feed_url() - * @access private - */ - var $feed_url; - - /** - * @var object Instance of SimplePie_File to use as a feed - * @see SimplePie::set_file() - * @access private - */ - var $file; - - /** - * @var string Raw feed data - * @see SimplePie::set_raw_data() - * @access private - */ - var $raw_data; - - /** - * @var int Timeout for fetching remote files - * @see SimplePie::set_timeout() - * @access private - */ - var $timeout = 10; - - /** - * @var bool Forces fsockopen() to be used for remote files instead - * of cURL, even if a new enough version is installed - * @see SimplePie::force_fsockopen() - * @access private - */ - var $force_fsockopen = false; - - /** - * @var bool Force the given data/URL to be treated as a feed no matter what - * it appears like - * @see SimplePie::force_feed() - * @access private - */ - var $force_feed = false; - - /** - * @var bool Enable/Disable XML dump - * @see SimplePie::enable_xml_dump() - * @access private - */ - var $xml_dump = false; - - /** - * @var bool Enable/Disable Caching - * @see SimplePie::enable_cache() - * @access private - */ - var $cache = true; - - /** - * @var int Cache duration (in seconds) - * @see SimplePie::set_cache_duration() - * @access private - */ - var $cache_duration = 3600; - - /** - * @var int Auto-discovery cache duration (in seconds) - * @see SimplePie::set_autodiscovery_cache_duration() - * @access private - */ - var $autodiscovery_cache_duration = 604800; // 7 Days. - - /** - * @var string Cache location (relative to executing script) - * @see SimplePie::set_cache_location() - * @access private - */ - var $cache_location = './cache'; - - /** - * @var string Function that creates the cache filename - * @see SimplePie::set_cache_name_function() - * @access private - */ - var $cache_name_function = 'md5'; - - /** - * @var bool Reorder feed by date descending - * @see SimplePie::enable_order_by_date() - * @access private - */ - var $order_by_date = true; - - /** - * @var mixed Force input encoding to be set to the follow value - * (false, or anything type-cast to false, disables this feature) - * @see SimplePie::set_input_encoding() - * @access private - */ - var $input_encoding = false; - - /** - * @var int Feed Autodiscovery Level - * @see SimplePie::set_autodiscovery_level() - * @access private - */ - var $autodiscovery = SIMPLEPIE_LOCATOR_ALL; - - /** - * @var string Class used for caching feeds - * @see SimplePie::set_cache_class() - * @access private - */ - var $cache_class = 'SimplePie_Cache'; - - /** - * @var string Class used for locating feeds - * @see SimplePie::set_locator_class() - * @access private - */ - var $locator_class = 'SimplePie_Locator'; - - /** - * @var string Class used for parsing feeds - * @see SimplePie::set_parser_class() - * @access private - */ - var $parser_class = 'SimplePie_Parser'; - - /** - * @var string Class used for fetching feeds - * @see SimplePie::set_file_class() - * @access private - */ - var $file_class = 'SimplePie_File'; - - /** - * @var string Class used for items - * @see SimplePie::set_item_class() - * @access private - */ - var $item_class = 'SimplePie_Item'; - - /** - * @var string Class used for authors - * @see SimplePie::set_author_class() - * @access private - */ - var $author_class = 'SimplePie_Author'; - - /** - * @var string Class used for categories - * @see SimplePie::set_category_class() - * @access private - */ - var $category_class = 'SimplePie_Category'; - - /** - * @var string Class used for enclosures - * @see SimplePie::set_enclosures_class() - * @access private - */ - var $enclosure_class = 'SimplePie_Enclosure'; - - /** - * @var string Class used for Media RSS captions - * @see SimplePie::set_caption_class() - * @access private - */ - var $caption_class = 'SimplePie_Caption'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_copyright_class() - * @access private - */ - var $copyright_class = 'SimplePie_Copyright'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_credit_class() - * @access private - */ - var $credit_class = 'SimplePie_Credit'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_rating_class() - * @access private - */ - var $rating_class = 'SimplePie_Rating'; - - /** - * @var string Class used for Media RSS - * @see SimplePie::set_restriction_class() - * @access private - */ - var $restriction_class = 'SimplePie_Restriction'; - - /** - * @var string Class used for content-type sniffing - * @see SimplePie::set_content_type_sniffer_class() - * @access private - */ - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - /** - * @var string Class used for item sources. - * @see SimplePie::set_source_class() - * @access private - */ - var $source_class = 'SimplePie_Source'; - - /** - * @var mixed Set javascript query string parameter (false, or - * anything type-cast to false, disables this feature) - * @see SimplePie::set_javascript() - * @access private - */ - var $javascript = 'js'; - - /** - * @var int Maximum number of feeds to check with autodiscovery - * @see SimplePie::set_max_checked_feeds() - * @access private - */ - var $max_checked_feeds = 10; - - /** - * @var array All the feeds found during the autodiscovery process - * @see SimplePie::get_all_discovered_feeds() - * @access private - */ - var $all_discovered_feeds = array(); - - /** - * @var string Web-accessible path to the handler_favicon.php file. - * @see SimplePie::set_favicon_handler() - * @access private - */ - var $favicon_handler = ''; - - /** - * @var string Web-accessible path to the handler_image.php file. - * @see SimplePie::set_image_handler() - * @access private - */ - var $image_handler = ''; - - /** - * @var array Stores the URLs when multiple feeds are being initialized. - * @see SimplePie::set_feed_url() - * @access private - */ - var $multifeed_url = array(); - - /** - * @var array Stores SimplePie objects when multiple feeds initialized. - * @access private - */ - var $multifeed_objects = array(); - - /** - * @var array Stores the get_object_vars() array for use with multifeeds. - * @see SimplePie::set_feed_url() - * @access private - */ - var $config_settings = null; - - /** - * @var integer Stores the number of items to return per-feed with multifeeds. - * @see SimplePie::set_item_limit() - * @access private - */ - var $item_limit = 0; - - /** - * @var array Stores the default attributes to be stripped by strip_attributes(). - * @see SimplePie::strip_attributes() - * @access private - */ - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); - - /** - * @var array Stores the default tags to be stripped by strip_htmltags(). - * @see SimplePie::strip_htmltags() - * @access private - */ - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); - - /** - * The SimplePie class contains feed level data and options - * - * There are two ways that you can create a new SimplePie object. The first - * is by passing a feed URL as a parameter to the SimplePie constructor - * (as well as optionally setting the cache location and cache expiry). This - * will initialise the whole feed with all of the default settings, and you - * can begin accessing methods and properties immediately. - * - * The second way is to create the SimplePie object with no parameters - * at all. This will enable you to set configuration options. After setting - * them, you must initialise the feed using $feed->init(). At that point the - * object's methods and properties will be available to you. This format is - * what is used throughout this documentation. - * - * @access public - * @since 1.0 Preview Release - * @param string $feed_url This is the URL you want to parse. - * @param string $cache_location This is where you want the cache to be stored. - * @param int $cache_duration This is the number of seconds that you want to store the cache file for. - */ - function SimplePie($feed_url = null, $cache_location = null, $cache_duration = null) - { - // Other objects, instances created here so we can set options on them - $this->sanitize = new SimplePie_Sanitize; - - // Set options if they're passed to the constructor - if ($cache_location !== null) - { - $this->set_cache_location($cache_location); - } - - if ($cache_duration !== null) - { - $this->set_cache_duration($cache_duration); - } - - // Only init the script if we're passed a feed URL - if ($feed_url !== null) - { - $this->set_feed_url($feed_url); - $this->init(); - } - } - - /** - * Used for converting object to a string - */ - function __toString() - { - return md5(serialize($this->data)); - } - - /** - * Remove items that link back to this before destroying this object - */ - function __destruct() - { - if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) - { - if (!empty($this->data['items'])) - { - foreach ($this->data['items'] as $item) - { - $item->__destruct(); - } - unset($item, $this->data['items']); - } - if (!empty($this->data['ordered_items'])) - { - foreach ($this->data['ordered_items'] as $item) - { - $item->__destruct(); - } - unset($item, $this->data['ordered_items']); - } - } - } - - /** - * Force the given data/URL to be treated as a feed no matter what it - * appears like - * - * @access public - * @since 1.1 - * @param bool $enable Force the given data/URL to be treated as a feed - */ - function force_feed($enable = false) - { - $this->force_feed = (bool) $enable; - } - - /** - * This is the URL of the feed you want to parse. - * - * This allows you to enter the URL of the feed you want to parse, or the - * website you want to try to use auto-discovery on. This takes priority - * over any set raw data. - * - * You can set multiple feeds to mash together by passing an array instead - * of a string for the $url. Remember that with each additional feed comes - * additional processing and resources. - * - * @access public - * @since 1.0 Preview Release - * @param mixed $url This is the URL (or array of URLs) that you want to parse. - * @see SimplePie::set_raw_data() - */ - function set_feed_url($url) - { - if (is_array($url)) - { - $this->multifeed_url = array(); - foreach ($url as $value) - { - $this->multifeed_url[] = SimplePie_Misc::fix_protocol($value, 1); - } - } - else - { - $this->feed_url = SimplePie_Misc::fix_protocol($url, 1); - } - } - - /** - * Provides an instance of SimplePie_File to use as a feed - * - * @access public - * @param object &$file Instance of SimplePie_File (or subclass) - * @return bool True on success, false on failure - */ - function set_file(&$file) - { - if (is_a($file, 'SimplePie_File')) - { - $this->feed_url = $file->url; - $this->file =& $file; - return true; - } - return false; - } - - /** - * Allows you to use a string of RSS/Atom data instead of a remote feed. - * - * If you have a feed available as a string in PHP, you can tell SimplePie - * to parse that data string instead of a remote feed. Any set feed URL - * takes precedence. - * - * @access public - * @since 1.0 Beta 3 - * @param string $data RSS or Atom data as a string. - * @see SimplePie::set_feed_url() - */ - function set_raw_data($data) - { - $this->raw_data = $data; - } - - /** - * Allows you to override the default timeout for fetching remote feeds. - * - * This allows you to change the maximum time the feed's server to respond - * and send the feed back. - * - * @access public - * @since 1.0 Beta 3 - * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. - */ - function set_timeout($timeout = 10) - { - $this->timeout = (int) $timeout; - } - - /** - * Forces SimplePie to use fsockopen() instead of the preferred cURL - * functions. - * - * @access public - * @since 1.0 Beta 3 - * @param bool $enable Force fsockopen() to be used - */ - function force_fsockopen($enable = false) - { - $this->force_fsockopen = (bool) $enable; - } - - /** - * Outputs the raw XML content of the feed, after it has gone through - * SimplePie's filters. - * - * Used only for debugging, this function will output the XML content as - * text/xml. When SimplePie reads in a feed, it does a bit of cleaning up - * before trying to parse it. Many parts of the feed are re-written in - * memory, and in the end, you have a parsable feed. XML dump shows you the - * actual XML that SimplePie tries to parse, which may or may not be very - * different from the original feed. - * - * @access public - * @since 1.0 Preview Release - * @param bool $enable Enable XML dump - */ - function enable_xml_dump($enable = false) - { - $this->xml_dump = (bool) $enable; - } - - /** - * Enables/disables caching in SimplePie. - * - * This option allows you to disable caching all-together in SimplePie. - * However, disabling the cache can lead to longer load times. - * - * @access public - * @since 1.0 Preview Release - * @param bool $enable Enable caching - */ - function enable_cache($enable = true) - { - $this->cache = (bool) $enable; - } - - /** - * Set the length of time (in seconds) that the contents of a feed - * will be cached. - * - * @access public - * @param int $seconds The feed content cache duration. - */ - function set_cache_duration($seconds = 3600) - { - $this->cache_duration = (int) $seconds; - } - - /** - * Set the length of time (in seconds) that the autodiscovered feed - * URL will be cached. - * - * @access public - * @param int $seconds The autodiscovered feed URL cache duration. - */ - function set_autodiscovery_cache_duration($seconds = 604800) - { - $this->autodiscovery_cache_duration = (int) $seconds; - } - - /** - * Set the file system location where the cached files should be stored. - * - * @access public - * @param string $location The file system location. - */ - function set_cache_location($location = './cache') - { - $this->cache_location = (string) $location; - } - - /** - * Determines whether feed items should be sorted into reverse chronological order. - * - * @access public - * @param bool $enable Sort as reverse chronological order. - */ - function enable_order_by_date($enable = true) - { - $this->order_by_date = (bool) $enable; - } - - /** - * Allows you to override the character encoding reported by the feed. - * - * @access public - * @param string $encoding Character encoding. - */ - function set_input_encoding($encoding = false) - { - if ($encoding) - { - $this->input_encoding = (string) $encoding; - } - else - { - $this->input_encoding = false; - } - } - - /** - * Set how much feed autodiscovery to do - * - * @access public - * @see SIMPLEPIE_LOCATOR_NONE - * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY - * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION - * @see SIMPLEPIE_LOCATOR_LOCAL_BODY - * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION - * @see SIMPLEPIE_LOCATOR_REMOTE_BODY - * @see SIMPLEPIE_LOCATOR_ALL - * @param int $level Feed Autodiscovery Level (level can be a - * combination of the above constants, see bitwise OR operator) - */ - function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) - { - $this->autodiscovery = (int) $level; - } - - /** - * Allows you to change which class SimplePie uses for caching. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_cache_class($class = 'SimplePie_Cache') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Cache')) - { - $this->cache_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for auto-discovery. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_locator_class($class = 'SimplePie_Locator') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Locator')) - { - $this->locator_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for XML parsing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_parser_class($class = 'SimplePie_Parser') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Parser')) - { - $this->parser_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for remote file fetching. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_file_class($class = 'SimplePie_File') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_File')) - { - $this->file_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for data sanitization. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_sanitize_class($class = 'SimplePie_Sanitize') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Sanitize')) - { - $this->sanitize = new $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling feed items. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_item_class($class = 'SimplePie_Item') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Item')) - { - $this->item_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling author data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_author_class($class = 'SimplePie_Author') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Author')) - { - $this->author_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for handling category data. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_category_class($class = 'SimplePie_Category') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Category')) - { - $this->category_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for feed enclosures. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_enclosure_class($class = 'SimplePie_Enclosure') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Enclosure')) - { - $this->enclosure_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for captions - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_caption_class($class = 'SimplePie_Caption') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Caption')) - { - $this->caption_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_copyright_class($class = 'SimplePie_Copyright') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Copyright')) - { - $this->copyright_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_credit_class($class = 'SimplePie_Credit') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Credit')) - { - $this->credit_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_rating_class($class = 'SimplePie_Rating') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Rating')) - { - $this->rating_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_restriction_class($class = 'SimplePie_Restriction') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Restriction')) - { - $this->restriction_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses for content-type sniffing. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Content_Type_Sniffer')) - { - $this->content_type_sniffer_class = $class; - return true; - } - return false; - } - - /** - * Allows you to change which class SimplePie uses item sources. - * Useful when you are overloading or extending SimplePie's default classes. - * - * @access public - * @param string $class Name of custom class. - * @link http://php.net/manual/en/keyword.extends.php PHP4 extends documentation - * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation - */ - function set_source_class($class = 'SimplePie_Source') - { - if (SimplePie_Misc::is_subclass_of($class, 'SimplePie_Source')) - { - $this->source_class = $class; - return true; - } - return false; - } - - /** - * Allows you to override the default user agent string. - * - * @access public - * @param string $ua New user agent string. - */ - function set_useragent($ua = SIMPLEPIE_USERAGENT) - { - $this->useragent = (string) $ua; - } - - /** - * Set callback function to create cache filename with - * - * @access public - * @param mixed $function Callback function - */ - function set_cache_name_function($function = 'md5') - { - if (is_callable($function)) - { - $this->cache_name_function = $function; - } - } - - /** - * Set javascript query string parameter - * - * @access public - * @param mixed $get Javascript query string parameter - */ - function set_javascript($get = 'js') - { - if ($get) - { - $this->javascript = (string) $get; - } - else - { - $this->javascript = false; - } - } - - /** - * Set options to make SP as fast as possible. Forgoes a - * substantial amount of data sanitization in favor of speed. - * - * @access public - * @param bool $set Whether to set them or not - */ - function set_stupidly_fast($set = false) - { - if ($set) - { - $this->enable_order_by_date(false); - $this->remove_div(false); - $this->strip_comments(false); - $this->strip_htmltags(false); - $this->strip_attributes(false); - $this->set_image_handler(false); - } - } - - /** - * Set maximum number of feeds to check with autodiscovery - * - * @access public - * @param int $max Maximum number of feeds to check - */ - function set_max_checked_feeds($max = 10) - { - $this->max_checked_feeds = (int) $max; - } - - function remove_div($enable = true) - { - $this->sanitize->remove_div($enable); - } - - function strip_htmltags($tags = '', $encode = null) - { - if ($tags === '') - { - $tags = $this->strip_htmltags; - } - $this->sanitize->strip_htmltags($tags); - if ($encode !== null) - { - $this->sanitize->encode_instead_of_strip($tags); - } - } - - function encode_instead_of_strip($enable = true) - { - $this->sanitize->encode_instead_of_strip($enable); - } - - function strip_attributes($attribs = '') - { - if ($attribs === '') - { - $attribs = $this->strip_attributes; - } - $this->sanitize->strip_attributes($attribs); - } - - function set_output_encoding($encoding = 'UTF-8') - { - $this->sanitize->set_output_encoding($encoding); - } - - function strip_comments($strip = false) - { - $this->sanitize->strip_comments($strip); - } - - /** - * Set element/attribute key/value pairs of HTML attributes - * containing URLs that need to be resolved relative to the feed - * - * @access public - * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs - */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) - { - $this->sanitize->set_url_replacements($element_attribute); - } - - /** - * Set the handler to enable the display of cached favicons. - * - * @access public - * @param str $page Web-accessible path to the handler_favicon.php file. - * @param str $qs The query string that the value should be passed to. - */ - function set_favicon_handler($page = false, $qs = 'i') - { - if ($page !== false) - { - $this->favicon_handler = $page . '?' . $qs . '='; - } - else - { - $this->favicon_handler = ''; - } - } - - /** - * Set the handler to enable the display of cached images. - * - * @access public - * @param str $page Web-accessible path to the handler_image.php file. - * @param str $qs The query string that the value should be passed to. - */ - function set_image_handler($page = false, $qs = 'i') - { - if ($page !== false) - { - $this->sanitize->set_image_handler($page . '?' . $qs . '='); - } - else - { - $this->image_handler = ''; - } - } - - /** - * Set the limit for items returned per-feed with multifeeds. - * - * @access public - * @param integer $limit The maximum number of items to return. - */ - function set_item_limit($limit = 0) - { - $this->item_limit = (int) $limit; - } - - function init() - { - // Check absolute bare minimum requirements. - if ((function_exists('version_compare') && version_compare(PHP_VERSION, '4.3.0', '<')) || !extension_loaded('xml') || !extension_loaded('pcre')) - { - return false; - } - // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. - elseif (!extension_loaded('xmlreader')) - { - static $xml_is_sane = null; - if ($xml_is_sane === null) - { - $parser_check = xml_parser_create(); - xml_parse_into_struct($parser_check, '&', $values); - xml_parser_free($parser_check); - $xml_is_sane = isset($values[0]['value']); - } - if (!$xml_is_sane) - { - return false; - } - } - - if (isset($_GET[$this->javascript])) - { - SimplePie_Misc::output_javascript(); - exit; - } - - // Pass whatever was set with config options over to the sanitizer. - $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->cache_class); - $this->sanitize->pass_file_data($this->file_class, $this->timeout, $this->useragent, $this->force_fsockopen); - - if ($this->feed_url !== null || $this->raw_data !== null) - { - $this->data = array(); - $this->multifeed_objects = array(); - $cache = false; - - if ($this->feed_url !== null) - { - $parsed_feed_url = SimplePie_Misc::parse_url($this->feed_url); - // Decide whether to enable caching - if ($this->cache && $parsed_feed_url['scheme'] !== '') - { - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'); - } - // If it's enabled and we don't want an XML dump, use the cache - if ($cache && !$this->xml_dump) - { - // Load the Cache - $this->data = $cache->load(); - if (!empty($this->data)) - { - // If the cache is for an outdated build of SimplePie - if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) - { - $cache->unlink(); - $this->data = array(); - } - // If we've hit a collision just rerun it with caching disabled - elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) - { - $cache = false; - $this->data = array(); - } - // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. - elseif (isset($this->data['feed_url'])) - { - // If the autodiscovery cache is still valid use it. - if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) - { - // Do not need to do feed autodiscovery yet. - if ($this->data['feed_url'] === $this->data['url']) - { - $cache->unlink(); - $this->data = array(); - } - else - { - $this->set_feed_url($this->data['feed_url']); - return $this->init(); - } - } - } - // Check if the cache has been updated - elseif ($cache->mtime() + $this->cache_duration < time()) - { - // If we have last-modified and/or etag set - if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) - { - $headers = array(); - if (isset($this->data['headers']['last-modified'])) - { - $headers['if-modified-since'] = $this->data['headers']['last-modified']; - } - if (isset($this->data['headers']['etag'])) - { - $headers['if-none-match'] = '"' . $this->data['headers']['etag'] . '"'; - } - $file = new $this->file_class($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen); - if ($file->success) - { - if ($file->status_code === 304) - { - $cache->touch(); - return true; - } - else - { - $headers = $file->headers; - } - } - else - { - unset($file); - } - } - } - // If the cache is still valid, just return true - else - { - return true; - } - } - // If the cache is empty, delete it - else - { - $cache->unlink(); - $this->data = array(); - } - } - // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. - if (!isset($file)) - { - if (is_a($this->file, 'SimplePie_File') && $this->file->url === $this->feed_url) - { - $file =& $this->file; - } - else - { - $file = new $this->file_class($this->feed_url, $this->timeout, 5, null, $this->useragent, $this->force_fsockopen); - } - } - // If the file connection has an error, set SimplePie::error to that and quit - if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) - { - $this->error = $file->error; - if (!empty($this->data)) - { - return true; - } - else - { - return false; - } - } - - if (!$this->force_feed) - { - // Check if the supplied URL is a feed, if it isn't, look for it. - $locate = new $this->locator_class($file, $this->timeout, $this->useragent, $this->file_class, $this->max_checked_feeds, $this->content_type_sniffer_class); - if (!$locate->is_feed($file)) - { - // We need to unset this so that if SimplePie::set_file() has been called that object is untouched - unset($file); - if ($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds)) - { - if ($cache) - { - $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); - if (!$cache->save($this)) - { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - } - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'); - } - $this->feed_url = $file->url; - } - else - { - $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed."; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - } - $locate = null; - } - - $headers = $file->headers; - $data = $file->body; - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); - } - else - { - $data = $this->raw_data; - } - - // Set up array of possible encodings - $encodings = array(); - - // First check to see if input has been overridden. - if ($this->input_encoding !== false) - { - $encodings[] = $this->input_encoding; - } - - $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); - $text_types = array('text/xml', 'text/xml-external-parsed-entity'); - - // RFC 3023 (only applies to sniffed content) - if (isset($sniffed)) - { - if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') - { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) - { - $encodings[] = strtoupper($charset[1]); - } - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - } - elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') - { - if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) - { - $encodings[] = $charset[1]; - } - $encodings[] = 'US-ASCII'; - } - // Text MIME-type default - elseif (substr($sniffed, 0, 5) === 'text/') - { - $encodings[] = 'US-ASCII'; - } - } - - // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 - $encodings = array_merge($encodings, SimplePie_Misc::xml_encoding($data)); - $encodings[] = 'UTF-8'; - $encodings[] = 'ISO-8859-1'; - - // There's no point in trying an encoding twice - $encodings = array_unique($encodings); - - // If we want the XML, just output that with the most likely encoding and quit - if ($this->xml_dump) - { - header('Content-type: text/xml; charset=' . $encodings[0]); - echo $data; - exit; - } - - // Loop through each possible encoding, till we return something, or run out of possibilities - foreach ($encodings as $encoding) - { - // Change the encoding to UTF-8 (as we always use UTF-8 internally) - if ($utf8_data = SimplePie_Misc::change_encoding($data, $encoding, 'UTF-8')) - { - // Create new parser - $parser = new $this->parser_class(); - - // If it's parsed fine - if ($parser->parse($utf8_data, 'UTF-8')) - { - $this->data = $parser->get_data(); - if ($this->get_type() & ~SIMPLEPIE_TYPE_NONE) - { - if (isset($headers)) - { - $this->data['headers'] = $headers; - } - $this->data['build'] = SIMPLEPIE_BUILD; - - // Cache the file if caching is enabled - if ($cache && !$cache->save($this)) - { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - } - return true; - } - else - { - $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - } - } - } - if (isset($parser)) - { - // We have an error, just set SimplePie_Misc::error to it and quit - $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); - } - else - { - $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; - } - SimplePie_Misc::error($this->error, E_USER_NOTICE, __FILE__, __LINE__); - return false; - } - elseif (!empty($this->multifeed_url)) - { - $i = 0; - $success = 0; - $this->multifeed_objects = array(); - foreach ($this->multifeed_url as $url) - { - if (SIMPLEPIE_PHP5) - { - // This keyword needs to defy coding standards for PHP4 compatibility - $this->multifeed_objects[$i] = clone($this); - } - else - { - $this->multifeed_objects[$i] = $this; - } - $this->multifeed_objects[$i]->set_feed_url($url); - $success |= $this->multifeed_objects[$i]->init(); - $i++; - } - return (bool) $success; - } - else - { - return false; - } - } - - /** - * Return the error message for the occured error - * - * @access public - * @return string Error message - */ - function error() - { - return $this->error; - } - - function get_encoding() - { - return $this->sanitize->output_encoding; - } - - function handle_content_type($mime = 'text/html') - { - if (!headers_sent()) - { - $header = "Content-type: $mime;"; - if ($this->get_encoding()) - { - $header .= ' charset=' . $this->get_encoding(); - } - else - { - $header .= ' charset=UTF-8'; - } - header($header); - } - } - - function get_type() - { - if (!isset($this->data['type'])) - { - $this->data['type'] = SIMPLEPIE_TYPE_ALL; - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; - } - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) - || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; - } - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'])) - { - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) - { - switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) - { - case '0.91': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) - { - switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) - { - case '0': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; - break; - - case '24': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; - break; - } - } - break; - - case '0.92': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; - break; - - case '0.93': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; - break; - - case '0.94': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; - break; - - case '2.0': - $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; - break; - } - } - } - else - { - $this->data['type'] = SIMPLEPIE_TYPE_NONE; - } - } - return $this->data['type']; - } - - /** - * Returns the URL for the favicon of the feed's website. - * - * @todo Cache atom:icon - * @access public - * @since 1.0 - */ - function get_favicon() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif (($url = $this->get_link()) !== null && preg_match('/^http(s)?:\/\//i', $url)) - { - $favicon = SimplePie_Misc::absolutize_url('/favicon.ico', $url); - - if ($this->cache && $this->favicon_handler) - { - $favicon_filename = call_user_func($this->cache_name_function, $favicon); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $favicon_filename, 'spi'); - - if ($cache->load()) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $file = new $this->file_class($favicon, $this->timeout / 10, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - - if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)) && strlen($file->body) > 0) - { - $sniffer = new $this->content_type_sniffer_class($file); - if (substr($sniffer->get_type(), 0, 6) === 'image/') - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - return $this->sanitize($this->favicon_handler . $favicon_filename, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - trigger_error("$cache->name is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - // not an image - else - { - return false; - } - } - } - } - else - { - return $this->sanitize($favicon, SIMPLEPIE_CONSTRUCT_IRI); - } - } - return false; - } - - /** - * @todo If we have a perm redirect we should return the new URL - * @todo When we make the above change, let's support as well - * @todo Also, |atom:link|@rel=self - */ - function subscribe_url() - { - if ($this->feed_url !== null) - { - return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_feed() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_outlook() - { - if ($this->feed_url !== null) - { - return $this->sanitize('outlook' . SimplePie_Misc::fix_protocol($this->feed_url, 2), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_podcast() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 3), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_itunes() - { - if ($this->feed_url !== null) - { - return $this->sanitize(SimplePie_Misc::fix_protocol($this->feed_url, 4), SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - /** - * Creates the subscribe_* methods' return data - * - * @access private - * @param string $feed_url String to prefix to the feed URL - * @param string $site_url String to prefix to the site URL (and - * suffix to the feed URL) - * @return mixed URL if feed exists, false otherwise - */ - function subscribe_service($feed_url, $site_url = null) - { - if ($this->subscribe_url()) - { - $return = $feed_url . rawurlencode($this->feed_url); - if ($site_url !== null && $this->get_link() !== null) - { - $return .= $site_url . rawurlencode($this->get_link()); - } - return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function subscribe_aol() - { - return $this->subscribe_service('http://feeds.my.aol.com/add.jsp?url='); - } - - function subscribe_bloglines() - { - return $this->subscribe_service('http://www.bloglines.com/sub/'); - } - - function subscribe_eskobo() - { - return $this->subscribe_service('http://www.eskobo.com/?AddToMyPage='); - } - - function subscribe_feedfeeds() - { - return $this->subscribe_service('http://www.feedfeeds.com/add?feed='); - } - - function subscribe_feedster() - { - return $this->subscribe_service('http://www.feedster.com/myfeedster.php?action=addrss&confirm=no&rssurl='); - } - - function subscribe_google() - { - return $this->subscribe_service('http://fusion.google.com/add?feedurl='); - } - - function subscribe_gritwire() - { - return $this->subscribe_service('http://my.gritwire.com/feeds/addExternalFeed.aspx?FeedUrl='); - } - - function subscribe_msn() - { - return $this->subscribe_service('http://my.msn.com/addtomymsn.armx?id=rss&ut=', '&ru='); - } - - function subscribe_netvibes() - { - return $this->subscribe_service('http://www.netvibes.com/subscribe.php?url='); - } - - function subscribe_newsburst() - { - return $this->subscribe_service('http://www.newsburst.com/Source/?add='); - } - - function subscribe_newsgator() - { - return $this->subscribe_service('http://www.newsgator.com/ngs/subscriber/subext.aspx?url='); - } - - function subscribe_odeo() - { - return $this->subscribe_service('http://www.odeo.com/listen/subscribe?feed='); - } - - function subscribe_podnova() - { - return $this->subscribe_service('http://www.podnova.com/index_your_podcasts.srf?action=add&url='); - } - - function subscribe_rojo() - { - return $this->subscribe_service('http://www.rojo.com/add-subscription?resource='); - } - - function subscribe_yahoo() - { - return $this->subscribe_service('http://add.my.yahoo.com/rss?url='); - } - - function get_feed_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_ATOM_10) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_ATOM_03) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_RDF) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) - { - return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; - } - } - return null; - } - - function get_channel_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_ATOM_ALL) - { - if ($return = $this->get_feed_tags($namespace, $tag)) - { - return $return; - } - } - if ($type & SIMPLEPIE_TYPE_RSS_10) - { - if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_090) - { - if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel')) - { - if (isset($channel[0]['child'][$namespace][$tag])) - { - return $channel[0]['child'][$namespace][$tag]; - } - } - } - return null; - } - - function get_image_tags($namespace, $tag) - { - $type = $this->get_type(); - if ($type & SIMPLEPIE_TYPE_RSS_10) - { - if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_090) - { - if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) - { - if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image')) - { - if (isset($image[0]['child'][$namespace][$tag])) - { - return $image[0]['child'][$namespace][$tag]; - } - } - } - return null; - } - - function get_base($element = array()) - { - if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) - { - return $element['xml_base']; - } - elseif ($this->get_link() !== null) - { - return $this->get_link(); - } - else - { - return $this->subscribe_url(); - } - } - - function sanitize($data, $type, $base = '') - { - return $this->sanitize->sanitize($data, $type, $base); - } - - function get_title() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) - { - // This is really the label, but keep this as the term also for BC. - // Label will also work on retrieving because that falls back to term. - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - if (isset($category['attribs']['']['domain'])) - { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = null; - } - $categories[] = new $this->category_class($term, $scheme, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] = new $this->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - $avatar = null; - $name_date = null; - $uri_date = null; - $avatar_date = null; - - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'])) - { - $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0])); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'])) - { - $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'])) - { - $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'])) - { - $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']; - } - - if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null ) - { - $authors[] = new $this->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date); - } - } - if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] = new $this->author_class($name, $url, $email); - } - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] = new $this->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] = new $this->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] = new $this->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if (isset($links[$key])) - { - return $links[$key]; - } - else - { - return null; - } - } - - /** - * Added for parity between the parent-level and the item/entry-level. - */ - function get_permalink() - { - return $this->get_link(0); - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - function get_all_discovered_feeds() - { - return $this->all_discovered_feeds; - } - - function get_description() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_language() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) - { - return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['headers']['content-language'])) - { - return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_latitude() - { - - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_image_title() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_image_url() - { - if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) - { - return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_image_link() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_image_width() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width')) - { - return round($return[0]['data']); - } - elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) - { - return 88.0; - } - else - { - return null; - } - } - - function get_image_height() - { - if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height')) - { - return round($return[0]['data']); - } - elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) - { - return 31.0; - } - else - { - return null; - } - } - - function get_item_quantity($max = 0) - { - $max = (int) $max; - $qty = count($this->get_items()); - if ($max === 0) - { - return $qty; - } - else - { - return ($qty > $max) ? $max : $qty; - } - } - - function get_item($key = 0) - { - $items = $this->get_items(); - if (isset($items[$key])) - { - return $items[$key]; - } - else - { - return null; - } - } - - function get_items($start = 0, $end = 0) - { - if (!isset($this->data['items'])) - { - if (!empty($this->multifeed_objects)) - { - $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); - } - else - { - $this->data['items'] = array(); - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = new $this->item_class($this, $items[$key]); - } - } - if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) - { - $keys = array_keys($items); - foreach ($keys as $key) - { - $this->data['items'][] = new $this->item_class($this, $items[$key]); - } - } - } - } - - if (!empty($this->data['items'])) - { - // If we want to order it by date, check if all items have a date, and then sort it - if ($this->order_by_date && empty($this->multifeed_objects)) - { - if (!isset($this->data['ordered_items'])) - { - $do_sort = true; - foreach ($this->data['items'] as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - $this->data['ordered_items'] = $this->data['items']; - if ($do_sort) - { - usort($this->data['ordered_items'], array(&$this, 'sort_items')); - } - } - $items = $this->data['ordered_items']; - } - else - { - $items = $this->data['items']; - } - - // Slice the data as desired - if ($end === 0) - { - return array_slice($items, $start); - } - else - { - return array_slice($items, $start, $end); - } - } - else - { - return array(); - } - } - - /** - * @static - */ - function sort_items($a, $b) - { - return $a->get_date('U') <= $b->get_date('U'); - } - - /** - * @static - */ - function merge_items($urls, $start = 0, $end = 0, $limit = 0) - { - if (is_array($urls) && sizeof($urls) > 0) - { - $items = array(); - foreach ($urls as $arg) - { - if (is_a($arg, 'SimplePie')) - { - $items = array_merge($items, $arg->get_items(0, $limit)); - } - else - { - trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); - } - } - - $do_sort = true; - foreach ($items as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - if ($do_sort) - { - usort($items, array('SimplePie', 'sort_items')); - } - - if ($end === 0) - { - return array_slice($items, $start); - } - else - { - return array_slice($items, $start, $end); - } - } - else - { - trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); - return array(); - } - } -} - -class SimplePie_Item -{ - var $feed; - var $data = array(); - - function SimplePie_Item($feed, $data) - { - $this->feed = $feed; - $this->data = $data; - } - - function __toString() - { - return md5(serialize($this->data)); - } - - /** - * Remove items that link back to this before destroying this object - */ - function __destruct() - { - if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) - { - unset($this->feed); - } - } - - function get_item_tags($namespace, $tag) - { - if (isset($this->data['child'][$namespace][$tag])) - { - return $this->data['child'][$namespace][$tag]; - } - else - { - return null; - } - } - - function get_base($element = array()) - { - return $this->feed->get_base($element); - } - - function sanitize($data, $type, $base = '') - { - return $this->feed->sanitize($data, $type, $base); - } - - function get_feed() - { - return $this->feed; - } - - function get_id($hash = false) - { - if (!$hash) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (($return = $this->get_permalink()) !== null) - { - return $return; - } - elseif (($return = $this->get_title()) !== null) - { - return $return; - } - } - if ($this->get_permalink() !== null || $this->get_title() !== null) - { - return md5($this->get_permalink() . $this->get_title()); - } - else - { - return md5(serialize($this->data)); - } - } - - function get_title() - { - if (!isset($this->data['title'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $this->data['title'] = null; - } - } - return $this->data['title']; - } - - function get_description($description_only = false) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (!$description_only) - { - return $this->get_content(true); - } - else - { - return null; - } - } - - function get_content($content_only = false) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_content_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif (!$content_only) - { - return $this->get_description(true); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) - { - // This is really the label, but keep this as the term also for BC. - // Label will also work on retrieving because that falls back to term. - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - if (isset($category['attribs']['']['domain'])) - { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = null; - } - $categories[] = new $this->feed->category_class($term, $scheme, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] = new $this->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] = new $this->feed->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] = new $this->feed->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - $avatar = null; - $name_date = null; - $uri_date = null; - $avatar_date = null; - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'])) - { - $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0])); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'])) - { - $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'])) - { - $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'])) - { - $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']; - } - - if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null ) - { - $authors[] = new $this->feed->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date); - } - } - if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] = new $this->feed->author_class($name, $url, $email); - } - } - if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) - { - $authors[] = new $this->feed->author_class(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] = new $this->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) - { - return $authors; - } - elseif ($authors = $this->feed->get_authors()) - { - return $authors; - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_date($date_format = 'j F Y, g:i a') - { - if (!isset($this->data['date'])) - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) - { - $this->data['date']['raw'] = $return[0]['data']; - } - - if (!empty($this->data['date']['raw'])) - { - $parser = SimplePie_Parse_Date::get(); - $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); - } - else - { - $this->data['date'] = null; - } - } - if ($this->data['date']) - { - $date_format = (string) $date_format; - switch ($date_format) - { - case '': - return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); - - case 'U': - return $this->data['date']['parsed']; - - default: - return date($date_format, $this->data['date']['parsed']); - } - } - else - { - return null; - } - } - - function get_local_date($date_format = '%c') - { - if (!$date_format) - { - return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (($date = $this->get_date('U')) !== null) - { - return strftime($date_format, $date); - } - else - { - return null; - } - } - - function get_permalink() - { - $link = $this->get_link(); - $enclosure = $this->get_enclosure(0); - if ($link !== null) - { - return $link; - } - elseif ($enclosure !== null) - { - return $enclosure->get_link(); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if ($links[$key] !== null) - { - return $links[$key]; - } - else - { - return null; - } - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) - { - if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - /** - * @todo Add ability to prefer one type of content over another (in a media group). - */ - function get_enclosure($key = 0, $prefer = null) - { - $enclosures = $this->get_enclosures(); - if (isset($enclosures[$key])) - { - return $enclosures[$key]; - } - else - { - return null; - } - } - - /** - * Grabs all available enclosures (podcasts, etc.) - * - * Supports the RSS tag, as well as Media RSS and iTunes RSS. - * - * At this point, we're pretty much assuming that all enclosures for an item are the same content. Anything else is too complicated to properly support. - * - * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). - * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). - */ - function get_enclosures() - { - if (!isset($this->data['enclosures'])) - { - $this->data['enclosures'] = array(); - - // Elements - $captions_parent = null; - $categories_parent = null; - $copyrights_parent = null; - $credits_parent = null; - $description_parent = null; - $duration_parent = null; - $hashes_parent = null; - $keywords_parent = null; - $player_parent = null; - $ratings_parent = null; - $restrictions_parent = null; - $thumbnails_parent = null; - $title_parent = null; - - // Let's do the channel and item-level ones first, and just re-use them if we need to. - $parent = $this->get_feed(); - - // CAPTIONS - if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) - { - foreach ($captions as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions_parent[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - } - if (is_array($captions_parent)) - { - $captions_parent = array_values(SimplePie_Misc::array_unique($captions_parent)); - } - - // CATEGORIES - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - } - foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) - { - $term = null; - $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - $label = null; - if (isset($category['attribs']['']['text'])) - { - $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - - if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) - { - foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) - { - if (isset($subcategory['attribs']['']['text'])) - { - $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories_parent[] = new $this->feed->category_class($term, $scheme, $label); - } - } - } - if (is_array($categories_parent)) - { - $categories_parent = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - - // COPYRIGHT - if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) - { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) - { - $copyright_url = null; - $copyright_label = null; - if (isset($copyright[0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($copyright[0]['data'])) - { - $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights_parent = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - - // CREDITS - if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) - { - foreach ($credits as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits_parent[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - } - if (is_array($credits_parent)) - { - $credits_parent = array_values(SimplePie_Misc::array_unique($credits_parent)); - } - - // DESCRIPTION - if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) - { - if (isset($description_parent[0]['data'])) - { - $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - - // DURATION - if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) - { - $seconds = null; - $minutes = null; - $hours = null; - if (isset($duration_parent[0]['data'])) - { - $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - if (sizeof($temp) > 0) - { - (int) $seconds = array_pop($temp); - } - if (sizeof($temp) > 0) - { - (int) $minutes = array_pop($temp); - $seconds += $minutes * 60; - } - if (sizeof($temp) > 0) - { - (int) $hours = array_pop($temp); - $seconds += $hours * 3600; - } - unset($temp); - $duration_parent = $seconds; - } - } - - // HASHES - if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) - { - foreach ($hashes_iterator as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes_parent[] = $algo.':'.$value; - } - } - if (is_array($hashes_parent)) - { - $hashes_parent = array_values(SimplePie_Misc::array_unique($hashes_parent)); - } - - // KEYWORDS - if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) - { - if (isset($keywords[0]['data'])) - { - $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords_parent[] = trim($word); - } - } - unset($temp); - } - if (is_array($keywords_parent)) - { - $keywords_parent = array_values(SimplePie_Misc::array_unique($keywords_parent)); - } - - // PLAYER - if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) - { - if (isset($player_parent[0]['attribs']['']['url'])) - { - $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - - // RATINGS - if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) - { - foreach ($ratings as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) - { - foreach ($ratings as $rating) - { - $rating_scheme = 'urn:itunes'; - $rating_value = null; - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings_parent[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - } - if (is_array($ratings_parent)) - { - $ratings_parent = array_values(SimplePie_Misc::array_unique($ratings_parent)); - } - - // RESTRICTIONS - if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) - { - foreach ($restrictions as $restriction) - { - $restriction_relationship = 'allow'; - $restriction_type = null; - $restriction_value = 'itunes'; - if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') - { - $restriction_relationship = 'deny'; - } - $restrictions_parent[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - } - if (is_array($restrictions_parent)) - { - $restrictions_parent = array_values(SimplePie_Misc::array_unique($restrictions_parent)); - } - - // THUMBNAILS - if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) - { - foreach ($thumbnails as $thumbnail) - { - if (isset($thumbnail['attribs']['']['url'])) - { - $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - } - } - - // TITLES - if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) - { - if (isset($title_parent[0]['data'])) - { - $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - } - - // Clear the memory - unset($parent); - - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // If we have media:group tags, loop through them. - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) - { - if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) - { - // If we have media:content tags, loop through them. - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } - - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } - - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } - - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } - - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } - - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } - - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } - - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } - - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } - - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } - - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } - - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } - } - } - } - - // If we have standalone media:content tags, loop through them. - if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) - { - foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) - { - if (isset($content['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - // Elements - $captions = null; - $categories = null; - $copyrights = null; - $credits = null; - $description = null; - $hashes = null; - $keywords = null; - $player = null; - $ratings = null; - $restrictions = null; - $thumbnails = null; - $title = null; - - // Start checking the attributes of media:content - if (isset($content['attribs']['']['bitrate'])) - { - $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['channels'])) - { - $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['duration'])) - { - $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $duration = $duration_parent; - } - if (isset($content['attribs']['']['expression'])) - { - $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['framerate'])) - { - $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['height'])) - { - $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['lang'])) - { - $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['fileSize'])) - { - $length = ceil($content['attribs']['']['fileSize']); - } - if (isset($content['attribs']['']['medium'])) - { - $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['samplingrate'])) - { - $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['type'])) - { - $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['attribs']['']['width'])) - { - $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - - // Checking the other optional media: elements. Priority: media:content, media:group, item, channel - - // CAPTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) - { - $caption_type = null; - $caption_lang = null; - $caption_startTime = null; - $caption_endTime = null; - $caption_text = null; - if (isset($caption['attribs']['']['type'])) - { - $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['lang'])) - { - $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['start'])) - { - $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['attribs']['']['end'])) - { - $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($caption['data'])) - { - $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $captions[] = new $this->feed->caption_class($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text); - } - if (is_array($captions)) - { - $captions = array_values(SimplePie_Misc::array_unique($captions)); - } - } - else - { - $captions = $captions_parent; - } - - // CATEGORIES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) - { - foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['data'])) - { - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = 'http://search.yahoo.com/mrss/category_schema'; - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->feed->category_class($term, $scheme, $label); - } - } - if (is_array($categories) && is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique(array_merge($categories, $categories_parent))); - } - elseif (is_array($categories)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories)); - } - elseif (is_array($categories_parent)) - { - $categories = array_values(SimplePie_Misc::array_unique($categories_parent)); - } - else - { - $categories = null; - } - - // COPYRIGHTS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) - { - $copyright_url = null; - $copyright_label = null; - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) - { - $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) - { - $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $copyrights = new $this->feed->copyright_class($copyright_url, $copyright_label); - } - else - { - $copyrights = $copyrights_parent; - } - - // CREDITS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) - { - $credit_role = null; - $credit_scheme = null; - $credit_name = null; - if (isset($credit['attribs']['']['role'])) - { - $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($credit['attribs']['']['scheme'])) - { - $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $credit_scheme = 'urn:ebu'; - } - if (isset($credit['data'])) - { - $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $credits[] = new $this->feed->credit_class($credit_role, $credit_scheme, $credit_name); - } - if (is_array($credits)) - { - $credits = array_values(SimplePie_Misc::array_unique($credits)); - } - } - else - { - $credits = $credits_parent; - } - - // DESCRIPTION - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) - { - $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $description = $description_parent; - } - - // HASHES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) - { - $value = null; - $algo = null; - if (isset($hash['data'])) - { - $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($hash['attribs']['']['algo'])) - { - $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $algo = 'md5'; - } - $hashes[] = $algo.':'.$value; - } - if (is_array($hashes)) - { - $hashes = array_values(SimplePie_Misc::array_unique($hashes)); - } - } - else - { - $hashes = $hashes_parent; - } - - // KEYWORDS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) - { - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) - { - $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); - foreach ($temp as $word) - { - $keywords[] = trim($word); - } - unset($temp); - } - if (is_array($keywords)) - { - $keywords = array_values(SimplePie_Misc::array_unique($keywords)); - } - } - else - { - $keywords = $keywords_parent; - } - - // PLAYER - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) - { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - $player = $player_parent; - } - - // RATINGS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) - { - $rating_scheme = null; - $rating_value = null; - if (isset($rating['attribs']['']['scheme'])) - { - $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $rating_scheme = 'urn:simple'; - } - if (isset($rating['data'])) - { - $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $ratings[] = new $this->feed->rating_class($rating_scheme, $rating_value); - } - if (is_array($ratings)) - { - $ratings = array_values(SimplePie_Misc::array_unique($ratings)); - } - } - else - { - $ratings = $ratings_parent; - } - - // RESTRICTIONS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) - { - $restriction_relationship = null; - $restriction_type = null; - $restriction_value = null; - if (isset($restriction['attribs']['']['relationship'])) - { - $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['attribs']['']['type'])) - { - $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($restriction['data'])) - { - $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $restrictions[] = new $this->feed->restriction_class($restriction_relationship, $restriction_type, $restriction_value); - } - if (is_array($restrictions)) - { - $restrictions = array_values(SimplePie_Misc::array_unique($restrictions)); - } - } - else - { - $restrictions = $restrictions_parent; - } - - // THUMBNAILS - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) - { - foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) - { - $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); - } - if (is_array($thumbnails)) - { - $thumbnails = array_values(SimplePie_Misc::array_unique($thumbnails)); - } - } - else - { - $thumbnails = $thumbnails_parent; - } - - // TITLES - if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) - { - $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $title = $title_parent; - } - - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width); - } - } - } - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - $title = $title_parent; - - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } - if (isset($link['attribs']['']['title'])) - { - $title = $this->sanitize($link['attribs']['']['title'], SIMPLEPIE_CONSTRUCT_TEXT); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width); - } - } - - foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) - { - if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - if (isset($link['attribs']['']['type'])) - { - $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($link['attribs']['']['length'])) - { - $length = ceil($link['attribs']['']['length']); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } - - if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure')) - { - if (isset($enclosure[0]['attribs']['']['url'])) - { - // Attributes - $bitrate = null; - $channels = null; - $duration = null; - $expression = null; - $framerate = null; - $height = null; - $javascript = null; - $lang = null; - $length = null; - $medium = null; - $samplingrate = null; - $type = null; - $url = null; - $width = null; - - $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); - if (isset($enclosure[0]['attribs']['']['type'])) - { - $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($enclosure[0]['attribs']['']['length'])) - { - $length = ceil($enclosure[0]['attribs']['']['length']); - } - - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - } - - if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) - { - // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); - } - - $this->data['enclosures'] = array_values(SimplePie_Misc::array_unique($this->data['enclosures'])); - } - if (!empty($this->data['enclosures'])) - { - return $this->data['enclosures']; - } - else - { - return null; - } - } - - function get_latitude() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_source() - { - if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) - { - return new $this->feed->source_class($this, $return[0]); - } - else - { - return null; - } - } - - /** - * Creates the add_to_* methods' return data - * - * @access private - * @param string $item_url String to prefix to the item permalink - * @param string $title_url String to prefix to the item title - * (and suffix to the item permalink) - * @return mixed URL if feed exists, false otherwise - */ - function add_to_service($item_url, $title_url = null, $summary_url = null) - { - if ($this->get_permalink() !== null) - { - $return = $item_url . rawurlencode($this->get_permalink()); - if ($title_url !== null && $this->get_title() !== null) - { - $return .= $title_url . rawurlencode($this->get_title()); - } - if ($summary_url !== null && $this->get_description() !== null) - { - $return .= $summary_url . rawurlencode($this->get_description()); - } - return $this->sanitize($return, SIMPLEPIE_CONSTRUCT_IRI); - } - else - { - return null; - } - } - - function add_to_blinklist() - { - return $this->add_to_service('http://www.blinklist.com/index.php?Action=Blink/addblink.php&Description=&Url=', '&Title='); - } - - function add_to_blogmarks() - { - return $this->add_to_service('http://blogmarks.net/my/new.php?mini=1&simple=1&url=', '&title='); - } - - function add_to_delicious() - { - return $this->add_to_service('http://del.icio.us/post/?v=4&url=', '&title='); - } - - function add_to_digg() - { - return $this->add_to_service('http://digg.com/submit?url=', '&title=', '&bodytext='); - } - - function add_to_furl() - { - return $this->add_to_service('http://www.furl.net/storeIt.jsp?u=', '&t='); - } - - function add_to_magnolia() - { - return $this->add_to_service('http://ma.gnolia.com/bookmarklet/add?url=', '&title='); - } - - function add_to_myweb20() - { - return $this->add_to_service('http://myweb2.search.yahoo.com/myresults/bookmarklet?u=', '&t='); - } - - function add_to_newsvine() - { - return $this->add_to_service('http://www.newsvine.com/_wine/save?u=', '&h='); - } - - function add_to_reddit() - { - return $this->add_to_service('http://reddit.com/submit?url=', '&title='); - } - - function add_to_segnalo() - { - return $this->add_to_service('http://segnalo.com/post.html.php?url=', '&title='); - } - - function add_to_simpy() - { - return $this->add_to_service('http://www.simpy.com/simpy/LinkAdd.do?href=', '&title='); - } - - function add_to_spurl() - { - return $this->add_to_service('http://www.spurl.net/spurl.php?v=3&url=', '&title='); - } - - function add_to_wists() - { - return $this->add_to_service('http://wists.com/r.php?c=&r=', '&title='); - } - - function search_technorati() - { - return $this->add_to_service('http://www.technorati.com/search/'); - } -} - -class SimplePie_Source -{ - var $item; - var $data = array(); - - function SimplePie_Source($item, $data) - { - $this->item = $item; - $this->data = $data; - } - - function __toString() - { - return md5(serialize($this->data)); - } - - function get_source_tags($namespace, $tag) - { - if (isset($this->data['child'][$namespace][$tag])) - { - return $this->data['child'][$namespace][$tag]; - } - else - { - return null; - } - } - - function get_base($element = array()) - { - return $this->item->get_base($element); - } - - function sanitize($data, $type, $base = '') - { - return $this->item->sanitize($data, $type, $base); - } - - function get_item() - { - return $this->item; - } - - function get_title() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - $categories = array(); - - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) - { - $term = null; - $scheme = null; - $label = null; - if (isset($category['attribs']['']['term'])) - { - $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['scheme'])) - { - $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($category['attribs']['']['label'])) - { - $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); - } - $categories[] = new $this->item->feed->category_class($term, $scheme, $label); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) - { - // This is really the label, but keep this as the term also for BC. - // Label will also work on retrieving because that falls back to term. - $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); - if (isset($category['attribs']['']['domain'])) - { - $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - $scheme = null; - } - $categories[] = new $this->item->feed->category_class($term, $scheme, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) - { - $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) - { - $categories[] = new $this->item->feed->category_class($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($categories)) - { - return SimplePie_Misc::array_unique($categories); - } - else - { - return null; - } - } - - function get_author($key = 0) - { - $authors = $this->get_authors(); - if (isset($authors[$key])) - { - return $authors[$key]; - } - else - { - return null; - } - } - - function get_authors() - { - $authors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) - { - $name = null; - $uri = null; - $email = null; - $avatar = null; - $name_date = null; - $uri_date = null; - $avatar_date = null; - - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'])) - { - $avatar = $this->sanitize($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar'][0])); - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data'])) - { - $name_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['name-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data'])) - { - $uri_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['uri-updated'][0]['data']; - } - if (isset($author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data'])) - { - $avatar_date = $author['child']['http://purl.org/macgirvin/dfrn/1.0']['avatar-updated'][0]['data']; - } - - if ($name !== null || $email !== null || $uri !== null || $avatar !== null || $name_date !== null || $uri_date !== null || $avatar_date !== null ) - { - $authors[] = new $this->item->feed->author_class($name, $uri, $email, $avatar, $name_date, $uri_date, $avatar_date); - } - } - if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) - { - $name = null; - $url = null; - $email = null; - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $authors[] = new $this->item->feed->author_class($name, $url, $email); - } - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) - { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) - { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) - { - $authors[] = new $this->item->feed->author_class($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null); - } - - if (!empty($authors)) - { - return SimplePie_Misc::array_unique($authors); - } - else - { - return null; - } - } - - function get_contributor($key = 0) - { - $contributors = $this->get_contributors(); - if (isset($contributors[$key])) - { - return $contributors[$key]; - } - else - { - return null; - } - } - - function get_contributors() - { - $contributors = array(); - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) - { - $name = null; - $uri = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) - { - $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $uri !== null) - { - $contributors[] = new $this->item->feed->author_class($name, $uri, $email); - } - } - foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) - { - $name = null; - $url = null; - $email = null; - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) - { - $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) - { - $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); - } - if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) - { - $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - if ($name !== null || $email !== null || $url !== null) - { - $contributors[] = new $this->item->feed->author_class($name, $url, $email); - } - } - - if (!empty($contributors)) - { - return SimplePie_Misc::array_unique($contributors); - } - else - { - return null; - } - } - - function get_link($key = 0, $rel = 'alternate') - { - $links = $this->get_links($rel); - if (isset($links[$key])) - { - return $links[$key]; - } - else - { - return null; - } - } - - /** - * Added for parity between the parent-level and the item/entry-level. - */ - function get_permalink() - { - return $this->get_link(0); - } - - function get_links($rel = 'alternate') - { - if (!isset($this->data['links'])) - { - $this->data['links'] = array(); - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - } - } - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) - { - foreach ($links as $link) - { - if (isset($link['attribs']['']['href'])) - { - $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; - $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); - - } - } - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) - { - $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); - } - - $keys = array_keys($this->data['links']); - foreach ($keys as $key) - { - if (SimplePie_Misc::is_isegment_nz_nc($key)) - { - if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); - $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; - } - else - { - $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; - } - } - elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) - { - $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; - } - $this->data['links'][$key] = array_unique($this->data['links'][$key]); - } - } - - if (isset($this->data['links'][$rel])) - { - return $this->data['links'][$rel]; - } - else - { - return null; - } - } - - function get_description() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); - } - else - { - return null; - } - } - - function get_copyright() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_10_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) - { - return $this->sanitize($return[0]['data'], SimplePie_Misc::atom_03_construct_type($return[0]['attribs']), $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_language() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); - } - elseif (isset($this->data['xml_lang'])) - { - return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); - } - else - { - return null; - } - } - - function get_latitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[1]; - } - else - { - return null; - } - } - - function get_longitude() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) - { - return (float) $return[0]['data']; - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) - { - return (float) $return[0]['data']; - } - elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) - { - return (float) $match[2]; - } - else - { - return null; - } - } - - function get_image_url() - { - if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) - { - return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) - { - return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); - } - else - { - return null; - } - } -} - -class SimplePie_Author -{ - var $name; - var $link; - var $email; - var $avatar; - var $name_date; - var $uri_date; - var $avatar_date; - - // Constructor, used to input the data - function SimplePie_Author($name = null, $link = null, $email = null, $avatar = null, $name_date = null, $uri_date = null, $avatar_date = null) - { - $this->name = $name; - $this->link = $link; - $this->email = $email; - $this->avatar = $avatar; - $this->name_date = $name_date; - $this->uri_date = $uri_date; - $this->avatar_date = $avatar_date; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_name() - { - if ($this->name !== null) - { - return $this->name; - } - else - { - return null; - } - } - - function get_link() - { - if ($this->link !== null) - { - return $this->link; - } - else - { - return null; - } - } - - function get_email() - { - if ($this->email !== null) - { - return $this->email; - } - else - { - return null; - } - } - - function get_avatar() - { - if ($this->avatar !== null) - { - return $this->avatar; - } - else - { - return null; - } - } - - function get_name_date() - { - if ($this->name_date !== null) - { - return $this->name_date; - } - else - { - return null; - } - } - function get_uri_date() - { - if ($this->uri_date !== null) - { - return $this->uri_date; - } - else - { - return null; - } - } - function get_avatar_date() - { - if ($this->avatar_date !== null) - { - return $this->avatar_date; - } - else - { - return null; - } - } - - -} - -class SimplePie_Category -{ - var $term; - var $scheme; - var $label; - - // Constructor, used to input the data - function SimplePie_Category($term = null, $scheme = null, $label = null) - { - $this->term = $term; - $this->scheme = $scheme; - $this->label = $label; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_term() - { - if ($this->term !== null) - { - return $this->term; - } - else - { - return null; - } - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_label() - { - if ($this->label !== null) - { - return $this->label; - } - else - { - return $this->get_term(); - } - } -} - -class SimplePie_Enclosure -{ - var $bitrate; - var $captions; - var $categories; - var $channels; - var $copyright; - var $credits; - var $description; - var $duration; - var $expression; - var $framerate; - var $handler; - var $hashes; - var $height; - var $javascript; - var $keywords; - var $lang; - var $length; - var $link; - var $medium; - var $player; - var $ratings; - var $restrictions; - var $samplingrate; - var $thumbnails; - var $title; - var $type; - var $width; - - // Constructor, used to input the data - function SimplePie_Enclosure($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) - { - $this->bitrate = $bitrate; - $this->captions = $captions; - $this->categories = $categories; - $this->channels = $channels; - $this->copyright = $copyright; - $this->credits = $credits; - $this->description = $description; - $this->duration = $duration; - $this->expression = $expression; - $this->framerate = $framerate; - $this->hashes = $hashes; - $this->height = $height; - $this->javascript = $javascript; - $this->keywords = $keywords; - $this->lang = $lang; - $this->length = $length; - $this->link = $link; - $this->medium = $medium; - $this->player = $player; - $this->ratings = $ratings; - $this->restrictions = $restrictions; - $this->samplingrate = $samplingrate; - $this->thumbnails = $thumbnails; - $this->title = $title; - $this->type = $type; - $this->width = $width; - if (class_exists('idna_convert')) - { - $idn = new idna_convert; - $parsed = SimplePie_Misc::parse_url($link); - $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); - } - $this->handler = $this->get_handler(); // Needs to load last - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_bitrate() - { - if ($this->bitrate !== null) - { - return $this->bitrate; - } - else - { - return null; - } - } - - function get_caption($key = 0) - { - $captions = $this->get_captions(); - if (isset($captions[$key])) - { - return $captions[$key]; - } - else - { - return null; - } - } - - function get_captions() - { - if ($this->captions !== null) - { - return $this->captions; - } - else - { - return null; - } - } - - function get_category($key = 0) - { - $categories = $this->get_categories(); - if (isset($categories[$key])) - { - return $categories[$key]; - } - else - { - return null; - } - } - - function get_categories() - { - if ($this->categories !== null) - { - return $this->categories; - } - else - { - return null; - } - } - - function get_channels() - { - if ($this->channels !== null) - { - return $this->channels; - } - else - { - return null; - } - } - - function get_copyright() - { - if ($this->copyright !== null) - { - return $this->copyright; - } - else - { - return null; - } - } - - function get_credit($key = 0) - { - $credits = $this->get_credits(); - if (isset($credits[$key])) - { - return $credits[$key]; - } - else - { - return null; - } - } - - function get_credits() - { - if ($this->credits !== null) - { - return $this->credits; - } - else - { - return null; - } - } - - function get_description() - { - if ($this->description !== null) - { - return $this->description; - } - else - { - return null; - } - } - - function get_duration($convert = false) - { - if ($this->duration !== null) - { - if ($convert) - { - $time = SimplePie_Misc::time_hms($this->duration); - return $time; - } - else - { - return $this->duration; - } - } - else - { - return null; - } - } - - function get_expression() - { - if ($this->expression !== null) - { - return $this->expression; - } - else - { - return 'full'; - } - } - - function get_extension() - { - if ($this->link !== null) - { - $url = SimplePie_Misc::parse_url($this->link); - if ($url['path'] !== '') - { - return pathinfo($url['path'], PATHINFO_EXTENSION); - } - } - return null; - } - - function get_framerate() - { - if ($this->framerate !== null) - { - return $this->framerate; - } - else - { - return null; - } - } - - function get_handler() - { - return $this->get_real_type(true); - } - - function get_hash($key = 0) - { - $hashes = $this->get_hashes(); - if (isset($hashes[$key])) - { - return $hashes[$key]; - } - else - { - return null; - } - } - - function get_hashes() - { - if ($this->hashes !== null) - { - return $this->hashes; - } - else - { - return null; - } - } - - function get_height() - { - if ($this->height !== null) - { - return $this->height; - } - else - { - return null; - } - } - - function get_language() - { - if ($this->lang !== null) - { - return $this->lang; - } - else - { - return null; - } - } - - function get_keyword($key = 0) - { - $keywords = $this->get_keywords(); - if (isset($keywords[$key])) - { - return $keywords[$key]; - } - else - { - return null; - } - } - - function get_keywords() - { - if ($this->keywords !== null) - { - return $this->keywords; - } - else - { - return null; - } - } - - function get_length() - { - if ($this->length !== null) - { - return $this->length; - } - else - { - return null; - } - } - - function get_link() - { - if ($this->link !== null) - { - return urldecode($this->link); - } - else - { - return null; - } - } - - function get_medium() - { - if ($this->medium !== null) - { - return $this->medium; - } - else - { - return null; - } - } - - function get_player() - { - if ($this->player !== null) - { - return $this->player; - } - else - { - return null; - } - } - - function get_rating($key = 0) - { - $ratings = $this->get_ratings(); - if (isset($ratings[$key])) - { - return $ratings[$key]; - } - else - { - return null; - } - } - - function get_ratings() - { - if ($this->ratings !== null) - { - return $this->ratings; - } - else - { - return null; - } - } - - function get_restriction($key = 0) - { - $restrictions = $this->get_restrictions(); - if (isset($restrictions[$key])) - { - return $restrictions[$key]; - } - else - { - return null; - } - } - - function get_restrictions() - { - if ($this->restrictions !== null) - { - return $this->restrictions; - } - else - { - return null; - } - } - - function get_sampling_rate() - { - if ($this->samplingrate !== null) - { - return $this->samplingrate; - } - else - { - return null; - } - } - - function get_size() - { - $length = $this->get_length(); - if ($length !== null) - { - return round($length/1048576, 2); - } - else - { - return null; - } - } - - function get_thumbnail($key = 0) - { - $thumbnails = $this->get_thumbnails(); - if (isset($thumbnails[$key])) - { - return $thumbnails[$key]; - } - else - { - return null; - } - } - - function get_thumbnails() - { - if ($this->thumbnails !== null) - { - return $this->thumbnails; - } - else - { - return null; - } - } - - function get_title() - { - if ($this->title !== null) - { - return $this->title; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } - - function get_width() - { - if ($this->width !== null) - { - return $this->width; - } - else - { - return null; - } - } - - function native_embed($options='') - { - return $this->embed($options, true); - } - - /** - * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. - */ - function embed($options = '', $native = false) - { - // Set up defaults - $audio = ''; - $video = ''; - $alt = ''; - $altclass = ''; - $loop = 'false'; - $width = 'auto'; - $height = 'auto'; - $bgcolor = '#ffffff'; - $mediaplayer = ''; - $widescreen = false; - $handler = $this->get_handler(); - $type = $this->get_real_type(); - - // Process options and reassign values as necessary - if (is_array($options)) - { - extract($options); - } - else - { - $options = explode(',', $options); - foreach($options as $option) - { - $opt = explode(':', $option, 2); - if (isset($opt[0], $opt[1])) - { - $opt[0] = trim($opt[0]); - $opt[1] = trim($opt[1]); - switch ($opt[0]) - { - case 'audio': - $audio = $opt[1]; - break; - - case 'video': - $video = $opt[1]; - break; - - case 'alt': - $alt = $opt[1]; - break; - - case 'altclass': - $altclass = $opt[1]; - break; - - case 'loop': - $loop = $opt[1]; - break; - - case 'width': - $width = $opt[1]; - break; - - case 'height': - $height = $opt[1]; - break; - - case 'bgcolor': - $bgcolor = $opt[1]; - break; - - case 'mediaplayer': - $mediaplayer = $opt[1]; - break; - - case 'widescreen': - $widescreen = $opt[1]; - break; - } - } - } - } - - $mime = explode('/', $type, 2); - $mime = $mime[0]; - - // Process values for 'auto' - if ($width === 'auto') - { - if ($mime === 'video') - { - if ($height === 'auto') - { - $width = 480; - } - elseif ($widescreen) - { - $width = round((intval($height)/9)*16); - } - else - { - $width = round((intval($height)/3)*4); - } - } - else - { - $width = '100%'; - } - } - - if ($height === 'auto') - { - if ($mime === 'audio') - { - $height = 0; - } - elseif ($mime === 'video') - { - if ($width === 'auto') - { - if ($widescreen) - { - $height = 270; - } - else - { - $height = 360; - } - } - elseif ($widescreen) - { - $height = round((intval($width)/16)*9); - } - else - { - $height = round((intval($width)/4)*3); - } - } - else - { - $height = 376; - } - } - elseif ($mime === 'audio') - { - $height = 0; - } - - // Set proper placeholder value - if ($mime === 'audio') - { - $placeholder = $audio; - } - elseif ($mime === 'video') - { - $placeholder = $video; - } - - $embed = ''; - - // Make sure the JS library is included - if (!$native) - { - static $javascript_outputted = null; - if (!$javascript_outputted && $this->javascript) - { - $embed .= ''; - $javascript_outputted = true; - } - } - - // Odeo Feed MP3's - if ($handler === 'odeo') - { - if ($native) - { - $embed .= ''; - } - else - { - $embed .= ''; - } - } - - // Flash - elseif ($handler === 'flash') - { - if ($native) - { - $embed .= "get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\">"; - } - else - { - $embed .= ""; - } - } - - // Flash Media Player file types. - // Preferred handler for MP3 file types. - elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) - { - $height += 20; - if ($native) - { - $embed .= "get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\">"; - } - else - { - $embed .= ""; - } - } - - // QuickTime 7 file types. Need to test with QuickTime 6. - // Only handle MP3's if the Flash Media Player is not present. - elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) - { - $height += 16; - if ($native) - { - if ($placeholder !== '') - { - $embed .= "get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; - } - else - { - $embed .= "get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; - } - } - else - { - $embed .= ""; - } - } - - // Windows Media - elseif ($handler === 'wmedia') - { - $height += 45; - if ($native) - { - $embed .= "get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\">"; - } - else - { - $embed .= ""; - } - } - - // Everything else - else $embed .= '' . $alt . ''; - - return $embed; - } - - function get_real_type($find_handler = false) - { - // If it's Odeo, let's get it out of the way. - if (substr(strtolower($this->get_link()), 0, 15) === 'http://odeo.com') - { - return 'odeo'; - } - - // Mime-types by handler. - $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash - $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player - $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime - $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media - $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 - - if ($this->get_type() !== null) - { - $type = strtolower($this->type); - } - else - { - $type = null; - } - - // If we encounter an unsupported mime-type, check the file extension and guess intelligently. - if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) - { - switch (strtolower($this->get_extension())) - { - // Audio mime-types - case 'aac': - case 'adts': - $type = 'audio/acc'; - break; - - case 'aif': - case 'aifc': - case 'aiff': - case 'cdda': - $type = 'audio/aiff'; - break; - - case 'bwf': - $type = 'audio/wav'; - break; - - case 'kar': - case 'mid': - case 'midi': - case 'smf': - $type = 'audio/midi'; - break; - - case 'm4a': - $type = 'audio/x-m4a'; - break; - - case 'mp3': - case 'swa': - $type = 'audio/mp3'; - break; - - case 'wav': - $type = 'audio/wav'; - break; - - case 'wax': - $type = 'audio/x-ms-wax'; - break; - - case 'wma': - $type = 'audio/x-ms-wma'; - break; - - // Video mime-types - case '3gp': - case '3gpp': - $type = 'video/3gpp'; - break; - - case '3g2': - case '3gp2': - $type = 'video/3gpp2'; - break; - - case 'asf': - $type = 'video/x-ms-asf'; - break; - - case 'flv': - $type = 'video/x-flv'; - break; - - case 'm1a': - case 'm1s': - case 'm1v': - case 'm15': - case 'm75': - case 'mp2': - case 'mpa': - case 'mpeg': - case 'mpg': - case 'mpm': - case 'mpv': - $type = 'video/mpeg'; - break; - - case 'm4v': - $type = 'video/x-m4v'; - break; - - case 'mov': - case 'qt': - $type = 'video/quicktime'; - break; - - case 'mp4': - case 'mpg4': - $type = 'video/mp4'; - break; - - case 'sdv': - $type = 'video/sd-video'; - break; - - case 'wm': - $type = 'video/x-ms-wm'; - break; - - case 'wmv': - $type = 'video/x-ms-wmv'; - break; - - case 'wvx': - $type = 'video/x-ms-wvx'; - break; - - // Flash mime-types - case 'spl': - $type = 'application/futuresplash'; - break; - - case 'swf': - $type = 'application/x-shockwave-flash'; - break; - } - } - - if ($find_handler) - { - if (in_array($type, $types_flash)) - { - return 'flash'; - } - elseif (in_array($type, $types_fmedia)) - { - return 'fmedia'; - } - elseif (in_array($type, $types_quicktime)) - { - return 'quicktime'; - } - elseif (in_array($type, $types_wmedia)) - { - return 'wmedia'; - } - elseif (in_array($type, $types_mp3)) - { - return 'mp3'; - } - else - { - return null; - } - } - else - { - return $type; - } - } -} - -class SimplePie_Caption -{ - var $type; - var $lang; - var $startTime; - var $endTime; - var $text; - - // Constructor, used to input the data - function SimplePie_Caption($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) - { - $this->type = $type; - $this->lang = $lang; - $this->startTime = $startTime; - $this->endTime = $endTime; - $this->text = $text; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_endtime() - { - if ($this->endTime !== null) - { - return $this->endTime; - } - else - { - return null; - } - } - - function get_language() - { - if ($this->lang !== null) - { - return $this->lang; - } - else - { - return null; - } - } - - function get_starttime() - { - if ($this->startTime !== null) - { - return $this->startTime; - } - else - { - return null; - } - } - - function get_text() - { - if ($this->text !== null) - { - return $this->text; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } -} - -class SimplePie_Credit -{ - var $role; - var $scheme; - var $name; - - // Constructor, used to input the data - function SimplePie_Credit($role = null, $scheme = null, $name = null) - { - $this->role = $role; - $this->scheme = $scheme; - $this->name = $name; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_role() - { - if ($this->role !== null) - { - return $this->role; - } - else - { - return null; - } - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_name() - { - if ($this->name !== null) - { - return $this->name; - } - else - { - return null; - } - } -} - -class SimplePie_Copyright -{ - var $url; - var $label; - - // Constructor, used to input the data - function SimplePie_Copyright($url = null, $label = null) - { - $this->url = $url; - $this->label = $label; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_url() - { - if ($this->url !== null) - { - return $this->url; - } - else - { - return null; - } - } - - function get_attribution() - { - if ($this->label !== null) - { - return $this->label; - } - else - { - return null; - } - } -} - -class SimplePie_Rating -{ - var $scheme; - var $value; - - // Constructor, used to input the data - function SimplePie_Rating($scheme = null, $value = null) - { - $this->scheme = $scheme; - $this->value = $value; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_scheme() - { - if ($this->scheme !== null) - { - return $this->scheme; - } - else - { - return null; - } - } - - function get_value() - { - if ($this->value !== null) - { - return $this->value; - } - else - { - return null; - } - } -} - -class SimplePie_Restriction -{ - var $relationship; - var $type; - var $value; - - // Constructor, used to input the data - function SimplePie_Restriction($relationship = null, $type = null, $value = null) - { - $this->relationship = $relationship; - $this->type = $type; - $this->value = $value; - } - - function __toString() - { - // There is no $this->data here - return md5(serialize($this)); - } - - function get_relationship() - { - if ($this->relationship !== null) - { - return $this->relationship; - } - else - { - return null; - } - } - - function get_type() - { - if ($this->type !== null) - { - return $this->type; - } - else - { - return null; - } - } - - function get_value() - { - if ($this->value !== null) - { - return $this->value; - } - else - { - return null; - } - } -} - -/** - * @todo Move to properly supporting RFC2616 (HTTP/1.1) - */ -class SimplePie_File -{ - var $url; - var $useragent; - var $success = true; - var $headers = array(); - var $body; - var $status_code; - var $redirects = 0; - var $error; - var $method = SIMPLEPIE_FILE_SOURCE_NONE; - - function SimplePie_File($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) - { - if (class_exists('idna_convert')) - { - $idn = new idna_convert; - $parsed = SimplePie_Misc::parse_url($url); - $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); - } - $this->url = $url; - $this->useragent = $useragent; - if (preg_match('/^http(s)?:\/\//i', $url)) - { - if ($useragent === null) - { - $useragent = ini_get('user_agent'); - $this->useragent = $useragent; - } - if (!is_array($headers)) - { - $headers = array(); - } - if (!$force_fsockopen && function_exists('curl_exec')) - { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; - $fp = curl_init(); - $headers2 = array(); - foreach ($headers as $key => $value) - { - $headers2[] = "$key: $value"; - } - if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) - { - curl_setopt($fp, CURLOPT_ENCODING, ''); - } - curl_setopt($fp, CURLOPT_URL, $url); - curl_setopt($fp, CURLOPT_HEADER, 1); - curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); - curl_setopt($fp, CURLOPT_REFERER, $url); - curl_setopt($fp, CURLOPT_USERAGENT, $useragent); - curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); - if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) - { - curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); - } - - $this->headers = curl_exec($fp); - if (curl_errno($fp) === 23 || curl_errno($fp) === 61) - { - curl_setopt($fp, CURLOPT_ENCODING, 'none'); - $this->headers = curl_exec($fp); - } - if (curl_errno($fp)) - { - $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); - $this->success = false; - } - else - { - $info = curl_getinfo($fp); - curl_close($fp); - $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); - $this->headers = array_pop($this->headers); - $parser = new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - } - } - } - else - { - $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; - $url_parts = parse_url($url); - if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') - { - $url_parts['host'] = "ssl://$url_parts[host]"; - $url_parts['port'] = 443; - } - if (!isset($url_parts['port'])) - { - $url_parts['port'] = 80; - } - $fp = @fsockopen($url_parts['host'], $url_parts['port'], $errno, $errstr, $timeout); - if (!$fp) - { - $this->error = 'fsockopen error: ' . $errstr; - $this->success = false; - } - else - { - stream_set_timeout($fp, $timeout); - if (isset($url_parts['path'])) - { - if (isset($url_parts['query'])) - { - $get = "$url_parts[path]?$url_parts[query]"; - } - else - { - $get = $url_parts['path']; - } - } - else - { - $get = '/'; - } - $out = "GET $get HTTP/1.0\r\n"; - $out .= "Host: $url_parts[host]\r\n"; - $out .= "User-Agent: $useragent\r\n"; - if (extension_loaded('zlib')) - { - $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; - } - - if (isset($url_parts['user']) && isset($url_parts['pass'])) - { - $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; - } - foreach ($headers as $key => $value) - { - $out .= "$key: $value\r\n"; - } - $out .= "Connection: Close\r\n\r\n"; - fwrite($fp, $out); - - $info = stream_get_meta_data($fp); - - $this->headers = ''; - while (!$info['eof'] && !$info['timed_out']) - { - $this->headers .= fread($fp, 1160); - $info = stream_get_meta_data($fp); - } - if (!$info['timed_out']) - { - $parser = new SimplePie_HTTP_Parser($this->headers); - if ($parser->parse()) - { - $this->headers = $parser->headers; - $this->body = $parser->body; - $this->status_code = $parser->status_code; - if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) - { - $this->redirects++; - $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->SimplePie_File($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - } - if (isset($this->headers['content-encoding'])) - { - // Hey, we act dumb elsewhere, so let's do that here too - switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) - { - case 'gzip': - case 'x-gzip': - $decoder = new SimplePie_gzdecode($this->body); - if (!$decoder->parse()) - { - $this->error = 'Unable to decode HTTP "gzip" stream'; - $this->success = false; - } - else - { - $this->body = $decoder->data; - } - break; - - case 'deflate': - if (($body = gzuncompress($this->body)) === false) - { - if (($body = gzinflate($this->body)) === false) - { - $this->error = 'Unable to decode HTTP "deflate" stream'; - $this->success = false; - } - } - $this->body = $body; - break; - - default: - $this->error = 'Unknown content coding'; - $this->success = false; - } - } - } - } - else - { - $this->error = 'fsocket timed out'; - $this->success = false; - } - fclose($fp); - } - } - } - else - { - $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; - if (!$this->body = file_get_contents($url)) - { - $this->error = 'file_get_contents could not read the file'; - $this->success = false; - } - } - } -} - -/** - * HTTP Response Parser - * - * @package SimplePie - */ -class SimplePie_HTTP_Parser -{ - /** - * HTTP Version - * - * @access public - * @var float - */ - var $http_version = 0.0; - - /** - * Status code - * - * @access public - * @var int - */ - var $status_code = 0; - - /** - * Reason phrase - * - * @access public - * @var string - */ - var $reason = ''; - - /** - * Key/value pairs of the headers - * - * @access public - * @var array - */ - var $headers = array(); - - /** - * Body of the response - * - * @access public - * @var string - */ - var $body = ''; - - /** - * Current state of the state machine - * - * @access private - * @var string - */ - var $state = 'http_version'; - - /** - * Input data - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Input data length (to avoid calling strlen() everytime this is needed) - * - * @access private - * @var int - */ - var $data_length = 0; - - /** - * Current position of the pointer - * - * @var int - * @access private - */ - var $position = 0; - - /** - * Name of the hedaer currently being parsed - * - * @access private - * @var string - */ - var $name = ''; - - /** - * Value of the hedaer currently being parsed - * - * @access private - * @var string - */ - var $value = ''; - - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_HTTP_Parser($data) - { - $this->data = $data; - $this->data_length = strlen($this->data); - } - - /** - * Parse the input data - * - * @access public - * @return bool true on success, false on failure - */ - function parse() - { - while ($this->state && $this->state !== 'emit' && $this->has_data()) - { - $state = $this->state; - $this->$state(); - } - $this->data = ''; - if ($this->state === 'emit' || $this->state === 'body') - { - return true; - } - else - { - $this->http_version = ''; - $this->status_code = ''; - $this->reason = ''; - $this->headers = array(); - $this->body = ''; - return false; - } - } - - /** - * Check whether there is data beyond the pointer - * - * @access private - * @return bool true if there is further data, false if not - */ - function has_data() - { - return (bool) ($this->position < $this->data_length); - } - - /** - * See if the next character is LWS - * - * @access private - * @return bool true if the next character is LWS, false if not - */ - function is_linear_whitespace() - { - return (bool) ($this->data[$this->position] === "\x09" - || $this->data[$this->position] === "\x20" - || ($this->data[$this->position] === "\x0A" - && isset($this->data[$this->position + 1]) - && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); - } - - /** - * Parse the HTTP version - * - * @access private - */ - function http_version() - { - if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') - { - $len = strspn($this->data, '0123456789.', 5); - $this->http_version = substr($this->data, 5, $len); - $this->position += 5 + $len; - if (substr_count($this->http_version, '.') <= 1) - { - $this->http_version = (float) $this->http_version; - $this->position += strspn($this->data, "\x09\x20", $this->position); - $this->state = 'status'; - } - else - { - $this->state = false; - } - } - else - { - $this->state = false; - } - } - - /** - * Parse the status code - * - * @access private - */ - function status() - { - if ($len = strspn($this->data, '0123456789', $this->position)) - { - $this->status_code = (int) substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'reason'; - } - else - { - $this->state = false; - } - } - - /** - * Parse the reason phrase - * - * @access private - */ - function reason() - { - $len = strcspn($this->data, "\x0A", $this->position); - $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); - $this->position += $len + 1; - $this->state = 'new_line'; - } - - /** - * Deal with a new line, shifting data around as needed - * - * @access private - */ - function new_line() - { - $this->value = trim($this->value, "\x0D\x20"); - if ($this->name !== '' && $this->value !== '') - { - $this->name = strtolower($this->name); - if (isset($this->headers[$this->name])) - { - $this->headers[$this->name] .= ', ' . $this->value; - } - else - { - $this->headers[$this->name] = $this->value; - } - } - $this->name = ''; - $this->value = ''; - if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") - { - $this->position += 2; - $this->state = 'body'; - } - elseif ($this->data[$this->position] === "\x0A") - { - $this->position++; - $this->state = 'body'; - } - else - { - $this->state = 'name'; - } - } - - /** - * Parse a header name - * - * @access private - */ - function name() - { - $len = strcspn($this->data, "\x0A:", $this->position); - if (isset($this->data[$this->position + $len])) - { - if ($this->data[$this->position + $len] === "\x0A") - { - $this->position += $len; - $this->state = 'new_line'; - } - else - { - $this->name = substr($this->data, $this->position, $len); - $this->position += $len + 1; - $this->state = 'value'; - } - } - else - { - $this->state = false; - } - } - - /** - * Parse LWS, replacing consecutive LWS characters with a single space - * - * @access private - */ - function linear_whitespace() - { - do - { - if (substr($this->data, $this->position, 2) === "\x0D\x0A") - { - $this->position += 2; - } - elseif ($this->data[$this->position] === "\x0A") - { - $this->position++; - } - $this->position += strspn($this->data, "\x09\x20", $this->position); - } while ($this->has_data() && $this->is_linear_whitespace()); - $this->value .= "\x20"; - } - - /** - * See what state to move to while within non-quoted header values - * - * @access private - */ - function value() - { - if ($this->is_linear_whitespace()) - { - $this->linear_whitespace(); - } - else - { - switch ($this->data[$this->position]) - { - case '"': - $this->position++; - $this->state = 'quote'; - break; - - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; - - default: - $this->state = 'value_char'; - break; - } - } - } - - /** - * Parse a header value while outside quotes - * - * @access private - */ - function value_char() - { - $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; - } - - /** - * See what state to move to while within quoted header values - * - * @access private - */ - function quote() - { - if ($this->is_linear_whitespace()) - { - $this->linear_whitespace(); - } - else - { - switch ($this->data[$this->position]) - { - case '"': - $this->position++; - $this->state = 'value'; - break; - - case "\x0A": - $this->position++; - $this->state = 'new_line'; - break; - - case '\\': - $this->position++; - $this->state = 'quote_escaped'; - break; - - default: - $this->state = 'quote_char'; - break; - } - } - } - - /** - * Parse a header value while within quotes - * - * @access private - */ - function quote_char() - { - $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); - $this->value .= substr($this->data, $this->position, $len); - $this->position += $len; - $this->state = 'value'; - } - - /** - * Parse an escaped character within quotes - * - * @access private - */ - function quote_escaped() - { - $this->value .= $this->data[$this->position]; - $this->position++; - $this->state = 'quote'; - } - - /** - * Parse the body - * - * @access private - */ - function body() - { - $this->body = substr($this->data, $this->position); - $this->state = 'emit'; - } -} - -/** - * gzdecode - * - * @package SimplePie - */ -class SimplePie_gzdecode -{ - /** - * Compressed data - * - * @access private - * @see gzdecode::$data - */ - var $compressed_data; - - /** - * Size of compressed data - * - * @access private - */ - var $compressed_size; - - /** - * Minimum size of a valid gzip string - * - * @access private - */ - var $min_compressed_size = 18; - - /** - * Current position of pointer - * - * @access private - */ - var $position = 0; - - /** - * Flags (FLG) - * - * @access private - */ - var $flags; - - /** - * Uncompressed data - * - * @access public - * @see gzdecode::$compressed_data - */ - var $data; - - /** - * Modified time - * - * @access public - */ - var $MTIME; - - /** - * Extra Flags - * - * @access public - */ - var $XFL; - - /** - * Operating System - * - * @access public - */ - var $OS; - - /** - * Subfield ID 1 - * - * @access public - * @see gzdecode::$extra_field - * @see gzdecode::$SI2 - */ - var $SI1; - - /** - * Subfield ID 2 - * - * @access public - * @see gzdecode::$extra_field - * @see gzdecode::$SI1 - */ - var $SI2; - - /** - * Extra field content - * - * @access public - * @see gzdecode::$SI1 - * @see gzdecode::$SI2 - */ - var $extra_field; - - /** - * Original filename - * - * @access public - */ - var $filename; - - /** - * Human readable comment - * - * @access public - */ - var $comment; - - /** - * Don't allow anything to be set - * - * @access public - */ - function __set($name, $value) - { - trigger_error("Cannot write property $name", E_USER_ERROR); - } - - /** - * Set the compressed string and related properties - * - * @access public - */ - function SimplePie_gzdecode($data) - { - $this->compressed_data = $data; - $this->compressed_size = strlen($data); - } - - /** - * Decode the GZIP stream - * - * @access public - */ - function parse() - { - if ($this->compressed_size >= $this->min_compressed_size) - { - // Check ID1, ID2, and CM - if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") - { - return false; - } - - // Get the FLG (FLaGs) - $this->flags = ord($this->compressed_data[3]); - - // FLG bits above (1 << 4) are reserved - if ($this->flags > 0x1F) - { - return false; - } - - // Advance the pointer after the above - $this->position += 4; - - // MTIME - $mtime = substr($this->compressed_data, $this->position, 4); - // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness - if (current(unpack('S', "\x00\x01")) === 1) - { - $mtime = strrev($mtime); - } - $this->MTIME = current(unpack('l', $mtime)); - $this->position += 4; - - // Get the XFL (eXtra FLags) - $this->XFL = ord($this->compressed_data[$this->position++]); - - // Get the OS (Operating System) - $this->OS = ord($this->compressed_data[$this->position++]); - - // Parse the FEXTRA - if ($this->flags & 4) - { - // Read subfield IDs - $this->SI1 = $this->compressed_data[$this->position++]; - $this->SI2 = $this->compressed_data[$this->position++]; - - // SI2 set to zero is reserved for future use - if ($this->SI2 === "\x00") - { - return false; - } - - // Get the length of the extra field - $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); - $position += 2; - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 4; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Set the extra field to the given data - $this->extra_field = substr($this->compressed_data, $this->position, $len); - $this->position += $len; - } - else - { - return false; - } - } - - // Parse the FNAME - if ($this->flags & 8) - { - // Get the length of the filename - $len = strcspn($this->compressed_data, "\x00", $this->position); - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 1; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Set the original filename to the given string - $this->filename = substr($this->compressed_data, $this->position, $len); - $this->position += $len + 1; - } - else - { - return false; - } - } - - // Parse the FCOMMENT - if ($this->flags & 16) - { - // Get the length of the comment - $len = strcspn($this->compressed_data, "\x00", $this->position); - - // Check the length of the string is still valid - $this->min_compressed_size += $len + 1; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Set the original comment to the given string - $this->comment = substr($this->compressed_data, $this->position, $len); - $this->position += $len + 1; - } - else - { - return false; - } - } - - // Parse the FHCRC - if ($this->flags & 2) - { - // Check the length of the string is still valid - $this->min_compressed_size += $len + 2; - if ($this->compressed_size >= $this->min_compressed_size) - { - // Read the CRC - $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); - - // Check the CRC matches - if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) - { - $this->position += 2; - } - else - { - return false; - } - } - else - { - return false; - } - } - - // Decompress the actual data - if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) - { - return false; - } - else - { - $this->position = $this->compressed_size - 8; - } - - // Check CRC of data - $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); - $this->position += 4; - /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) - { - return false; - }*/ - - // Check ISIZE of data - $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); - $this->position += 4; - if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) - { - return false; - } - - // Wow, against all odds, we've actually got a valid gzip string - return true; - } - else - { - return false; - } - } -} - -class SimplePie_Cache -{ - /** - * Don't call the constructor. Please. - * - * @access private - */ - function SimplePie_Cache() - { - trigger_error('Please call SimplePie_Cache::create() instead of the constructor', E_USER_ERROR); - } - - /** - * Create a new SimplePie_Cache object - * - * @static - * @access public - */ - function create($location, $filename, $extension) - { - $location_iri = new SimplePie_IRI($location); - switch ($location_iri->get_scheme()) - { - case 'mysql': - if (extension_loaded('mysql')) - { - return new SimplePie_Cache_MySQL($location_iri, $filename, $extension); - } - break; - - default: - return new SimplePie_Cache_File($location, $filename, $extension); - } - } -} - -class SimplePie_Cache_File -{ - var $location; - var $filename; - var $extension; - var $name; - - function SimplePie_Cache_File($location, $filename, $extension) - { - $this->location = $location; - $this->filename = $filename; - $this->extension = $extension; - $this->name = "$this->location/$this->filename.$this->extension"; - } - - function save($data) - { - if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) - { - if (is_a($data, 'SimplePie')) - { - $data = $data->data; - } - - $data = serialize($data); - - if (function_exists('file_put_contents')) - { - return (bool) file_put_contents($this->name, $data); - } - else - { - $fp = fopen($this->name, 'wb'); - if ($fp) - { - fwrite($fp, $data); - fclose($fp); - return true; - } - } - } - return false; - } - - function load() - { - if (file_exists($this->name) && is_readable($this->name)) - { - return unserialize(file_get_contents($this->name)); - } - return false; - } - - function mtime() - { - if (file_exists($this->name)) - { - return filemtime($this->name); - } - return false; - } - - function touch() - { - if (file_exists($this->name)) - { - return touch($this->name); - } - return false; - } - - function unlink() - { - if (file_exists($this->name)) - { - return unlink($this->name); - } - return false; - } -} - -class SimplePie_Cache_DB -{ - function prepare_simplepie_object_for_cache($data) - { - $items = $data->get_items(); - $items_by_id = array(); - - if (!empty($items)) - { - foreach ($items as $item) - { - $items_by_id[$item->get_id()] = $item; - } - - if (count($items_by_id) !== count($items)) - { - $items_by_id = array(); - foreach ($items as $item) - { - $items_by_id[$item->get_id(true)] = $item; - } - } - - if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; - } - elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) - { - $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; - } - else - { - $channel = null; - } - - if ($channel !== null) - { - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) - { - unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); - } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) - { - unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); - } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) - { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); - } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) - { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); - } - if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) - { - unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); - } - } - if (isset($data->data['items'])) - { - unset($data->data['items']); - } - if (isset($data->data['ordered_items'])) - { - unset($data->data['ordered_items']); - } - } - return array(serialize($data->data), $items_by_id); - } -} - -class SimplePie_Cache_MySQL extends SimplePie_Cache_DB -{ - var $mysql; - var $options; - var $id; - - function SimplePie_Cache_MySQL($mysql_location, $name, $extension) - { - $host = $mysql_location->get_host(); - if (SimplePie_Misc::stripos($host, 'unix(') === 0 && substr($host, -1) === ')') - { - $server = ':' . substr($host, 5, -1); - } - else - { - $server = $host; - if ($mysql_location->get_port() !== null) - { - $server .= ':' . $mysql_location->get_port(); - } - } - - if (strpos($mysql_location->get_userinfo(), ':') !== false) - { - list($username, $password) = explode(':', $mysql_location->get_userinfo(), 2); - } - else - { - $username = $mysql_location->get_userinfo(); - $password = null; - } - - if ($this->mysql = mysql_connect($server, $username, $password)) - { - $this->id = $name . $extension; - $this->options = SimplePie_Misc::parse_str($mysql_location->get_query()); - if (!isset($this->options['prefix'][0])) - { - $this->options['prefix'][0] = ''; - } - - if (mysql_select_db(ltrim($mysql_location->get_path(), '/')) - && mysql_query('SET NAMES utf8') - && ($query = mysql_unbuffered_query('SHOW TABLES'))) - { - $db = array(); - while ($row = mysql_fetch_row($query)) - { - $db[] = $row[0]; - } - - if (!in_array($this->options['prefix'][0] . 'cache_data', $db)) - { - if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))')) - { - $this->mysql = null; - } - } - - if (!in_array($this->options['prefix'][0] . 'items', $db)) - { - if (!mysql_query('CREATE TABLE `' . $this->options['prefix'][0] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))')) - { - $this->mysql = null; - } - } - } - else - { - $this->mysql = null; - } - } - } - - function save($data) - { - if ($this->mysql) - { - $feed_id = "'" . mysql_real_escape_string($this->id) . "'"; - - if (is_a($data, 'SimplePie')) - { - if (SIMPLEPIE_PHP5) - { - // This keyword needs to defy coding standards for PHP4 compatibility - $data = clone($data); - } - - $prepared = $this->prepare_simplepie_object_for_cache($data); - - if ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql)) - { - if (mysql_num_rows($query)) - { - $items = count($prepared[1]); - if ($items) - { - $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = ' . $items . ', `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id; - } - else - { - $sql = 'UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `data` = \'' . mysql_real_escape_string($prepared[0]) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id; - } - - if (!mysql_query($sql, $this->mysql)) - { - return false; - } - } - elseif (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(' . $feed_id . ', ' . count($prepared[1]) . ', \'' . mysql_real_escape_string($prepared[0]) . '\', ' . time() . ')', $this->mysql)) - { - return false; - } - - $ids = array_keys($prepared[1]); - if (!empty($ids)) - { - foreach ($ids as $id) - { - $database_ids[] = mysql_real_escape_string($id); - } - - if ($query = mysql_unbuffered_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'items` WHERE `id` = \'' . implode('\' OR `id` = \'', $database_ids) . '\' AND `feed_id` = ' . $feed_id, $this->mysql)) - { - $existing_ids = array(); - while ($row = mysql_fetch_row($query)) - { - $existing_ids[] = $row[0]; - } - - $new_ids = array_diff($ids, $existing_ids); - - foreach ($new_ids as $new_id) - { - if (!($date = $prepared[1][$new_id]->get_date('U'))) - { - $date = time(); - } - - if (!mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(' . $feed_id . ', \'' . mysql_real_escape_string($new_id) . '\', \'' . mysql_real_escape_string(serialize($prepared[1][$new_id]->data)) . '\', ' . $date . ')', $this->mysql)) - { - return false; - } - } - return true; - } - } - else - { - return true; - } - } - } - elseif ($query = mysql_query('SELECT `id` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = ' . $feed_id, $this->mysql)) - { - if (mysql_num_rows($query)) - { - if (mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `items` = 0, `data` = \'' . mysql_real_escape_string(serialize($data)) . '\', `mtime` = ' . time() . ' WHERE `id` = ' . $feed_id, $this->mysql)) - { - return true; - } - } - elseif (mysql_query('INSERT INTO `' . $this->options['prefix'][0] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(\'' . mysql_real_escape_string($this->id) . '\', 0, \'' . mysql_real_escape_string(serialize($data)) . '\', ' . time() . ')', $this->mysql)) - { - return true; - } - } - } - return false; - } - - function load() - { - if ($this->mysql && ($query = mysql_query('SELECT `items`, `data` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query))) - { - $data = unserialize($row[1]); - - if (isset($this->options['items'][0])) - { - $items = (int) $this->options['items'][0]; - } - else - { - $items = (int) $row[0]; - } - - if ($items !== 0) - { - if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; - } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; - } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; - } - elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0])) - { - $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]; - } - else - { - $feed = null; - } - - if ($feed !== null) - { - $sql = 'SELECT `data` FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . '\' ORDER BY `posted` DESC'; - if ($items > 0) - { - $sql .= ' LIMIT ' . $items; - } - - if ($query = mysql_unbuffered_query($sql, $this->mysql)) - { - while ($row = mysql_fetch_row($query)) - { - $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row[0]); - } - } - else - { - return false; - } - } - } - return $data; - } - return false; - } - - function mtime() - { - if ($this->mysql && ($query = mysql_query('SELECT `mtime` FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($row = mysql_fetch_row($query))) - { - return $row[0]; - } - else - { - return false; - } - } - - function touch() - { - if ($this->mysql && ($query = mysql_query('UPDATE `' . $this->options['prefix'][0] . 'cache_data` SET `mtime` = ' . time() . ' WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && mysql_affected_rows($this->mysql)) - { - return true; - } - else - { - return false; - } - } - - function unlink() - { - if ($this->mysql && ($query = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'cache_data` WHERE `id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql)) && ($query2 = mysql_query('DELETE FROM `' . $this->options['prefix'][0] . 'items` WHERE `feed_id` = \'' . mysql_real_escape_string($this->id) . "'", $this->mysql))) - { - return true; - } - else - { - return false; - } - } -} - -class SimplePie_Misc -{ - function time_hms($seconds) - { - $time = ''; - - $hours = floor($seconds / 3600); - $remainder = $seconds % 3600; - if ($hours > 0) - { - $time .= $hours.':'; - } - - $minutes = floor($remainder / 60); - $seconds = $remainder % 60; - if ($minutes < 10 && $hours > 0) - { - $minutes = '0' . $minutes; - } - if ($seconds < 10) - { - $seconds = '0' . $seconds; - } - - $time .= $minutes.':'; - $time .= $seconds; - - return $time; - } - - function absolutize_url($relative, $base) - { -return $relative; - $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); - return $iri->get_iri(); - } - - function remove_dot_segments($input) - { - $output = ''; - while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') - { - // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, - if (strpos($input, '../') === 0) - { - $input = substr($input, 3); - } - elseif (strpos($input, './') === 0) - { - $input = substr($input, 2); - } - // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, - elseif (strpos($input, '/./') === 0) - { - $input = substr_replace($input, '/', 0, 3); - } - elseif ($input === '/.') - { - $input = '/'; - } - // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, - elseif (strpos($input, '/../') === 0) - { - $input = substr_replace($input, '/', 0, 4); - $output = substr_replace($output, '', strrpos($output, '/')); - } - elseif ($input === '/..') - { - $input = '/'; - $output = substr_replace($output, '', strrpos($output, '/')); - } - // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, - elseif ($input === '.' || $input === '..') - { - $input = ''; - } - // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer - elseif (($pos = strpos($input, '/', 1)) !== false) - { - $output .= substr($input, 0, $pos); - $input = substr_replace($input, '', 0, $pos); - } - else - { - $output .= $input; - $input = ''; - } - } - return $output . $input; - } - - function get_element($realname, $string) - { - $return = array(); - $name = preg_quote($realname, '/'); - if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) - { - for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) - { - $return[$i]['tag'] = $realname; - $return[$i]['full'] = $matches[$i][0][0]; - $return[$i]['offset'] = $matches[$i][0][1]; - if (strlen($matches[$i][3][0]) <= 2) - { - $return[$i]['self_closing'] = true; - } - else - { - $return[$i]['self_closing'] = false; - $return[$i]['content'] = $matches[$i][4][0]; - } - $return[$i]['attribs'] = array(); - if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) - { - for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) - { - if (count($attribs[$j]) === 2) - { - $attribs[$j][2] = $attribs[$j][1]; - } - $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); - } - } - } - } - return $return; - } - - function element_implode($element) - { - $full = "<$element[tag]"; - foreach ($element['attribs'] as $key => $value) - { - $key = strtolower($key); - $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; - } - if ($element['self_closing']) - { - $full .= ' />'; - } - else - { - $full .= ">$element[content]"; - } - return $full; - } - - function error($message, $level, $file, $line) - { - if ((ini_get('error_reporting') & $level) > 0) - { - switch ($level) - { - case E_USER_ERROR: - $note = 'PHP Error'; - break; - case E_USER_WARNING: - $note = 'PHP Warning'; - break; - case E_USER_NOTICE: - $note = 'PHP Notice'; - break; - default: - $note = 'Unknown Error'; - break; - } - - $log_error = true; - if (!function_exists('error_log')) - { - $log_error = false; - } - - $log_file = @ini_get('error_log'); - if (!empty($log_file) && ('syslog' != $log_file) && !@is_writable($log_file)) - { - $log_error = false; - } - - if ($log_error) - { - @error_log("$note: $message in $file on line $line", 0); - } - } - - return $message; - } - - /** - * If a file has been cached, retrieve and display it. - * - * This is most useful for caching images (get_favicon(), etc.), - * however it works for all cached files. This WILL NOT display ANY - * file/image/page/whatever, but rather only display what has already - * been cached by SimplePie. - * - * @access public - * @see SimplePie::get_favicon() - * @param str $identifier_url URL that is used to identify the content. - * This may or may not be the actual URL of the live content. - * @param str $cache_location Location of SimplePie's cache. Defaults - * to './cache'. - * @param str $cache_extension The file extension that the file was - * cached with. Defaults to 'spc'. - * @param str $cache_class Name of the cache-handling class being used - * in SimplePie. Defaults to 'SimplePie_Cache', and should be left - * as-is unless you've overloaded the class. - * @param str $cache_name_function Obsolete. Exists for backwards - * compatibility reasons only. - */ - function display_cached_file($identifier_url, $cache_location = './cache', $cache_extension = 'spc', $cache_class = 'SimplePie_Cache', $cache_name_function = 'md5') - { - $cache = call_user_func(array($cache_class, 'create'), $cache_location, $identifier_url, $cache_extension); - - if ($file = $cache->load()) - { - if (isset($file['headers']['content-type'])) - { - header('Content-type:' . $file['headers']['content-type']); - } - else - { - header('Content-type: application/octet-stream'); - } - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - echo $file['body']; - exit; - } - - die('Cached file for ' . $identifier_url . ' cannot be found.'); - } - - function fix_protocol($url, $http = 1) - { - $url = SimplePie_Misc::normalize_url($url); - $parsed = SimplePie_Misc::parse_url($url); - if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); - } - - if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) - { - return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); - } - - if ($http === 2 && $parsed['scheme'] !== '') - { - return "feed:$url"; - } - elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') - { - return substr_replace($url, 'podcast', 0, 4); - } - elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') - { - return substr_replace($url, 'itpc', 0, 4); - } - else - { - return $url; - } - } - - function parse_url($url) - { - $iri = new SimplePie_IRI($url); - return array( - 'scheme' => (string) $iri->get_scheme(), - 'authority' => (string) $iri->get_authority(), - 'path' => (string) $iri->get_path(), - 'query' => (string) $iri->get_query(), - 'fragment' => (string) $iri->get_fragment() - ); - } - - function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') - { - $iri = new SimplePie_IRI(''); - $iri->set_scheme($scheme); - $iri->set_authority($authority); - $iri->set_path($path); - $iri->set_query($query); - $iri->set_fragment($fragment); - return $iri->get_iri(); - } - - function normalize_url($url) - { - $iri = new SimplePie_IRI($url); - return $iri->get_iri(); - } - - function percent_encoding_normalization($match) - { - $integer = hexdec($match[1]); - if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) - { - return chr($integer); - } - else - { - return strtoupper($match[0]); - } - } - - /** - * Remove bad UTF-8 bytes - * - * PCRE Pattern to locate bad bytes in a UTF-8 string comes from W3C - * FAQ: Multilingual Forms (modified to include full ASCII range) - * - * @author Geoffrey Sneddon - * @see http://www.w3.org/International/questions/qa-forms-utf-8 - * @param string $str String to remove bad UTF-8 bytes from - * @return string UTF-8 string - */ - function utf8_bad_replace($str) - { - if (function_exists('iconv') && ($return = @iconv('UTF-8', 'UTF-8//IGNORE', $str))) - { - return $return; - } - elseif (function_exists('mb_convert_encoding') && ($return = @mb_convert_encoding($str, 'UTF-8', 'UTF-8'))) - { - return $return; - } - elseif (preg_match_all('/(?:[\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})+/', $str, $matches)) - { - return implode("\xEF\xBF\xBD", $matches[0]); - } - elseif ($str !== '') - { - return "\xEF\xBF\xBD"; - } - else - { - return ''; - } - } - - /** - * Converts a Windows-1252 encoded string to a UTF-8 encoded string - * - * @static - * @access public - * @param string $string Windows-1252 encoded string - * @return string UTF-8 encoded string - */ - function windows_1252_to_utf8($string) - { - static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); - - return strtr($string, $convert_table); - } - - function change_encoding($data, $input, $output) - { - $input = SimplePie_Misc::encoding($input); - $output = SimplePie_Misc::encoding($output); - - // We fail to fail on non US-ASCII bytes - if ($input === 'US-ASCII') - { - static $non_ascii_octects = ''; - if (!$non_ascii_octects) - { - for ($i = 0x80; $i <= 0xFF; $i++) - { - $non_ascii_octects .= chr($i); - } - } - $data = substr($data, 0, strcspn($data, $non_ascii_octects)); - } - - // This is first, as behaviour of this is completely predictable - if ($input === 'Windows-1252' && $output === 'UTF-8') - { - return SimplePie_Misc::windows_1252_to_utf8($data); - } - // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported). - elseif (function_exists('mb_convert_encoding') && @mb_convert_encoding("\x80", 'UTF-16BE', $input) !== "\x00\x80" && ($return = @mb_convert_encoding($data, $output, $input))) - { - return $return; - } - // This is last, as behaviour of this varies with OS userland and PHP version - elseif (function_exists('iconv') && ($return = @iconv($input, $output, $data))) - { - return $return; - } - // If we can't do anything, just fail - else - { - return false; - } - } - - function encoding($charset) - { - // Normalization from UTS #22 - switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) - { - case 'adobestandardencoding': - case 'csadobestandardencoding': - return 'Adobe-Standard-Encoding'; - - case 'adobesymbolencoding': - case 'cshppsmath': - return 'Adobe-Symbol-Encoding'; - - case 'ami1251': - case 'amiga1251': - return 'Amiga-1251'; - - case 'ansix31101983': - case 'csat5001983': - case 'csiso99naplps': - case 'isoir99': - case 'naplps': - return 'ANSI_X3.110-1983'; - - case 'arabic7': - case 'asmo449': - case 'csiso89asmo449': - case 'iso9036': - case 'isoir89': - return 'ASMO_449'; - - case 'big5': - case 'csbig5': - case 'xxbig5': - return 'Big5'; - - case 'big5hkscs': - return 'Big5-HKSCS'; - - case 'bocu1': - case 'csbocu1': - return 'BOCU-1'; - - case 'brf': - case 'csbrf': - return 'BRF'; - - case 'bs4730': - case 'csiso4unitedkingdom': - case 'gb': - case 'iso646gb': - case 'isoir4': - case 'uk': - return 'BS_4730'; - - case 'bsviewdata': - case 'csiso47bsviewdata': - case 'isoir47': - return 'BS_viewdata'; - - case 'cesu8': - case 'cscesu8': - return 'CESU-8'; - - case 'ca': - case 'csa71': - case 'csaz243419851': - case 'csiso121canadian1': - case 'iso646ca': - case 'isoir121': - return 'CSA_Z243.4-1985-1'; - - case 'csa72': - case 'csaz243419852': - case 'csiso122canadian2': - case 'iso646ca2': - case 'isoir122': - return 'CSA_Z243.4-1985-2'; - - case 'csaz24341985gr': - case 'csiso123csaz24341985gr': - case 'isoir123': - return 'CSA_Z243.4-1985-gr'; - - case 'csiso139csn369103': - case 'csn369103': - case 'isoir139': - return 'CSN_369103'; - - case 'csdecmcs': - case 'dec': - case 'decmcs': - return 'DEC-MCS'; - - case 'csiso21german': - case 'de': - case 'din66003': - case 'iso646de': - case 'isoir21': - return 'DIN_66003'; - - case 'csdkus': - case 'dkus': - return 'dk-us'; - - case 'csiso646danish': - case 'dk': - case 'ds2089': - case 'iso646dk': - return 'DS_2089'; - - case 'csibmebcdicatde': - case 'ebcdicatde': - return 'EBCDIC-AT-DE'; - - case 'csebcdicatdea': - case 'ebcdicatdea': - return 'EBCDIC-AT-DE-A'; - - case 'csebcdiccafr': - case 'ebcdiccafr': - return 'EBCDIC-CA-FR'; - - case 'csebcdicdkno': - case 'ebcdicdkno': - return 'EBCDIC-DK-NO'; - - case 'csebcdicdknoa': - case 'ebcdicdknoa': - return 'EBCDIC-DK-NO-A'; - - case 'csebcdices': - case 'ebcdices': - return 'EBCDIC-ES'; - - case 'csebcdicesa': - case 'ebcdicesa': - return 'EBCDIC-ES-A'; - - case 'csebcdicess': - case 'ebcdicess': - return 'EBCDIC-ES-S'; - - case 'csebcdicfise': - case 'ebcdicfise': - return 'EBCDIC-FI-SE'; - - case 'csebcdicfisea': - case 'ebcdicfisea': - return 'EBCDIC-FI-SE-A'; - - case 'csebcdicfr': - case 'ebcdicfr': - return 'EBCDIC-FR'; - - case 'csebcdicit': - case 'ebcdicit': - return 'EBCDIC-IT'; - - case 'csebcdicpt': - case 'ebcdicpt': - return 'EBCDIC-PT'; - - case 'csebcdicuk': - case 'ebcdicuk': - return 'EBCDIC-UK'; - - case 'csebcdicus': - case 'ebcdicus': - return 'EBCDIC-US'; - - case 'csiso111ecmacyrillic': - case 'ecmacyrillic': - case 'isoir111': - case 'koi8e': - return 'ECMA-cyrillic'; - - case 'csiso17spanish': - case 'es': - case 'iso646es': - case 'isoir17': - return 'ES'; - - case 'csiso85spanish2': - case 'es2': - case 'iso646es2': - case 'isoir85': - return 'ES2'; - - case 'cseucfixwidjapanese': - case 'extendedunixcodefixedwidthforjapanese': - return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; - - case 'cseucpkdfmtjapanese': - case 'eucjp': - case 'extendedunixcodepackedformatforjapanese': - return 'Extended_UNIX_Code_Packed_Format_for_Japanese'; - - case 'gb18030': - return 'GB18030'; - - case 'chinese': - case 'cp936': - case 'csgb2312': - case 'csiso58gb231280': - case 'gb2312': - case 'gb231280': - case 'gbk': - case 'isoir58': - case 'ms936': - case 'windows936': - return 'GBK'; - - case 'cn': - case 'csiso57gb1988': - case 'gb198880': - case 'iso646cn': - case 'isoir57': - return 'GB_1988-80'; - - case 'csiso153gost1976874': - case 'gost1976874': - case 'isoir153': - case 'stsev35888': - return 'GOST_19768-74'; - - case 'csiso150': - case 'csiso150greekccitt': - case 'greekccitt': - case 'isoir150': - return 'greek-ccitt'; - - case 'csiso88greek7': - case 'greek7': - case 'isoir88': - return 'greek7'; - - case 'csiso18greek7old': - case 'greek7old': - case 'isoir18': - return 'greek7-old'; - - case 'cshpdesktop': - case 'hpdesktop': - return 'HP-DeskTop'; - - case 'cshplegal': - case 'hplegal': - return 'HP-Legal'; - - case 'cshpmath8': - case 'hpmath8': - return 'HP-Math8'; - - case 'cshppifont': - case 'hppifont': - return 'HP-Pi-font'; - - case 'cshproman8': - case 'hproman8': - case 'r8': - case 'roman8': - return 'hp-roman8'; - - case 'hzgb2312': - return 'HZ-GB-2312'; - - case 'csibmsymbols': - case 'ibmsymbols': - return 'IBM-Symbols'; - - case 'csibmthai': - case 'ibmthai': - return 'IBM-Thai'; - - case 'ccsid858': - case 'cp858': - case 'ibm858': - case 'pcmultilingual850euro': - return 'IBM00858'; - - case 'ccsid924': - case 'cp924': - case 'ebcdiclatin9euro': - case 'ibm924': - return 'IBM00924'; - - case 'ccsid1140': - case 'cp1140': - case 'ebcdicus37euro': - case 'ibm1140': - return 'IBM01140'; - - case 'ccsid1141': - case 'cp1141': - case 'ebcdicde273euro': - case 'ibm1141': - return 'IBM01141'; - - case 'ccsid1142': - case 'cp1142': - case 'ebcdicdk277euro': - case 'ebcdicno277euro': - case 'ibm1142': - return 'IBM01142'; - - case 'ccsid1143': - case 'cp1143': - case 'ebcdicfi278euro': - case 'ebcdicse278euro': - case 'ibm1143': - return 'IBM01143'; - - case 'ccsid1144': - case 'cp1144': - case 'ebcdicit280euro': - case 'ibm1144': - return 'IBM01144'; - - case 'ccsid1145': - case 'cp1145': - case 'ebcdices284euro': - case 'ibm1145': - return 'IBM01145'; - - case 'ccsid1146': - case 'cp1146': - case 'ebcdicgb285euro': - case 'ibm1146': - return 'IBM01146'; - - case 'ccsid1147': - case 'cp1147': - case 'ebcdicfr297euro': - case 'ibm1147': - return 'IBM01147'; - - case 'ccsid1148': - case 'cp1148': - case 'ebcdicinternational500euro': - case 'ibm1148': - return 'IBM01148'; - - case 'ccsid1149': - case 'cp1149': - case 'ebcdicis871euro': - case 'ibm1149': - return 'IBM01149'; - - case 'cp37': - case 'csibm37': - case 'ebcdiccpca': - case 'ebcdiccpnl': - case 'ebcdiccpus': - case 'ebcdiccpwt': - case 'ibm37': - return 'IBM037'; - - case 'cp38': - case 'csibm38': - case 'ebcdicint': - case 'ibm38': - return 'IBM038'; - - case 'cp273': - case 'csibm273': - case 'ibm273': - return 'IBM273'; - - case 'cp274': - case 'csibm274': - case 'ebcdicbe': - case 'ibm274': - return 'IBM274'; - - case 'cp275': - case 'csibm275': - case 'ebcdicbr': - case 'ibm275': - return 'IBM275'; - - case 'csibm277': - case 'ebcdiccpdk': - case 'ebcdiccpno': - case 'ibm277': - return 'IBM277'; - - case 'cp278': - case 'csibm278': - case 'ebcdiccpfi': - case 'ebcdiccpse': - case 'ibm278': - return 'IBM278'; - - case 'cp280': - case 'csibm280': - case 'ebcdiccpit': - case 'ibm280': - return 'IBM280'; - - case 'cp281': - case 'csibm281': - case 'ebcdicjpe': - case 'ibm281': - return 'IBM281'; - - case 'cp284': - case 'csibm284': - case 'ebcdiccpes': - case 'ibm284': - return 'IBM284'; - - case 'cp285': - case 'csibm285': - case 'ebcdiccpgb': - case 'ibm285': - return 'IBM285'; - - case 'cp290': - case 'csibm290': - case 'ebcdicjpkana': - case 'ibm290': - return 'IBM290'; - - case 'cp297': - case 'csibm297': - case 'ebcdiccpfr': - case 'ibm297': - return 'IBM297'; - - case 'cp420': - case 'csibm420': - case 'ebcdiccpar1': - case 'ibm420': - return 'IBM420'; - - case 'cp423': - case 'csibm423': - case 'ebcdiccpgr': - case 'ibm423': - return 'IBM423'; - - case 'cp424': - case 'csibm424': - case 'ebcdiccphe': - case 'ibm424': - return 'IBM424'; - - case '437': - case 'cp437': - case 'cspc8codepage437': - case 'ibm437': - return 'IBM437'; - - case 'cp500': - case 'csibm500': - case 'ebcdiccpbe': - case 'ebcdiccpch': - case 'ibm500': - return 'IBM500'; - - case 'cp775': - case 'cspc775baltic': - case 'ibm775': - return 'IBM775'; - - case '850': - case 'cp850': - case 'cspc850multilingual': - case 'ibm850': - return 'IBM850'; - - case '851': - case 'cp851': - case 'csibm851': - case 'ibm851': - return 'IBM851'; - - case '852': - case 'cp852': - case 'cspcp852': - case 'ibm852': - return 'IBM852'; - - case '855': - case 'cp855': - case 'csibm855': - case 'ibm855': - return 'IBM855'; - - case '857': - case 'cp857': - case 'csibm857': - case 'ibm857': - return 'IBM857'; - - case '860': - case 'cp860': - case 'csibm860': - case 'ibm860': - return 'IBM860'; - - case '861': - case 'cp861': - case 'cpis': - case 'csibm861': - case 'ibm861': - return 'IBM861'; - - case '862': - case 'cp862': - case 'cspc862latinhebrew': - case 'ibm862': - return 'IBM862'; - - case '863': - case 'cp863': - case 'csibm863': - case 'ibm863': - return 'IBM863'; - - case 'cp864': - case 'csibm864': - case 'ibm864': - return 'IBM864'; - - case '865': - case 'cp865': - case 'csibm865': - case 'ibm865': - return 'IBM865'; - - case '866': - case 'cp866': - case 'csibm866': - case 'ibm866': - return 'IBM866'; - - case 'cp868': - case 'cpar': - case 'csibm868': - case 'ibm868': - return 'IBM868'; - - case '869': - case 'cp869': - case 'cpgr': - case 'csibm869': - case 'ibm869': - return 'IBM869'; - - case 'cp870': - case 'csibm870': - case 'ebcdiccproece': - case 'ebcdiccpyu': - case 'ibm870': - return 'IBM870'; - - case 'cp871': - case 'csibm871': - case 'ebcdiccpis': - case 'ibm871': - return 'IBM871'; - - case 'cp880': - case 'csibm880': - case 'ebcdiccyrillic': - case 'ibm880': - return 'IBM880'; - - case 'cp891': - case 'csibm891': - case 'ibm891': - return 'IBM891'; - - case 'cp903': - case 'csibm903': - case 'ibm903': - return 'IBM903'; - - case '904': - case 'cp904': - case 'csibbm904': - case 'ibm904': - return 'IBM904'; - - case 'cp905': - case 'csibm905': - case 'ebcdiccptr': - case 'ibm905': - return 'IBM905'; - - case 'cp918': - case 'csibm918': - case 'ebcdiccpar2': - case 'ibm918': - return 'IBM918'; - - case 'cp1026': - case 'csibm1026': - case 'ibm1026': - return 'IBM1026'; - - case 'ibm1047': - return 'IBM1047'; - - case 'csiso143iecp271': - case 'iecp271': - case 'isoir143': - return 'IEC_P27-1'; - - case 'csiso49inis': - case 'inis': - case 'isoir49': - return 'INIS'; - - case 'csiso50inis8': - case 'inis8': - case 'isoir50': - return 'INIS-8'; - - case 'csiso51iniscyrillic': - case 'iniscyrillic': - case 'isoir51': - return 'INIS-cyrillic'; - - case 'csinvariant': - case 'invariant': - return 'INVARIANT'; - - case 'iso2022cn': - return 'ISO-2022-CN'; - - case 'iso2022cnext': - return 'ISO-2022-CN-EXT'; - - case 'csiso2022jp': - case 'iso2022jp': - return 'ISO-2022-JP'; - - case 'csiso2022jp2': - case 'iso2022jp2': - return 'ISO-2022-JP-2'; - - case 'csiso2022kr': - case 'iso2022kr': - return 'ISO-2022-KR'; - - case 'cswindows30latin1': - case 'iso88591windows30latin1': - return 'ISO-8859-1-Windows-3.0-Latin-1'; - - case 'cswindows31latin1': - case 'iso88591windows31latin1': - return 'ISO-8859-1-Windows-3.1-Latin-1'; - - case 'csisolatin2': - case 'iso88592': - case 'iso885921987': - case 'isoir101': - case 'l2': - case 'latin2': - return 'ISO-8859-2'; - - case 'cswindows31latin2': - case 'iso88592windowslatin2': - return 'ISO-8859-2-Windows-Latin-2'; - - case 'csisolatin3': - case 'iso88593': - case 'iso885931988': - case 'isoir109': - case 'l3': - case 'latin3': - return 'ISO-8859-3'; - - case 'csisolatin4': - case 'iso88594': - case 'iso885941988': - case 'isoir110': - case 'l4': - case 'latin4': - return 'ISO-8859-4'; - - case 'csisolatincyrillic': - case 'cyrillic': - case 'iso88595': - case 'iso885951988': - case 'isoir144': - return 'ISO-8859-5'; - - case 'arabic': - case 'asmo708': - case 'csisolatinarabic': - case 'ecma114': - case 'iso88596': - case 'iso885961987': - case 'isoir127': - return 'ISO-8859-6'; - - case 'csiso88596e': - case 'iso88596e': - return 'ISO-8859-6-E'; - - case 'csiso88596i': - case 'iso88596i': - return 'ISO-8859-6-I'; - - case 'csisolatingreek': - case 'ecma118': - case 'elot928': - case 'greek': - case 'greek8': - case 'iso88597': - case 'iso885971987': - case 'isoir126': - return 'ISO-8859-7'; - - case 'csisolatinhebrew': - case 'hebrew': - case 'iso88598': - case 'iso885981988': - case 'isoir138': - return 'ISO-8859-8'; - - case 'csiso88598e': - case 'iso88598e': - return 'ISO-8859-8-E'; - - case 'csiso88598i': - case 'iso88598i': - return 'ISO-8859-8-I'; - - case 'cswindows31latin5': - case 'iso88599windowslatin5': - return 'ISO-8859-9-Windows-Latin-5'; - - case 'csisolatin6': - case 'iso885910': - case 'iso8859101992': - case 'isoir157': - case 'l6': - case 'latin6': - return 'ISO-8859-10'; - - case 'iso885913': - return 'ISO-8859-13'; - - case 'iso885914': - case 'iso8859141998': - case 'isoceltic': - case 'isoir199': - case 'l8': - case 'latin8': - return 'ISO-8859-14'; - - case 'iso885915': - case 'latin9': - return 'ISO-8859-15'; - - case 'iso885916': - case 'iso8859162001': - case 'isoir226': - case 'l10': - case 'latin10': - return 'ISO-8859-16'; - - case 'iso10646j1': - return 'ISO-10646-J-1'; - - case 'csunicode': - case 'iso10646ucs2': - return 'ISO-10646-UCS-2'; - - case 'csucs4': - case 'iso10646ucs4': - return 'ISO-10646-UCS-4'; - - case 'csunicodeascii': - case 'iso10646ucsbasic': - return 'ISO-10646-UCS-Basic'; - - case 'csunicodelatin1': - case 'iso10646': - case 'iso10646unicodelatin1': - return 'ISO-10646-Unicode-Latin1'; - - case 'csiso10646utf1': - case 'iso10646utf1': - return 'ISO-10646-UTF-1'; - - case 'csiso115481': - case 'iso115481': - case 'isotr115481': - return 'ISO-11548-1'; - - case 'csiso90': - case 'isoir90': - return 'iso-ir-90'; - - case 'csunicodeibm1261': - case 'isounicodeibm1261': - return 'ISO-Unicode-IBM-1261'; - - case 'csunicodeibm1264': - case 'isounicodeibm1264': - return 'ISO-Unicode-IBM-1264'; - - case 'csunicodeibm1265': - case 'isounicodeibm1265': - return 'ISO-Unicode-IBM-1265'; - - case 'csunicodeibm1268': - case 'isounicodeibm1268': - return 'ISO-Unicode-IBM-1268'; - - case 'csunicodeibm1276': - case 'isounicodeibm1276': - return 'ISO-Unicode-IBM-1276'; - - case 'csiso646basic1983': - case 'iso646basic1983': - case 'ref': - return 'ISO_646.basic:1983'; - - case 'csiso2intlrefversion': - case 'irv': - case 'iso646irv1983': - case 'isoir2': - return 'ISO_646.irv:1983'; - - case 'csiso2033': - case 'e13b': - case 'iso20331983': - case 'isoir98': - return 'ISO_2033-1983'; - - case 'csiso5427cyrillic': - case 'iso5427': - case 'isoir37': - return 'ISO_5427'; - - case 'iso5427cyrillic1981': - case 'iso54271981': - case 'isoir54': - return 'ISO_5427:1981'; - - case 'csiso5428greek': - case 'iso54281980': - case 'isoir55': - return 'ISO_5428:1980'; - - case 'csiso6937add': - case 'iso6937225': - case 'isoir152': - return 'ISO_6937-2-25'; - - case 'csisotextcomm': - case 'iso69372add': - case 'isoir142': - return 'ISO_6937-2-add'; - - case 'csiso8859supp': - case 'iso8859supp': - case 'isoir154': - case 'latin125': - return 'ISO_8859-supp'; - - case 'csiso10367box': - case 'iso10367box': - case 'isoir155': - return 'ISO_10367-box'; - - case 'csiso15italian': - case 'iso646it': - case 'isoir15': - case 'it': - return 'IT'; - - case 'csiso13jisc6220jp': - case 'isoir13': - case 'jisc62201969': - case 'jisc62201969jp': - case 'katakana': - case 'x2017': - return 'JIS_C6220-1969-jp'; - - case 'csiso14jisc6220ro': - case 'iso646jp': - case 'isoir14': - case 'jisc62201969ro': - case 'jp': - return 'JIS_C6220-1969-ro'; - - case 'csiso42jisc62261978': - case 'isoir42': - case 'jisc62261978': - return 'JIS_C6226-1978'; - - case 'csiso87jisx208': - case 'isoir87': - case 'jisc62261983': - case 'jisx2081983': - case 'x208': - return 'JIS_C6226-1983'; - - case 'csiso91jisc62291984a': - case 'isoir91': - case 'jisc62291984a': - case 'jpocra': - return 'JIS_C6229-1984-a'; - - case 'csiso92jisc62991984b': - case 'iso646jpocrb': - case 'isoir92': - case 'jisc62291984b': - case 'jpocrb': - return 'JIS_C6229-1984-b'; - - case 'csiso93jis62291984badd': - case 'isoir93': - case 'jisc62291984badd': - case 'jpocrbadd': - return 'JIS_C6229-1984-b-add'; - - case 'csiso94jis62291984hand': - case 'isoir94': - case 'jisc62291984hand': - case 'jpocrhand': - return 'JIS_C6229-1984-hand'; - - case 'csiso95jis62291984handadd': - case 'isoir95': - case 'jisc62291984handadd': - case 'jpocrhandadd': - return 'JIS_C6229-1984-hand-add'; - - case 'csiso96jisc62291984kana': - case 'isoir96': - case 'jisc62291984kana': - return 'JIS_C6229-1984-kana'; - - case 'csjisencoding': - case 'jisencoding': - return 'JIS_Encoding'; - - case 'cshalfwidthkatakana': - case 'jisx201': - case 'x201': - return 'JIS_X0201'; - - case 'csiso159jisx2121990': - case 'isoir159': - case 'jisx2121990': - case 'x212': - return 'JIS_X0212-1990'; - - case 'csiso141jusib1002': - case 'iso646yu': - case 'isoir141': - case 'js': - case 'jusib1002': - case 'yu': - return 'JUS_I.B1.002'; - - case 'csiso147macedonian': - case 'isoir147': - case 'jusib1003mac': - case 'macedonian': - return 'JUS_I.B1.003-mac'; - - case 'csiso146serbian': - case 'isoir146': - case 'jusib1003serb': - case 'serbian': - return 'JUS_I.B1.003-serb'; - - case 'koi7switched': - return 'KOI7-switched'; - - case 'cskoi8r': - case 'koi8r': - return 'KOI8-R'; - - case 'koi8u': - return 'KOI8-U'; - - case 'csksc5636': - case 'iso646kr': - case 'ksc5636': - return 'KSC5636'; - - case 'cskz1048': - case 'kz1048': - case 'rk1048': - case 'strk10482002': - return 'KZ-1048'; - - case 'csiso19latingreek': - case 'isoir19': - case 'latingreek': - return 'latin-greek'; - - case 'csiso27latingreek1': - case 'isoir27': - case 'latingreek1': - return 'Latin-greek-1'; - - case 'csiso158lap': - case 'isoir158': - case 'lap': - case 'latinlap': - return 'latin-lap'; - - case 'csmacintosh': - case 'mac': - case 'macintosh': - return 'macintosh'; - - case 'csmicrosoftpublishing': - case 'microsoftpublishing': - return 'Microsoft-Publishing'; - - case 'csmnem': - case 'mnem': - return 'MNEM'; - - case 'csmnemonic': - case 'mnemonic': - return 'MNEMONIC'; - - case 'csiso86hungarian': - case 'hu': - case 'iso646hu': - case 'isoir86': - case 'msz77953': - return 'MSZ_7795.3'; - - case 'csnatsdano': - case 'isoir91': - case 'natsdano': - return 'NATS-DANO'; - - case 'csnatsdanoadd': - case 'isoir92': - case 'natsdanoadd': - return 'NATS-DANO-ADD'; - - case 'csnatssefi': - case 'isoir81': - case 'natssefi': - return 'NATS-SEFI'; - - case 'csnatssefiadd': - case 'isoir82': - case 'natssefiadd': - return 'NATS-SEFI-ADD'; - - case 'csiso151cuba': - case 'cuba': - case 'iso646cu': - case 'isoir151': - case 'ncnc1081': - return 'NC_NC00-10:81'; - - case 'csiso69french': - case 'fr': - case 'iso646fr': - case 'isoir69': - case 'nfz62010': - return 'NF_Z_62-010'; - - case 'csiso25french': - case 'iso646fr1': - case 'isoir25': - case 'nfz620101973': - return 'NF_Z_62-010_(1973)'; - - case 'csiso60danishnorwegian': - case 'csiso60norwegian1': - case 'iso646no': - case 'isoir60': - case 'no': - case 'ns45511': - return 'NS_4551-1'; - - case 'csiso61norwegian2': - case 'iso646no2': - case 'isoir61': - case 'no2': - case 'ns45512': - return 'NS_4551-2'; - - case 'osdebcdicdf3irv': - return 'OSD_EBCDIC_DF03_IRV'; - - case 'osdebcdicdf41': - return 'OSD_EBCDIC_DF04_1'; - - case 'osdebcdicdf415': - return 'OSD_EBCDIC_DF04_15'; - - case 'cspc8danishnorwegian': - case 'pc8danishnorwegian': - return 'PC8-Danish-Norwegian'; - - case 'cspc8turkish': - case 'pc8turkish': - return 'PC8-Turkish'; - - case 'csiso16portuguese': - case 'iso646pt': - case 'isoir16': - case 'pt': - return 'PT'; - - case 'csiso84portuguese2': - case 'iso646pt2': - case 'isoir84': - case 'pt2': - return 'PT2'; - - case 'cp154': - case 'csptcp154': - case 'cyrillicasian': - case 'pt154': - case 'ptcp154': - return 'PTCP154'; - - case 'scsu': - return 'SCSU'; - - case 'csiso10swedish': - case 'fi': - case 'iso646fi': - case 'iso646se': - case 'isoir10': - case 'se': - case 'sen850200b': - return 'SEN_850200_B'; - - case 'csiso11swedishfornames': - case 'iso646se2': - case 'isoir11': - case 'se2': - case 'sen850200c': - return 'SEN_850200_C'; - - case 'csshiftjis': - case 'mskanji': - case 'shiftjis': - return 'Shift_JIS'; - - case 'csiso102t617bit': - case 'isoir102': - case 't617bit': - return 'T.61-7bit'; - - case 'csiso103t618bit': - case 'isoir103': - case 't61': - case 't618bit': - return 'T.61-8bit'; - - case 'csiso128t101g2': - case 'isoir128': - case 't101g2': - return 'T.101-G2'; - - case 'cstscii': - case 'tscii': - return 'TSCII'; - - case 'csunicode11': - case 'unicode11': - return 'UNICODE-1-1'; - - case 'csunicode11utf7': - case 'unicode11utf7': - return 'UNICODE-1-1-UTF-7'; - - case 'csunknown8bit': - case 'unknown8bit': - return 'UNKNOWN-8BIT'; - - case 'ansix341968': - case 'ansix341986': - case 'ascii': - case 'cp367': - case 'csascii': - case 'ibm367': - case 'iso646irv1991': - case 'iso646us': - case 'isoir6': - case 'us': - case 'usascii': - return 'US-ASCII'; - - case 'csusdk': - case 'usdk': - return 'us-dk'; - - case 'utf7': - return 'UTF-7'; - - case 'utf8': - return 'UTF-8'; - - case 'utf16': - return 'UTF-16'; - - case 'utf16be': - return 'UTF-16BE'; - - case 'utf16le': - return 'UTF-16LE'; - - case 'utf32': - return 'UTF-32'; - - case 'utf32be': - return 'UTF-32BE'; - - case 'utf32le': - return 'UTF-32LE'; - - case 'csventurainternational': - case 'venturainternational': - return 'Ventura-International'; - - case 'csventuramath': - case 'venturamath': - return 'Ventura-Math'; - - case 'csventuraus': - case 'venturaus': - return 'Ventura-US'; - - case 'csiso70videotexsupp1': - case 'isoir70': - case 'videotexsuppl': - return 'videotex-suppl'; - - case 'csviqr': - case 'viqr': - return 'VIQR'; - - case 'csviscii': - case 'viscii': - return 'VISCII'; - - case 'cswindows31j': - case 'windows31j': - return 'Windows-31J'; - - case 'iso885911': - case 'tis620': - return 'windows-874'; - - case 'cseuckr': - case 'csksc56011987': - case 'euckr': - case 'isoir149': - case 'korean': - case 'ksc5601': - case 'ksc56011987': - case 'ksc56011989': - case 'windows949': - return 'windows-949'; - - case 'windows1250': - return 'windows-1250'; - - case 'windows1251': - return 'windows-1251'; - - case 'cp819': - case 'csisolatin1': - case 'ibm819': - case 'iso88591': - case 'iso885911987': - case 'isoir100': - case 'l1': - case 'latin1': - case 'windows1252': - return 'windows-1252'; - - case 'windows1253': - return 'windows-1253'; - - case 'csisolatin5': - case 'iso88599': - case 'iso885991989': - case 'isoir148': - case 'l5': - case 'latin5': - case 'windows1254': - return 'windows-1254'; - - case 'windows1255': - return 'windows-1255'; - - case 'windows1256': - return 'windows-1256'; - - case 'windows1257': - return 'windows-1257'; - - case 'windows1258': - return 'windows-1258'; - - default: - return $charset; - } - } - - function get_curl_version() - { - if (is_array($curl = curl_version())) - { - $curl = $curl['version']; - } - elseif (substr($curl, 0, 5) === 'curl/') - { - $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); - } - elseif (substr($curl, 0, 8) === 'libcurl/') - { - $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); - } - else - { - $curl = 0; - } - return $curl; - } - - function is_subclass_of($class1, $class2) - { - if (func_num_args() !== 2) - { - trigger_error('Wrong parameter count for SimplePie_Misc::is_subclass_of()', E_USER_WARNING); - } - elseif (version_compare(PHP_VERSION, '5.0.3', '>=') || is_object($class1)) - { - return is_subclass_of($class1, $class2); - } - elseif (is_string($class1) && is_string($class2)) - { - if (class_exists($class1)) - { - if (class_exists($class2)) - { - $class2 = strtolower($class2); - while ($class1 = strtolower(get_parent_class($class1))) - { - if ($class1 === $class2) - { - return true; - } - } - } - } - else - { - trigger_error('Unknown class passed as parameter', E_USER_WARNNG); - } - } - return false; - } - - /** - * Strip HTML comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function strip_comments($data) - { - $output = ''; - while (($start = strpos($data, '', $start)) !== false) - { - $data = substr_replace($data, '', 0, $end + 3); - } - else - { - $data = ''; - } - } - return $output . $data; - } - - function parse_date($dt) - { - $parser = SimplePie_Parse_Date::get(); - return $parser->parse($dt); - } - - /** - * Decode HTML entities - * - * @static - * @access public - * @param string $data Input data - * @return string Output data - */ - function entities_decode($data) - { - $decoder = new SimplePie_Decode_HTML_Entities($data); - return $decoder->parse(); - } - - /** - * Remove RFC822 comments - * - * @access public - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function uncomment_rfc822($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; - - $output = ''; - - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) - { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') - { - $depth++; - while ($depth && $position < $length) - { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; - - case ')': - $depth--; - break; - } - $position++; - } - else - { - break; - } - } - } - else - { - $output .= '('; - } - } - $output .= substr($string, $position); - - return $output; - } - - function parse_mime($mime) - { - if (($pos = strpos($mime, ';')) === false) - { - return trim($mime); - } - else - { - return trim(substr($mime, 0, $pos)); - } - } - - function htmlspecialchars_decode($string, $quote_style) - { - if (function_exists('htmlspecialchars_decode')) - { - return htmlspecialchars_decode($string, $quote_style); - } - else - { - return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style))); - } - } - - function atom_03_construct_type($attribs) - { - if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64')) - { - $mode = SIMPLEPIE_CONSTRUCT_BASE64; - } - else - { - $mode = SIMPLEPIE_CONSTRUCT_NONE; - } - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - case 'text/plain': - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; - - case 'html': - case 'text/html': - return SIMPLEPIE_CONSTRUCT_HTML | $mode; - - case 'xhtml': - case 'application/xhtml+xml': - return SIMPLEPIE_CONSTRUCT_XHTML | $mode; - - default: - return SIMPLEPIE_CONSTRUCT_NONE | $mode; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT | $mode; - } - } - - function atom_10_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - switch (strtolower(trim($attribs['']['type']))) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; - - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; - - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; - - default: - return SIMPLEPIE_CONSTRUCT_NONE; - } - } - return SIMPLEPIE_CONSTRUCT_TEXT; - } - - function atom_10_content_construct_type($attribs) - { - if (isset($attribs['']['type'])) - { - $type = strtolower(trim($attribs['']['type'])); - switch ($type) - { - case 'text': - return SIMPLEPIE_CONSTRUCT_TEXT; - - case 'html': - return SIMPLEPIE_CONSTRUCT_HTML; - - case 'xhtml': - return SIMPLEPIE_CONSTRUCT_XHTML; - } - if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/') - { - return SIMPLEPIE_CONSTRUCT_NONE; - } - else - { - return SIMPLEPIE_CONSTRUCT_BASE64; - } - } - else - { - return SIMPLEPIE_CONSTRUCT_TEXT; - } - } - - function is_isegment_nz_nc($string) - { - return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); - } - - function space_seperated_tokens($string) - { - $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; - $string_length = strlen($string); - - $position = strspn($string, $space_characters); - $tokens = array(); - - while ($position < $string_length) - { - $len = strcspn($string, $space_characters, $position); - $tokens[] = substr($string, $position, $len); - $position += $len; - $position += strspn($string, $space_characters, $position); - } - - return $tokens; - } - - function array_unique($array) - { - if (version_compare(PHP_VERSION, '5.2', '>=')) - { - return array_unique($array); - } - else - { - $array = (array) $array; - $new_array = array(); - $new_array_strings = array(); - foreach ($array as $key => $value) - { - if (is_object($value)) - { - if (method_exists($value, '__toString')) - { - $cmp = $value->__toString(); - } - else - { - trigger_error('Object of class ' . get_class($value) . ' could not be converted to string', E_USER_ERROR); - } - } - elseif (is_array($value)) - { - $cmp = (string) reset($value); - } - else - { - $cmp = (string) $value; - } - if (!in_array($cmp, $new_array_strings)) - { - $new_array[$key] = $value; - $new_array_strings[] = $cmp; - } - } - return $new_array; - } - } - - /** - * Converts a unicode codepoint to a UTF-8 character - * - * @static - * @access public - * @param int $codepoint Unicode codepoint - * @return string UTF-8 character - */ - function codepoint_to_utf8($codepoint) - { - $codepoint = (int) $codepoint; - if ($codepoint < 0) - { - return false; - } - else if ($codepoint <= 0x7f) - { - return chr($codepoint); - } - else if ($codepoint <= 0x7ff) - { - return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0xffff) - { - return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else if ($codepoint <= 0x10ffff) - { - return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); - } - else - { - // U+FFFD REPLACEMENT CHARACTER - return "\xEF\xBF\xBD"; - } - } - - /** - * Re-implementation of PHP 5's stripos() - * - * Returns the numeric position of the first occurrence of needle in the - * haystack string. - * - * @static - * @access string - * @param object $haystack - * @param string $needle Note that the needle may be a string of one or more - * characters. If needle is not a string, it is converted to an integer - * and applied as the ordinal value of a character. - * @param int $offset The optional offset parameter allows you to specify which - * character in haystack to start searching. The position returned is still - * relative to the beginning of haystack. - * @return bool If needle is not found, stripos() will return boolean false. - */ - function stripos($haystack, $needle, $offset = 0) - { - if (function_exists('stripos')) - { - return stripos($haystack, $needle, $offset); - } - else - { - if (is_string($needle)) - { - $needle = strtolower($needle); - } - elseif (is_int($needle) || is_bool($needle) || is_double($needle)) - { - $needle = strtolower(chr($needle)); - } - else - { - trigger_error('needle is not a string or an integer', E_USER_WARNING); - return false; - } - - return strpos(strtolower($haystack), $needle, $offset); - } - } - - /** - * Similar to parse_str() - * - * Returns an associative array of name/value pairs, where the value is an - * array of values that have used the same name - * - * @static - * @access string - * @param string $str The input string. - * @return array - */ - function parse_str($str) - { - $return = array(); - $str = explode('&', $str); - - foreach ($str as $section) - { - if (strpos($section, '=') !== false) - { - list($name, $value) = explode('=', $section, 2); - $return[urldecode($name)][] = urldecode($value); - } - else - { - $return[urldecode($section)][] = null; - } - } - - return $return; - } - - /** - * Detect XML encoding, as per XML 1.0 Appendix F.1 - * - * @todo Add support for EBCDIC - * @param string $data XML data - * @return array Possible encodings - */ - function xml_encoding($data) - { - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") - { - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") - { - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") - { - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") - { - $encoding[] = 'UTF-16LE'; - } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") - { - $encoding[] = 'UTF-8'; - } - // UTF-32 Big Endian Without BOM - elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") - { - if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32BE'; - } - // UTF-32 Little Endian Without BOM - elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") - { - if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-32LE'; - } - // UTF-16 Big Endian Without BOM - elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") - { - if ($pos = strpos($data, "\x00\x3F\x00\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16BE'; - } - // UTF-16 Little Endian Without BOM - elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") - { - if ($pos = strpos($data, "\x3F\x00\x3E\x00")) - { - $parser = new SimplePie_XML_Declaration_Parser(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8')); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-16LE'; - } - // US-ASCII (or superset) - elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") - { - if ($pos = strpos($data, "\x3F\x3E")) - { - $parser = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($parser->parse()) - { - $encoding[] = $parser->encoding; - } - } - $encoding[] = 'UTF-8'; - } - // Fallback to UTF-8 - else - { - $encoding[] = 'UTF-8'; - } - return $encoding; - } - - function output_javascript() - { - if (function_exists('ob_gzhandler')) - { - ob_start('ob_gzhandler'); - } - header('Content-type: text/javascript; charset: UTF-8'); - header('Cache-Control: must-revalidate'); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days - ?> -function embed_odeo(link) { - document.writeln(''); -} - -function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { - if (placeholder != '') { - document.writeln(''); - } - else { - document.writeln(''); - } -} - -function embed_flash(bgcolor, width, height, link, loop, type) { - document.writeln(''); -} - -function embed_flv(width, height, link, placeholder, loop, player) { - document.writeln(''); -} - -function embed_wmedia(width, height, link) { - document.writeln(''); -} - data = $data; - } - - /** - * Parse the input data - * - * @access public - * @return string Output data - */ - function parse() - { - while (($this->position = strpos($this->data, '&', $this->position)) !== false) - { - $this->consume(); - $this->entity(); - $this->consumed = ''; - } - return $this->data; - } - - /** - * Consume the next byte - * - * @access private - * @return mixed The next byte, or false, if there is no more data - */ - function consume() - { - if (isset($this->data[$this->position])) - { - $this->consumed .= $this->data[$this->position]; - return $this->data[$this->position++]; - } - else - { - return false; - } - } - - /** - * Consume a range of characters - * - * @access private - * @param string $chars Characters to consume - * @return mixed A series of characters that match the range, or false - */ - function consume_range($chars) - { - if ($len = strspn($this->data, $chars, $this->position)) - { - $data = substr($this->data, $this->position, $len); - $this->consumed .= $data; - $this->position += $len; - return $data; - } - else - { - return false; - } - } - - /** - * Unconsume one byte - * - * @access private - */ - function unconsume() - { - $this->consumed = substr($this->consumed, 0, -1); - $this->position--; - } - - /** - * Decode an entity - * - * @access private - */ - function entity() - { - switch ($this->consume()) - { - case "\x09": - case "\x0A": - case "\x0B": - case "\x0B": - case "\x0C": - case "\x20": - case "\x3C": - case "\x26": - case false: - break; - - case "\x23": - switch ($this->consume()) - { - case "\x78": - case "\x58": - $range = '0123456789ABCDEFabcdef'; - $hex = true; - break; - - default: - $range = '0123456789'; - $hex = false; - $this->unconsume(); - break; - } - - if ($codepoint = $this->consume_range($range)) - { - static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); - - if ($hex) - { - $codepoint = hexdec($codepoint); - } - else - { - $codepoint = intval($codepoint); - } - - if (isset($windows_1252_specials[$codepoint])) - { - $replacement = $windows_1252_specials[$codepoint]; - } - else - { - $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); - } - - if (!in_array($this->consume(), array(';', false), true)) - { - $this->unconsume(); - } - - $consumed_length = strlen($this->consumed); - $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); - $this->position += strlen($replacement) - $consumed_length; - } - break; - - default: - static $entities = array('Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C"); - - for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) - { - $consumed = substr($this->consumed, 1); - if (isset($entities[$consumed])) - { - $match = $consumed; - } - } - - if ($match !== null) - { - $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); - $this->position += strlen($entities[$match]) - strlen($consumed) - 1; - } - break; - } - } -} - -/** - * IRI parser/serialiser - * - * @package SimplePie - */ -class SimplePie_IRI -{ - /** - * Scheme - * - * @access private - * @var string - */ - var $scheme; - - /** - * User Information - * - * @access private - * @var string - */ - var $userinfo; - - /** - * Host - * - * @access private - * @var string - */ - var $host; - - /** - * Port - * - * @access private - * @var string - */ - var $port; - - /** - * Path - * - * @access private - * @var string - */ - var $path; - - /** - * Query - * - * @access private - * @var string - */ - var $query; - - /** - * Fragment - * - * @access private - * @var string - */ - var $fragment; - - /** - * Whether the object represents a valid IRI - * - * @access private - * @var array - */ - var $valid = array(); - - /** - * Return the entire IRI when you try and read the object as a string - * - * @access public - * @return string - */ - function __toString() - { - return $this->get_iri(); - } - - /** - * Create a new IRI object, from a specified string - * - * @access public - * @param string $iri - * @return SimplePie_IRI - */ - function SimplePie_IRI($iri) - { - $iri = (string) $iri; - if ($iri !== '') - { - $parsed = $this->parse_iri($iri); - $this->set_scheme($parsed['scheme']); - $this->set_authority($parsed['authority']); - $this->set_path($parsed['path']); - $this->set_query($parsed['query']); - $this->set_fragment($parsed['fragment']); - } - } - - /** - * Create a new IRI object by resolving a relative IRI - * - * @static - * @access public - * @param SimplePie_IRI $base Base IRI - * @param string $relative Relative IRI - * @return SimplePie_IRI - */ - function absolutize($base, $relative) - { - $relative = (string) $relative; - if ($relative !== '') - { - $relative = new SimplePie_IRI($relative); - if ($relative->get_scheme() !== null) - { - $target = $relative; - } - elseif ($base->get_iri() !== null) - { - if ($relative->get_authority() !== null) - { - $target = $relative; - $target->set_scheme($base->get_scheme()); - } - else - { - $target = new SimplePie_IRI(''); - $target->set_scheme($base->get_scheme()); - $target->set_userinfo($base->get_userinfo()); - $target->set_host($base->get_host()); - $target->set_port($base->get_port()); - if ($relative->get_path() !== null) - { - if (strpos($relative->get_path(), '/') === 0) - { - $target->set_path($relative->get_path()); - } - elseif (($base->get_userinfo() !== null || $base->get_host() !== null || $base->get_port() !== null) && $base->get_path() === null) - { - $target->set_path('/' . $relative->get_path()); - } - elseif (($last_segment = strrpos($base->get_path(), '/')) !== false) - { - $target->set_path(substr($base->get_path(), 0, $last_segment + 1) . $relative->get_path()); - } - else - { - $target->set_path($relative->get_path()); - } - $target->set_query($relative->get_query()); - } - else - { - $target->set_path($base->get_path()); - if ($relative->get_query() !== null) - { - $target->set_query($relative->get_query()); - } - elseif ($base->get_query() !== null) - { - $target->set_query($base->get_query()); - } - } - } - $target->set_fragment($relative->get_fragment()); - } - else - { - // No base URL, just return the relative URL - $target = $relative; - } - } - else - { - $target = $base; - } - return $target; - } - - /** - * Parse an IRI into scheme/authority/path/query/fragment segments - * - * @access private - * @param string $iri - * @return array - */ - function parse_iri($iri) - { - preg_match('/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/', $iri, $match); - for ($i = count($match); $i <= 9; $i++) - { - $match[$i] = ''; - } - return array('scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5], 'query' => $match[7], 'fragment' => $match[9]); - } - - /** - * Remove dot segments from a path - * - * @access private - * @param string $input - * @return string - */ - function remove_dot_segments($input) - { - $output = ''; - while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') - { - // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, - if (strpos($input, '../') === 0) - { - $input = substr($input, 3); - } - elseif (strpos($input, './') === 0) - { - $input = substr($input, 2); - } - // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, - elseif (strpos($input, '/./') === 0) - { - $input = substr_replace($input, '/', 0, 3); - } - elseif ($input === '/.') - { - $input = '/'; - } - // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, - elseif (strpos($input, '/../') === 0) - { - $input = substr_replace($input, '/', 0, 4); - $output = substr_replace($output, '', strrpos($output, '/')); - } - elseif ($input === '/..') - { - $input = '/'; - $output = substr_replace($output, '', strrpos($output, '/')); - } - // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, - elseif ($input === '.' || $input === '..') - { - $input = ''; - } - // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer - elseif (($pos = strpos($input, '/', 1)) !== false) - { - $output .= substr($input, 0, $pos); - $input = substr_replace($input, '', 0, $pos); - } - else - { - $output .= $input; - $input = ''; - } - } - return $output . $input; - } - - /** - * Replace invalid character with percent encoding - * - * @access private - * @param string $string Input string - * @param string $valid_chars Valid characters - * @param int $case Normalise case - * @return string - */ - function replace_invalid_with_pct_encoding($string, $valid_chars, $case = SIMPLEPIE_SAME_CASE) - { - // Normalise case - if ($case & SIMPLEPIE_LOWERCASE) - { - $string = strtolower($string); - } - elseif ($case & SIMPLEPIE_UPPERCASE) - { - $string = strtoupper($string); - } - - // Store position and string length (to avoid constantly recalculating this) - $position = 0; - $strlen = strlen($string); - - // Loop as long as we have invalid characters, advancing the position to the next invalid character - while (($position += strspn($string, $valid_chars, $position)) < $strlen) - { - // If we have a % character - if ($string[$position] === '%') - { - // If we have a pct-encoded section - if ($position + 2 < $strlen && strspn($string, '0123456789ABCDEFabcdef', $position + 1, 2) === 2) - { - // Get the the represented character - $chr = chr(hexdec(substr($string, $position + 1, 2))); - - // If the character is valid, replace the pct-encoded with the actual character while normalising case - if (strpos($valid_chars, $chr) !== false) - { - if ($case & SIMPLEPIE_LOWERCASE) - { - $chr = strtolower($chr); - } - elseif ($case & SIMPLEPIE_UPPERCASE) - { - $chr = strtoupper($chr); - } - $string = substr_replace($string, $chr, $position, 3); - $strlen -= 2; - $position++; - } - - // Otherwise just normalise the pct-encoded to uppercase - else - { - $string = substr_replace($string, strtoupper(substr($string, $position + 1, 2)), $position + 1, 2); - $position += 3; - } - } - // If we don't have a pct-encoded section, just replace the % with its own esccaped form - else - { - $string = substr_replace($string, '%25', $position, 1); - $strlen += 2; - $position += 3; - } - } - // If we have an invalid character, change into its pct-encoded form - else - { - $replacement = sprintf("%%%02X", ord($string[$position])); - $string = str_replace($string[$position], $replacement, $string); - $strlen = strlen($string); - } - } - return $string; - } - - /** - * Check if the object represents a valid IRI - * - * @access public - * @return bool - */ - function is_valid() - { - return array_sum($this->valid) === count($this->valid); - } - - /** - * Set the scheme. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $scheme - * @return bool - */ - function set_scheme($scheme) - { - if ($scheme === null || $scheme === '') - { - $this->scheme = null; - } - else - { - $len = strlen($scheme); - switch (true) - { - case $len > 1: - if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-.', 1)) - { - $this->scheme = null; - $this->valid[__FUNCTION__] = false; - return false; - } - - case $len > 0: - if (!strspn($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 0, 1)) - { - $this->scheme = null; - $this->valid[__FUNCTION__] = false; - return false; - } - } - $this->scheme = strtolower($scheme); - } - $this->valid[__FUNCTION__] = true; - return true; - } - - /** - * Set the authority. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $authority - * @return bool - */ - function set_authority($authority) - { - if (($userinfo_end = strrpos($authority, '@')) !== false) - { - $userinfo = substr($authority, 0, $userinfo_end); - $authority = substr($authority, $userinfo_end + 1); - } - else - { - $userinfo = null; - } - - if (($port_start = strpos($authority, ':')) !== false) - { - $port = substr($authority, $port_start + 1); - $authority = substr($authority, 0, $port_start); - } - else - { - $port = null; - } - - return $this->set_userinfo($userinfo) && $this->set_host($authority) && $this->set_port($port); - } - - /** - * Set the userinfo. - * - * @access public - * @param string $userinfo - * @return bool - */ - function set_userinfo($userinfo) - { - if ($userinfo === null || $userinfo === '') - { - $this->userinfo = null; - } - else - { - $this->userinfo = $this->replace_invalid_with_pct_encoding($userinfo, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:'); - } - $this->valid[__FUNCTION__] = true; - return true; - } - - /** - * Set the host. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $host - * @return bool - */ - function set_host($host) - { - if ($host === null || $host === '') - { - $this->host = null; - $this->valid[__FUNCTION__] = true; - return true; - } - elseif ($host[0] === '[' && substr($host, -1) === ']') - { - if (Net_IPv6::checkIPv6(substr($host, 1, -1))) - { - $this->host = $host; - $this->valid[__FUNCTION__] = true; - return true; - } - else - { - $this->host = null; - $this->valid[__FUNCTION__] = false; - return false; - } - } - else - { - $this->host = $this->replace_invalid_with_pct_encoding($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=', SIMPLEPIE_LOWERCASE); - $this->valid[__FUNCTION__] = true; - return true; - } - } - - /** - * Set the port. Returns true on success, false on failure (if there are - * any invalid characters). - * - * @access public - * @param string $port - * @return bool - */ - function set_port($port) - { - if ($port === null || $port === '') - { - $this->port = null; - $this->valid[__FUNCTION__] = true; - return true; - } - elseif (strspn($port, '0123456789') === strlen($port)) - { - $this->port = (int) $port; - $this->valid[__FUNCTION__] = true; - return true; - } - else - { - $this->port = null; - $this->valid[__FUNCTION__] = false; - return false; - } - } - - /** - * Set the path. - * - * @access public - * @param string $path - * @return bool - */ - function set_path($path) - { - if ($path === null || $path === '') - { - $this->path = null; - $this->valid[__FUNCTION__] = true; - return true; - } - elseif (substr($path, 0, 2) === '//' && $this->userinfo === null && $this->host === null && $this->port === null) - { - $this->path = null; - $this->valid[__FUNCTION__] = false; - return false; - } - else - { - $this->path = $this->replace_invalid_with_pct_encoding($path, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=@/'); - if ($this->scheme !== null) - { - $this->path = $this->remove_dot_segments($this->path); - } - $this->valid[__FUNCTION__] = true; - return true; - } - } - - /** - * Set the query. - * - * @access public - * @param string $query - * @return bool - */ - function set_query($query) - { - if ($query === null || $query === '') - { - $this->query = null; - } - else - { - $this->query = $this->replace_invalid_with_pct_encoding($query, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$\'()*+,;:@/?'); - } - $this->valid[__FUNCTION__] = true; - return true; - } - - /** - * Set the fragment. - * - * @access public - * @param string $fragment - * @return bool - */ - function set_fragment($fragment) - { - if ($fragment === null || $fragment === '') - { - $this->fragment = null; - } - else - { - $this->fragment = $this->replace_invalid_with_pct_encoding($fragment, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&\'()*+,;=:@/?'); - } - $this->valid[__FUNCTION__] = true; - return true; - } - - /** - * Get the complete IRI - * - * @access public - * @return string - */ - function get_iri() - { - $iri = ''; - if ($this->scheme !== null) - { - $iri .= $this->scheme . ':'; - } - if (($authority = $this->get_authority()) !== null) - { - $iri .= '//' . $authority; - } - if ($this->path !== null) - { - $iri .= $this->path; - } - if ($this->query !== null) - { - $iri .= '?' . $this->query; - } - if ($this->fragment !== null) - { - $iri .= '#' . $this->fragment; - } - - if ($iri !== '') - { - return $iri; - } - else - { - return null; - } - } - - /** - * Get the scheme - * - * @access public - * @return string - */ - function get_scheme() - { - return $this->scheme; - } - - /** - * Get the complete authority - * - * @access public - * @return string - */ - function get_authority() - { - $authority = ''; - if ($this->userinfo !== null) - { - $authority .= $this->userinfo . '@'; - } - if ($this->host !== null) - { - $authority .= $this->host; - } - if ($this->port !== null) - { - $authority .= ':' . $this->port; - } - - if ($authority !== '') - { - return $authority; - } - else - { - return null; - } - } - - /** - * Get the user information - * - * @access public - * @return string - */ - function get_userinfo() - { - return $this->userinfo; - } - - /** - * Get the host - * - * @access public - * @return string - */ - function get_host() - { - return $this->host; - } - - /** - * Get the port - * - * @access public - * @return string - */ - function get_port() - { - return $this->port; - } - - /** - * Get the path - * - * @access public - * @return string - */ - function get_path() - { - return $this->path; - } - - /** - * Get the query - * - * @access public - * @return string - */ - function get_query() - { - return $this->query; - } - - /** - * Get the fragment - * - * @access public - * @return string - */ - function get_fragment() - { - return $this->fragment; - } -} - -/** - * Class to validate and to work with IPv6 addresses. - * - * @package SimplePie - * @copyright 2003-2005 The PHP Group - * @license http://www.opensource.org/licenses/bsd-license.php - * @link http://pear.php.net/package/Net_IPv6 - * @author Alexander Merz - * @author elfrink at introweb dot nl - * @author Josh Peck - * @author Geoffrey Sneddon - */ -class SimplePie_Net_IPv6 -{ - /** - * Removes a possible existing netmask specification of an IP address. - * - * @param string $ip the (compressed) IP as Hex representation - * @return string the IP the without netmask - * @since 1.1.0 - * @access public - * @static - */ - function removeNetmaskSpec($ip) - { - if (strpos($ip, '/') !== false) - { - list($addr, $nm) = explode('/', $ip); - } - else - { - $addr = $ip; - } - return $addr; - } - - /** - * Uncompresses an IPv6 address - * - * RFC 2373 allows you to compress zeros in an address to '::'. This - * function expects an valid IPv6 address and expands the '::' to - * the required zeros. - * - * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 - * ::1 -> 0:0:0:0:0:0:0:1 - * - * @access public - * @static - * @param string $ip a valid IPv6-address (hex format) - * @return string the uncompressed IPv6-address (hex format) - */ - function Uncompress($ip) - { - $uip = SimplePie_Net_IPv6::removeNetmaskSpec($ip); - $c1 = -1; - $c2 = -1; - if (strpos($ip, '::') !== false) - { - list($ip1, $ip2) = explode('::', $ip); - if ($ip1 === '') - { - $c1 = -1; - } - else - { - $pos = 0; - if (($pos = substr_count($ip1, ':')) > 0) - { - $c1 = $pos; - } - else - { - $c1 = 0; - } - } - if ($ip2 === '') - { - $c2 = -1; - } - else - { - $pos = 0; - if (($pos = substr_count($ip2, ':')) > 0) - { - $c2 = $pos; - } - else - { - $c2 = 0; - } - } - if (strstr($ip2, '.')) - { - $c2++; - } - // :: - if ($c1 === -1 && $c2 === -1) - { - $uip = '0:0:0:0:0:0:0:0'; - } - // ::xxx - else if ($c1 === -1) - { - $fill = str_repeat('0:', 7 - $c2); - $uip = str_replace('::', $fill, $uip); - } - // xxx:: - else if ($c2 === -1) - { - $fill = str_repeat(':0', 7 - $c1); - $uip = str_replace('::', $fill, $uip); - } - // xxx::xxx - else - { - $fill = str_repeat(':0:', 6 - $c2 - $c1); - $uip = str_replace('::', $fill, $uip); - $uip = str_replace('::', ':', $uip); - } - } - return $uip; - } - - /** - * Splits an IPv6 address into the IPv6 and a possible IPv4 part - * - * RFC 2373 allows you to note the last two parts of an IPv6 address as - * an IPv4 compatible address - * - * Example: 0:0:0:0:0:0:13.1.68.3 - * 0:0:0:0:0:FFFF:129.144.52.38 - * - * @access public - * @static - * @param string $ip a valid IPv6-address (hex format) - * @return array [0] contains the IPv6 part, [1] the IPv4 part (hex format) - */ - function SplitV64($ip) - { - $ip = SimplePie_Net_IPv6::Uncompress($ip); - if (strstr($ip, '.')) - { - $pos = strrpos($ip, ':'); - $ip[$pos] = '_'; - $ipPart = explode('_', $ip); - return $ipPart; - } - else - { - return array($ip, ''); - } - } - - /** - * Checks an IPv6 address - * - * Checks if the given IP is IPv6-compatible - * - * @access public - * @static - * @param string $ip a valid IPv6-address - * @return bool true if $ip is an IPv6 address - */ - function checkIPv6($ip) - { - $ipPart = SimplePie_Net_IPv6::SplitV64($ip); - $count = 0; - if (!empty($ipPart[0])) - { - $ipv6 = explode(':', $ipPart[0]); - for ($i = 0; $i < count($ipv6); $i++) - { - $dec = hexdec($ipv6[$i]); - $hex = strtoupper(preg_replace('/^[0]{1,3}(.*[0-9a-fA-F])$/', '\\1', $ipv6[$i])); - if ($ipv6[$i] >= 0 && $dec <= 65535 && $hex === strtoupper(dechex($dec))) - { - $count++; - } - } - if ($count === 8) - { - return true; - } - elseif ($count === 6 && !empty($ipPart[1])) - { - $ipv4 = explode('.', $ipPart[1]); - $count = 0; - foreach ($ipv4 as $ipv4_part) - { - if ($ipv4_part >= 0 && $ipv4_part <= 255 && preg_match('/^\d{1,3}$/', $ipv4_part)) - { - $count++; - } - } - if ($count === 4) - { - return true; - } - } - else - { - return false; - } - - } - else - { - return false; - } - } -} - -/** - * Date Parser - * - * @package SimplePie - */ -class SimplePie_Parse_Date -{ - /** - * Input data - * - * @access protected - * @var string - */ - var $date; - - /** - * List of days, calendar day name => ordinal day number in the week - * - * @access protected - * @var array - */ - var $day = array( - // English - 'mon' => 1, - 'monday' => 1, - 'tue' => 2, - 'tuesday' => 2, - 'wed' => 3, - 'wednesday' => 3, - 'thu' => 4, - 'thursday' => 4, - 'fri' => 5, - 'friday' => 5, - 'sat' => 6, - 'saturday' => 6, - 'sun' => 7, - 'sunday' => 7, - // Dutch - 'maandag' => 1, - 'dinsdag' => 2, - 'woensdag' => 3, - 'donderdag' => 4, - 'vrijdag' => 5, - 'zaterdag' => 6, - 'zondag' => 7, - // French - 'lundi' => 1, - 'mardi' => 2, - 'mercredi' => 3, - 'jeudi' => 4, - 'vendredi' => 5, - 'samedi' => 6, - 'dimanche' => 7, - // German - 'montag' => 1, - 'dienstag' => 2, - 'mittwoch' => 3, - 'donnerstag' => 4, - 'freitag' => 5, - 'samstag' => 6, - 'sonnabend' => 6, - 'sonntag' => 7, - // Italian - 'lunedì' => 1, - 'martedì' => 2, - 'mercoledì' => 3, - 'giovedì' => 4, - 'venerdì' => 5, - 'sabato' => 6, - 'domenica' => 7, - // Spanish - 'lunes' => 1, - 'martes' => 2, - 'miércoles' => 3, - 'jueves' => 4, - 'viernes' => 5, - 'sábado' => 6, - 'domingo' => 7, - // Finnish - 'maanantai' => 1, - 'tiistai' => 2, - 'keskiviikko' => 3, - 'torstai' => 4, - 'perjantai' => 5, - 'lauantai' => 6, - 'sunnuntai' => 7, - // Hungarian - 'hétfő' => 1, - 'kedd' => 2, - 'szerda' => 3, - 'csütörtok' => 4, - 'péntek' => 5, - 'szombat' => 6, - 'vasárnap' => 7, - // Greek - 'Δευ' => 1, - 'Τρι' => 2, - 'Τετ' => 3, - 'Πεμ' => 4, - 'Παρ' => 5, - 'Σαβ' => 6, - 'Κυρ' => 7, - ); - - /** - * List of months, calendar month name => calendar month number - * - * @access protected - * @var array - */ - var $month = array( - // English - 'jan' => 1, - 'january' => 1, - 'feb' => 2, - 'february' => 2, - 'mar' => 3, - 'march' => 3, - 'apr' => 4, - 'april' => 4, - 'may' => 5, - // No long form of May - 'jun' => 6, - 'june' => 6, - 'jul' => 7, - 'july' => 7, - 'aug' => 8, - 'august' => 8, - 'sep' => 9, - 'september' => 8, - 'oct' => 10, - 'october' => 10, - 'nov' => 11, - 'november' => 11, - 'dec' => 12, - 'december' => 12, - // Dutch - 'januari' => 1, - 'februari' => 2, - 'maart' => 3, - 'april' => 4, - 'mei' => 5, - 'juni' => 6, - 'juli' => 7, - 'augustus' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'december' => 12, - // French - 'janvier' => 1, - 'février' => 2, - 'mars' => 3, - 'avril' => 4, - 'mai' => 5, - 'juin' => 6, - 'juillet' => 7, - 'août' => 8, - 'septembre' => 9, - 'octobre' => 10, - 'novembre' => 11, - 'décembre' => 12, - // German - 'januar' => 1, - 'februar' => 2, - 'märz' => 3, - 'april' => 4, - 'mai' => 5, - 'juni' => 6, - 'juli' => 7, - 'august' => 8, - 'september' => 9, - 'oktober' => 10, - 'november' => 11, - 'dezember' => 12, - // Italian - 'gennaio' => 1, - 'febbraio' => 2, - 'marzo' => 3, - 'aprile' => 4, - 'maggio' => 5, - 'giugno' => 6, - 'luglio' => 7, - 'agosto' => 8, - 'settembre' => 9, - 'ottobre' => 10, - 'novembre' => 11, - 'dicembre' => 12, - // Spanish - 'enero' => 1, - 'febrero' => 2, - 'marzo' => 3, - 'abril' => 4, - 'mayo' => 5, - 'junio' => 6, - 'julio' => 7, - 'agosto' => 8, - 'septiembre' => 9, - 'setiembre' => 9, - 'octubre' => 10, - 'noviembre' => 11, - 'diciembre' => 12, - // Finnish - 'tammikuu' => 1, - 'helmikuu' => 2, - 'maaliskuu' => 3, - 'huhtikuu' => 4, - 'toukokuu' => 5, - 'kesäkuu' => 6, - 'heinäkuu' => 7, - 'elokuu' => 8, - 'suuskuu' => 9, - 'lokakuu' => 10, - 'marras' => 11, - 'joulukuu' => 12, - // Hungarian - 'január' => 1, - 'február' => 2, - 'március' => 3, - 'április' => 4, - 'május' => 5, - 'június' => 6, - 'július' => 7, - 'augusztus' => 8, - 'szeptember' => 9, - 'október' => 10, - 'november' => 11, - 'december' => 12, - // Greek - 'Ιαν' => 1, - 'Φεβ' => 2, - 'Μάώ' => 3, - 'Μαώ' => 3, - 'Απρ' => 4, - 'Μάι' => 5, - 'Μαϊ' => 5, - 'Μαι' => 5, - 'Ιούν' => 6, - 'Ιον' => 6, - 'Ιούλ' => 7, - 'Ιολ' => 7, - 'Αύγ' => 8, - 'Αυγ' => 8, - 'Σεπ' => 9, - 'Οκτ' => 10, - 'Νοέ' => 11, - 'Δεκ' => 12, - ); - - /** - * List of timezones, abbreviation => offset from UTC - * - * @access protected - * @var array - */ - var $timezone = array( - 'ACDT' => 37800, - 'ACIT' => 28800, - 'ACST' => 34200, - 'ACT' => -18000, - 'ACWDT' => 35100, - 'ACWST' => 31500, - 'AEDT' => 39600, - 'AEST' => 36000, - 'AFT' => 16200, - 'AKDT' => -28800, - 'AKST' => -32400, - 'AMDT' => 18000, - 'AMT' => -14400, - 'ANAST' => 46800, - 'ANAT' => 43200, - 'ART' => -10800, - 'AZOST' => -3600, - 'AZST' => 18000, - 'AZT' => 14400, - 'BIOT' => 21600, - 'BIT' => -43200, - 'BOT' => -14400, - 'BRST' => -7200, - 'BRT' => -10800, - 'BST' => 3600, - 'BTT' => 21600, - 'CAST' => 18000, - 'CAT' => 7200, - 'CCT' => 23400, - 'CDT' => -18000, - 'CEDT' => 7200, - 'CET' => 3600, - 'CGST' => -7200, - 'CGT' => -10800, - 'CHADT' => 49500, - 'CHAST' => 45900, - 'CIST' => -28800, - 'CKT' => -36000, - 'CLDT' => -10800, - 'CLST' => -14400, - 'COT' => -18000, - 'CST' => -21600, - 'CVT' => -3600, - 'CXT' => 25200, - 'DAVT' => 25200, - 'DTAT' => 36000, - 'EADT' => -18000, - 'EAST' => -21600, - 'EAT' => 10800, - 'ECT' => -18000, - 'EDT' => -14400, - 'EEST' => 10800, - 'EET' => 7200, - 'EGT' => -3600, - 'EKST' => 21600, - 'EST' => -18000, - 'FJT' => 43200, - 'FKDT' => -10800, - 'FKST' => -14400, - 'FNT' => -7200, - 'GALT' => -21600, - 'GEDT' => 14400, - 'GEST' => 10800, - 'GFT' => -10800, - 'GILT' => 43200, - 'GIT' => -32400, - 'GST' => 14400, - 'GST' => -7200, - 'GYT' => -14400, - 'HAA' => -10800, - 'HAC' => -18000, - 'HADT' => -32400, - 'HAE' => -14400, - 'HAP' => -25200, - 'HAR' => -21600, - 'HAST' => -36000, - 'HAT' => -9000, - 'HAY' => -28800, - 'HKST' => 28800, - 'HMT' => 18000, - 'HNA' => -14400, - 'HNC' => -21600, - 'HNE' => -18000, - 'HNP' => -28800, - 'HNR' => -25200, - 'HNT' => -12600, - 'HNY' => -32400, - 'IRDT' => 16200, - 'IRKST' => 32400, - 'IRKT' => 28800, - 'IRST' => 12600, - 'JFDT' => -10800, - 'JFST' => -14400, - 'JST' => 32400, - 'KGST' => 21600, - 'KGT' => 18000, - 'KOST' => 39600, - 'KOVST' => 28800, - 'KOVT' => 25200, - 'KRAST' => 28800, - 'KRAT' => 25200, - 'KST' => 32400, - 'LHDT' => 39600, - 'LHST' => 37800, - 'LINT' => 50400, - 'LKT' => 21600, - 'MAGST' => 43200, - 'MAGT' => 39600, - 'MAWT' => 21600, - 'MDT' => -21600, - 'MESZ' => 7200, - 'MEZ' => 3600, - 'MHT' => 43200, - 'MIT' => -34200, - 'MNST' => 32400, - 'MSDT' => 14400, - 'MSST' => 10800, - 'MST' => -25200, - 'MUT' => 14400, - 'MVT' => 18000, - 'MYT' => 28800, - 'NCT' => 39600, - 'NDT' => -9000, - 'NFT' => 41400, - 'NMIT' => 36000, - 'NOVST' => 25200, - 'NOVT' => 21600, - 'NPT' => 20700, - 'NRT' => 43200, - 'NST' => -12600, - 'NUT' => -39600, - 'NZDT' => 46800, - 'NZST' => 43200, - 'OMSST' => 25200, - 'OMST' => 21600, - 'PDT' => -25200, - 'PET' => -18000, - 'PETST' => 46800, - 'PETT' => 43200, - 'PGT' => 36000, - 'PHOT' => 46800, - 'PHT' => 28800, - 'PKT' => 18000, - 'PMDT' => -7200, - 'PMST' => -10800, - 'PONT' => 39600, - 'PST' => -28800, - 'PWT' => 32400, - 'PYST' => -10800, - 'PYT' => -14400, - 'RET' => 14400, - 'ROTT' => -10800, - 'SAMST' => 18000, - 'SAMT' => 14400, - 'SAST' => 7200, - 'SBT' => 39600, - 'SCDT' => 46800, - 'SCST' => 43200, - 'SCT' => 14400, - 'SEST' => 3600, - 'SGT' => 28800, - 'SIT' => 28800, - 'SRT' => -10800, - 'SST' => -39600, - 'SYST' => 10800, - 'SYT' => 7200, - 'TFT' => 18000, - 'THAT' => -36000, - 'TJT' => 18000, - 'TKT' => -36000, - 'TMT' => 18000, - 'TOT' => 46800, - 'TPT' => 32400, - 'TRUT' => 36000, - 'TVT' => 43200, - 'TWT' => 28800, - 'UYST' => -7200, - 'UYT' => -10800, - 'UZT' => 18000, - 'VET' => -14400, - 'VLAST' => 39600, - 'VLAT' => 36000, - 'VOST' => 21600, - 'VUT' => 39600, - 'WAST' => 7200, - 'WAT' => 3600, - 'WDT' => 32400, - 'WEST' => 3600, - 'WFT' => 43200, - 'WIB' => 25200, - 'WIT' => 32400, - 'WITA' => 28800, - 'WKST' => 18000, - 'WST' => 28800, - 'YAKST' => 36000, - 'YAKT' => 32400, - 'YAPT' => 36000, - 'YEKST' => 21600, - 'YEKT' => 18000, - ); - - /** - * Cached PCRE for SimplePie_Parse_Date::$day - * - * @access protected - * @var string - */ - var $day_pcre; - - /** - * Cached PCRE for SimplePie_Parse_Date::$month - * - * @access protected - * @var string - */ - var $month_pcre; - - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $built_in = array(); - - /** - * Array of user-added callback methods - * - * @access private - * @var array - */ - var $user = array(); - - /** - * Create new SimplePie_Parse_Date object, and set self::day_pcre, - * self::month_pcre, and self::built_in - * - * @access private - */ - function SimplePie_Parse_Date() - { - $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; - $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; - - static $cache; - if (!isset($cache[get_class($this)])) - { - $all_methods = get_class_methods($this); - - foreach ($all_methods as $method) - { - if (strtolower(substr($method, 0, 5)) === 'date_') - { - $cache[get_class($this)][] = $method; - } - } - } - - foreach ($cache[get_class($this)] as $method) - { - $this->built_in[] = $method; - } - } - - /** - * Get the object - * - * @access public - */ - function get() - { - static $object; - if (!$object) - { - $object = new SimplePie_Parse_Date; - } - return $object; - } - - /** - * Parse a date - * - * @final - * @access public - * @param string $date Date to parse - * @return int Timestamp corresponding to date string, or false on failure - */ - function parse($date) - { - foreach ($this->user as $method) - { - if (($returned = call_user_func($method, $date)) !== false) - { - return $returned; - } - } - - foreach ($this->built_in as $method) - { - if (($returned = call_user_func(array(&$this, $method), $date)) !== false) - { - return $returned; - } - } - - return false; - } - - /** - * Add a callback method to parse a date - * - * @final - * @access public - * @param callback $callback - */ - function add_callback($callback) - { - if (is_callable($callback)) - { - $this->user[] = $callback; - } - else - { - trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); - } - } - - /** - * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as - * well as allowing any of upper or lower case "T", horizontal tabs, or - * spaces to be used as the time seperator (including more than one)) - * - * @access protected - * @return int Timestamp - */ - function date_w3cdtf($date) - { - static $pcre; - if (!$pcre) - { - $year = '([0-9]{4})'; - $month = $day = $hour = $minute = $second = '([0-9]{2})'; - $decimal = '([0-9]*)'; - $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; - $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Year - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Decimal fraction of a second - 8: Zulu - 9: Timezone ± - 10: Timezone hours - 11: Timezone minutes - */ - - // Fill in empty matches - for ($i = count($match); $i <= 3; $i++) - { - $match[$i] = '1'; - } - - for ($i = count($match); $i <= 7; $i++) - { - $match[$i] = '0'; - } - - // Numeric timezone - if (isset($match[9]) && $match[9] !== '') - { - $timezone = $match[10] * 3600; - $timezone += $match[11] * 60; - if ($match[9] === '-') - { - $timezone = 0 - $timezone; - } - } - else - { - $timezone = 0; - } - - // Convert the number of seconds to an integer, taking decimals into account - $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); - - return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; - } - else - { - return false; - } - } - - /** - * Remove RFC822 comments - * - * @access protected - * @param string $data Data to strip comments from - * @return string Comment stripped string - */ - function remove_rfc2822_comments($string) - { - $string = (string) $string; - $position = 0; - $length = strlen($string); - $depth = 0; - - $output = ''; - - while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) - { - $output .= substr($string, $position, $pos - $position); - $position = $pos + 1; - if ($string[$pos - 1] !== '\\') - { - $depth++; - while ($depth && $position < $length) - { - $position += strcspn($string, '()', $position); - if ($string[$position - 1] === '\\') - { - $position++; - continue; - } - elseif (isset($string[$position])) - { - switch ($string[$position]) - { - case '(': - $depth++; - break; - - case ')': - $depth--; - break; - } - $position++; - } - else - { - break; - } - } - } - else - { - $output .= '('; - } - } - $output .= substr($string, $position); - - return $output; - } - - /** - * Parse RFC2822's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc2822($date) - { - static $pcre; - if (!$pcre) - { - $wsp = '[\x09\x20]'; - $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; - $optional_fws = $fws . '?'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $minute = $second = '([0-9]{2})'; - $year = '([0-9]{2,4})'; - $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; - $character_zone = '([A-Z]{1,5})'; - $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; - $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; - } - if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone ± - 9: Timezone hours - 10: Timezone minutes - 11: Alphabetic timezone - */ - - // Find the month number - $month = $this->month[strtolower($match[3])]; - - // Numeric timezone - if ($match[8] !== '') - { - $timezone = $match[9] * 3600; - $timezone += $match[10] * 60; - if ($match[8] === '-') - { - $timezone = 0 - $timezone; - } - } - // Character timezone - elseif (isset($this->timezone[strtoupper($match[11])])) - { - $timezone = $this->timezone[strtoupper($match[11])]; - } - // Assume everything else to be -0000 - else - { - $timezone = 0; - } - - // Deal with 2/3 digit years - if ($match[4] < 50) - { - $match[4] += 2000; - } - elseif ($match[4] < 1000) - { - $match[4] += 1900; - } - - // Second is optional, if it is empty set it to zero - if ($match[7] !== '') - { - $second = $match[7]; - } - else - { - $second = 0; - } - - return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; - } - else - { - return false; - } - } - - /** - * Parse RFC850's date format - * - * @access protected - * @return int Timestamp - */ - function date_rfc850($date) - { - static $pcre; - if (!$pcre) - { - $space = '[\x09\x20]+'; - $day_name = $this->day_pcre; - $month = $this->month_pcre; - $day = '([0-9]{1,2})'; - $year = $hour = $minute = $second = '([0-9]{2})'; - $zone = '([A-Z]{1,5})'; - $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Day - 3: Month - 4: Year - 5: Hour - 6: Minute - 7: Second - 8: Timezone - */ - - // Month - $month = $this->month[strtolower($match[3])]; - - // Character timezone - if (isset($this->timezone[strtoupper($match[8])])) - { - $timezone = $this->timezone[strtoupper($match[8])]; - } - // Assume everything else to be -0000 - else - { - $timezone = 0; - } - - // Deal with 2 digit year - if ($match[4] < 50) - { - $match[4] += 2000; - } - else - { - $match[4] += 1900; - } - - return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; - } - else - { - return false; - } - } - - /** - * Parse C99's asctime()'s date format - * - * @access protected - * @return int Timestamp - */ - function date_asctime($date) - { - static $pcre; - if (!$pcre) - { - $space = '[\x09\x20]+'; - $wday_name = $this->day_pcre; - $mon_name = $this->month_pcre; - $day = '([0-9]{1,2})'; - $hour = $sec = $min = '([0-9]{2})'; - $year = '([0-9]{4})'; - $terminator = '\x0A?\x00?'; - $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; - } - if (preg_match($pcre, $date, $match)) - { - /* - Capturing subpatterns: - 1: Day name - 2: Month - 3: Day - 4: Hour - 5: Minute - 6: Second - 7: Year - */ - - $month = $this->month[strtolower($match[2])]; - return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); - } - else - { - return false; - } - } - - /** - * Parse dates using strtotime() - * - * @access protected - * @return int Timestamp - */ - function date_strtotime($date) - { - $strtotime = strtotime($date); - if ($strtotime === -1 || $strtotime === false) - { - return false; - } - else - { - return $strtotime; - } - } -} - -/** - * Content-type sniffing - * - * @package SimplePie - */ -class SimplePie_Content_Type_Sniffer -{ - /** - * File object - * - * @var SimplePie_File - * @access private - */ - var $file; - - /** - * Create an instance of the class with the input file - * - * @access public - * @param SimplePie_Content_Type_Sniffer $file Input file - */ - function SimplePie_Content_Type_Sniffer($file) - { - $this->file = $file; - } - - /** - * Get the Content-Type of the specified file - * - * @access public - * @return string Actual Content-Type - */ - function get_type() - { - if (isset($this->file->headers['content-type'])) - { - if (!isset($this->file->headers['content-encoding']) - && ($this->file->headers['content-type'] === 'text/plain' - || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' - || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1')) - { - return $this->text_or_binary(); - } - - if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) - { - $official = substr($this->file->headers['content-type'], 0, $pos); - } - else - { - $official = $this->file->headers['content-type']; - } - $official = strtolower($official); - - if ($official === 'unknown/unknown' - || $official === 'application/unknown') - { - return $this->unknown(); - } - elseif (substr($official, -4) === '+xml' - || $official === 'text/xml' - || $official === 'application/xml') - { - return $official; - } - elseif (substr($official, 0, 6) === 'image/') - { - if ($return = $this->image()) - { - return $return; - } - else - { - return $official; - } - } - elseif ($official === 'text/html') - { - return $this->feed_or_html(); - } - else - { - return $official; - } - } - else - { - return $this->unknown(); - } - } - - /** - * Sniff text or binary - * - * @access private - * @return string Actual Content-Type - */ - function text_or_binary() - { - if (substr($this->file->body, 0, 2) === "\xFE\xFF" - || substr($this->file->body, 0, 2) === "\xFF\xFE" - || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" - || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") - { - return 'text/plain'; - } - elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) - { - return 'application/octect-stream'; - } - else - { - return 'text/plain'; - } - } - - /** - * Sniff unknown - * - * @access private - * @return string Actual Content-Type - */ - function unknown() - { - $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); - if (strtolower(substr($this->file->body, $ws, 14)) === 'file->body, $ws, 5)) === 'file->body, $ws, 7)) === 'file->body, 0, 5) === '%PDF-') - { - return 'application/pdf'; - } - elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') - { - return 'application/postscript'; - } - elseif (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") - { - return 'image/png'; - } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") - { - return 'image/jpeg'; - } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") - { - return 'image/bmp'; - } - else - { - return $this->text_or_binary(); - } - } - - /** - * Sniff images - * - * @access private - * @return string Actual Content-Type - */ - function image() - { - if (substr($this->file->body, 0, 6) === 'GIF87a' - || substr($this->file->body, 0, 6) === 'GIF89a') - { - return 'image/gif'; - } - elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") - { - return 'image/png'; - } - elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") - { - return 'image/jpeg'; - } - elseif (substr($this->file->body, 0, 2) === "\x42\x4D") - { - return 'image/bmp'; - } - else - { - return false; - } - } - - /** - * Sniff HTML - * - * @access private - * @return string Actual Content-Type - */ - function feed_or_html() - { - $len = strlen($this->file->body); - $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); - - while ($pos < $len) - { - switch ($this->file->body[$pos]) - { - case "\x09": - case "\x0A": - case "\x0D": - case "\x20": - $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); - continue 2; - - case '<': - $pos++; - break; - - default: - return 'text/html'; - } - - if (substr($this->file->body, $pos, 3) === '!--') - { - $pos += 3; - if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) - { - $pos += 3; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '!') - { - if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) - { - $pos++; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 1) === '?') - { - if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) - { - $pos += 2; - } - else - { - return 'text/html'; - } - } - elseif (substr($this->file->body, $pos, 3) === 'rss' - || substr($this->file->body, $pos, 7) === 'rdf:RDF') - { - return 'application/rss+xml'; - } - elseif (substr($this->file->body, $pos, 4) === 'feed') - { - return 'application/atom+xml'; - } - else - { - return 'text/html'; - } - } - - return 'text/html'; - } -} - -/** - * Parses the XML Declaration - * - * @package SimplePie - */ -class SimplePie_XML_Declaration_Parser -{ - /** - * XML Version - * - * @access public - * @var string - */ - var $version = '1.0'; - - /** - * Encoding - * - * @access public - * @var string - */ - var $encoding = 'UTF-8'; - - /** - * Standalone - * - * @access public - * @var bool - */ - var $standalone = false; - - /** - * Current state of the state machine - * - * @access private - * @var string - */ - var $state = 'before_version_name'; - - /** - * Input data - * - * @access private - * @var string - */ - var $data = ''; - - /** - * Input data length (to avoid calling strlen() everytime this is needed) - * - * @access private - * @var int - */ - var $data_length = 0; - - /** - * Current position of the pointer - * - * @var int - * @access private - */ - var $position = 0; - - /** - * Create an instance of the class with the input data - * - * @access public - * @param string $data Input data - */ - function SimplePie_XML_Declaration_Parser($data) - { - $this->data = $data; - $this->data_length = strlen($this->data); - } - - /** - * Parse the input data - * - * @access public - * @return bool true on success, false on failure - */ - function parse() - { - while ($this->state && $this->state !== 'emit' && $this->has_data()) - { - $state = $this->state; - $this->$state(); - } - $this->data = ''; - if ($this->state === 'emit') - { - return true; - } - else - { - $this->version = ''; - $this->encoding = ''; - $this->standalone = ''; - return false; - } - } - - /** - * Check whether there is data beyond the pointer - * - * @access private - * @return bool true if there is further data, false if not - */ - function has_data() - { - return (bool) ($this->position < $this->data_length); - } - - /** - * Advance past any whitespace - * - * @return int Number of whitespace characters passed - */ - function skip_whitespace() - { - $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); - $this->position += $whitespace; - return $whitespace; - } - - /** - * Read value - */ - function get_value() - { - $quote = substr($this->data, $this->position, 1); - if ($quote === '"' || $quote === "'") - { - $this->position++; - $len = strcspn($this->data, $quote, $this->position); - if ($this->has_data()) - { - $value = substr($this->data, $this->position, $len); - $this->position += $len + 1; - return $value; - } - } - return false; - } - - function before_version_name() - { - if ($this->skip_whitespace()) - { - $this->state = 'version_name'; - } - else - { - $this->state = false; - } - } - - function version_name() - { - if (substr($this->data, $this->position, 7) === 'version') - { - $this->position += 7; - $this->skip_whitespace(); - $this->state = 'version_equals'; - } - else - { - $this->state = false; - } - } - - function version_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'version_value'; - } - else - { - $this->state = false; - } - } - - function version_value() - { - if ($this->version = $this->get_value()) - { - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = 'encoding_name'; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = false; - } - } - - function encoding_name() - { - if (substr($this->data, $this->position, 8) === 'encoding') - { - $this->position += 8; - $this->skip_whitespace(); - $this->state = 'encoding_equals'; - } - else - { - $this->state = 'standalone_name'; - } - } - - function encoding_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'encoding_value'; - } - else - { - $this->state = false; - } - } - - function encoding_value() - { - if ($this->encoding = $this->get_value()) - { - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = 'standalone_name'; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = false; - } - } - - function standalone_name() - { - if (substr($this->data, $this->position, 10) === 'standalone') - { - $this->position += 10; - $this->skip_whitespace(); - $this->state = 'standalone_equals'; - } - else - { - $this->state = false; - } - } - - function standalone_equals() - { - if (substr($this->data, $this->position, 1) === '=') - { - $this->position++; - $this->skip_whitespace(); - $this->state = 'standalone_value'; - } - else - { - $this->state = false; - } - } - - function standalone_value() - { - if ($standalone = $this->get_value()) - { - switch ($standalone) - { - case 'yes': - $this->standalone = true; - break; - - case 'no': - $this->standalone = false; - break; - - default: - $this->state = false; - return; - } - - $this->skip_whitespace(); - if ($this->has_data()) - { - $this->state = false; - } - else - { - $this->state = 'emit'; - } - } - else - { - $this->state = false; - } - } -} - -class SimplePie_Locator -{ - var $useragent; - var $timeout; - var $file; - var $local = array(); - var $elsewhere = array(); - var $file_class = 'SimplePie_File'; - var $cached_entities = array(); - var $http_base; - var $base; - var $base_location = 0; - var $checked_feeds = 0; - var $max_checked_feeds = 10; - var $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer'; - - function SimplePie_Locator(&$file, $timeout = 10, $useragent = null, $file_class = 'SimplePie_File', $max_checked_feeds = 10, $content_type_sniffer_class = 'SimplePie_Content_Type_Sniffer') - { - $this->file =& $file; - $this->file_class = $file_class; - $this->useragent = $useragent; - $this->timeout = $timeout; - $this->max_checked_feeds = $max_checked_feeds; - $this->content_type_sniffer_class = $content_type_sniffer_class; - } - - function find($type = SIMPLEPIE_LOCATOR_ALL, &$working) - { - if ($this->is_feed($this->file)) - { - return $this->file; - } - - if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) - { - $sniffer = new $this->content_type_sniffer_class($this->file); - if ($sniffer->get_type() !== 'text/html') - { - return null; - } - } - - if ($type & ~SIMPLEPIE_LOCATOR_NONE) - { - $this->get_base(); - } - - if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) - { - return $working[0]; - } - - if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) - { - if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) - { - return $working; - } - - if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) - { - return $working; - } - } - return null; - } - - function is_feed(&$file) - { - if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) - { - $sniffer = new $this->content_type_sniffer_class($file); - $sniffed = $sniffer->get_type(); - if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) - { - return true; - } - else - { - return false; - } - } - elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) - { - return true; - } - else - { - return false; - } - } - - function get_base() - { - $this->http_base = $this->file->url; - $this->base = $this->http_base; - $elements = SimplePie_Misc::get_element('base', $this->file->body); - foreach ($elements as $element) - { - if ($element['attribs']['href']['data'] !== '') - { - $this->base = SimplePie_Misc::absolutize_url(trim($element['attribs']['href']['data']), $this->http_base); - $this->base_location = $element['offset']; - break; - } - } - } - - function autodiscovery() - { - $links = array_merge(SimplePie_Misc::get_element('link', $this->file->body), SimplePie_Misc::get_element('a', $this->file->body), SimplePie_Misc::get_element('area', $this->file->body)); - $done = array(); - $feeds = array(); - foreach ($links as $link) - { - if ($this->checked_feeds === $this->max_checked_feeds) - { - break; - } - if (isset($link['attribs']['href']['data']) && isset($link['attribs']['rel']['data'])) - { - $rel = array_unique(SimplePie_Misc::space_seperated_tokens(strtolower($link['attribs']['rel']['data']))); - - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !empty($link['attribs']['type']['data']) && in_array(strtolower(SimplePie_Misc::parse_mime($link['attribs']['type']['data'])), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href])) - { - $this->checked_feeds++; - $feed = new $this->file_class($href, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) - { - $feeds[$href] = $feed; - } - } - $done[] = $href; - } - } - - if (!empty($feeds)) - { - return array_values($feeds); - } - else { - return null; - } - } - - function get_links() - { - $links = SimplePie_Misc::get_element('a', $this->file->body); - foreach ($links as $link) - { - if (isset($link['attribs']['href']['data'])) - { - $href = trim($link['attribs']['href']['data']); - $parsed = SimplePie_Misc::parse_url($href); - if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) - { - if ($this->base_location < $link['offset']) - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->base); - } - else - { - $href = SimplePie_Misc::absolutize_url(trim($link['attribs']['href']['data']), $this->http_base); - } - - $current = SimplePie_Misc::parse_url($this->file->url); - - if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) - { - $this->local[] = $href; - } - else - { - $this->elsewhere[] = $href; - } - } - } - } - $this->local = array_unique($this->local); - $this->elsewhere = array_unique($this->elsewhere); - if (!empty($this->local) || !empty($this->elsewhere)) - { - return true; - } - return null; - } - - function extension(&$array) - { - foreach ($array as $key => $value) - { - if ($this->checked_feeds === $this->max_checked_feeds) - { - break; - } - if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) - { - $this->checked_feeds++; - $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) - { - return $feed; - } - else - { - unset($array[$key]); - } - } - } - return null; - } - - function body(&$array) - { - foreach ($array as $key => $value) - { - if ($this->checked_feeds === $this->max_checked_feeds) - { - break; - } - if (preg_match('/(rss|rdf|atom|xml)/i', $value)) - { - $this->checked_feeds++; - $feed = new $this->file_class($value, $this->timeout, 5, null, $this->useragent); - if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) - { - return $feed; - } - else - { - unset($array[$key]); - } - } - } - return null; - } -} - -class SimplePie_Parser -{ - var $error_code; - var $error_string; - var $current_line; - var $current_column; - var $current_byte; - var $separator = ' '; - var $namespace = array(''); - var $element = array(''); - var $xml_base = array(''); - var $xml_base_explicit = array(false); - var $xml_lang = array(''); - var $data = array(); - var $datas = array(array()); - var $current_xhtml_construct = -1; - var $encoding; - - function parse(&$data, $encoding) - { - // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character - if (strtoupper($encoding) === 'US-ASCII') - { - $this->encoding = 'UTF-8'; - } - else - { - $this->encoding = $encoding; - } - - // Strip BOM: - // UTF-32 Big Endian BOM - if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") - { - $data = substr($data, 4); - } - // UTF-32 Little Endian BOM - elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") - { - $data = substr($data, 4); - } - // UTF-16 Big Endian BOM - elseif (substr($data, 0, 2) === "\xFE\xFF") - { - $data = substr($data, 2); - } - // UTF-16 Little Endian BOM - elseif (substr($data, 0, 2) === "\xFF\xFE") - { - $data = substr($data, 2); - } - // UTF-8 BOM - elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") - { - $data = substr($data, 3); - } - - if (substr($data, 0, 5) === '')) !== false) - { - $declaration = new SimplePie_XML_Declaration_Parser(substr($data, 5, $pos - 5)); - if ($declaration->parse()) - { - $data = substr($data, $pos + 2); - $data = 'version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; - } - else - { - $this->error_string = 'SimplePie bug! Please report this!'; - return false; - } - } - - $return = true; - - static $xml_is_sane = null; - if ($xml_is_sane === null) - { - $parser_check = xml_parser_create(); - xml_parse_into_struct($parser_check, '&', $values); - xml_parser_free($parser_check); - $xml_is_sane = isset($values[0]['value']); - } - - // Create the parser - if ($xml_is_sane) - { - $xml = xml_parser_create_ns($this->encoding, $this->separator); - xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); - xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); - xml_set_object($xml, $this); - xml_set_character_data_handler($xml, 'cdata'); - xml_set_element_handler($xml, 'tag_open', 'tag_close'); - - // Parse! - if (!xml_parse($xml, $data, true)) - { - $this->error_code = xml_get_error_code($xml); - $this->error_string = xml_error_string($this->error_code); - $return = false; - } - $this->current_line = xml_get_current_line_number($xml); - $this->current_column = xml_get_current_column_number($xml); - $this->current_byte = xml_get_current_byte_index($xml); - xml_parser_free($xml); - return $return; - } - else - { - libxml_clear_errors(); - $xml = new XMLReader(); - $xml->xml($data); - while (@$xml->read()) - { - switch ($xml->nodeType) - { - - case constant('XMLReader::END_ELEMENT'): - if ($xml->namespaceURI !== '') - { - $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}"; - } - else - { - $tagName = $xml->localName; - } - $this->tag_close(null, $tagName); - break; - case constant('XMLReader::ELEMENT'): - $empty = $xml->isEmptyElement; - if ($xml->namespaceURI !== '') - { - $tagName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}"; - } - else - { - $tagName = $xml->localName; - } - $attributes = array(); - while ($xml->moveToNextAttribute()) - { - if ($xml->namespaceURI !== '') - { - $attrName = "{$xml->namespaceURI}{$this->separator}{$xml->localName}"; - } - else - { - $attrName = $xml->localName; - } - $attributes[$attrName] = $xml->value; - } - $this->tag_open(null, $tagName, $attributes); - if ($empty) - { - $this->tag_close(null, $tagName); - } - break; - case constant('XMLReader::TEXT'): - - case constant('XMLReader::CDATA'): - $this->cdata(null, $xml->value); - break; - } - } - if ($error = libxml_get_last_error()) - { - $this->error_code = $error->code; - $this->error_string = $error->message; - $this->current_line = $error->line; - $this->current_column = $error->column; - return false; - } - else - { - return true; - } - } - } - - function get_error_code() - { - return $this->error_code; - } - - function get_error_string() - { - return $this->error_string; - } - - function get_current_line() - { - return $this->current_line; - } - - function get_current_column() - { - return $this->current_column; - } - - function get_current_byte() - { - return $this->current_byte; - } - - function get_data() - { - return $this->data; - } - - function tag_open($parser, $tag, $attributes) - { - list($this->namespace[], $this->element[]) = $this->split_ns($tag); - - $attribs = array(); - foreach ($attributes as $name => $value) - { - list($attrib_namespace, $attribute) = $this->split_ns($name); - $attribs[$attrib_namespace][$attribute] = $value; - } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) - { - $this->xml_base[] = SimplePie_Misc::absolutize_url($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base)); - $this->xml_base_explicit[] = true; - } - else - { - $this->xml_base[] = end($this->xml_base); - $this->xml_base_explicit[] = end($this->xml_base_explicit); - } - - if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) - { - $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; - } - else - { - $this->xml_lang[] = end($this->xml_lang); - } - - if ($this->current_xhtml_construct >= 0) - { - $this->current_xhtml_construct++; - if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML) - { - $this->data['data'] .= '<' . end($this->element); - if (isset($attribs[''])) - { - foreach ($attribs[''] as $name => $value) - { - $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; - } - } - $this->data['data'] .= '>'; - } - } - else - { - $this->datas[] =& $this->data; - $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; - $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); - if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml') - || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml')) - { - $this->current_xhtml_construct = 0; - } - } - } - - function cdata($parser, $cdata) - { - if ($this->current_xhtml_construct >= 0) - { - $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); - } - else - { - $this->data['data'] .= $cdata; - } - } - - function tag_close($parser, $tag) - { - if ($this->current_xhtml_construct >= 0) - { - $this->current_xhtml_construct--; - if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) - { - $this->data['data'] .= 'element) . '>'; - } - } - if ($this->current_xhtml_construct === -1) - { - $this->data =& $this->datas[count($this->datas) - 1]; - array_pop($this->datas); - } - - array_pop($this->element); - array_pop($this->namespace); - array_pop($this->xml_base); - array_pop($this->xml_base_explicit); - array_pop($this->xml_lang); - } - - function split_ns($string) - { - static $cache = array(); - if (!isset($cache[$string])) - { - if ($pos = strpos($string, $this->separator)) - { - static $separator_length; - if (!$separator_length) - { - $separator_length = strlen($this->separator); - } - $namespace = substr($string, 0, $pos); - $local_name = substr($string, $pos + $separator_length); - if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) - { - $namespace = SIMPLEPIE_NAMESPACE_ITUNES; - } - - // Normalize the Media RSS namespaces - if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG) - { - $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; - } - $cache[$string] = array($namespace, $local_name); - } - else - { - $cache[$string] = array('', $string); - } - } - return $cache[$string]; - } -} - -/** - * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags - */ -class SimplePie_Sanitize -{ - // Private vars - var $base; - - // Options - var $remove_div = true; - var $image_handler = ''; - var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); - var $encode_instead_of_strip = false; - var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); - var $strip_comments = false; - var $output_encoding = 'UTF-8'; - var $enable_cache = true; - var $cache_location = './cache'; - var $cache_name_function = 'md5'; - var $cache_class = 'SimplePie_Cache'; - var $file_class = 'SimplePie_File'; - var $timeout = 10; - var $useragent = ''; - var $force_fsockopen = false; - - var $replace_url_attributes = array( - 'a' => 'href', - 'area' => 'href', - 'blockquote' => 'cite', - 'del' => 'cite', - 'form' => 'action', - 'img' => array('longdesc', 'src'), - 'input' => 'src', - 'ins' => 'cite', - 'q' => 'cite' - ); - - function remove_div($enable = true) - { - $this->remove_div = (bool) $enable; - } - - function set_image_handler($page = false) - { - if ($page) - { - $this->image_handler = (string) $page; - } - else - { - $this->image_handler = false; - } - } - - function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') - { - if (isset($enable_cache)) - { - $this->enable_cache = (bool) $enable_cache; - } - - if ($cache_location) - { - $this->cache_location = (string) $cache_location; - } - - if ($cache_name_function) - { - $this->cache_name_function = (string) $cache_name_function; - } - - if ($cache_class) - { - $this->cache_class = (string) $cache_class; - } - } - - function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) - { - if ($file_class) - { - $this->file_class = (string) $file_class; - } - - if ($timeout) - { - $this->timeout = (string) $timeout; - } - - if ($useragent) - { - $this->useragent = (string) $useragent; - } - - if ($force_fsockopen) - { - $this->force_fsockopen = (string) $force_fsockopen; - } - } - - function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) - { - if ($tags) - { - if (is_array($tags)) - { - $this->strip_htmltags = $tags; - } - else - { - $this->strip_htmltags = explode(',', $tags); - } - } - else - { - $this->strip_htmltags = false; - } - } - - function encode_instead_of_strip($encode = false) - { - $this->encode_instead_of_strip = (bool) $encode; - } - - function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) - { - if ($attribs) - { - if (is_array($attribs)) - { - $this->strip_attributes = $attribs; - } - else - { - $this->strip_attributes = explode(',', $attribs); - } - } - else - { - $this->strip_attributes = false; - } - } - - function strip_comments($strip = false) - { - $this->strip_comments = (bool) $strip; - } - - function set_output_encoding($encoding = 'UTF-8') - { - $this->output_encoding = (string) $encoding; - } - - /** - * Set element/attribute key/value pairs of HTML attributes - * containing URLs that need to be resolved relative to the feed - * - * @access public - * @since 1.0 - * @param array $element_attribute Element/attribute key/value pairs - */ - function set_url_replacements($element_attribute = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite')) - { - $this->replace_url_attributes = (array) $element_attribute; - } - - function sanitize($data, $type, $base = '') - { - $data = trim($data); - if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) - { - if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) - { - if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) - { - $type |= SIMPLEPIE_CONSTRUCT_HTML; - } - else - { - $type |= SIMPLEPIE_CONSTRUCT_TEXT; - } - } - - if ($type & SIMPLEPIE_CONSTRUCT_BASE64) - { - $data = base64_decode($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_XHTML) - { - if ($this->remove_div) - { - $data = preg_replace('/^/', '', $data); - $data = preg_replace('/<\/div>$/', '', $data); - } - else - { - $data = preg_replace('/^/', '
            ', $data); - } - } - - if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) - { - // Strip comments - if ($this->strip_comments) - { - $data = SimplePie_Misc::strip_comments($data); - } - - // Strip out HTML tags and attributes that might cause various security problems. - // Based on recommendations by Mark Pilgrim at: - // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely - if ($this->strip_htmltags) - { - foreach ($this->strip_htmltags as $tag) - { - $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU'; - while (preg_match($pcre, $data)) - { - $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data); - } - } - } - - if ($this->strip_attributes) - { - foreach ($this->strip_attributes as $attrib) - { - $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data); - } - } - - // Replace relative URLs - $this->base = $base; - foreach ($this->replace_url_attributes as $element => $attributes) - { - $data = $this->replace_urls($data, $element, $attributes); - } - - // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. - if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) - { - $images = SimplePie_Misc::get_element('img', $data); - foreach ($images as $img) - { - if (isset($img['attribs']['src']['data'])) - { - $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']); - $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi'); - - if ($cache->load()) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - $file = new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen); - $headers = $file->headers; - - if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) - { - if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) - { - $img['attribs']['src']['data'] = $this->image_handler . $image_url; - $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data); - } - else - { - trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); - } - } - } - } - } - } - - // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data - $data = trim($data); - } - - if ($type & SIMPLEPIE_CONSTRUCT_IRI) - { - $data = SimplePie_Misc::absolutize_url($data, $base); - } - - if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) - { - $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); - } - - if ($this->output_encoding !== 'UTF-8') - { - $data = SimplePie_Misc::change_encoding($data, 'UTF-8', $this->output_encoding); - } - } - return $data; - } - - function replace_urls($data, $tag, $attributes) - { - if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) - { - $elements = SimplePie_Misc::get_element($tag, $data); - foreach ($elements as $element) - { - if (is_array($attributes)) - { - foreach ($attributes as $attribute) - { - if (isset($element['attribs'][$attribute]['data'])) - { - $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base); - $new_element = SimplePie_Misc::element_implode($element); - $data = str_replace($element['full'], $new_element, $data); - $element['full'] = $new_element; - } - } - } - elseif (isset($element['attribs'][$attributes]['data'])) - { - $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base); - $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data); - } - } - } - return $data; - } - - function do_strip_htmltags($match) - { - if ($this->encode_instead_of_strip) - { - if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) - { - $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); - $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); - return "<$match[1]$match[2]>$match[3]</$match[1]>"; - } - else - { - return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); - } - } - elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) - { - return $match[4]; - } - else - { - return ''; - } - } -} - -?> diff --git a/mod/admin.php b/mod/admin.php index 7f9000807b..2fc9c48a78 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -55,13 +55,13 @@ function admin_post(&$a){ $func($a); } } - goaway($a->get_baseurl(true) . '/admin/plugins/' . $a->argv[2] ); + goaway('admin/plugins/'.$a->argv[2]); return; // NOTREACHED break; case 'themes': if($a->argc < 2) { if(is_ajax()) return; - goaway($a->get_baseurl(true) . '/admin/' ); + goaway('admin/'); return; } @@ -92,7 +92,7 @@ function admin_post(&$a){ info(t('Theme settings updated.')); if(is_ajax()) return; - goaway($a->get_baseurl(true) . '/admin/themes/' . $theme ); + goaway('admin/themes/'.$theme); return; break; case 'features': @@ -107,7 +107,7 @@ function admin_post(&$a){ } } - goaway($a->get_baseurl(true) . '/admin' ); + goaway('admin'); return; // NOTREACHED } @@ -150,17 +150,17 @@ function admin_content(&$a) { * Side bar links */ $aside_tools = array(); - // array( url, name, extra css classes ) + // array(url, name, extra css classes) // not part of $aside to make the template more adjustable $aside_sub = array( - 'site' => array($a->get_baseurl(true)."/admin/site/", t("Site") , "site"), - 'users' => array($a->get_baseurl(true)."/admin/users/", t("Users") , "users"), - 'plugins'=> array($a->get_baseurl(true)."/admin/plugins/", t("Plugins") , "plugins"), - 'themes' => array($a->get_baseurl(true)."/admin/themes/", t("Themes") , "themes"), - 'features' => array($a->get_baseurl(true)."/admin/features/", t("Additional features") , "features"), - 'dbsync' => array($a->get_baseurl(true)."/admin/dbsync/", t('DB updates'), "dbsync"), - 'queue' => array($a->get_baseurl(true)."/admin/queue/", t('Inspect Queue'), "queue"), - 'federation' => array($a->get_baseurl(true)."/admin/federation/", t('Federation Statistics'), "federation"), + 'site' => array("admin/site/", t("Site") , "site"), + 'users' => array("admin/users/", t("Users") , "users"), + 'plugins'=> array("admin/plugins/", t("Plugins") , "plugins"), + 'themes' => array("admin/themes/", t("Themes") , "themes"), + 'features' => array("admin/features/", t("Additional features") , "features"), + 'dbsync' => array("admin/dbsync/", t('DB updates'), "dbsync"), + 'queue' => array("admin/queue/", t('Inspect Queue'), "queue"), + 'federation' => array("admin/federation/", t('Federation Statistics'), "federation"), ); /* get plugins admin page */ @@ -169,18 +169,18 @@ function admin_content(&$a) { $aside_tools['plugins_admin']=array(); foreach ($r as $h){ $plugin =$h['name']; - $aside_tools['plugins_admin'][] = array($a->get_baseurl(true)."/admin/plugins/".$plugin, $plugin, "plugin"); + $aside_tools['plugins_admin'][] = array("admin/plugins/".$plugin, $plugin, "plugin"); // temp plugins with admin $a->plugins_admin[] = $plugin; } - $aside_tools['logs'] = array($a->get_baseurl(true)."/admin/logs/", t("Logs"), "logs"); - $aside_tools['viewlogs'] = array($a->get_baseurl(true)."/admin/viewlogs/", t("View Logs"), 'viewlogs'); - $aside_tools['diagnostics_probe'] = array($a->get_baseurl(true).'/probe/', t('probe address'), 'probe'); - $aside_tools['diagnostics_webfinger'] = array($a->get_baseurl(true).'/webfinger/', t('check webfinger'), 'webfinger'); + $aside_tools['logs'] = array("admin/logs/", t("Logs"), "logs"); + $aside_tools['viewlogs'] = array("admin/viewlogs/", t("View Logs"), 'viewlogs'); + $aside_tools['diagnostics_probe'] = array('probe/', t('probe address'), 'probe'); + $aside_tools['diagnostics_webfinger'] = array('webfinger/', t('check webfinger'), 'webfinger'); $t = get_markup_template("admin_aside.tpl"); - $a->page['aside'] .= replace_macros( $t, array( + $a->page['aside'] .= replace_macros($t, array( '$admin' => $aside_tools, '$subpages' => $aside_sub, '$admtxt' => t('Admin'), @@ -188,7 +188,7 @@ function admin_content(&$a) { '$logtxt' => t('Logs'), '$diagnosticstxt' => t('diagnostics'), '$h_pending' => t('User registrations waiting for confirmation'), - '$admurl'=> $a->get_baseurl(true)."/admin/" + '$admurl'=> "admin/" )); @@ -231,7 +231,7 @@ function admin_content(&$a) { $o = admin_page_federation($a); break; default: - notice( t("Item not found.") ); + notice(t("Item not found.")); } } else { $o = admin_page_summary($a); @@ -270,6 +270,12 @@ function admin_page_federation(&$a) { // Add more platforms if you like, when one returns 0 known nodes it is not // displayed on the stats page. $platforms = array('Friendica', 'Diaspora', '%%red%%', 'Hubzilla', 'GNU Social', 'StatusNet'); + $colors = array('Friendica' => '#ffc018', // orange from the logo + 'Diaspora' => '#a1a1a1', // logo is black and white, makes a gray + '%%red%%' => '#c50001', // fire red from the logo + 'Hubzilla' => '#43488a', // blue from the logo + 'GNU Social'=> '#a22430', // dark red from the logo + 'StatusNet' => '#789240'); // the green from the logo (red and blue have already others $counts = array(); $total = 0; @@ -277,14 +283,14 @@ function admin_page_federation(&$a) { // get a total count for the platform, the name and version of the // highest version and the protocol tpe $c = q('SELECT count(*) AS total, platform, network, version FROM gserver - WHERE platform LIKE "%s" AND last_contact > last_failure + WHERE platform LIKE "%s" AND last_contact > last_failure AND `version` != "" ORDER BY version ASC;', $p); $total = $total + $c[0]['total']; // what versions for that platform do we know at all? // again only the active nodes $v = q('SELECT count(*) AS total, version FROM gserver - WHERE last_contact > last_failure AND platform LIKE "%s" + WHERE last_contact > last_failure AND platform LIKE "%s" AND `version` != "" GROUP BY version ORDER BY version;', $p); @@ -338,9 +344,12 @@ function admin_page_federation(&$a) { $v = $newVv; } + foreach ($v as $key => $vv) + $v[$key]["version"] = trim(strip_tags($vv["version"])); + // the 3rd array item is needed for the JavaScript graphs as JS does // not like some characters in the names of variables... - $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p)); + $counts[$p]=array($c[0], $v, str_replace(array(' ','%'),'',$p), $colors[$p]); } // some helpful text @@ -409,18 +418,18 @@ function admin_page_queue(&$a) { function admin_page_summary(&$a) { $r = q("SELECT `page-flags`, COUNT(uid) as `count` FROM `user` GROUP BY `page-flags`"); $accounts = array( - array( t('Normal Account'), 0), - array( t('Soapbox Account'), 0), - array( t('Community/Celebrity Account'), 0), - array( t('Automatic Friend Account'), 0), - array( t('Blog Account'), 0), - array( t('Private Forum'), 0) + array(t('Normal Account'), 0), + array(t('Soapbox Account'), 0), + array(t('Community/Celebrity Account'), 0), + array(t('Automatic Friend Account'), 0), + array(t('Blog Account'), 0), + array(t('Private Forum'), 0) ); $users=0; foreach ($r as $u){ $accounts[$u['page-flags']][1] = $u['count']; $users+= $u['count']; } - logger('accounts: ' . print_r($accounts,true),LOGGER_DATA); + logger('accounts: '.print_r($accounts,true),LOGGER_DATA); $r = q("SELECT COUNT(id) as `count` FROM `register`"); $pending = $r[0]['count']; @@ -433,7 +442,7 @@ function admin_page_summary(&$a) { // We can do better, but this is a quick queue status - $queues = array( 'label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue ); + $queues = array('label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue); $t = get_markup_template("admin_summary.tpl"); @@ -441,15 +450,15 @@ function admin_page_summary(&$a) { '$title' => t('Administration'), '$page' => t('Summary'), '$queues' => $queues, - '$users' => array( t('Registered users'), $users), + '$users' => array(t('Registered users'), $users), '$accounts' => $accounts, - '$pending' => array( t('Pending registrations'), $pending), - '$version' => array( t('Version'), FRIENDICA_VERSION), + '$pending' => array(t('Pending registrations'), $pending), + '$version' => array(t('Version'), FRIENDICA_VERSION), '$baseurl' => $a->get_baseurl(), '$platform' => FRIENDICA_PLATFORM, '$codename' => FRIENDICA_CODENAME, '$build' => get_config('system','build'), - '$plugins' => array( t('Active plugins'), $a->plugins ) + '$plugins' => array(t('Active plugins'), $a->plugins) )); } @@ -473,7 +482,7 @@ function admin_page_site_post(&$a) { $parsed = @parse_url($new_url); if(!$parsed || (!x($parsed,'host') || !x($parsed,'scheme'))) { notice(t("Can not parse base url. Must have at least ://")); - goaway($a->get_baseurl(true) . '/admin/site' ); + goaway('admin/site'); } /* steps: @@ -483,6 +492,10 @@ function admin_page_site_post(&$a) { $old_url = $a->get_baseurl(true); + // Generate host names for relocation the addresses in the format user@address.tld + $new_host = str_replace("http://", "@", normalise_link($new_url)); + $old_host = str_replace("http://", "@", normalise_link($old_url)); + function update_table($table_name, $fields, $old_url, $new_url) { global $db, $a; @@ -501,17 +514,22 @@ function admin_page_site_post(&$a) { $q = sprintf("UPDATE %s SET %s;", $table_name, $upds); $r = q($q); if(!$r) { - notice( "Failed updating '$table_name': " . $db->error ); - goaway($a->get_baseurl(true) . '/admin/site' ); + notice("Failed updating '$table_name': ".$db->error); + goaway('admin/site'); } } // update tables + // update profile links in the format "http://server.tld" update_table("profile", array('photo', 'thumb'), $old_url, $new_url); update_table("term", array('url'), $old_url, $new_url); - update_table("contact", array('photo','thumb','micro','url','nurl','request','notify','poll','confirm','poco'), $old_url, $new_url); - update_table("gcontact", array('photo','url','nurl','server_url'), $old_url, $new_url); - update_table("item", array('owner-link','owner-avatar','author-name','author-link','author-avatar','body','plink','tag'), $old_url, $new_url); + update_table("contact", array('photo','thumb','micro','url','nurl','alias','request','notify','poll','confirm','poco', 'avatar'), $old_url, $new_url); + update_table("gcontact", array('url','nurl','photo','server_url','notify','alias'), $old_url, $new_url); + update_table("item", array('owner-link','owner-avatar','author-link','author-avatar','body','plink','tag'), $old_url, $new_url); + + // update profile addresses in the format "user@server.tld" + update_table("contact", array('addr'), $old_host, $new_host); + update_table("gcontact", array('connect','addr'), $old_host, $new_host); // update config $a->set_baseurl($new_url); @@ -526,7 +544,7 @@ function admin_page_site_post(&$a) { info("Relocation started. Could take a while to complete."); - goaway($a->get_baseurl(true) . '/admin/site' ); + goaway('admin/site'); } // end relocate @@ -589,6 +607,7 @@ function admin_page_site_post(&$a) { $dfrn_only = ((x($_POST,'dfrn_only')) ? True : False); $ostatus_disabled = !((x($_POST,'ostatus_disabled')) ? True : False); $ostatus_poll_interval = ((x($_POST,'ostatus_poll_interval')) ? intval(trim($_POST['ostatus_poll_interval'])) : 0); + $ostatus_full_threads = ((x($_POST,'ostatus_full_threads')) ? True : False); $diaspora_enabled = ((x($_POST,'diaspora_enabled')) ? True : False); $ssl_policy = ((x($_POST,'ssl_policy')) ? intval($_POST['ssl_policy']) : 0); $force_ssl = ((x($_POST,'force_ssl')) ? True : False); @@ -609,6 +628,9 @@ function admin_page_site_post(&$a) { $only_tag_search = ((x($_POST,'only_tag_search')) ? True : False); $rino = ((x($_POST,'rino')) ? intval($_POST['rino']) : 0); $embedly = ((x($_POST,'embedly')) ? notags(trim($_POST['embedly'])) : ''); + $worker = ((x($_POST,'worker')) ? True : False); + $worker_queues = ((x($_POST,'worker_queues')) ? intval($_POST['worker_queues']) : 4); + $worker_dont_fork = ((x($_POST,'worker_dont_fork')) ? True : False); if($a->get_path() != "") $diaspora_enabled = false; @@ -695,12 +717,12 @@ function admin_page_site_post(&$a) { set_config('system','language', $language); set_config('system','theme', $theme); - if( $theme_mobile === '---' ) { + if($theme_mobile === '---') { del_config('system','mobile-theme'); } else { set_config('system','mobile-theme', $theme_mobile); } - if( $singleuser === '---' ) { + if($singleuser === '---') { del_config('system','singleuser'); } else { set_config('system','singleuser', $singleuser); @@ -737,6 +759,7 @@ function admin_page_site_post(&$a) { set_config('system','dfrn_only', $dfrn_only); set_config('system','ostatus_disabled', $ostatus_disabled); set_config('system','ostatus_poll_interval', $ostatus_poll_interval); + set_config('system','ostatus_full_threads', $ostatus_full_threads); set_config('system','diaspora_enabled', $diaspora_enabled); set_config('config','private_addons', $private_addons); @@ -754,7 +777,9 @@ function admin_page_site_post(&$a) { set_config('system','proxy_disabled', $proxy_disabled); set_config('system','old_pager', $old_pager); set_config('system','only_tag_search', $only_tag_search); - + set_config('system','worker', $worker); + set_config('system','worker_queues', $worker_queues); + set_config('system','worker_dont_fork', $worker_dont_fork); if($rino==2 and !function_exists('mcrypt_create_iv')) { notice(t("RINO2 needs mcrypt php extension to work.")); @@ -765,8 +790,8 @@ function admin_page_site_post(&$a) { set_config('system','embedly', $embedly); - info( t('Site settings updated.') . EOL); - goaway($a->get_baseurl(true) . '/admin/site' ); + info(t('Site settings updated.').EOL); + goaway('admin/site'); return; // NOTREACHED } @@ -797,12 +822,12 @@ function admin_page_site(&$a) { $files = glob('view/theme/*'); if($files) { foreach($files as $file) { - if(intval(file_exists($file . '/unsupported'))) + if(intval(file_exists($file.'/unsupported'))) continue; $f = basename($file); - $theme_name = ((file_exists($file . '/experimental')) ? sprintf("%s - \x28Experimental\x29", $f) : $f); - if(file_exists($file . '/mobile')) { + $theme_name = ((file_exists($file.'/experimental')) ? sprintf("%s - \x28Experimental\x29", $f) : $f); + if(file_exists($file.'/mobile')) { $theme_choices_mobile[$f] = $theme_name; } else { $theme_choices[$f] = $theme_name; @@ -893,6 +918,7 @@ function admin_page_site(&$a) { '$advanced' => t('Advanced'), '$portable_contacts' => t('Auto Discovered Contact Directory'), '$performance' => t('Performance'), + '$worker_title' => t('Worker'), '$relocate'=> t('Relocate - WARNING: advanced function. Could make this server unreachable.'), '$baseurl' => $a->get_baseurl(true), // name, label, value, help string, extra data... @@ -938,6 +964,7 @@ function admin_page_site(&$a) { '$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), get_config('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")), '$ostatus_disabled' => array('ostatus_disabled', t("Enable OStatus support"), !get_config('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")), '$ostatus_poll_interval' => array('ostatus_poll_interval', t("OStatus conversation completion interval"), (string) intval(get_config('system','ostatus_poll_interval')), t("How often shall the poller check for new entries in OStatus conversations? This can be a very ressource task."), $ostatus_poll_choices), + '$ostatus_full_threads' => array('ostatus_full_threads', t("Only import OStatus threads from our contacts"), get_config('system','ostatus_full_threads'), t("Normally we import every content from our OStatus contacts. With this option we only store threads that are started by a contact that is known on our system.")), '$ostatus_not_able' => t("OStatus support can only be enabled if threading is enabled."), '$diaspora_able' => $diaspora_able, '$diaspora_not_able' => t("Diaspora support can't be enabled because Friendica was installed into a sub directory."), @@ -980,6 +1007,10 @@ function admin_page_site(&$a) { '$rino' => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")), '$embedly' => array('embedly', t("Embedly API key"), get_config('system','embedly'), t("Embedly is used to fetch additional data for web pages. This is an optional parameter.")), + '$worker' => array('worker', t("Enable 'worker' background processing"), get_config('system','worker'), t("The worker background processing limits the number of parallel background jobs to a maximum number and respects the system load.")), + '$worker_queues' => array('worker_queues', t("Maximum number of parallel workers"), get_config('system','worker_queues'), t("On shared hosters set this to 2. On larger systems, values of 10 are great. Default value is 4.")), + '$worker_dont_fork' => array('worker_dont_fork', t("Don't use 'proc_open' with the worker"), get_config('system','worker_dont_fork'), t("Enable this if your system doesn't allow the use of 'proc_open'. This can happen on shared hosters. If this is enabled you should increase the frequency of poller calls in your crontab.")), + '$form_security_token' => get_form_security_token("admin_site") )); @@ -1003,12 +1034,12 @@ function admin_page_dbsync(&$a) { $o = ''; if($a->argc > 3 && intval($a->argv[3]) && $a->argv[2] === 'mark') { - set_config('database', 'update_' . intval($a->argv[3]), 'success'); + set_config('database', 'update_'.intval($a->argv[3]), 'success'); $curr = get_config('system','build'); if(intval($curr) == intval($a->argv[3])) set_config('system','build',intval($curr) + 1); - info( t('Update has been marked successful') . EOL); - goaway($a->get_baseurl(true) . '/admin/dbsync'); + info(t('Update has been marked successful').EOL); + goaway('admin/dbsync'); } if(($a->argc > 2) AND (intval($a->argv[2]) OR ($a->argv[2] === 'check'))) { @@ -1026,7 +1057,7 @@ function admin_page_dbsync(&$a) { if($a->argc > 2 && intval($a->argv[2])) { require_once('update.php'); - $func = 'update_' . intval($a->argv[2]); + $func = 'update_'.intval($a->argv[2]); if(function_exists($func)) { $retval = $func(); if($retval === UPDATE_FAILED) { @@ -1082,18 +1113,18 @@ function admin_page_dbsync(&$a) { * @param App $a */ function admin_page_users_post(&$a){ - $pending = ( x($_POST, 'pending') ? $_POST['pending'] : array() ); - $users = ( x($_POST, 'user') ? $_POST['user'] : array() ); - $nu_name = ( x($_POST, 'new_user_name') ? $_POST['new_user_name'] : ''); - $nu_nickname = ( x($_POST, 'new_user_nickname') ? $_POST['new_user_nickname'] : ''); - $nu_email = ( x($_POST, 'new_user_email') ? $_POST['new_user_email'] : ''); + $pending = (x($_POST, 'pending') ? $_POST['pending'] : array()); + $users = (x($_POST, 'user') ? $_POST['user'] : array()); + $nu_name = (x($_POST, 'new_user_name') ? $_POST['new_user_name'] : ''); + $nu_nickname = (x($_POST, 'new_user_nickname') ? $_POST['new_user_nickname'] : ''); + $nu_email = (x($_POST, 'new_user_email') ? $_POST['new_user_email'] : ''); check_form_security_token_redirectOnErr('/admin/users', 'admin_users'); if(!($nu_name==="") && !($nu_email==="") && !($nu_nickname==="")) { require_once('include/user.php'); - $result = create_user( array('username'=>$nu_name, 'email'=>$nu_email, 'nickname'=>$nu_nickname, 'verified'=>1) ); + $result = create_user(array('username'=>$nu_name, 'email'=>$nu_email, 'nickname'=>$nu_nickname, 'verified'=>1)); if(! $result['success']) { notice($result['message']); return; @@ -1134,7 +1165,7 @@ function admin_page_users_post(&$a){ notification(array( 'type' => "SYSTEM_EMAIL", 'to_email' => $nu['email'], - 'subject'=> sprintf( t('Registration details for %s'), $a->config['sitename']), + 'subject'=> sprintf(t('Registration details for %s'), $a->config['sitename']), 'preamble'=> $preamble, 'body' => $body)); @@ -1143,17 +1174,17 @@ function admin_page_users_post(&$a){ if(x($_POST,'page_users_block')) { foreach($users as $uid){ q("UPDATE `user` SET `blocked`=1-`blocked` WHERE `uid`=%s", - intval( $uid ) + intval($uid) ); } - notice( sprintf( tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users)) ); + notice(sprintf(tt("%s user blocked/unblocked", "%s users blocked/unblocked", count($users)), count($users))); } if(x($_POST,'page_users_delete')) { require_once("include/Contact.php"); foreach($users as $uid){ user_remove($uid); } - notice( sprintf( tt("%s user deleted", "%s users deleted", count($users)), count($users)) ); + notice(sprintf(tt("%s user deleted", "%s users deleted", count($users)), count($users))); } if(x($_POST,'page_users_approve')) { @@ -1168,7 +1199,7 @@ function admin_page_users_post(&$a){ user_deny($hash); } } - goaway($a->get_baseurl(true) . '/admin/users' ); + goaway('admin/users'); return; // NOTREACHED } @@ -1189,8 +1220,8 @@ function admin_page_users(&$a){ $uid = $a->argv[3]; $user = q("SELECT username, blocked FROM `user` WHERE `uid`=%d", intval($uid)); if(count($user)==0) { - notice( 'User not found' . EOL); - goaway($a->get_baseurl(true) . '/admin/users' ); + notice('User not found'.EOL); + goaway('admin/users'); return ''; // NOTREACHED } switch($a->argv[2]){ @@ -1200,18 +1231,18 @@ function admin_page_users(&$a){ require_once("include/Contact.php"); user_remove($uid); - notice( sprintf(t("User '%s' deleted"), $user[0]['username']) . EOL); + notice(sprintf(t("User '%s' deleted"), $user[0]['username']).EOL); }; break; case "block":{ check_form_security_token_redirectOnErr('/admin/users', 'admin_users', 't'); q("UPDATE `user` SET `blocked`=%d WHERE `uid`=%s", - intval( 1-$user[0]['blocked'] ), - intval( $uid ) + intval(1-$user[0]['blocked']), + intval($uid) ); - notice( sprintf( ($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']) . EOL); + notice(sprintf(($user[0]['blocked']?t("User '%s' unblocked"):t("User '%s' blocked")) , $user[0]['username']).EOL); }; break; } - goaway($a->get_baseurl(true) . '/admin/users' ); + goaway('admin/users'); return ''; // NOTREACHED } @@ -1230,7 +1261,7 @@ function admin_page_users(&$a){ $a->set_pager_itemspage(100); } - $users = q("SELECT `user` . * , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired` + $users = q("SELECT `user`.* , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired` FROM (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid` FROM `item` @@ -1277,7 +1308,7 @@ function admin_page_users(&$a){ while(count($users)) { $new_user = array(); - foreach( array_pop($users) as $k => $v) { + foreach(array_pop($users) as $k => $v) { $k = str_replace('-','_',$k); $new_user[$k] = $v; } @@ -1303,7 +1334,7 @@ function admin_page_users(&$a){ '$select_all' => t('select all'), '$h_pending' => t('User registrations waiting for confirm'), '$h_deleted' => t('User waiting for permanent deletion'), - '$th_pending' => array( t('Request date'), t('Name'), t('Email') ), + '$th_pending' => array(t('Request date'), t('Name'), t('Email')), '$no_pending' => t('No registrations.'), '$approve' => t('Approve'), '$deny' => t('Deny'), @@ -1315,8 +1346,8 @@ function admin_page_users(&$a){ '$h_users' => t('Users'), '$h_newuser' => t('New User'), - '$th_deleted' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since') ), - '$th_users' => array( t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Account') ), + '$th_deleted' => array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Deleted since')), + '$th_users' => array(t('Name'), t('Email'), t('Register date'), t('Last login'), t('Last item'), t('Account')), '$confirm_delete_multi' => t('Selected users will be deleted!\n\nEverything these users had posted on this site will be permanently deleted!\n\nAre you sure?'), '$confirm_delete' => t('The user {0} will be deleted!\n\nEverything this user has posted on this site will be permanently deleted!\n\nAre you sure?'), @@ -1362,7 +1393,7 @@ function admin_page_plugins(&$a){ if($a->argc == 3) { $plugin = $a->argv[2]; if(!is_file("addon/$plugin/$plugin.php")) { - notice( t("Item not found.") ); + notice(t("Item not found.")); return ''; } @@ -1374,14 +1405,14 @@ function admin_page_plugins(&$a){ if($idx !== false) { unset($a->plugins[$idx]); uninstall_plugin($plugin); - info( sprintf( t("Plugin %s disabled."), $plugin ) ); + info(sprintf(t("Plugin %s disabled."), $plugin)); } else { $a->plugins[] = $plugin; install_plugin($plugin); - info( sprintf( t("Plugin %s enabled."), $plugin ) ); + info(sprintf(t("Plugin %s enabled."), $plugin)); } set_config("system","addon", implode(", ",$a->plugins)); - goaway($a->get_baseurl(true) . '/admin/plugins' ); + goaway('admin/plugins'); return ''; // NOTREACHED } @@ -1480,7 +1511,7 @@ function admin_page_plugins(&$a){ '$function' => 'plugins', '$plugins' => $plugins, '$pcount' => count($plugins), - '$noplugshint' => sprintf( t('There are currently no plugins available on your node. You can find the official plugin repository at %1$s and might find other interesting plugins in the open plugin registry at %2$s'), 'https://github.com/friendica/friendica-addons', 'http://addons.friendi.ca'), + '$noplugshint' => sprintf(t('There are currently no plugins available on your node. You can find the official plugin repository at %1$s and might find other interesting plugins in the open plugin registry at %2$s'), 'https://github.com/friendica/friendica-addons', 'http://addons.friendi.ca'), '$form_security_token' => get_form_security_token("admin_themes"), )); } @@ -1575,8 +1606,8 @@ function admin_page_themes(&$a){ if($files) { foreach($files as $file) { $f = basename($file); - $is_experimental = intval(file_exists($file . '/experimental')); - $is_supported = 1-(intval(file_exists($file . '/unsupported'))); + $is_experimental = intval(file_exists($file.'/experimental')); + $is_supported = 1-(intval(file_exists($file.'/unsupported'))); $is_allowed = intval(in_array($f,$allowed_themes)); if($is_allowed OR $is_supported OR get_config("system", "show_unsupported_themes")) @@ -1585,7 +1616,7 @@ function admin_page_themes(&$a){ } if(! count($themes)) { - notice( t('No themes found.')); + notice(t('No themes found.')); return ''; } @@ -1596,7 +1627,7 @@ function admin_page_themes(&$a){ if($a->argc == 3) { $theme = $a->argv[2]; if(! is_dir("view/theme/$theme")) { - notice( t("Item not found.") ); + notice(t("Item not found.")); return ''; } @@ -1609,15 +1640,15 @@ function admin_page_themes(&$a){ $s = rebuild_theme_table($themes); if($result) { install_theme($theme); - info( sprintf('Theme %s enabled.',$theme)); + info(sprintf('Theme %s enabled.',$theme)); } else { uninstall_theme($theme); - info( sprintf('Theme %s disabled.',$theme)); + info(sprintf('Theme %s disabled.',$theme)); } set_config('system','allowed_themes',$s); - goaway($a->get_baseurl(true) . '/admin/themes' ); + goaway('admin/themes'); return ''; // NOTREACHED } @@ -1663,7 +1694,7 @@ function admin_page_themes(&$a){ $admin_form = __get_theme_admin_form($a, $theme); } - $screenshot = array( get_theme_screenshot($theme), t('Screenshot')); + $screenshot = array(get_theme_screenshot($theme), t('Screenshot')); if(! stristr($screenshot[0],$theme)) $screenshot = null; @@ -1754,8 +1785,8 @@ function admin_page_logs_post(&$a) { set_config('system','loglevel', $loglevel); } - info( t("Log settings updated.") ); - goaway($a->get_baseurl(true) . '/admin/logs' ); + info(t("Log settings updated.")); + goaway('admin/logs'); return; // NOTREACHED } @@ -1803,7 +1834,7 @@ function admin_page_logs(&$a){ '$form_security_token' => get_form_security_token("admin_logs"), '$phpheader' => t("PHP logging"), '$phphint' => t("To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."), - '$phplogcode' => "error_reporting(E_ERROR | E_WARNING | E_PARSE );\nini_set('error_log','php.out');\nini_set('log_errors','1');\nini_set('display_errors', '1');", + '$phplogcode' => "error_reporting(E_ERROR | E_WARNING | E_PARSE);\nini_set('error_log','php.out');\nini_set('log_errors','1');\nini_set('display_errors', '1');", )); } @@ -1871,7 +1902,7 @@ function admin_page_features_post(&$a) { check_form_security_token_redirectOnErr('/admin/features', 'admin_manage_features'); - logger('postvars: ' . print_r($_POST,true),LOGGER_DATA); + logger('postvars: '.print_r($_POST,true),LOGGER_DATA); $arr = array(); $features = get_features(false); @@ -1879,11 +1910,11 @@ function admin_page_features_post(&$a) { foreach($features as $fname => $fdata) { foreach(array_slice($fdata,1) as $f) { $feature = $f[0]; - $feature_state = 'feature_' . $feature; - $featurelock = 'featurelock_' . $feature; + $feature_state = 'feature_'.$feature; + $featurelock = 'featurelock_'.$feature; if(x($_POST[$feature_state])) - $val = intval($_POST['feature_' . $feature]); + $val = intval($_POST['feature_'.$feature]); else $val = 0; set_config('feature',$feature,$val); @@ -1895,7 +1926,7 @@ function admin_page_features_post(&$a) { } } - goaway($a->get_baseurl(true) . '/admin/features' ); + goaway('admin/features'); return; // NOTREACHED } @@ -1929,7 +1960,7 @@ function admin_page_features(&$a) { $set = $f[3]; $arr[$fname][1][] = array( array('feature_' .$f[0],$f[1],$set,$f[2],array(t('Off'),t('On'))), - array('featurelock_' .$f[0],sprintf( t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'),t('On'))) + array('featurelock_' .$f[0],sprintf(t('Lock feature %s'),$f[1]),(($f[4] !== false) ? "1" : ''),'',array(t('Off'),t('On'))) ); } } diff --git a/mod/common.php b/mod/common.php index c9409b3ef1..62a5185fed 100644 --- a/mod/common.php +++ b/mod/common.php @@ -40,7 +40,7 @@ function common_content(&$a) { $vcard_widget .= replace_macros(get_markup_template("vcard-widget.tpl"),array( '$name' => htmlentities($c[0]['name']), '$photo' => $c[0]['photo'], - 'url' => z_root() . '/contacts/' . $cid + 'url' => 'contacts/' . $cid )); if(! x($a->page,'aside')) diff --git a/mod/contacts.php b/mod/contacts.php index 0b421433e0..4897663a05 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -44,7 +44,7 @@ function contacts_init(&$a) { $vcard_widget = replace_macros(get_markup_template("vcard-widget.tpl"),array( '$name' => htmlentities($a->data['contact']['name']), '$photo' => $a->data['contact']['photo'], - '$url' => ($a->data['contact']['network'] == NETWORK_DFRN) ? z_root()."/redir/".$a->data['contact']['id'] : $a->data['contact']['url'], + '$url' => ($a->data['contact']['network'] == NETWORK_DFRN) ? "redir/".$a->data['contact']['id'] : $a->data['contact']['url'], '$addr' => (($a->data['contact']['addr'] != "") ? ($a->data['contact']['addr']) : ""), '$network_name' => $networkname, '$network' => t('Network:'), @@ -129,9 +129,9 @@ function contacts_batch_actions(&$a){ } if(x($_SESSION,'return_url')) - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway('' . $_SESSION['return_url']); else - goaway($a->get_baseurl(true) . '/contacts'); + goaway('contacts'); } @@ -157,7 +157,7 @@ function contacts_post(&$a) { if(! count($orig_record)) { notice( t('Could not access contact record.') . EOL); - goaway($a->get_baseurl(true) . '/contacts'); + goaway('contacts'); return; // NOTREACHED } @@ -366,19 +366,19 @@ function contacts_content(&$a) { if(! count($orig_record)) { notice( t('Could not access contact record.') . EOL); - goaway($a->get_baseurl(true) . '/contacts'); + goaway('contacts'); return; // NOTREACHED } if($cmd === 'update') { _contact_update($contact_id); - goaway($a->get_baseurl(true) . '/contacts/' . $contact_id); + goaway('contacts/' . $contact_id); // NOTREACHED } if($cmd === 'updateprofile') { _contact_update_profile($contact_id); - goaway($a->get_baseurl(true) . '/crepair/' . $contact_id); + goaway('crepair/' . $contact_id); // NOTREACHED } @@ -389,7 +389,7 @@ function contacts_content(&$a) { info((($blocked) ? t('Contact has been blocked') : t('Contact has been unblocked')).EOL); } - goaway($a->get_baseurl(true) . '/contacts/' . $contact_id); + goaway('contacts/' . $contact_id); return; // NOTREACHED } @@ -400,7 +400,7 @@ function contacts_content(&$a) { info((($readonly) ? t('Contact has been ignored') : t('Contact has been unignored')).EOL); } - goaway($a->get_baseurl(true) . '/contacts/' . $contact_id); + goaway('contacts/' . $contact_id); return; // NOTREACHED } @@ -412,7 +412,7 @@ function contacts_content(&$a) { info((($archived) ? t('Contact has been archived') : t('Contact has been unarchived')).EOL); } - goaway($a->get_baseurl(true) . '/contacts/' . $contact_id); + goaway('contacts/' . $contact_id); return; // NOTREACHED } @@ -447,17 +447,17 @@ function contacts_content(&$a) { // Now check how the user responded to the confirmation query if($_REQUEST['canceled']) { if(x($_SESSION,'return_url')) - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway('' . $_SESSION['return_url']); else - goaway($a->get_baseurl(true) . '/contacts'); + goaway('contacts'); } _contact_drop($contact_id, $orig_record[0]); info( t('Contact has been removed.') . EOL ); if(x($_SESSION,'return_url')) - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway('' . $_SESSION['return_url']); else - goaway($a->get_baseurl(true) . '/contacts'); + goaway('contacts'); return; // NOTREACHED } if($cmd === 'posts') { @@ -565,6 +565,9 @@ function contacts_content(&$a) { ($contact['rel'] == CONTACT_IS_FOLLOWER)) $follow = $a->get_baseurl(true)."/follow?url=".urlencode($contact["url"]); + // Load contactact related actions like hide, suggest, delete and others + $contact_actions = contact_actions($contact); + $o .= replace_macros($tpl, array( //'$header' => t('Contact Editor'), @@ -575,7 +578,7 @@ function contacts_content(&$a) { '$lbl_info1' => t('Contact Information / Notes'), '$infedit' => t('Edit contact notes'), '$common_text' => $common_text, - '$common_link' => $a->get_baseurl(true) . '/common/loc/' . local_user() . '/' . $contact['id'], + '$common_link' => 'common/loc/' . local_user() . '/' . $contact['id'], '$all_friends' => $all_friends, '$relation_text' => $relation_text, '$visit' => sprintf( t('Visit %s\'s profile [%s]'),$contact['name'],$contact['url']), @@ -584,7 +587,7 @@ function contacts_content(&$a) { '$lblcrepair' => t("Repair URL settings"), '$lblrecent' => t('View conversations'), '$lblsuggest' => $lblsuggest, - '$delete' => t('Delete contact'), + //'$delete' => t('Delete contact'), '$nettype' => $nettype, '$poll_interval' => $poll_interval, '$poll_enabled' => $poll_enabled, @@ -622,7 +625,11 @@ function contacts_content(&$a) { '$about' => bbcode($contact["about"], false, false), '$about_label' => t("About:"), '$keywords' => $contact["keywords"], - '$keywords_label' => t("Tags:") + '$keywords_label' => t("Tags:"), + '$contact_action_button' => t("Actions"), + '$contact_actions' => $contact_actions, + '$contact_status' => t("Status"), + '$contact_settings_label' => t('Contact Settings'), )); @@ -668,7 +675,7 @@ function contacts_content(&$a) { $tabs = array( array( 'label' => t('Suggestions'), - 'url' => $a->get_baseurl(true) . '/suggest', + 'url' => 'suggest', 'sel' => '', 'title' => t('Suggest potential friends'), 'id' => 'suggestions-tab', @@ -676,7 +683,7 @@ function contacts_content(&$a) { ), array( 'label' => t('All Contacts'), - 'url' => $a->get_baseurl(true) . '/contacts/all', + 'url' => 'contacts/all', 'sel' => ($all) ? 'active' : '', 'title' => t('Show all contacts'), 'id' => 'showall-tab', @@ -684,7 +691,7 @@ function contacts_content(&$a) { ), array( 'label' => t('Unblocked'), - 'url' => $a->get_baseurl(true) . '/contacts', + 'url' => 'contacts', 'sel' => ((! $all) && (! $blocked) && (! $hidden) && (! $search) && (! $nets) && (! $ignored) && (! $archived)) ? 'active' : '', 'title' => t('Only show unblocked contacts'), 'id' => 'showunblocked-tab', @@ -693,7 +700,7 @@ function contacts_content(&$a) { array( 'label' => t('Blocked'), - 'url' => $a->get_baseurl(true) . '/contacts/blocked', + 'url' => 'contacts/blocked', 'sel' => ($blocked) ? 'active' : '', 'title' => t('Only show blocked contacts'), 'id' => 'showblocked-tab', @@ -702,7 +709,7 @@ function contacts_content(&$a) { array( 'label' => t('Ignored'), - 'url' => $a->get_baseurl(true) . '/contacts/ignored', + 'url' => 'contacts/ignored', 'sel' => ($ignored) ? 'active' : '', 'title' => t('Only show ignored contacts'), 'id' => 'showignored-tab', @@ -711,7 +718,7 @@ function contacts_content(&$a) { array( 'label' => t('Archived'), - 'url' => $a->get_baseurl(true) . '/contacts/archived', + 'url' => 'contacts/archived', 'sel' => ($archived) ? 'active' : '', 'title' => t('Only show archived contacts'), 'id' => 'showarchived-tab', @@ -720,7 +727,7 @@ function contacts_content(&$a) { array( 'label' => t('Hidden'), - 'url' => $a->get_baseurl(true) . '/contacts/hidden', + 'url' => 'contacts/hidden', 'sel' => ($hidden) ? 'active' : '', 'title' => t('Only show hidden contacts'), 'id' => 'showhidden-tab', @@ -800,6 +807,17 @@ function contacts_content(&$a) { return $o; } +/** + * @brief List of pages for the Contact TabBar + * + * Available Pages are 'Status', 'Profile', 'Contacts' and 'Common Friends' + * + * @param app $a + * @param int $contact_id The ID of the contact + * @param int $active_tab 1 if tab should be marked as active + * + * @return array with with contact TabBar data + */ function contacts_tab($a, $contact_id, $active_tab) { // tabs $tabs = array( @@ -821,6 +839,7 @@ function contacts_tab($a, $contact_id, $active_tab) { ) ); + // Show this tab only if there is visible friend list $x = count_all_friends(local_user(), $contact_id); if ($x) $tabs[] = array('label'=>t('Contacts'), @@ -830,6 +849,7 @@ function contacts_tab($a, $contact_id, $active_tab) { 'id' => 'allfriends-tab', 'accesskey' => 't'); + // Show this tab only if there is visible common friend list $common = count_common_friends(local_user(),$contact_id); if ($common) $tabs[] = array('label'=>t('Common Friends'), @@ -839,35 +859,13 @@ function contacts_tab($a, $contact_id, $active_tab) { 'id' => 'common-loc-tab', 'accesskey' => 'd'); - $tabs[] = array('label' => t('Repair'), - 'url' => $a->get_baseurl(true) . '/crepair/' . $contact_id, + $tabs[] = array('label' => t('Advanced'), + 'url' => 'crepair/' . $contact_id, 'sel' => (($active_tab == 5)?'active':''), 'title' => t('Advanced Contact Settings'), - 'id' => 'repair-tab', + 'id' => 'advanced-tab', 'accesskey' => 'r'); - - $tabs[] = array('label' => (($contact['blocked']) ? t('Unblock') : t('Block') ), - 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/block', - 'sel' => '', - 'title' => t('Toggle Blocked status'), - 'id' => 'toggle-block-tab', - 'accesskey' => 'b'); - - $tabs[] = array('label' => (($contact['readonly']) ? t('Unignore') : t('Ignore') ), - 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/ignore', - 'sel' => '', - 'title' => t('Toggle Ignored status'), - 'id' => 'toggle-ignore-tab', - 'accesskey' => 'i'); - - $tabs[] = array('label' => (($contact['archive']) ? t('Unarchive') : t('Archive') ), - 'url' => $a->get_baseurl(true) . '/contacts/' . $contact_id . '/archive', - 'sel' => '', - 'title' => t('Toggle Archive status'), - 'id' => 'toggle-archive-tab', - 'accesskey' => 'v'); - $tab_tpl = get_markup_template('common_tabs.tpl'); $tab_str = replace_macros($tab_tpl, array('$tabs' => $tabs)); @@ -954,3 +952,72 @@ function _contact_detail_for_template($rr){ ); } + +/** + * @brief Gives a array with actions which can performed to a given contact + * + * This includes actions like e.g. 'block', 'hide', 'archive', 'delete' and others + * + * @param array $contact Data about the Contact + * @return array with contact related actions + */ +function contact_actions($contact) { + + $poll_enabled = in_array($contact['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_FEED, NETWORK_MAIL, NETWORK_MAIL2)); + $contact_action = array(); + + // Provide friend suggestion only for Friendica contacts + if($contact['network'] === NETWORK_DFRN) { + $contact_actions['suggest'] = array( + 'label' => t('Suggest friends'), + 'url' => 'fsuggest/' . $contact['id'], + 'title' => '', + 'sel' => '', + 'id' => 'suggest', + ); + } + + if($poll_enabled) { + $contact_actions['update'] = array( + 'label' => t('Update now'), + 'url' => 'contacts/' . $contact['id'] . '/update', + 'title' => '', + 'sel' => '', + 'id' => 'update', + ); + } + + $contact_actions['block'] = array( + 'label' => (intval($contact['blocked']) ? t('Unblock') : t('Block') ), + 'url' => 'contacts/' . $contact['id'] . '/block', + 'title' => t('Toggle Blocked status'), + 'sel' => (intval($contact['blocked']) ? 'active' : ''), + 'id' => 'toggle-block', + ); + + $contact_actions['ignore'] = array( + 'label' => (intval($contact['readonly']) ? t('Unignore') : t('Ignore') ), + 'url' => 'contacts/' . $contact['id'] . '/ignore', + 'title' => t('Toggle Ignored status'), + 'sel' => (intval($contact['readonly']) ? 'active' : ''), + 'id' => 'toggle-ignore', + ); + + $contact_actions['archive'] = array( + 'label' => (intval($contact['archive']) ? t('Unarchive') : t('Archive') ), + 'url' => 'contacts/' . $contact['id'] . '/archive', + 'title' => t('Toggle Archive status'), + 'sel' => (intval($contact['archive']) ? 'active' : ''), + 'id' => 'toggle-archive', + ); + + $contact_actions['delete'] = array( + 'label' => t('Delete'), + 'url' => 'contacts/' . $contact['id'] . '/drop', + 'title' => t('Delete contact'), + 'sel' => '', + 'id' => 'delete', + ); + + return $contact_actions; +} diff --git a/mod/content.php b/mod/content.php index c5a5556116..49cff74d2d 100644 --- a/mod/content.php +++ b/mod/content.php @@ -420,7 +420,7 @@ function render_content(&$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); @@ -615,7 +615,7 @@ function render_content(&$a, $items, $mode, $update, $preview = false) { $comment_lastcollapsed = true; } - $redirect_url = $a->get_baseurl($ssl_state) . '/redir/' . $item['cid'] ; + $redirect_url = 'redir/' . $item['cid'] ; $lock = ((($item['private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) @@ -791,7 +791,7 @@ function render_content(&$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'])) && $diff_author) ? $item['author-avatar'] : $a->get_cached_avatar_image($thumb)); + $profile_avatar = $a->remove_baseurl(((strlen($item['author-avatar']) && $diff_author) ? $item['author-avatar'] : $thumb)); $like = ((x($alike,$item['uri'])) ? format_like($alike[$item['uri']],$alike[$item['uri'] . '-l'],'like',$item['uri']) : ''); $dislike = ((x($dlike,$item['uri'])) ? format_like($dlike[$item['uri']],$dlike[$item['uri'] . '-l'],'dislike',$item['uri']) : ''); diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index 27c04a908d..5e0e5c85c5 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -427,8 +427,8 @@ function dfrn_confirm_post(&$a,$handsfree = null) { if(($contact) && ($contact['network'] === NETWORK_DIASPORA)) { require_once('include/diaspora.php'); - $ret = diaspora_share($user[0],$r[0]); - logger('mod_follow: diaspora_share returns: ' . $ret); + $ret = diaspora::send_share($user[0],$r[0]); + logger('share returns: ' . $ret); } // Send a new friend post if we are allowed to... @@ -448,6 +448,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) { if(count($self)) { $arr = array(); + $arr['guid'] = get_guid(32); $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), $uid); $arr['uid'] = $uid; $arr['contact-id'] = $self[0]['id']; @@ -466,7 +467,7 @@ function dfrn_confirm_post(&$a,$handsfree = null) { $BPhoto = '[url=' . $contact['url'] . ']' . '[img]' . $contact['thumb'] . '[/img][/url]'; $arr['verb'] = ACTIVITY_FRIEND; - $arr['object-type'] = ACTIVITY_OBJ_PERSON; + $arr['object-type'] = ACTIVITY_OBJ_PERSON; $arr['body'] = sprintf( t('%1$s is now friends with %2$s'), $A, $B)."\n\n\n".$BPhoto; $arr['object'] = '' . ACTIVITY_OBJ_PERSON . '' . $contact['name'] . '' @@ -489,13 +490,10 @@ function dfrn_confirm_post(&$a,$handsfree = null) { } } - - $g = q("select def_gid from user where uid = %d limit 1", - intval($uid) - ); - if($contact && $g && intval($g[0]['def_gid'])) { + $def_gid = get_default_group($uid, $contact["network"]); + if($contact && intval($def_gid)) { require_once('include/group.php'); - group_add_member($uid,'',$contact['id'],$g[0]['def_gid']); + group_add_member($uid, '', $contact['id'], $def_gid); } // Let's send our user to the contact editor in case they want to diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php index 4aa777b550..780fb456f5 100644 --- a/mod/dfrn_notify.php +++ b/mod/dfrn_notify.php @@ -1,6 +1,7 @@ argc != 2) || (! count($a->profile))) + if(($a->argc != 2) || (! count($a->profile))) { + logger('Wrong count of argc or profiles: argc=' . $a->argc . ',profile()=' . count($a->profile)); return; + } if(x($_POST, 'cancel')) { @@ -172,18 +174,16 @@ function dfrn_request_post(&$a) { info( t("Introduction complete.") . EOL); } - $r = q("select id from contact where uid = %d and url = '%s' and `site-pubkey` = '%s' limit 1", + $r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1", intval(local_user()), dbesc($dfrn_url), $parms['key'] // this was already escaped ); if(count($r)) { - $g = q("select def_gid from user where uid = %d limit 1", - intval(local_user()) - ); - if($g && intval($g[0]['def_gid'])) { + $def_gid = get_default_group(local_user(), $r[0]["network"]); + if(intval($def_gid)) { require_once('include/group.php'); - group_add_member(local_user(),'',$r[0]['id'],$g[0]['def_gid']); + group_add_member(local_user(), '', $r[0]['id'], $def_gid); } $forwardurl = $a->get_baseurl()."/contacts/".$r[0]['id']; } else @@ -386,19 +386,17 @@ function dfrn_request_post(&$a) { intval($rel) ); - $r = q("select id from contact where poll = '%s' and uid = %d limit 1", + $r = q("SELECT `id`, `network` FROM `contact` WHERE `poll` = '%s' AND `uid` = %d LIMIT 1", dbesc($poll), intval($uid) ); if(count($r)) { $contact_id = $r[0]['id']; - $g = q("select def_gid from user where uid = %d limit 1", - intval($uid) - ); - if($g && intval($g[0]['def_gid'])) { + $def_gid = get_default_group($uid, $r[0]["network"]); + if (intval($def_gid)) { require_once('include/group.php'); - group_add_member($uid,'',$contact_id,$g[0]['def_gid']); + group_add_member($uid, '', $contact_id, $def_gid); } $photo = avatar_img($addr); @@ -461,7 +459,7 @@ function dfrn_request_post(&$a) { $network = NETWORK_DFRN; } - logger('dfrn_request: url: ' . $url); + logger('dfrn_request: url: ' . $url . ',network=' . $network, LOGGER_DEBUG); if($network === NETWORK_DFRN) { $ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1", @@ -825,7 +823,7 @@ function dfrn_request_content(&$a) { else $tpl = get_markup_template('auto_request.tpl'); - $page_desc .= t("Please enter your 'Identity Address' from one of the following supported communications networks:"); + $page_desc = t("Please enter your 'Identity Address' from one of the following supported communications networks:"); // see if we are allowed to have NETWORK_MAIL2 contacts @@ -850,7 +848,7 @@ function dfrn_request_content(&$a) { get_server() ); - $o .= replace_macros($tpl,array( + $o = replace_macros($tpl,array( '$header' => t('Friend/Connection Request'), '$desc' => t('Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca'), '$pls_answer' => t('Please answer the following:'), diff --git a/mod/directory.php b/mod/directory.php index 294a55585d..625f6c95ac 100644 --- a/mod/directory.php +++ b/mod/directory.php @@ -104,7 +104,7 @@ function directory_content(&$a) { $itemurl = (($rr['addr'] != "") ? $rr['addr'] : $rr['profile_url']); - $profile_link = z_root() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); + $profile_link = 'profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); $pdesc = (($rr['pdesc']) ? $rr['pdesc'] . '
            ' : ''); @@ -158,14 +158,14 @@ function directory_content(&$a) { else { $location_e = $location; } - + $photo_menu = array(array(t("View Profile"), zrl($profile_link))); $entry = array( 'id' => $rr['id'], 'url' => $profile_link, 'itemurl' => $itemurl, - 'thumb' => proxy_url($a->get_cached_avatar_image($rr[$photo]), false, PROXY_SIZE_THUMB), + 'thumb' => proxy_url($rr[$photo], false, PROXY_SIZE_THUMB), 'img_hover' => $rr['name'], 'name' => $rr['name'], 'details' => $details, diff --git a/mod/display.php b/mod/display.php index 4e33927072..e53f9e2066 100644 --- a/mod/display.php +++ b/mod/display.php @@ -17,7 +17,7 @@ function display_init(&$a) { // Does the local user have this item? if (local_user()) { $r = q("SELECT `id`, `parent`, `author-name`, `author-link`, `author-avatar`, `network`, `body`, `uid` FROM `item` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user()); if (count($r)) { $nick = $a->user["nickname"]; @@ -30,12 +30,12 @@ function display_init(&$a) { $r = q("SELECT `user`.`nickname`, `item`.`id`, `item`.`parent`, `item`.`author-name`, `item`.`author-link`, `item`.`author-avatar`, `item`.`network`, `item`.`uid`, `item`.`body` FROM `item` INNER JOIN `user` ON `user`.`uid` = `item`.`uid` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`private` = 0 AND NOT `user`.`hidewall` + AND NOT `item`.`private` AND NOT `user`.`hidewall` AND `item`.`guid` = '%s'", dbesc($a->argv[1])); - // AND `item`.`private` = 0 AND `item`.`wall` = 1 + // AND NOT `item`.`private` AND `item`.`wall` if (count($r)) { $nick = $r[0]["nickname"]; $itemuid = $r[0]["uid"]; @@ -46,17 +46,17 @@ function display_init(&$a) { if ($nick == "") { $r = q("SELECT `item`.`id`, `item`.`parent`, `item`.`author-name`, `item`.`author-link`, `item`.`author-avatar`, `item`.`network`, `item`.`uid`, `item`.`body` - FROM `item` WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + FROM `item` WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`private` = 0 AND `item`.`uid` = 0 + AND NOT `item`.`private` AND `item`.`uid` = 0 AND `item`.`guid` = '%s'", dbesc($a->argv[1])); - // AND `item`.`private` = 0 AND `item`.`wall` = 1 + // AND NOT `item`.`private` AND `item`.`wall` } if (count($r)) { if ($r[0]["id"] != $r[0]["parent"]) $r = q("SELECT `id`, `author-name`, `author-link`, `author-avatar`, `network`, `body`, `uid` FROM `item` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `id` = %d", $r[0]["parent"]); $profiledata = display_fetchauthor($a, $r[0]); @@ -67,7 +67,7 @@ function display_init(&$a) { if (($nickname != $a->user["nickname"])) { $r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `user`.* FROM `profile` INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid` - WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` = 1 and `contact`.`self` = 1 LIMIT 1", + WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` AND `contact`.`self` LIMIT 1", dbesc($nickname) ); if (count($r)) @@ -120,27 +120,27 @@ function display_fetchauthor($a, $item) { } if (!$skip) { - $author = ""; - preg_match("/author='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") $profiledata["name"] = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8'); - preg_match('/author="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") + preg_match('/author="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") $profiledata["name"] = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8'); - $profile = ""; - preg_match("/profile='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") $profiledata["url"] = $matches[1]; - preg_match('/profile="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") + preg_match('/profile="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") $profiledata["url"] = $matches[1]; - $avatar = ""; - preg_match("/avatar='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") + $avatar = ""; + preg_match("/avatar='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") $profiledata["photo"] = $matches[1]; preg_match('/avatar="(.*?)"/ism', $attributes, $matches); @@ -257,7 +257,7 @@ function display_content(&$a, $update = 0) { if (local_user()) { $r = q("SELECT `id` FROM `item` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `guid` = '%s' AND `uid` = %d", dbesc($a->argv[1]), local_user()); if (count($r)) { $item_id = $r[0]["id"]; @@ -267,12 +267,12 @@ function display_content(&$a, $update = 0) { if ($nick == "") { $r = q("SELECT `user`.`nickname`, `item`.`id` FROM `item` INNER JOIN `user` ON `user`.`uid` = `item`.`uid` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`private` = 0 AND NOT `user`.`hidewall` + AND NOT `item`.`private` AND NOT `user`.`hidewall` AND `item`.`guid` = '%s'", dbesc($a->argv[1])); - // AND `item`.`private` = 0 AND `item`.`wall` = 1 + // AND NOT `item`.`private` AND `item`.`wall` if (count($r)) { $item_id = $r[0]["id"]; $nick = $r[0]["nickname"]; @@ -280,12 +280,12 @@ function display_content(&$a, $update = 0) { } if ($nick == "") { $r = q("SELECT `item`.`id` FROM `item` - WHERE `item`.`visible` = 1 AND `item`.`deleted` = 0 and `item`.`moderated` = 0 + WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`private` = 0 AND `item`.`uid` = 0 + AND NOT `item`.`private` AND `item`.`uid` = 0 AND `item`.`guid` = '%s'", dbesc($a->argv[1])); - // AND `item`.`private` = 0 AND `item`.`wall` = 1 + // AND NOT `item`.`private` AND `item`.`wall` if (count($r)) { $item_id = $r[0]["id"]; } @@ -293,12 +293,22 @@ function display_content(&$a, $update = 0) { } } - if(! $item_id) { + if ($item_id AND !is_numeric($item_id)) { + $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($item_id), intval($a->profile['uid'])); + if ($r) + $item_id = $r[0]["id"]; + else + $item_id = false; + } + + if (!$item_id) { $a->error = 404; - notice( t('Item not found.') . EOL); + notice(t('Item not found.').EOL); return; } + $groups = array(); $contact = null; @@ -334,7 +344,7 @@ function display_content(&$a, $update = 0) { } } - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1", + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1", intval($a->profile['uid']) ); if(count($r)) @@ -347,10 +357,8 @@ function display_content(&$a, $update = 0) { return; } - // Why do we need this on the display page? We don't have the possibility to write new content here. - // Ad editing of posts work without this as well. - // We should remove this completely for the 3.5.1 release. - /* + // We need the editor here to be able to reshare an item. + if ($is_owner) { $x = array( 'is_owner' => true, @@ -366,66 +374,56 @@ function display_content(&$a, $update = 0) { ); $o .= status_editor($a,$x,0,true); } - */ $sql_extra = item_permissions_sql($a->profile['uid'],$remote_contact,$groups); - // AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE ( `id` = '%s' OR `uri` = '%s' )) - if($update) { - $r = q("SELECT id FROM item WHERE item.uid = %d - AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE (`id` = '%s' OR `uri` = '%s')) - $sql_extra AND unseen = 1", - intval($a->profile['uid']), - dbesc($item_id), - dbesc($item_id) + $r = q("SELECT `id` FROM `item` WHERE `item`.`uid` = %d + AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `id` = %d) + $sql_extra AND `unseen`", + intval($a->profile['uid']), + intval($item_id) ); if(!$r) return ''; } - // AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE ( `id` = '%s' OR `uri` = '%s' ) - $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0 - and `item`.`moderated` = 0 - AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE (`id` = '%s' OR `uri` = '%s') - AND uid = %d) + AND NOT `contact`.`blocked` AND NOT `contact`.`pending` + WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted` + AND NOT `item`.`moderated` + AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `id` = %d) $sql_extra ORDER BY `parent` DESC, `gravity` ASC, `id` ASC", intval($a->profile['uid']), - dbesc($item_id), - dbesc($item_id), - intval($a->profile['uid']) + intval($item_id) ); if(!$r && local_user()) { // Check if this is another person's link to a post that we have $r = q("SELECT `item`.uri FROM `item` - WHERE (`item`.`id` = '%s' OR `item`.`uri` = '%s' ) + WHERE (`item`.`id` = %d OR `item`.`uri` = '%s') LIMIT 1", - dbesc($item_id), + intval($item_id), dbesc($item_id) ); if($r) { $item_uri = $r[0]['uri']; - // AND `item`.`parent` = ( SELECT `parent` FROM `item` FORCE INDEX (PRIMARY, `uri`) WHERE `uri` = '%s' AND uid = %d ) $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`network`, `contact`.`thumb`, `contact`.`self`, `contact`.`writable`, `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0 - and `item`.`moderated` = 0 + AND NOT `contact`.`blocked` AND NOT `contact`.`pending` + WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted` + AND NOT `item`.`moderated` AND `item`.`parent` = (SELECT `parent` FROM `item` WHERE `uri` = '%s' AND uid = %d) ORDER BY `parent` DESC, `gravity` ASC, `id` ASC ", intval(local_user()), @@ -440,7 +438,7 @@ function display_content(&$a, $update = 0) { if((local_user()) && (local_user() == $a->profile['uid'])) { q("UPDATE `item` SET `unseen` = 0 - WHERE `parent` = %d AND `unseen` = 1", + WHERE `parent` = %d AND `unseen`", intval($r[0]['parent']) ); } diff --git a/mod/fbrowser.php b/mod/fbrowser.php index 0a2a7dead5..110ca9585c 100644 --- a/mod/fbrowser.php +++ b/mod/fbrowser.php @@ -74,10 +74,18 @@ function fbrowser_content($a){ $filename_e = $rr['filename']; } + // Take the largest picture that is smaller or equal 640 pixels + $p = q("SELECT `scale` FROM `photo` WHERE `resource-id` = '%s' AND `height` <= 640 AND `width` <= 640 ORDER BY `resource-id`, `scale` LIMIT 1", + dbesc($rr['resource-id'])); + if ($p) + $scale = $p[0]["scale"]; + else + $scale = $rr['loq']; + return array( $a->get_baseurl() . '/photos/' . $a->user['nickname'] . '/image/' . $rr['resource-id'], $filename_e, - $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['loq'] . '.'. $ext + $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $scale . '.'. $ext ); } $files = array_map("_map_files1", $r); diff --git a/mod/help.php b/mod/help.php index 5465d3e900..7222569279 100644 --- a/mod/help.php +++ b/mod/help.php @@ -62,7 +62,7 @@ function help_content(&$a) { if ($filename !== "Home") { // create TOC but not for home $lines = explode("\n", $html); - $toc="

            TOC

              "; + $toc="

              TOC

                "; $lastlevel=1; $idnum = array(0,0,0,0,0,0,0); foreach($lines as &$line){ @@ -84,7 +84,7 @@ function help_content(&$a) { } } } - for($k=1;$k<$lastlevel; $k++) $toc.="
              "; + for($k=0;$k<$lastlevel; $k++) $toc.="
            "; $html = implode("\n",$lines); $a->page['aside'] = $toc.$a->page['aside']; diff --git a/mod/install.php b/mod/install.php index 8434b38e38..b1c2010eef 100755 --- a/mod/install.php +++ b/mod/install.php @@ -77,6 +77,7 @@ function install_post(&$a) { $dbdata = notags(trim($_POST['dbdata'])); $phpath = notags(trim($_POST['phpath'])); $timezone = notags(trim($_POST['timezone'])); + $language = notags(trim($_POST['language'])); $adminmail = notags(trim($_POST['adminmail'])); // connect to db @@ -89,6 +90,7 @@ function install_post(&$a) { '$dbpass' => $dbpass, '$dbdata' => $dbdata, '$timezone' => $timezone, + '$language' => $language, '$urlpath' => $urlpath, '$phpath' => $phpath, '$adminmail' => $adminmail @@ -273,6 +275,8 @@ function install_content(&$a) { $adminmail = notags(trim($_POST['adminmail'])); $timezone = ((x($_POST,'timezone')) ? ($_POST['timezone']) : 'America/Los_Angeles'); + /* Installed langs */ + $lang_choices = get_avaiable_languages(); $tpl = get_markup_template('install_settings.tpl'); $o .= replace_macros($tpl, array( @@ -291,7 +295,7 @@ function install_content(&$a) { '$timezone' => field_timezone('timezone', t('Please select a default timezone for your website'), $timezone, ''), - + '$language' => array('language', t('System Language:'), 'en', t('Set the default language for your Friendica installation interface and to send emails.'), $lang_choices), '$baseurl' => $a->get_baseurl(), diff --git a/mod/item.php b/mod/item.php index 8c5a479646..ffb486a7db 100644 --- a/mod/item.php +++ b/mod/item.php @@ -24,6 +24,7 @@ require_once('include/threads.php'); require_once('include/text.php'); require_once('include/items.php'); require_once('include/Scrape.php'); +require_once('include/diaspora.php'); function item_post(&$a) { @@ -160,6 +161,9 @@ function item_post(&$a) { logger('no contact found: '.print_r($thrparent, true), LOGGER_DEBUG); } else logger('parent contact: '.print_r($parent_contact, true), LOGGER_DEBUG); + + if ($parent_contact["nick"] == "") + $parent_contact["nick"] = $parent_contact["name"]; } } @@ -844,9 +848,6 @@ function item_post(&$a) { // NOTREACHED } - // Store the guid and other relevant data - add_guid($datarray); - $post_id = $r[0]['id']; logger('mod_item: saved item ' . $post_id); @@ -900,7 +901,7 @@ function item_post(&$a) { // Store the comment signature information in case we need to relay to Diaspora - store_diaspora_comment_sig($datarray, $author, ($self ? $user['prvkey'] : false), $parent_item, $post_id); + diaspora::store_comment_signature($datarray, $author, ($self ? $user['prvkey'] : false), $post_id); } else { $parent = $post_id; @@ -1064,10 +1065,11 @@ function item_content(&$a) { * the appropiate link. * * @param unknown_type $body the text to replace the tag in - * @param unknown_type $inform a comma-seperated string containing everybody to inform - * @param unknown_type $str_tags string to add the tag to - * @param unknown_type $profile_uid - * @param unknown_type $tag the tag to replace + * @param string $inform a comma-seperated string containing everybody to inform + * @param string $str_tags string to add the tag to + * @param integer $profile_uid + * @param string $tag the tag to replace + * @param string $network The network of the post * * @return boolean true if replaced, false if not replaced */ @@ -1175,7 +1177,7 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo //select someone from this user's contacts by name in the current network if (!$r AND ($network != "")) $r = q("SELECT `id`, `url`, `nick`, `name`, `alias`, `network` FROM `contact` WHERE `name` = '%s' AND `network` = '%s' AND `uid` = %d LIMIT 1", - dbesc($newname), + dbesc($name), dbesc($network), intval($profile_uid) ); @@ -1192,7 +1194,7 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo //select someone from this user's contacts by name if(!$r) $r = q("SELECT `id`, `url`, `nick`, `name`, `alias`, `network` FROM `contact` WHERE `name` = '%s' AND `uid` = %d LIMIT 1", - dbesc($newname), + dbesc($name), intval($profile_uid) ); } @@ -1215,13 +1217,13 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo } //if there is an url for this persons profile - if(isset($profile)) { + if (isset($profile) AND ($newname != "")) { $replaced = true; //create profile link $profile = str_replace(',','%2c',$profile); - $newtag = '@[url=' . $profile . ']' . $newname . '[/url]'; - $body = str_replace('@' . $name, $newtag, $body); + $newtag = '@[url='.$profile.']'.$newname.'[/url]'; + $body = str_replace('@'.$name, $newtag, $body); //append tag to str_tags if(! stristr($str_tags,$newtag)) { if(strlen($str_tags)) @@ -1233,7 +1235,7 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo // subscribed to you. But the nickname URL is OK if they are. Grrr. We'll tag both. if(strlen($alias)) { - $newtag = '@[url=' . $alias . ']' . $newname . '[/url]'; + $newtag = '@[url='.$alias.']'.$newname.'[/url]'; if(! stristr($str_tags,$newtag)) { if(strlen($str_tags)) $str_tags .= ','; @@ -1245,42 +1247,3 @@ function handle_tag($a, &$body, &$inform, &$str_tags, $profile_uid, $tag, $netwo return array('replaced' => $replaced, 'contact' => $r[0]); } - - -function store_diaspora_comment_sig($datarray, $author, $uprvkey, $parent_item, $post_id) { - // We won't be able to sign Diaspora comments for authenticated visitors - we don't have their private key - - $enabled = intval(get_config('system','diaspora_enabled')); - if(! $enabled) { - logger('mod_item: diaspora support disabled, not storing comment signature', LOGGER_DEBUG); - return; - } - - - logger('mod_item: storing diaspora comment signature'); - - require_once('include/bb2diaspora.php'); - $signed_body = html_entity_decode(bb2diaspora($datarray['body'])); - - // Only works for NETWORK_DFRN - $contact_baseurl_start = strpos($author['url'],'://') + 3; - $contact_baseurl_length = strpos($author['url'],'/profile') - $contact_baseurl_start; - $contact_baseurl = substr($author['url'], $contact_baseurl_start, $contact_baseurl_length); - $diaspora_handle = $author['nick'] . '@' . $contact_baseurl; - - $signed_text = $datarray['guid'] . ';' . $parent_item['guid'] . ';' . $signed_body . ';' . $diaspora_handle; - - if( $uprvkey !== false ) - $authorsig = rsa_sign($signed_text,$uprvkey,'sha256'); - else - $authorsig = ''; - - q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", - intval($post_id), - dbesc($signed_text), - dbesc(base64_encode($authorsig)), - dbesc($diaspora_handle) - ); - - return; -} diff --git a/mod/message.php b/mod/message.php index 1724ebc424..734bf34710 100644 --- a/mod/message.php +++ b/mod/message.php @@ -13,7 +13,7 @@ function message_init(&$a) { $new = array( 'label' => t('New Message'), - 'url' => $a->get_baseurl(true) . '/message/new', + 'url' => 'message/new', 'sel'=> ($a->argv[1] == 'new'), 'accesskey' => 'm', ); @@ -90,7 +90,7 @@ function message_post(&$a) { $a->argv[1] = 'new'; } else - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway($_SESSION['return_url']); } @@ -182,7 +182,7 @@ function message_content(&$a) { return; } - $myprofile = $a->get_baseurl(true) . '/profile/' . $a->user['nickname']; + $myprofile = 'profile/' . $a->user['nickname']; $tpl = get_markup_template('mail_head.tpl'); $header = replace_macros($tpl, array( @@ -221,7 +221,7 @@ function message_content(&$a) { } // Now check how the user responded to the confirmation query if($_REQUEST['canceled']) { - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway($_SESSION['return_url']); } $cmd = $a->argv[1]; @@ -234,7 +234,7 @@ function message_content(&$a) { info( t('Message deleted.') . EOL ); } //goaway($a->get_baseurl(true) . '/message' ); - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway($_SESSION['return_url']); } else { $r = q("SELECT `parent-uri`,`convid` FROM `mail` WHERE `id` = %d AND `uid` = %d LIMIT 1", @@ -265,7 +265,7 @@ function message_content(&$a) { info( t('Conversation removed.') . EOL ); } //goaway($a->get_baseurl(true) . '/message' ); - goaway($a->get_baseurl(true) . '/' . $_SESSION['return_url']); + goaway($_SESSION['return_url']); } } @@ -448,7 +448,7 @@ function message_content(&$a) { $sparkle = ''; } else { - $from_url = $a->get_baseurl(true) . '/redir/' . $message['contact-id']; + $from_url = 'redir/' . $message['contact-id']; $sparkle = ' sparkle'; } @@ -549,7 +549,7 @@ function render_messages($msg, $t) { $tpl = get_markup_template($t); $rslt = ''; - $myprofile = $a->get_baseurl(true) . '/profile/' . $a->user['nickname']; + $myprofile = 'profile/' . $a->user['nickname']; foreach($msg as $rr) { @@ -577,7 +577,7 @@ function render_messages($msg, $t) { $rslt .= replace_macros($tpl, array( '$id' => $rr['id'], '$from_name' => $participants, - '$from_url' => (($rr['network'] === NETWORK_DFRN) ? $a->get_baseurl(true) . '/redir/' . $rr['contact-id'] : $rr['url']), + '$from_url' => (($rr['network'] === NETWORK_DFRN) ? 'redir/' . $rr['contact-id'] : $rr['url']), '$sparkle' => ' sparkle', '$from_photo' => (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']), '$subject' => $subject_e, diff --git a/mod/mood.php b/mod/mood.php index eee11e20c5..5e6ca0fcfc 100644 --- a/mod/mood.php +++ b/mod/mood.php @@ -62,7 +62,7 @@ function mood_init(&$a) { $action = sprintf( t('%1$s is currently %2$s'), '[url=' . $poster['url'] . ']' . $poster['name'] . '[/url]' , $verbs[$verb]); $arr = array(); - + $arr['guid'] = get_guid(32); $arr['uid'] = $uid; $arr['uri'] = $uri; $arr['parent-uri'] = (($parent_uri) ? $parent_uri : $uri); diff --git a/mod/network.php b/mod/network.php index a07c5868ec..a9f369a894 100644 --- a/mod/network.php +++ b/mod/network.php @@ -114,7 +114,7 @@ function network_init(&$a) { require_once('include/group.php'); require_once('include/contact_widgets.php'); require_once('include/items.php'); - require_once('include/forums.php'); + require_once('include/ForumManager.php'); if(! x($a->page,'aside')) $a->page['aside'] = ''; @@ -148,11 +148,11 @@ function network_init(&$a) { } $a->page['aside'] .= (feature_enabled(local_user(),'groups') ? group_side('network/0','network','standard',$group_id) : ''); - $a->page['aside'] .= (feature_enabled(local_user(),'forumlist_widget') ? widget_forumlist(local_user(),$cid) : ''); - $a->page['aside'] .= posted_date_widget($a->get_baseurl() . '/network',local_user(),false); - $a->page['aside'] .= networks_widget($a->get_baseurl(true) . '/network',(x($_GET, 'nets') ? $_GET['nets'] : '')); + $a->page['aside'] .= (feature_enabled(local_user(),'forumlist_widget') ? ForumManager::widget(local_user(),$cid) : ''); + $a->page['aside'] .= posted_date_widget('network',local_user(),false); + $a->page['aside'] .= networks_widget('network',(x($_GET, 'nets') ? $_GET['nets'] : '')); $a->page['aside'] .= saved_searches($search); - $a->page['aside'] .= fileas_widget($a->get_baseurl(true) . '/network',(x($_GET, 'file') ? $_GET['file'] : '')); + $a->page['aside'] .= fileas_widget('network',(x($_GET, 'file') ? $_GET['file'] : '')); } @@ -363,7 +363,7 @@ function network_content(&$a, $update = 0) { $tabs = array( array( 'label' => t('Commented Order'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), + 'url' => str_replace('/new', '', $cmd) . '?f=&order=comment' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), 'sel' => $all_active, 'title' => t('Sort by Comment Date'), 'id' => 'commented-order-tab', @@ -371,7 +371,7 @@ function network_content(&$a, $update = 0) { ), array( 'label' => t('Posted Order'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), + 'url' => str_replace('/new', '', $cmd) . '?f=&order=post' . ((x($_GET,'cid')) ? '&cid=' . $_GET['cid'] : ''), 'sel' => $postord_active, 'title' => t('Sort by Post Date'), 'id' => 'posted-order-tab', @@ -382,7 +382,7 @@ function network_content(&$a, $update = 0) { if(feature_enabled(local_user(),'personal_tab')) { $tabs[] = array( 'label' => t('Personal'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1', + 'url' => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&conv=1', 'sel' => $conv_active, 'title' => t('Posts that mention or involve you'), 'id' => 'personal-tab', @@ -393,7 +393,7 @@ function network_content(&$a, $update = 0) { if(feature_enabled(local_user(),'new_tab')) { $tabs[] = array( 'label' => t('New'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ($len_naked_cmd ? '/' : '') . 'new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''), + 'url' => str_replace('/new', '', $cmd) . ($len_naked_cmd ? '/' : '') . 'new' . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : ''), 'sel' => $new_active, 'title' => t('Activity Stream - by date'), 'id' => 'activitiy-by-date-tab', @@ -404,7 +404,7 @@ function network_content(&$a, $update = 0) { if(feature_enabled(local_user(),'link_tab')) { $tabs[] = array( 'label' => t('Shared Links'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1', + 'url' => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&bmark=1', 'sel' => $bookmarked_active, 'title' => t('Interesting Links'), 'id' => 'shared-links-tab', @@ -415,7 +415,7 @@ function network_content(&$a, $update = 0) { if(feature_enabled(local_user(),'star_posts')) { $tabs[] = array( 'label' => t('Starred'), - 'url' => $a->get_baseurl(true) . '/' . str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1', + 'url' => str_replace('/new', '', $cmd) . ((x($_GET,'cid')) ? '/?f=&cid=' . $_GET['cid'] : '/?f=') . '&star=1', 'sel' => $starred_active, 'title' => t('Favourite Posts'), 'id' => 'starred-posts-tab', @@ -547,7 +547,7 @@ function network_content(&$a, $update = 0) { if($update) killme(); notice( t('No such group') . EOL ); - goaway($a->get_baseurl(true) . '/network/0'); + goaway('network/0'); // NOTREACHED } @@ -611,7 +611,7 @@ function network_content(&$a, $update = 0) { } else { notice( t('Invalid contact.') . EOL); - goaway($a->get_baseurl(true) . '/network'); + goaway('network'); // NOTREACHED } } diff --git a/mod/noscrape.php b/mod/noscrape.php index 51bd7234cf..4be1a740cd 100644 --- a/mod/noscrape.php +++ b/mod/noscrape.php @@ -22,13 +22,17 @@ function noscrape_init(&$a) { $keywords = str_replace(array('#',',',' ',',,'),array('',' ',',',','),$keywords); $keywords = explode(',', $keywords); + $r = q("SELECT `photo` FROM `contact` WHERE `self` AND `uid` = %d", + intval($a->profile['uid'])); + $json_info = array( 'fn' => $a->profile['name'], 'addr' => $a->profile['addr'], + 'nick' => $which, 'key' => $a->profile['pubkey'], 'homepage' => $a->get_baseurl()."/profile/{$which}", 'comm' => (x($a->profile,'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY), - 'photo' => $a->profile['photo'], + 'photo' => $r[0]["photo"], 'tags' => $keywords ); diff --git a/mod/notifications.php b/mod/notifications.php index a267b7c958..f6c4e8f51f 100644 --- a/mod/notifications.php +++ b/mod/notifications.php @@ -49,12 +49,12 @@ function notifications_post(&$a) { intval(local_user()) ); } - goaway($a->get_baseurl(true) . '/notifications/intros'); + goaway('notifications/intros'); } if($_POST['submit'] == t('Ignore')) { $r = q("UPDATE `intro` SET `ignore` = 1 WHERE `id` = %d", intval($intro_id)); - goaway($a->get_baseurl(true) . '/notifications/intros'); + goaway('notifications/intros'); } } } @@ -79,37 +79,37 @@ function notifications_content(&$a) { $tabs = array( array( 'label' => t('System'), - 'url'=>$a->get_baseurl(true) . '/notifications/system', + 'url'=>'notifications/system', 'sel'=> (($a->argv[1] == 'system') ? 'active' : ''), 'accesskey' => 'y', ), array( 'label' => t('Network'), - 'url'=>$a->get_baseurl(true) . '/notifications/network', + 'url'=>'notifications/network', 'sel'=> (($a->argv[1] == 'network') ? 'active' : ''), 'accesskey' => 'w', ), array( 'label' => t('Personal'), - 'url'=>$a->get_baseurl(true) . '/notifications/personal', + 'url'=>'notifications/personal', 'sel'=> (($a->argv[1] == 'personal') ? 'active' : ''), 'accesskey' => 'r', ), array( 'label' => t('Home'), - 'url' => $a->get_baseurl(true) . '/notifications/home', + 'url' => 'notifications/home', 'sel'=> (($a->argv[1] == 'home') ? 'active' : ''), 'accesskey' => 'h', ), array( 'label' => t('Introductions'), - 'url' => $a->get_baseurl(true) . '/notifications/intros', + 'url' => 'notifications/intros', 'sel'=> (($a->argv[1] == 'intros') ? 'active' : ''), 'accesskey' => 'i', ), /*array( 'label' => t('Messages'), - 'url' => $a->get_baseurl(true) . '/message', + 'url' => 'message', 'sel'=> '', ),*/ /*while I can have notifications for messages, this tablist is not place for message page link */ ); diff --git a/mod/notify.php b/mod/notify.php index 02260514af..938e2ffbe3 100644 --- a/mod/notify.php +++ b/mod/notify.php @@ -1,43 +1,34 @@ argc > 2 && $a->argv[1] === 'view' && intval($a->argv[2])) { - $r = q("select * from notify where id = %d and uid = %d limit 1", - intval($a->argv[2]), - intval(local_user()) - ); - if(count($r)) { - q("update notify set seen = 1 where ( link = '%s' or ( parent != 0 and parent = %d and otype = '%s' )) and uid = %d", - dbesc($r[0]['link']), - intval($r[0]['parent']), - dbesc($r[0]['otype']), - intval(local_user()) - ); - + $note = $nm->getByID($a->argv[2]); + if ($note) { + $nm->setSeen($note); + // The friendica client has problems with the GUID. this is some workaround if ($a->is_friendica_app()) { require_once("include/items.php"); - $urldata = parse_url($r[0]['link']); + $urldata = parse_url($note['link']); $guid = basename($urldata["path"]); $itemdata = get_item_id($guid, local_user()); if ($itemdata["id"] != 0) - $r[0]['link'] = $a->get_baseurl().'/display/'.$itemdata["nick"].'/'.$itemdata["id"]; + $note['link'] = $a->get_baseurl().'/display/'.$itemdata["nick"].'/'.$itemdata["id"]; } - goaway($r[0]['link']); + goaway($note['link']); } goaway($a->get_baseurl(true)); } if($a->argc > 2 && $a->argv[1] === 'mark' && $a->argv[2] === 'all' ) { - $r = q("update notify set seen = 1 where uid = %d", - intval(local_user()) - ); + $r = $nm->setAllSeen(); $j = json_encode(array('result' => ($r) ? 'success' : 'fail')); echo $j; killme(); @@ -45,38 +36,35 @@ function notify_init(&$a) { } - function notify_content(&$a) { - if(! local_user()) - return login(); + if(! local_user()) return login(); - $notif_tpl = get_markup_template('notifications.tpl'); + $nm = new NotificationsManager(); + + $notif_tpl = get_markup_template('notifications.tpl'); - $not_tpl = get_markup_template('notify.tpl'); - require_once('include/bbcode.php'); + $not_tpl = get_markup_template('notify.tpl'); + require_once('include/bbcode.php'); - $r = q("SELECT * from notify where uid = %d and seen = 0 order by date desc", - intval(local_user()) - ); - - if (count($r) > 0) { - foreach ($r as $it) { - $notif_content .= replace_macros($not_tpl,array( - '$item_link' => $a->get_baseurl(true).'/notify/view/'. $it['id'], - '$item_image' => $it['photo'], - '$item_text' => strip_tags(bbcode($it['msg'])), - '$item_when' => relative_date($it['date']) - )); - } - } else { - $notif_content .= t('No more system notifications.'); + $r = $nm->getAll(array('seen'=>0)); + if ($r!==false && count($r) > 0) { + foreach ($r as $it) { + $notif_content .= replace_macros($not_tpl,array( + '$item_link' => $a->get_baseurl(true).'/notify/view/'. $it['id'], + '$item_image' => $it['photo'], + '$item_text' => strip_tags(bbcode($it['msg'])), + '$item_when' => relative_date($it['date']) + )); } + } else { + $notif_content .= t('No more system notifications.'); + } - $o .= replace_macros($notif_tpl, array( - '$notif_header' => t('System Notifications'), - '$tabs' => '', // $tabs, - '$notif_content' => $notif_content, - )); + $o .= replace_macros($notif_tpl, array( + '$notif_header' => t('System Notifications'), + '$tabs' => false, // $tabs, + '$notif_content' => $notif_content, + )); return $o; diff --git a/mod/p.php b/mod/p.php index 92b72dc1ce..20d6cfdbaf 100644 --- a/mod/p.php +++ b/mod/p.php @@ -28,14 +28,14 @@ function p_init($a){ $post = array(); - $reshared = diaspora_is_reshare($item[0]["body"]); + $reshared = diaspora::is_reshare($item[0]["body"]); if ($reshared) { $nodename = "reshare"; $post["root_diaspora_id"] = $reshared["root_handle"]; $post["root_guid"] = $reshared["root_guid"]; $post["guid"] = $item[0]["guid"]; - $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]); + $post["diaspora_handle"] = diaspora::handle_from_contact($item[0]["contact-id"]); $post["public"] = (!$item[0]["private"] ? 'true':'false'); $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]); } else { @@ -48,7 +48,7 @@ function p_init($a){ $nodename = "status_message"; $post["raw_message"] = str_replace("&", "&", $body); $post["guid"] = $item[0]["guid"]; - $post["diaspora_handle"] = diaspora_handle_from_contact($item[0]["contact-id"]); + $post["diaspora_handle"] = diaspora::handle_from_contact($item[0]["contact-id"]); $post["public"] = (!$item[0]["private"] ? 'true':'false'); $post["created_at"] = datetime_convert('UTC','UTC',$item[0]["created"]); $post["provider_display_name"] = $item[0]["app"]; diff --git a/mod/photos.php b/mod/photos.php index a9dade6a81..4761b627d8 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -80,7 +80,7 @@ function photos_init(&$a) { $entry = array( 'text' => $album['album'], 'total' => $album['total'], - 'url' => z_root() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album['album']), + 'url' => 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album['album']), 'urlencode' => urlencode($album['album']), 'bin2hex' => bin2hex($album['album']) ); @@ -100,7 +100,7 @@ function photos_init(&$a) { '$recent' => t('Recent Photos'), '$albums' => $albums['albums'], '$baseurl' => z_root(), - '$upload' => array( t('Upload New Photos'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/upload'), + '$upload' => array( t('Upload New Photos'), 'photos/' . $a->data['user']['nickname'] . '/upload'), '$can_post' => $can_post )); } @@ -190,7 +190,7 @@ function photos_post(&$a) { $album = hex2bin($a->argv[3]); if($album === t('Profile Photos') || $album === 'Contact Photos' || $album === t('Contact Photos')) { - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); return; // NOTREACHED } @@ -200,13 +200,13 @@ function photos_post(&$a) { ); if(! count($r)) { notice( t('Album not found.') . EOL); - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); return; // NOTREACHED } // Check if the user has responded to a delete confirmation query if($_REQUEST['canceled']) { - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); } /* @@ -221,7 +221,7 @@ function photos_post(&$a) { intval($page_owner_uid) ); $newurl = str_replace(bin2hex($album),bin2hex($newalbum),$_SESSION['photo_return']); - goaway($a->get_baseurl() . '/' . $newurl); + goaway($newurl); return; // NOTREACHED } @@ -273,7 +273,7 @@ function photos_post(&$a) { } } else { - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); return; // NOTREACHED } @@ -309,14 +309,14 @@ function photos_post(&$a) { } } } - goaway($a->get_baseurl() . '/photos/' . $a->data['user']['nickname']); + goaway('photos/' . $a->data['user']['nickname']); return; // NOTREACHED } // Check if the user has responded to a delete confirmation query for a single photo if(($a->argc > 2) && $_REQUEST['canceled']) { - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); } if(($a->argc > 2) && (x($_POST,'delete')) && ($_POST['delete'] == t('Delete Photo'))) { @@ -379,7 +379,7 @@ function photos_post(&$a) { } } - goaway($a->get_baseurl() . '/photos/' . $a->data['user']['nickname']); + goaway('photos/' . $a->data['user']['nickname']); return; // NOTREACHED } @@ -488,7 +488,7 @@ function photos_post(&$a) { $uri = item_new_uri($a->get_hostname(),$page_owner_uid); $arr = array(); - + $arr['guid'] = get_guid(32); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; @@ -677,7 +677,7 @@ function photos_post(&$a) { $uri = item_new_uri($a->get_hostname(),$page_owner_uid); $arr = array(); - + $arr['guid'] = get_guid(32); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; @@ -718,12 +718,6 @@ function photos_post(&$a) { $item_id = item_store($arr); if($item_id) { - //q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d", - // dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id), - // intval($page_owner_uid), - // intval($item_id) - //); - proc_run('php',"include/notifier.php","tag","$item_id"); } } @@ -731,7 +725,7 @@ function photos_post(&$a) { } } - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); return; // NOTREACHED } @@ -910,6 +904,7 @@ function photos_post(&$a) { if($lat && $lon) $arr['coord'] = $lat . ' ' . $lon; + $arr['guid'] = get_guid(32); $arr['uid'] = $page_owner_uid; $arr['uri'] = $uri; $arr['parent-uri'] = $uri; @@ -938,14 +933,6 @@ function photos_post(&$a) { $item_id = item_store($arr); - //if($item_id) { - // q("UPDATE `item` SET `plink` = '%s' WHERE `uid` = %d AND `id` = %d", - // dbesc($a->get_baseurl() . '/display/' . $owner_record['nickname'] . '/' . $item_id), - // intval($page_owner_uid), - // intval($item_id) - // ); - //} - if($visible) proc_run('php', "include/notifier.php", 'wall-new', $item_id); @@ -954,7 +941,7 @@ function photos_post(&$a) { // addon uploaders should call "killme()" [e.g. exit] within the photo_post_end hook // if they do not wish to be redirected - goaway($a->get_baseurl() . '/' . $_SESSION['photo_return']); + goaway($_SESSION['photo_return']); // NOTREACHED } @@ -1125,7 +1112,7 @@ function photos_content(&$a) { $uploader = ''; - $ret = array('post_url' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'], + $ret = array('post_url' => 'photos/' . $a->data['user']['nickname'], 'addon_text' => $uploader, 'default_upload' => true); @@ -1267,15 +1254,15 @@ function photos_content(&$a) { else { if(($album !== t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== t('Contact Photos'))) { if($can_post) { - $edit = array(t('Edit Album'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit'); + $edit = array(t('Edit Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit'); } } } if($_GET['order'] === 'posted') - $order = array(t('Show Newest First'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album)); + $order = array(t('Show Newest First'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album)); else - $order = array(t('Show Oldest First'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '?f=&order=posted'); + $order = array(t('Show Oldest First'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '?f=&order=posted'); $photos = array(); @@ -1301,10 +1288,10 @@ function photos_content(&$a) { $photos[] = array( 'id' => $rr['id'], 'twist' => ' ' . $twist . rand(2,4), - 'link' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'] + 'link' => 'photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'] . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''), 'title' => t('View Photo'), - 'src' => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext, + 'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext, 'alt' => $imgalt_e, 'desc'=> $desc_e, 'ext' => $ext, @@ -1317,7 +1304,7 @@ function photos_content(&$a) { '$photos' => $photos, '$album' => $album, '$can_post' => $can_post, - '$upload' => array(t('Upload New Photos'), $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/upload/' . bin2hex($album)), + '$upload' => array(t('Upload New Photos'), 'photos/' . $a->data['user']['nickname'] . '/upload/' . bin2hex($album)), '$order' => $order, '$edit' => $edit )); @@ -1384,8 +1371,8 @@ function photos_content(&$a) { } } $edit_suffix = ((($cmd === 'edit') && ($can_post)) ? '/edit' : ''); - $prevlink = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); - $nextlink = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); + $prevlink = 'photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$prv]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); + $nextlink = 'photos/' . $a->data['user']['nickname'] . '/image/' . $prvnxt[$nxt]['resource-id'] . $edit_suffix . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''); } @@ -1402,14 +1389,14 @@ function photos_content(&$a) { } } - $album_link = $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($ph[0]['album']); + $album_link = 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($ph[0]['album']); $tools = Null; $lock = Null; if($can_post && ($ph[0]['uid'] == $owner_uid)) { $tools = array( - 'edit' => array($a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $datum . (($cmd === 'edit') ? '' : '/edit'), (($cmd === 'edit') ? t('View photo') : t('Edit photo'))), - 'profile'=>array($a->get_baseurl() . '/profile_photo/use/'.$ph[0]['resource-id'], t('Use as profile photo')), + 'edit' => array('photos/' . $a->data['user']['nickname'] . '/image/' . $datum . (($cmd === 'edit') ? '' : '/edit'), (($cmd === 'edit') ? t('View photo') : t('Edit photo'))), + 'profile'=>array('profile_photo/use/'.$ph[0]['resource-id'], t('Use as profile photo')), ); // lock @@ -1433,9 +1420,9 @@ function photos_content(&$a) { $prevlink = array($prevlink, '') ; $photo = array( - 'href' => $a->get_baseurl() . '/photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']], + 'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']], 'title'=> t('View Full Size'), - 'src' => $a->get_baseurl() . '/photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?f=&_u=' . datetime_convert('','','','ymdhis'), + 'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?f=&_u=' . datetime_convert('','','','ymdhis'), 'height' => $hires['height'], 'width' => $hires['width'], 'album' => $hires['album'], @@ -1522,7 +1509,7 @@ function photos_content(&$a) { } $tags = array(t('Tags: '), $tag_str); if($cmd === 'edit') { - $tags[] = $a->get_baseurl() . '/tagrm/' . $link_item['id']; + $tags[] = 'tagrm/' . $link_item['id']; $tags[] = t('[Remove any tag]'); } } @@ -1693,7 +1680,7 @@ function photos_content(&$a) { if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) && ($item['id'] != $item['parent'])) continue; - $redirect_url = $a->get_baseurl() . '/redir/' . $item['cid'] ; + $redirect_url = 'redir/' . $item['cid'] ; if(local_user() && ($item['contact-uid'] == local_user()) @@ -1880,12 +1867,12 @@ function photos_content(&$a) { $photos[] = array( 'id' => $rr['id'], 'twist' => ' ' . $twist . rand(2,4), - 'link' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'], + 'link' => 'photos/' . $a->data['user']['nickname'] . '/image/' . $rr['resource-id'], 'title' => t('View Photo'), - 'src' => $a->get_baseurl() . '/photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext, + 'src' => 'photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext, 'alt' => $alt_e, 'album' => array( - 'link' => $a->get_baseurl() . '/photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($rr['album']), + 'link' => 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($rr['album']), 'name' => $name_e, 'alt' => t('View Album'), ), @@ -1898,7 +1885,7 @@ function photos_content(&$a) { $o .= replace_macros($tpl, array( '$title' => t('Recent Photos'), '$can_post' => $can_post, - '$upload' => array(t('Upload New Photos'), $a->get_baseurl().'/photos/'.$a->data['user']['nickname'].'/upload'), + '$upload' => array(t('Upload New Photos'), 'photos/'.$a->data['user']['nickname'].'/upload'), '$photos' => $photos, )); diff --git a/mod/ping.php b/mod/ping.php index 57728d3294..544aa446bb 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -1,7 +1,7 @@ %s'."\n"; - return sprintf ( $notsxml, - xmlify($n['href']), xmlify($n['name']), xmlify($n['url']), xmlify($n['photo']), + $notsxml = '%s'."\n"; + return sprintf ( $notsxml, intval($n['id']), + xmlify($n['href']), xmlify(xmlify($n['name'])), xmlify($n['url']), xmlify($n['photo']), xmlify(relative_date($n['date'])), xmlify($n['seen']), xmlify(strtotime($local_time)), xmlify($n['message']) ); @@ -219,19 +219,21 @@ function ping_init(&$a) { $home\r\n"; if ($register!=0) echo "$register"; - if ( count($groups_unseen) ) { + if (count($groups_unseen)) { echo ''; - foreach ($groups_unseen as $it) { - echo '' . $it['count'] . ""; - } + foreach ($groups_unseen as $it) + if ($it['count'] > 0) + echo ''.$it['count'].""; + echo ""; } - if ( count($forums_unseen) ) { + if (count($forums_unseen)) { echo ''; - foreach ($forums_unseen as $it) { - echo '' . $it['count'] . ""; - } + foreach ($forums_unseen as $it) + if ($it['count'] > 0) + echo ''.$it['count'].""; + echo ""; } @@ -389,7 +391,11 @@ function ping_get_notifications($uid) { // Replace the name with {0} but ensure to make that only once // The {0} is used later and prints the name in bold. - $pos = strpos($notification["message"],$notification['name']); + if ($notification['name'] != "") + $pos = strpos($notification["message"],$notification['name']); + else + $pos = false; + if ($pos !== false) $notification["message"] = substr_replace($notification["message"],"{0}",$pos,strlen($notification["name"])); diff --git a/mod/poke.php b/mod/poke.php index 45a577cda6..4a643435be 100644 --- a/mod/poke.php +++ b/mod/poke.php @@ -91,6 +91,7 @@ function poke_init(&$a) { $arr = array(); + $arr['guid'] = get_guid(32); $arr['uid'] = $uid; $arr['uri'] = $uri; $arr['parent-uri'] = (($parent_uri) ? $parent_uri : $uri); diff --git a/mod/profiles.php b/mod/profiles.php index 5c372de8ee..39382fbdd5 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -16,7 +16,7 @@ function profiles_init(&$a) { ); if(! count($r)) { notice( t('Profile not found.') . EOL); - goaway($a->get_baseurl(true) . '/profiles'); + goaway('profiles'); return; // NOTREACHED } @@ -34,9 +34,9 @@ function profiles_init(&$a) { intval(local_user()) ); if($r) - info( t('Profile deleted.') . EOL); + info(t('Profile deleted.').EOL); - goaway($a->get_baseurl(true) . '/profiles'); + goaway('profiles'); return; // NOTREACHED } @@ -73,9 +73,9 @@ function profiles_init(&$a) { info( t('New profile created.') . EOL); if(count($r3) == 1) - goaway($a->get_baseurl(true) . '/profiles/' . $r3[0]['id']); + goaway('profiles/'.$r3[0]['id']); - goaway($a->get_baseurl(true) . '/profiles'); + goaway('profiles'); } if(($a->argc > 2) && ($a->argv[1] === 'clone')) { @@ -116,9 +116,9 @@ function profiles_init(&$a) { ); info( t('New profile created.') . EOL); if(count($r3) == 1) - goaway($a->get_baseurl(true) . '/profiles/' . $r3[0]['id']); + goaway('profiles/'.$r3[0]['id']); - goaway($a->get_baseurl(true) . '/profiles'); + goaway('profiles'); return; // NOTREACHED } @@ -526,6 +526,8 @@ function profile_activity($changed, $value) { return; $arr = array(); + + $arr['guid'] = get_guid(32); $arr['uri'] = $arr['parent-uri'] = item_new_uri($a->get_hostname(), local_user()); $arr['uid'] = local_user(); $arr['contact-id'] = $self[0]['id']; @@ -582,15 +584,7 @@ function profile_activity($changed, $value) { $i = item_store($arr); if($i) { - - // give it a permanent link - //q("update item set plink = '%s' where id = %d", - // dbesc($a->get_baseurl() . '/display/' . $a->user['nickname'] . '/' . $i), - // intval($i) - //); - proc_run('php',"include/notifier.php","activity","$i"); - } } @@ -786,7 +780,7 @@ function profiles_content(&$a) { ); if(count($r)){ //Go to the default profile. - goaway($a->get_baseurl(true) . '/profiles/'.$r[0]['id']); + goaway('profiles/'.$r[0]['id']); } } @@ -807,12 +801,12 @@ function profiles_content(&$a) { foreach($r as $rr) { $o .= replace_macros($tpl, array( - '$photo' => $a->get_cached_avatar_image($rr['thumb']), + '$photo' => $a->remove_baseurl($rr['thumb']), '$id' => $rr['id'], '$alt' => t('Profile Image'), '$profile_name' => $rr['profile-name'], '$visible' => (($rr['is-default']) ? '' . t('visible to everybody') . '' - : '' . t('Edit visibility') . '') + : '' . t('Edit visibility') . '') )); } } diff --git a/mod/pubsub.php b/mod/pubsub.php index beb73b4e2c..6053ee2fbe 100644 --- a/mod/pubsub.php +++ b/mod/pubsub.php @@ -122,8 +122,8 @@ function pubsub_post(&$a) { $importer = $r[0]; - $r = q("SELECT * FROM `contact` WHERE `subhub` = 1 AND `id` = %d AND `uid` = %d - AND ( `rel` = %d OR `rel` = %d OR network = '%s' ) AND `blocked` = 0 AND `readonly` = 0 LIMIT 1", + $r = q("SELECT * FROM `contact` WHERE `subhub` AND `id` = %d AND `uid` = %d + AND (`rel` = %d OR `rel` = %d OR network = '%s') AND NOT `blocked` LIMIT 1", intval($contact_id), intval($importer['uid']), intval(CONTACT_IS_SHARING), diff --git a/mod/receive.php b/mod/receive.php index 95a5101675..4991ac47e8 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -53,7 +53,7 @@ function receive_post(&$a) { logger('mod-diaspora: message is okay', LOGGER_DEBUG); - $msg = diaspora_decode($importer,$xml); + $msg = diaspora::decode($importer,$xml); logger('mod-diaspora: decoded', LOGGER_DEBUG); @@ -65,10 +65,11 @@ function receive_post(&$a) { logger('mod-diaspora: dispatching', LOGGER_DEBUG); $ret = 0; - if($public) - diaspora_dispatch_public($msg); - else - $ret = diaspora_dispatch($importer,$msg); + if($public) { + diaspora::dispatch_public($msg); + } else { + $ret = diaspora::dispatch($importer,$msg); + } http_status_exit(($ret) ? $ret : 200); // NOTREACHED diff --git a/mod/salmon.php b/mod/salmon.php index 9c22e42d11..37230a5573 100644 --- a/mod/salmon.php +++ b/mod/salmon.php @@ -84,7 +84,7 @@ function salmon_post(&$a) { // decode the data $data = base64url_decode($data); - $author = ostatus_salmon_author($data,$importer); + $author = ostatus::salmon_author($data,$importer); $author_link = $author["author-link"]; if(! $author_link) { @@ -181,7 +181,7 @@ function salmon_post(&$a) { $contact_rec = ((count($r)) ? $r[0] : null); - ostatus_import($data,$importer,$contact_rec, $hub); + ostatus::import($data,$importer,$contact_rec, $hub); http_status_exit(200); } diff --git a/mod/search.php b/mod/search.php index 7c78339c70..790f577ba6 100644 --- a/mod/search.php +++ b/mod/search.php @@ -147,7 +147,7 @@ function search_content(&$a) { } - $o .= search($search,'search-box','/search',((local_user()) ? true : false), false); + $o .= search($search,'search-box','search',((local_user()) ? true : false), false); if(strpos($search,'#') === 0) { $tag = true; @@ -217,11 +217,10 @@ function search_content(&$a) { FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND NOT `contact`.`blocked` AND NOT `contact`.`pending` WHERE `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` - AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND (`item`.`private` OR NOT `item`.`network` IN ('%s', '%s', '%s')))) + AND (`item`.`uid` = 0 OR (`item`.`uid` = %s AND NOT `item`.`global`)) $sql_extra GROUP BY `item`.`uri` ORDER BY `item`.`id` DESC LIMIT %d , %d ", - intval(local_user()), dbesc(NETWORK_DFRN), dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), - intval($a->pager['start']), intval($a->pager['itemspage'])); + intval(local_user()), intval($a->pager['start']), intval($a->pager['itemspage'])); } if(! count($r)) { diff --git a/mod/settings.php b/mod/settings.php index 3efdbf6bde..c7659212bf 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -1,5 +1,6 @@ t('Account'), - 'url' => $a->get_baseurl(true).'/settings', + 'url' => 'settings', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'settings')?'active':''), 'accesskey' => 'o', ), @@ -48,7 +49,7 @@ function settings_init(&$a) { if(get_features()) { $tabs[] = array( 'label' => t('Additional features'), - 'url' => $a->get_baseurl(true).'/settings/features', + 'url' => 'settings/features', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''), 'accesskey' => 't', ); @@ -56,49 +57,49 @@ function settings_init(&$a) { $tabs[] = array( 'label' => t('Display'), - 'url' => $a->get_baseurl(true).'/settings/display', + 'url' => 'settings/display', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'display')?'active':''), 'accesskey' => 'i', ); $tabs[] = array( 'label' => t('Social Networks'), - 'url' => $a->get_baseurl(true).'/settings/connectors', + 'url' => 'settings/connectors', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'connectors')?'active':''), 'accesskey' => 'w', ); $tabs[] = array( 'label' => t('Plugins'), - 'url' => $a->get_baseurl(true).'/settings/addon', + 'url' => 'settings/addon', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'addon')?'active':''), 'accesskey' => 'l', ); $tabs[] = array( 'label' => t('Delegations'), - 'url' => $a->get_baseurl(true).'/delegate', + 'url' => 'delegate', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'delegate')?'active':''), 'accesskey' => 'd', ); $tabs[] = array( 'label' => t('Connected apps'), - 'url' => $a->get_baseurl(true) . '/settings/oauth', + 'url' => 'settings/oauth', 'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth')?'active':''), 'accesskey' => 'b', ); $tabs[] = array( 'label' => t('Export personal data'), - 'url' => $a->get_baseurl(true) . '/uexport', + 'url' => 'uexport', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'uexport')?'active':''), 'accesskey' => 'e', ); $tabs[] = array( 'label' => t('Remove account'), - 'url' => $a->get_baseurl(true) . '/removeme', + 'url' => 'removeme', 'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme')?'active':''), 'accesskey' => 'r', ); @@ -199,6 +200,7 @@ function settings_post(&$a) { if(x($_POST, 'general-submit')) { set_pconfig(local_user(), 'system', 'no_intelligent_shortening', intval($_POST['no_intelligent_shortening'])); set_pconfig(local_user(), 'system', 'ostatus_autofriend', intval($_POST['snautofollow'])); + set_pconfig(local_user(), 'ostatus', 'default_group', $_POST['group-selection']); set_pconfig(local_user(), 'ostatus', 'legacy_contact', $_POST['legacy_contact']); } elseif(x($_POST, 'imap-submit')) { @@ -342,7 +344,7 @@ function settings_post(&$a) { ); call_hooks('display_settings_post', $_POST); - goaway($a->get_baseurl(true) . '/settings/display' ); + goaway('settings/display' ); return; // NOTREACHED } @@ -351,7 +353,7 @@ function settings_post(&$a) { if (x($_POST,'resend_relocate')) { proc_run('php', 'include/notifier.php', 'relocate', local_user()); info(t("Relocate message has been send to your contacts")); - goaway($a->get_baseurl(true) . '/settings'); + goaway('settings'); } call_hooks('settings_post', $_POST); @@ -627,7 +629,7 @@ function settings_post(&$a) { } - goaway($a->get_baseurl(true) . '/settings' ); + goaway('settings' ); return; // NOTREACHED } @@ -797,8 +799,11 @@ function settings_content(&$a) { $settings_connectors .= ''.t('If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user.').''; $settings_connectors .= ''; + $default_group = get_pconfig(local_user(), 'ostatus', 'default_group'); $legacy_contact = get_pconfig(local_user(), 'ostatus', 'legacy_contact'); + $settings_connectors .= mini_group_select(local_user(), $default_group, t("Default group for OStatus contacts")); + if ($legacy_contact != "") $a->page['htmlhead'] = ''; @@ -1152,7 +1157,7 @@ function settings_content(&$a) { info( t('Profile is not published.') . EOL ); - //$subdir = ((strlen($a->get_path())) ? '
            ' . t('or') . ' ' . $a->get_baseurl(true) . '/profile/' . $nickname : ''); + //$subdir = ((strlen($a->get_path())) ? '
            ' . t('or') . ' ' . 'profile/' . $nickname : ''); $tpl_addr = get_markup_template("settings_nick_set.tpl"); diff --git a/mod/subthread.php b/mod/subthread.php index 1486a33b42..33cf7489c1 100644 --- a/mod/subthread.php +++ b/mod/subthread.php @@ -103,10 +103,11 @@ EOT; $bodyverb = t('%1$s is following %2$s\'s %3$s'); if(! isset($bodyverb)) - return; + return; $arr = array(); + $arr['guid'] = get_guid(32); $arr['uri'] = $uri; $arr['uid'] = $owner_uid; $arr['contact-id'] = $contact['id']; @@ -123,7 +124,7 @@ EOT; $arr['author-name'] = $contact['name']; $arr['author-link'] = $contact['url']; $arr['author-avatar'] = $contact['thumb']; - + $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; $alink = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]'; $plink = '[url=' . $a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id'] . ']' . $post_type . '[/url]'; diff --git a/mod/tagger.php b/mod/tagger.php index 2c469a58bb..26166a3cc0 100644 --- a/mod/tagger.php +++ b/mod/tagger.php @@ -95,12 +95,13 @@ EOT; $bodyverb = t('%1$s tagged %2$s\'s %3$s with %4$s'); if(! isset($bodyverb)) - return; + return; $termlink = html_entity_decode('⌗') . '[url=' . $a->get_baseurl() . '/search?tag=' . urlencode($term) . ']'. $term . '[/url]'; $arr = array(); + $arr['guid'] = get_guid(32); $arr['uri'] = $uri; $arr['uid'] = $owner_uid; $arr['contact-id'] = $contact['id']; @@ -115,7 +116,7 @@ EOT; $arr['author-name'] = $contact['name']; $arr['author-link'] = $contact['url']; $arr['author-avatar'] = $contact['thumb']; - + $ulink = '[url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'; $alink = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]'; $plink = '[url=' . $item['plink'] . ']' . $post_type . '[/url]'; diff --git a/mod/uexport.php b/mod/uexport.php index a44620a976..3114add7e4 100644 --- a/mod/uexport.php +++ b/mod/uexport.php @@ -6,54 +6,6 @@ function uexport_init(&$a){ require_once("mod/settings.php"); settings_init($a); - -/* - $tabs = array( - array( - 'label' => t('Account settings'), - 'url' => $a->get_baseurl(true).'/settings', - 'selected' => '', - ), - array( - 'label' => t('Display settings'), - 'url' => $a->get_baseurl(true).'/settings/display', - 'selected' =>'', - ), - - array( - 'label' => t('Connector settings'), - 'url' => $a->get_baseurl(true).'/settings/connectors', - 'selected' => '', - ), - array( - 'label' => t('Plugin settings'), - 'url' => $a->get_baseurl(true).'/settings/addon', - 'selected' => '', - ), - array( - 'label' => t('Connected apps'), - 'url' => $a->get_baseurl(true) . '/settings/oauth', - 'selected' => '', - ), - array( - 'label' => t('Export personal data'), - 'url' => $a->get_baseurl(true) . '/uexport', - 'selected' => 'active' - ), - array( - 'label' => t('Remove account'), - 'url' => $a->get_baseurl(true) . '/removeme', - 'selected' => '' - ) - ); - - $tabtpl = get_markup_template("generic_links_widget.tpl"); - $a->page['aside'] = replace_macros($tabtpl, array( - '$title' => t('Settings'), - '$class' => 'settings-widget', - '$items' => $tabs, - )); -*/ } function uexport_content(&$a){ @@ -74,8 +26,8 @@ function uexport_content(&$a){ * list of array( 'link url', 'link text', 'help text' ) */ $options = array( - array('/uexport/account',t('Export account'),t('Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server.')), - array('/uexport/backup',t('Export all'),t('Export your accout info, contacts and all your items as json. Could be a very big file, and could take a lot of time. Use this to make a full backup of your account (photos are not exported)')), + array('uexport/account',t('Export account'),t('Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server.')), + array('uexport/backup',t('Export all'),t('Export your accout info, contacts and all your items as json. Could be a very big file, and could take a lot of time. Use this to make a full backup of your account (photos are not exported)')), ); call_hooks('uexport_options', $options); @@ -153,9 +105,9 @@ function uexport_account($a){ 'version' => FRIENDICA_VERSION, 'schema' => DB_UPDATE_VERSION, 'baseurl' => $a->get_baseurl(), - 'user' => $user, - 'contact' => $contact, - 'profile' => $profile, + 'user' => $user, + 'contact' => $contact, + 'profile' => $profile, 'photo' => $photo, 'pconfig' => $pconfig, 'group' => $group, @@ -171,8 +123,8 @@ function uexport_account($a){ * echoes account data and items as separated json, one per line */ function uexport_all(&$a) { - - uexport_account($a); + + uexport_account($a); echo "\n"; $r = q("SELECT count(*) as `total` FROM `item` WHERE `uid` = %d ", diff --git a/object/Item.php b/object/Item.php index 04c1a707e3..59659cdaff 100644 --- a/object/Item.php +++ b/object/Item.php @@ -50,7 +50,7 @@ class Item extends BaseObject { $this->writable = ($this->get_data_value('writable') || $this->get_data_value('self')); $ssl_state = ((local_user()) ? true : false); - $this->redirect_url = $a->get_baseurl($ssl_state) . '/redir/' . $this->get_data_value('cid') ; + $this->redirect_url = 'redir/' . $this->get_data_value('cid') ; if(get_config('system','thread_allow') && $a->theme_thread_allow && !$this->is_toplevel()) $this->threaded = true; @@ -119,9 +119,9 @@ class Item extends BaseObject { $shareable = ((($conv->get_profile_owner() == local_user()) && ($item['private'] != 1)) ? true : false); if(local_user() && link_compare($a->contact['url'],$item['author-link'])) { if ($item["event-id"] != 0) - $edpost = array($a->get_baseurl($ssl_state)."/events/event/".$item['event-id'], t("Edit")); + $edpost = array("events/event/".$item['event-id'], t("Edit")); else - $edpost = array($a->get_baseurl($ssl_state)."/editpost/".$item['id'], t("Edit")); + $edpost = array("editpost/".$item['id'], t("Edit")); } else $edpost = false; if(($this->get_data_value('uid') == local_user()) || $this->is_visiting()) @@ -154,13 +154,13 @@ class Item extends BaseObject { if(($normalised != 'mailbox') && (x($a->contacts,$normalised))) $profile_avatar = $a->contacts[$normalised]['thumb']; else - $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $a->get_cached_avatar_image($this->get_data_value('thumb'))); + $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $a->remove_baseurl($this->get_data_value('thumb'))); $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); call_hooks('render_location',$locate); $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate)); - $searchpath = $a->get_baseurl()."/search?tag="; + $searchpath = "search?tag="; $tags=array(); $hashtags = array(); $mentions = array(); @@ -324,7 +324,7 @@ class Item extends BaseObject { // Diaspora isn't able to do likes on comments - but red does if (($item["item_network"] == NETWORK_DIASPORA) AND ($indent == 'comment') AND - !diaspora_is_redmatrix($item["owner-link"]) AND isset($buttons["like"])) + !diaspora::is_redmatrix($item["owner-link"]) AND isset($buttons["like"])) unset($buttons["like"]); // Diaspora doesn't has multithreaded comments @@ -703,9 +703,9 @@ class Item extends BaseObject { '$parent' => $this->get_id(), '$qcomment' => $qcomment, '$profile_uid' => $conv->get_profile_owner(), - '$mylink' => $a->contact['url'], + '$mylink' => $a->remove_baseurl($a->contact['url']), '$mytitle' => t('This is you'), - '$myphoto' => $a->contact['thumb'], + '$myphoto' => $a->remove_baseurl($a->contact['thumb']), '$comment' => t('Comment'), '$submit' => t('Submit'), '$edbold' => t('Bold'), diff --git a/util/README b/util/README index 20a539215e..fad1270d47 100644 --- a/util/README +++ b/util/README @@ -10,7 +10,7 @@ Internationalisation extract.php - extracts translatable strings from our project files. It currently doesn't pick up strings in other libraries we might be using such as -tinymce, simplepie, and the HTML parsers. +tinymce and the HTML parsers. In order for extract to do its job, every use of the t() translation function must be preceded by one space. The string also can not contain parentheses. If diff --git a/util/createdoxygen.php b/util/createdoxygen.php new file mode 100644 index 0000000000..163c94bb97 --- /dev/null +++ b/util/createdoxygen.php @@ -0,0 +1,94 @@ +#!/usr/bin/php + 0) + $block .= $space." *\n"; + } + + $block .= $space." * @return \n". + $space." */\n"; + + return $block; +} +?> diff --git a/util/make_credits.py b/util/make_credits.py index 404b059133..eacb8707f4 100755 --- a/util/make_credits.py +++ b/util/make_credits.py @@ -46,17 +46,20 @@ for i in c: n1 = len(contributors) print(' > found %d contributors' % n1) # get the contributors to the addons -os.chdir(path+'/addon') -# get the contributors -print('> getting contributors to the addons') -p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) -c = iter(p.stdout.readline, b'') -for i in c: - name = i.decode().split('\t')[1].split('\n')[0] - if not name in contributors and name not in dontinclude: - contributors.append(name) +try: + os.chdir(path+'/addon') + # get the contributors + print('> getting contributors to the addons') + p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + c = iter(p.stdout.readline, b'') + for i in c: + name = i.decode().split('\t')[1].split('\n')[0] + if not name in contributors and name not in dontinclude: + contributors.append(name) +except FileNotFoundError: + print(' > no addon directory found ( THE LIST OF CONTRIBUTORS WILL BE INCOMPLETE )') n2 = len(contributors) print(' > found %d new contributors' % (n2-n1)) print('> total of %d contributors to the repositories of friendica' % n2) diff --git a/util/messages.po b/util/messages.po index 46a9913f45..df1dedd68a 100644 --- a/util/messages.po +++ b/util/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-24 06:49+0100\n" +"POT-Creation-Date: 2016-03-12 07:34+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,15 +18,15 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" -#: mod/contacts.php:50 include/identity.php:395 +#: mod/contacts.php:50 include/identity.php:396 msgid "Network:" msgstr "" -#: mod/contacts.php:51 mod/contacts.php:961 mod/videos.php:37 +#: mod/contacts.php:51 mod/contacts.php:947 mod/videos.php:37 #: mod/viewcontacts.php:105 mod/dirfind.php:214 mod/network.php:598 #: mod/allfriends.php:77 mod/match.php:82 mod/directory.php:172 #: mod/common.php:123 mod/suggest.php:95 mod/photos.php:41 -#: include/identity.php:298 +#: include/identity.php:299 msgid "Forum" msgstr "" @@ -37,7 +37,7 @@ msgid_plural "%d contacts edited." msgstr[0] "" msgstr[1] "" -#: mod/contacts.php:159 mod/contacts.php:383 +#: mod/contacts.php:159 mod/contacts.php:368 msgid "Could not access contact record." msgstr "" @@ -49,150 +49,150 @@ msgstr "" msgid "Contact updated." msgstr "" -#: mod/contacts.php:208 mod/dfrn_request.php:575 +#: mod/contacts.php:208 mod/dfrn_request.php:573 msgid "Failed to update contact record." msgstr "" -#: mod/contacts.php:365 mod/manage.php:96 mod/display.php:509 +#: mod/contacts.php:350 mod/manage.php:96 mod/display.php:513 #: mod/profile_photo.php:19 mod/profile_photo.php:175 mod/profile_photo.php:186 #: mod/profile_photo.php:199 mod/ostatus_subscribe.php:9 mod/follow.php:11 -#: mod/follow.php:73 mod/follow.php:155 mod/item.php:180 mod/item.php:192 +#: mod/follow.php:73 mod/follow.php:155 mod/item.php:183 mod/item.php:195 #: mod/group.php:19 mod/dfrn_confirm.php:55 mod/fsuggest.php:78 #: mod/wall_upload.php:77 mod/wall_upload.php:80 mod/viewcontacts.php:40 #: mod/notifications.php:69 mod/message.php:45 mod/message.php:181 -#: mod/crepair.php:117 mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4 +#: mod/crepair.php:100 mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4 #: mod/allfriends.php:12 mod/events.php:165 mod/wallmessage.php:9 #: mod/wallmessage.php:33 mod/wallmessage.php:79 mod/wallmessage.php:103 #: mod/wall_attach.php:67 mod/wall_attach.php:70 mod/settings.php:20 -#: mod/settings.php:126 mod/settings.php:646 mod/register.php:42 +#: mod/settings.php:126 mod/settings.php:647 mod/register.php:42 #: mod/delegate.php:12 mod/common.php:18 mod/mood.php:114 mod/suggest.php:58 -#: mod/profiles.php:165 mod/profiles.php:615 mod/editpost.php:10 mod/api.php:26 +#: mod/profiles.php:165 mod/profiles.php:593 mod/editpost.php:10 mod/api.php:26 #: mod/api.php:31 mod/notes.php:22 mod/poke.php:149 mod/repair_ostatus.php:9 -#: mod/invite.php:15 mod/invite.php:101 mod/photos.php:171 mod/photos.php:1105 +#: mod/invite.php:15 mod/invite.php:101 mod/photos.php:171 mod/photos.php:1091 #: mod/regmod.php:110 mod/uimport.php:23 mod/attach.php:33 -#: include/items.php:5096 index.php:383 +#: include/items.php:2002 index.php:384 msgid "Permission denied." msgstr "" -#: mod/contacts.php:404 +#: mod/contacts.php:389 msgid "Contact has been blocked" msgstr "" -#: mod/contacts.php:404 +#: mod/contacts.php:389 msgid "Contact has been unblocked" msgstr "" -#: mod/contacts.php:415 +#: mod/contacts.php:400 msgid "Contact has been ignored" msgstr "" -#: mod/contacts.php:415 +#: mod/contacts.php:400 msgid "Contact has been unignored" msgstr "" -#: mod/contacts.php:427 +#: mod/contacts.php:412 msgid "Contact has been archived" msgstr "" -#: mod/contacts.php:427 +#: mod/contacts.php:412 msgid "Contact has been unarchived" msgstr "" -#: mod/contacts.php:454 mod/contacts.php:802 +#: mod/contacts.php:439 mod/contacts.php:794 msgid "Do you really want to delete this contact?" msgstr "" -#: mod/contacts.php:456 mod/follow.php:110 mod/message.php:216 -#: mod/settings.php:1103 mod/settings.php:1109 mod/settings.php:1117 -#: mod/settings.php:1121 mod/settings.php:1126 mod/settings.php:1132 -#: mod/settings.php:1138 mod/settings.php:1144 mod/settings.php:1170 -#: mod/settings.php:1171 mod/settings.php:1172 mod/settings.php:1173 -#: mod/settings.php:1174 mod/dfrn_request.php:857 mod/register.php:238 -#: mod/suggest.php:29 mod/profiles.php:658 mod/profiles.php:661 -#: mod/profiles.php:687 mod/api.php:105 include/items.php:4928 +#: mod/contacts.php:441 mod/follow.php:110 mod/message.php:216 +#: mod/settings.php:1107 mod/settings.php:1113 mod/settings.php:1121 +#: mod/settings.php:1125 mod/settings.php:1130 mod/settings.php:1136 +#: mod/settings.php:1142 mod/settings.php:1148 mod/settings.php:1174 +#: mod/settings.php:1175 mod/settings.php:1176 mod/settings.php:1177 +#: mod/settings.php:1178 mod/dfrn_request.php:855 mod/register.php:238 +#: mod/suggest.php:29 mod/profiles.php:636 mod/profiles.php:639 +#: mod/profiles.php:665 mod/api.php:105 include/items.php:1834 msgid "Yes" msgstr "" -#: mod/contacts.php:459 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121 +#: mod/contacts.php:444 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121 #: mod/videos.php:131 mod/message.php:219 mod/fbrowser.php:93 -#: mod/fbrowser.php:128 mod/settings.php:660 mod/settings.php:686 -#: mod/dfrn_request.php:871 mod/suggest.php:32 mod/editpost.php:148 +#: mod/fbrowser.php:128 mod/settings.php:661 mod/settings.php:687 +#: mod/dfrn_request.php:869 mod/suggest.php:32 mod/editpost.php:148 #: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1220 -#: include/items.php:4931 +#: include/items.php:1837 msgid "Cancel" msgstr "" -#: mod/contacts.php:471 +#: mod/contacts.php:456 msgid "Contact has been removed." msgstr "" -#: mod/contacts.php:512 +#: mod/contacts.php:497 #, php-format msgid "You are mutual friends with %s" msgstr "" -#: mod/contacts.php:516 +#: mod/contacts.php:501 #, php-format msgid "You are sharing with %s" msgstr "" -#: mod/contacts.php:521 +#: mod/contacts.php:506 #, php-format msgid "%s is sharing with you" msgstr "" -#: mod/contacts.php:541 +#: mod/contacts.php:526 msgid "Private communications are not available for this contact." msgstr "" -#: mod/contacts.php:544 mod/admin.php:822 +#: mod/contacts.php:529 mod/admin.php:838 msgid "Never" msgstr "" -#: mod/contacts.php:548 +#: mod/contacts.php:533 msgid "(Update was successful)" msgstr "" -#: mod/contacts.php:548 +#: mod/contacts.php:533 msgid "(Update was not successful)" msgstr "" -#: mod/contacts.php:550 +#: mod/contacts.php:535 mod/contacts.php:972 msgid "Suggest friends" msgstr "" -#: mod/contacts.php:554 +#: mod/contacts.php:539 #, php-format msgid "Network type: %s" msgstr "" -#: mod/contacts.php:567 +#: mod/contacts.php:552 msgid "Communications lost with this contact!" msgstr "" -#: mod/contacts.php:570 +#: mod/contacts.php:555 msgid "Fetch further information for feeds" msgstr "" -#: mod/contacts.php:571 mod/admin.php:831 +#: mod/contacts.php:556 mod/admin.php:847 msgid "Disabled" msgstr "" -#: mod/contacts.php:571 +#: mod/contacts.php:556 msgid "Fetch information" msgstr "" -#: mod/contacts.php:571 +#: mod/contacts.php:556 msgid "Fetch information and keywords" msgstr "" -#: mod/contacts.php:587 mod/manage.php:143 mod/fsuggest.php:107 -#: mod/message.php:342 mod/message.php:525 mod/crepair.php:196 +#: mod/contacts.php:575 mod/manage.php:143 mod/fsuggest.php:107 +#: mod/message.php:342 mod/message.php:525 mod/crepair.php:179 #: mod/events.php:574 mod/content.php:712 mod/install.php:261 -#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:696 -#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140 mod/photos.php:1137 -#: mod/photos.php:1261 mod/photos.php:1579 mod/photos.php:1630 -#: mod/photos.php:1678 mod/photos.php:1766 object/Item.php:710 +#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:674 +#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140 mod/photos.php:1123 +#: mod/photos.php:1247 mod/photos.php:1565 mod/photos.php:1616 +#: mod/photos.php:1664 mod/photos.php:1752 object/Item.php:710 #: view/theme/cleanzero/config.php:80 view/theme/dispy/config.php:70 #: view/theme/quattro/config.php:64 view/theme/diabook/config.php:148 #: view/theme/diabook/theme.php:633 view/theme/vier/config.php:107 @@ -200,306 +200,316 @@ msgstr "" msgid "Submit" msgstr "" -#: mod/contacts.php:588 +#: mod/contacts.php:576 msgid "Profile Visibility" msgstr "" -#: mod/contacts.php:589 +#: mod/contacts.php:577 #, php-format msgid "" "Please choose the profile you would like to display to %s when viewing your " "profile securely." msgstr "" -#: mod/contacts.php:590 +#: mod/contacts.php:578 msgid "Contact Information / Notes" msgstr "" -#: mod/contacts.php:591 +#: mod/contacts.php:579 msgid "Edit contact notes" msgstr "" -#: mod/contacts.php:596 mod/contacts.php:952 mod/viewcontacts.php:97 +#: mod/contacts.php:584 mod/contacts.php:938 mod/viewcontacts.php:97 #: mod/nogroup.php:41 #, php-format msgid "Visit %s's profile [%s]" msgstr "" -#: mod/contacts.php:597 +#: mod/contacts.php:585 msgid "Block/Unblock contact" msgstr "" -#: mod/contacts.php:598 +#: mod/contacts.php:586 msgid "Ignore contact" msgstr "" -#: mod/contacts.php:599 +#: mod/contacts.php:587 msgid "Repair URL settings" msgstr "" -#: mod/contacts.php:600 +#: mod/contacts.php:588 msgid "View conversations" msgstr "" -#: mod/contacts.php:602 -msgid "Delete contact" -msgstr "" - -#: mod/contacts.php:606 +#: mod/contacts.php:594 msgid "Last update:" msgstr "" -#: mod/contacts.php:608 +#: mod/contacts.php:596 msgid "Update public posts" msgstr "" -#: mod/contacts.php:610 +#: mod/contacts.php:598 mod/contacts.php:982 msgid "Update now" msgstr "" -#: mod/contacts.php:612 mod/follow.php:103 mod/dirfind.php:196 +#: mod/contacts.php:600 mod/follow.php:103 mod/dirfind.php:196 #: mod/allfriends.php:65 mod/match.php:71 mod/suggest.php:82 -#: include/contact_widgets.php:32 include/Contact.php:297 +#: include/contact_widgets.php:32 include/Contact.php:299 #: include/conversation.php:924 msgid "Connect/Follow" msgstr "" -#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 -#: mod/admin.php:1312 +#: mod/contacts.php:603 mod/contacts.php:798 mod/contacts.php:991 +#: mod/admin.php:1334 msgid "Unblock" msgstr "" -#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 -#: mod/admin.php:1311 +#: mod/contacts.php:603 mod/contacts.php:798 mod/contacts.php:991 +#: mod/admin.php:1333 msgid "Block" msgstr "" -#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 +#: mod/contacts.php:604 mod/contacts.php:799 mod/contacts.php:999 msgid "Unignore" msgstr "" -#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 +#: mod/contacts.php:604 mod/contacts.php:799 mod/contacts.php:999 #: mod/notifications.php:54 mod/notifications.php:179 mod/notifications.php:259 msgid "Ignore" msgstr "" -#: mod/contacts.php:619 +#: mod/contacts.php:607 msgid "Currently blocked" msgstr "" -#: mod/contacts.php:620 +#: mod/contacts.php:608 msgid "Currently ignored" msgstr "" -#: mod/contacts.php:621 +#: mod/contacts.php:609 msgid "Currently archived" msgstr "" -#: mod/contacts.php:622 mod/notifications.php:172 mod/notifications.php:251 +#: mod/contacts.php:610 mod/notifications.php:172 mod/notifications.php:251 msgid "Hide this contact from others" msgstr "" -#: mod/contacts.php:622 +#: mod/contacts.php:610 msgid "" "Replies/likes to your public posts may still be visible" msgstr "" -#: mod/contacts.php:623 +#: mod/contacts.php:611 msgid "Notification for new posts" msgstr "" -#: mod/contacts.php:623 +#: mod/contacts.php:611 msgid "Send a notification of every new post of this contact" msgstr "" -#: mod/contacts.php:626 +#: mod/contacts.php:614 msgid "Blacklisted keywords" msgstr "" -#: mod/contacts.php:626 +#: mod/contacts.php:614 msgid "" "Comma separated list of keywords that should not be converted to hashtags, " "when \"Fetch information and keywords\" is selected" msgstr "" -#: mod/contacts.php:633 mod/follow.php:126 mod/notifications.php:255 +#: mod/contacts.php:621 mod/follow.php:126 mod/notifications.php:255 msgid "Profile URL" msgstr "" -#: mod/contacts.php:636 mod/notifications.php:244 mod/events.php:566 -#: mod/directory.php:145 include/identity.php:308 include/bb2diaspora.php:170 +#: mod/contacts.php:624 mod/notifications.php:244 mod/events.php:566 +#: mod/directory.php:145 include/identity.php:309 include/bb2diaspora.php:170 #: include/event.php:36 include/event.php:60 msgid "Location:" msgstr "" -#: mod/contacts.php:638 mod/notifications.php:246 mod/directory.php:153 -#: include/identity.php:317 include/identity.php:631 +#: mod/contacts.php:626 mod/notifications.php:246 mod/directory.php:153 +#: include/identity.php:318 include/identity.php:632 msgid "About:" msgstr "" -#: mod/contacts.php:640 mod/follow.php:134 mod/notifications.php:248 -#: include/identity.php:625 +#: mod/contacts.php:628 mod/follow.php:134 mod/notifications.php:248 +#: include/identity.php:626 msgid "Tags:" msgstr "" -#: mod/contacts.php:685 +#: mod/contacts.php:629 +msgid "Actions" +msgstr "" + +#: mod/contacts.php:631 mod/contacts.php:825 include/identity.php:687 +#: include/nav.php:75 +msgid "Status" +msgstr "" + +#: mod/contacts.php:632 +msgid "Contact Settings" +msgstr "" + +#: mod/contacts.php:677 msgid "Suggestions" msgstr "" -#: mod/contacts.php:688 +#: mod/contacts.php:680 msgid "Suggest potential friends" msgstr "" -#: mod/contacts.php:693 mod/group.php:192 +#: mod/contacts.php:685 mod/group.php:192 msgid "All Contacts" msgstr "" -#: mod/contacts.php:696 +#: mod/contacts.php:688 msgid "Show all contacts" msgstr "" -#: mod/contacts.php:701 +#: mod/contacts.php:693 msgid "Unblocked" msgstr "" -#: mod/contacts.php:704 +#: mod/contacts.php:696 msgid "Only show unblocked contacts" msgstr "" -#: mod/contacts.php:710 +#: mod/contacts.php:702 msgid "Blocked" msgstr "" -#: mod/contacts.php:713 +#: mod/contacts.php:705 msgid "Only show blocked contacts" msgstr "" -#: mod/contacts.php:719 +#: mod/contacts.php:711 msgid "Ignored" msgstr "" -#: mod/contacts.php:722 +#: mod/contacts.php:714 msgid "Only show ignored contacts" msgstr "" -#: mod/contacts.php:728 +#: mod/contacts.php:720 msgid "Archived" msgstr "" -#: mod/contacts.php:731 +#: mod/contacts.php:723 msgid "Only show archived contacts" msgstr "" -#: mod/contacts.php:737 +#: mod/contacts.php:729 msgid "Hidden" msgstr "" -#: mod/contacts.php:740 +#: mod/contacts.php:732 msgid "Only show hidden contacts" msgstr "" -#: mod/contacts.php:793 mod/contacts.php:841 mod/viewcontacts.php:116 -#: include/identity.php:741 include/identity.php:744 include/text.php:1012 +#: mod/contacts.php:785 mod/contacts.php:845 mod/viewcontacts.php:116 +#: include/identity.php:742 include/identity.php:745 include/text.php:983 #: include/nav.php:123 include/nav.php:187 view/theme/diabook/theme.php:125 msgid "Contacts" msgstr "" -#: mod/contacts.php:797 +#: mod/contacts.php:789 msgid "Search your contacts" msgstr "" -#: mod/contacts.php:798 +#: mod/contacts.php:790 msgid "Finding: " msgstr "" -#: mod/contacts.php:799 mod/directory.php:210 include/contact_widgets.php:34 +#: mod/contacts.php:791 mod/directory.php:210 include/contact_widgets.php:34 msgid "Find" msgstr "" -#: mod/contacts.php:805 mod/settings.php:156 mod/settings.php:685 +#: mod/contacts.php:797 mod/settings.php:156 mod/settings.php:686 msgid "Update" msgstr "" -#: mod/contacts.php:808 mod/contacts.php:879 +#: mod/contacts.php:800 mod/contacts.php:1007 msgid "Archive" msgstr "" -#: mod/contacts.php:808 mod/contacts.php:879 +#: mod/contacts.php:800 mod/contacts.php:1007 msgid "Unarchive" msgstr "" -#: mod/contacts.php:809 mod/group.php:171 mod/admin.php:1310 -#: mod/content.php:440 mod/content.php:743 mod/settings.php:722 -#: mod/photos.php:1723 object/Item.php:134 include/conversation.php:635 +#: mod/contacts.php:801 mod/contacts.php:1015 mod/group.php:171 +#: mod/admin.php:1332 mod/content.php:440 mod/content.php:743 +#: mod/settings.php:723 mod/photos.php:1709 object/Item.php:134 +#: include/conversation.php:635 msgid "Delete" msgstr "" -#: mod/contacts.php:822 include/identity.php:686 include/nav.php:75 -msgid "Status" -msgstr "" - -#: mod/contacts.php:825 mod/follow.php:143 include/identity.php:689 +#: mod/contacts.php:828 mod/follow.php:143 include/identity.php:690 msgid "Status Messages and Posts" msgstr "" -#: mod/contacts.php:830 mod/profperm.php:104 mod/newmember.php:32 -#: include/identity.php:579 include/identity.php:665 include/identity.php:694 +#: mod/contacts.php:833 mod/profperm.php:104 mod/newmember.php:32 +#: include/identity.php:580 include/identity.php:666 include/identity.php:695 #: include/nav.php:76 view/theme/diabook/theme.php:124 msgid "Profile" msgstr "" -#: mod/contacts.php:833 include/identity.php:697 +#: mod/contacts.php:836 include/identity.php:698 msgid "Profile Details" msgstr "" -#: mod/contacts.php:844 +#: mod/contacts.php:848 msgid "View all contacts" msgstr "" -#: mod/contacts.php:850 mod/common.php:134 +#: mod/contacts.php:855 mod/common.php:134 msgid "Common Friends" msgstr "" -#: mod/contacts.php:853 +#: mod/contacts.php:858 msgid "View all common friends" msgstr "" -#: mod/contacts.php:857 -msgid "Repair" +#: mod/contacts.php:862 mod/admin.php:909 +msgid "Advanced" msgstr "" -#: mod/contacts.php:860 +#: mod/contacts.php:865 msgid "Advanced Contact Settings" msgstr "" -#: mod/contacts.php:868 -msgid "Toggle Blocked status" -msgstr "" - -#: mod/contacts.php:875 -msgid "Toggle Ignored status" -msgstr "" - -#: mod/contacts.php:882 -msgid "Toggle Archive status" -msgstr "" - -#: mod/contacts.php:924 +#: mod/contacts.php:910 msgid "Mutual Friendship" msgstr "" -#: mod/contacts.php:928 +#: mod/contacts.php:914 msgid "is a fan of yours" msgstr "" -#: mod/contacts.php:932 +#: mod/contacts.php:918 msgid "you are a fan of" msgstr "" -#: mod/contacts.php:953 mod/nogroup.php:42 +#: mod/contacts.php:939 mod/nogroup.php:42 msgid "Edit contact" msgstr "" +#: mod/contacts.php:993 +msgid "Toggle Blocked status" +msgstr "" + +#: mod/contacts.php:1001 +msgid "Toggle Ignored status" +msgstr "" + +#: mod/contacts.php:1009 +msgid "Toggle Archive status" +msgstr "" + +#: mod/contacts.php:1017 +msgid "Delete contact" +msgstr "" + #: mod/hcard.php:10 msgid "No profile" msgstr "" @@ -522,7 +532,7 @@ msgstr "" msgid "Post successful." msgstr "" -#: mod/profperm.php:19 mod/group.php:72 index.php:382 +#: mod/profperm.php:19 mod/group.php:72 index.php:383 msgid "Permission denied" msgstr "" @@ -546,23 +556,23 @@ msgstr "" msgid "All Contacts (with secure profile access)" msgstr "" -#: mod/display.php:82 mod/display.php:291 mod/display.php:513 -#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1365 mod/admin.php:1599 -#: mod/notice.php:15 include/items.php:4887 +#: mod/display.php:82 mod/display.php:298 mod/display.php:517 +#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1387 mod/admin.php:1621 +#: mod/notice.php:15 include/items.php:1793 msgid "Item not found." msgstr "" -#: mod/display.php:220 mod/videos.php:197 mod/viewcontacts.php:35 -#: mod/community.php:22 mod/dfrn_request.php:786 mod/search.php:93 -#: mod/search.php:99 mod/directory.php:37 mod/photos.php:976 +#: mod/display.php:227 mod/videos.php:197 mod/viewcontacts.php:35 +#: mod/community.php:22 mod/dfrn_request.php:784 mod/search.php:93 +#: mod/search.php:99 mod/directory.php:37 mod/photos.php:962 msgid "Public access denied." msgstr "" -#: mod/display.php:339 mod/profile.php:155 +#: mod/display.php:346 mod/profile.php:155 msgid "Access to this profile has been restricted." msgstr "" -#: mod/display.php:506 +#: mod/display.php:510 msgid "Item has been removed." msgstr "" @@ -597,7 +607,7 @@ msgid "" "join." msgstr "" -#: mod/newmember.php:22 mod/admin.php:1418 mod/admin.php:1676 +#: mod/newmember.php:22 mod/admin.php:1440 mod/admin.php:1698 #: mod/settings.php:109 include/nav.php:182 view/theme/diabook/theme.php:544 #: view/theme/diabook/theme.php:648 msgid "Settings" @@ -622,7 +632,7 @@ msgid "" "potential friends know exactly how to find you." msgstr "" -#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:709 +#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:687 msgid "Upload Profile Photo" msgstr "" @@ -705,7 +715,7 @@ msgid "" "hours." msgstr "" -#: mod/newmember.php:61 include/group.php:283 +#: mod/newmember.php:61 include/group.php:286 msgid "Groups" msgstr "" @@ -765,8 +775,8 @@ msgstr "" #: mod/profile_photo.php:74 mod/profile_photo.php:81 mod/profile_photo.php:88 #: mod/profile_photo.php:210 mod/profile_photo.php:302 #: mod/profile_photo.php:311 mod/photos.php:78 mod/photos.php:192 -#: mod/photos.php:775 mod/photos.php:1245 mod/photos.php:1268 -#: mod/photos.php:1862 include/user.php:345 include/user.php:352 +#: mod/photos.php:769 mod/photos.php:1231 mod/photos.php:1254 +#: mod/photos.php:1848 include/user.php:345 include/user.php:352 #: include/user.php:359 view/theme/diabook/theme.php:500 msgid "Profile Photos" msgstr "" @@ -787,12 +797,12 @@ msgstr "" msgid "Unable to process image" msgstr "" -#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:811 +#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:805 #, php-format msgid "Image exceeds size limit of %s" msgstr "" -#: mod/profile_photo.php:159 mod/wall_upload.php:183 mod/photos.php:851 +#: mod/profile_photo.php:159 mod/wall_upload.php:188 mod/photos.php:845 msgid "Unable to process image." msgstr "" @@ -836,13 +846,13 @@ msgstr "" msgid "Image uploaded successfully." msgstr "" -#: mod/profile_photo.php:307 mod/wall_upload.php:216 mod/photos.php:878 +#: mod/profile_photo.php:307 mod/wall_upload.php:221 mod/photos.php:872 msgid "Image upload failed." msgstr "" #: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 #: include/conversation.php:130 include/conversation.php:266 -#: include/text.php:2000 include/diaspora.php:2169 +#: include/text.php:1923 include/diaspora.php:2117 #: view/theme/diabook/theme.php:471 msgid "photo" msgstr "" @@ -850,7 +860,7 @@ msgstr "" #: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 #: include/like.php:334 include/conversation.php:125 #: include/conversation.php:134 include/conversation.php:261 -#: include/conversation.php:270 include/diaspora.php:2169 +#: include/conversation.php:270 include/diaspora.php:2117 #: view/theme/diabook/theme.php:466 view/theme/diabook/theme.php:475 msgid "status" msgstr "" @@ -920,11 +930,11 @@ msgstr "" msgid "- select -" msgstr "" -#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61 include/text.php:1004 +#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61 include/text.php:975 msgid "Save" msgstr "" -#: mod/follow.php:19 mod/dfrn_request.php:870 +#: mod/follow.php:19 mod/dfrn_request.php:868 msgid "Submit Request" msgstr "" @@ -944,30 +954,30 @@ msgstr "" msgid "The network type couldn't be detected. Contact can't be added." msgstr "" -#: mod/follow.php:109 mod/dfrn_request.php:856 +#: mod/follow.php:109 mod/dfrn_request.php:854 msgid "Please answer the following:" msgstr "" -#: mod/follow.php:110 mod/dfrn_request.php:857 +#: mod/follow.php:110 mod/dfrn_request.php:855 #, php-format msgid "Does %s know you?" msgstr "" -#: mod/follow.php:110 mod/settings.php:1103 mod/settings.php:1109 -#: mod/settings.php:1117 mod/settings.php:1121 mod/settings.php:1126 -#: mod/settings.php:1132 mod/settings.php:1138 mod/settings.php:1144 -#: mod/settings.php:1170 mod/settings.php:1171 mod/settings.php:1172 -#: mod/settings.php:1173 mod/settings.php:1174 mod/dfrn_request.php:857 -#: mod/register.php:239 mod/profiles.php:658 mod/profiles.php:662 -#: mod/profiles.php:687 mod/api.php:106 +#: mod/follow.php:110 mod/settings.php:1107 mod/settings.php:1113 +#: mod/settings.php:1121 mod/settings.php:1125 mod/settings.php:1130 +#: mod/settings.php:1136 mod/settings.php:1142 mod/settings.php:1148 +#: mod/settings.php:1174 mod/settings.php:1175 mod/settings.php:1176 +#: mod/settings.php:1177 mod/settings.php:1178 mod/dfrn_request.php:855 +#: mod/register.php:239 mod/profiles.php:636 mod/profiles.php:640 +#: mod/profiles.php:665 mod/api.php:106 msgid "No" msgstr "" -#: mod/follow.php:111 mod/dfrn_request.php:861 +#: mod/follow.php:111 mod/dfrn_request.php:859 msgid "Add a personal note:" msgstr "" -#: mod/follow.php:117 mod/dfrn_request.php:867 +#: mod/follow.php:117 mod/dfrn_request.php:865 msgid "Your Identity Address:" msgstr "" @@ -979,17 +989,17 @@ msgstr "" msgid "Unable to locate original post." msgstr "" -#: mod/item.php:329 +#: mod/item.php:332 msgid "Empty post discarded." msgstr "" -#: mod/item.php:467 mod/wall_upload.php:213 mod/wall_upload.php:227 -#: mod/wall_upload.php:234 include/Photo.php:958 include/Photo.php:973 -#: include/Photo.php:980 include/Photo.php:1002 include/message.php:145 +#: mod/item.php:470 mod/wall_upload.php:218 mod/wall_upload.php:232 +#: mod/wall_upload.php:239 include/Photo.php:994 include/Photo.php:1009 +#: include/Photo.php:1016 include/Photo.php:1038 include/message.php:145 msgid "Wall Photos" msgstr "" -#: mod/item.php:842 +#: mod/item.php:845 msgid "System error. Post not saved." msgstr "" @@ -1039,7 +1049,7 @@ msgstr "" msgid "Create a group of contacts/friends." msgstr "" -#: mod/group.php:94 mod/group.php:178 include/group.php:289 +#: mod/group.php:94 mod/group.php:178 include/group.php:292 msgid "Group Name: " msgstr "" @@ -1063,7 +1073,7 @@ msgstr "" msgid "Group is empty" msgstr "" -#: mod/apps.php:7 index.php:226 +#: mod/apps.php:7 index.php:227 msgid "You must be logged in to use addons. " msgstr "" @@ -1076,12 +1086,12 @@ msgid "No installed applications." msgstr "" #: mod/dfrn_confirm.php:64 mod/profiles.php:18 mod/profiles.php:133 -#: mod/profiles.php:179 mod/profiles.php:627 +#: mod/profiles.php:179 mod/profiles.php:605 msgid "Profile not found." msgstr "" #: mod/dfrn_confirm.php:120 mod/fsuggest.php:20 mod/fsuggest.php:92 -#: mod/crepair.php:131 +#: mod/crepair.php:114 msgid "Contact not found." msgstr "" @@ -1115,57 +1125,57 @@ msgstr "" msgid "Introduction failed or was revoked." msgstr "" -#: mod/dfrn_confirm.php:430 +#: mod/dfrn_confirm.php:413 msgid "Unable to set contact photo." msgstr "" -#: mod/dfrn_confirm.php:487 include/conversation.php:185 -#: include/diaspora.php:637 +#: mod/dfrn_confirm.php:470 include/conversation.php:185 +#: include/diaspora.php:638 #, php-format msgid "%1$s is now friends with %2$s" msgstr "" -#: mod/dfrn_confirm.php:572 +#: mod/dfrn_confirm.php:552 #, php-format msgid "No user record found for '%s' " msgstr "" -#: mod/dfrn_confirm.php:582 +#: mod/dfrn_confirm.php:562 msgid "Our site encryption key is apparently messed up." msgstr "" -#: mod/dfrn_confirm.php:593 +#: mod/dfrn_confirm.php:573 msgid "Empty site URL was provided or URL could not be decrypted by us." msgstr "" -#: mod/dfrn_confirm.php:614 +#: mod/dfrn_confirm.php:594 msgid "Contact record was not found for you on our site." msgstr "" -#: mod/dfrn_confirm.php:628 +#: mod/dfrn_confirm.php:608 #, php-format msgid "Site public key not available in contact record for URL %s." msgstr "" -#: mod/dfrn_confirm.php:648 +#: mod/dfrn_confirm.php:628 msgid "" "The ID provided by your system is a duplicate on our system. It should work " "if you try again." msgstr "" -#: mod/dfrn_confirm.php:659 +#: mod/dfrn_confirm.php:639 msgid "Unable to set your contact credentials on our system." msgstr "" -#: mod/dfrn_confirm.php:726 +#: mod/dfrn_confirm.php:698 msgid "Unable to update your contact profile details on our system" msgstr "" -#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:741 include/items.php:4299 +#: mod/dfrn_confirm.php:725 mod/dfrn_request.php:739 include/items.php:1434 msgid "[Name Withheld]" msgstr "" -#: mod/dfrn_confirm.php:798 +#: mod/dfrn_confirm.php:770 #, php-format msgid "%1$s has joined %2$s" msgstr "" @@ -1190,15 +1200,15 @@ msgstr "" msgid "No videos selected" msgstr "" -#: mod/videos.php:308 mod/photos.php:1087 +#: mod/videos.php:308 mod/photos.php:1073 msgid "Access to this item is restricted." msgstr "" -#: mod/videos.php:383 include/text.php:1472 +#: mod/videos.php:383 include/text.php:1443 msgid "View Video" msgstr "" -#: mod/videos.php:390 mod/photos.php:1890 +#: mod/videos.php:390 mod/photos.php:1876 msgid "View Album" msgstr "" @@ -1230,7 +1240,7 @@ msgstr "" #: mod/wall_upload.php:20 mod/wall_upload.php:33 mod/wall_upload.php:86 #: mod/wall_upload.php:122 mod/wall_upload.php:125 mod/wall_attach.php:17 -#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1781 +#: mod/wall_attach.php:25 mod/wall_attach.php:76 msgid "Invalid request." msgstr "" @@ -1288,7 +1298,7 @@ msgid "" "Password reset failed." msgstr "" -#: mod/lostpass.php:109 boot.php:1444 +#: mod/lostpass.php:109 boot.php:1534 msgid "Password Reset" msgstr "" @@ -1364,15 +1374,15 @@ msgstr "" msgid "Reset" msgstr "" -#: mod/ping.php:265 +#: mod/ping.php:267 msgid "{0} wants to be your friend" msgstr "" -#: mod/ping.php:280 +#: mod/ping.php:282 msgid "{0} sent you a message" msgstr "" -#: mod/ping.php:295 +#: mod/ping.php:297 msgid "{0} requested registration" msgstr "" @@ -1392,7 +1402,7 @@ msgstr "" msgid "System" msgstr "" -#: mod/notifications.php:87 mod/admin.php:390 include/nav.php:154 +#: mod/notifications.php:87 mod/admin.php:399 include/nav.php:154 msgid "Network" msgstr "" @@ -1438,7 +1448,7 @@ msgstr "" msgid "if applicable" msgstr "" -#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1308 +#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1330 msgid "Approve" msgstr "" @@ -1488,8 +1498,8 @@ msgstr "" msgid "New Follower" msgstr "" -#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:310 -#: include/identity.php:590 +#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:311 +#: include/identity.php:591 msgid "Gender:" msgstr "" @@ -1538,11 +1548,11 @@ msgstr "" msgid "Network Notifications" msgstr "" -#: mod/notifications.php:385 mod/notify.php:72 +#: mod/notifications.php:385 mod/notify.php:60 msgid "No more system notifications." msgstr "" -#: mod/notifications.php:389 mod/notify.php:76 +#: mod/notifications.php:389 mod/notify.php:64 msgid "System Notifications" msgstr "" @@ -1693,7 +1703,7 @@ msgstr "" #: mod/message.php:341 mod/message.php:526 mod/content.php:501 #: mod/content.php:885 mod/wallmessage.php:156 mod/editpost.php:124 -#: mod/photos.php:1610 object/Item.php:396 include/conversation.php:713 +#: mod/photos.php:1596 object/Item.php:396 include/conversation.php:713 #: include/conversation.php:1201 msgid "Please wait" msgstr "" @@ -1755,98 +1765,98 @@ msgstr[1] "" msgid "[Embedded content - reload page to view]" msgstr "" -#: mod/crepair.php:104 +#: mod/crepair.php:87 msgid "Contact settings applied." msgstr "" -#: mod/crepair.php:106 +#: mod/crepair.php:89 msgid "Contact update failed." msgstr "" -#: mod/crepair.php:137 +#: mod/crepair.php:120 msgid "" "WARNING: This is highly advanced and if you enter incorrect " "information your communications with this contact may stop working." msgstr "" -#: mod/crepair.php:138 +#: mod/crepair.php:121 msgid "" "Please use your browser 'Back' button now if you are " "uncertain what to do on this page." msgstr "" -#: mod/crepair.php:151 mod/crepair.php:153 +#: mod/crepair.php:134 mod/crepair.php:136 msgid "No mirroring" msgstr "" -#: mod/crepair.php:151 +#: mod/crepair.php:134 msgid "Mirror as forwarded posting" msgstr "" -#: mod/crepair.php:151 mod/crepair.php:153 +#: mod/crepair.php:134 mod/crepair.php:136 msgid "Mirror as my own posting" msgstr "" -#: mod/crepair.php:167 +#: mod/crepair.php:150 msgid "Return to contact editor" msgstr "" -#: mod/crepair.php:169 +#: mod/crepair.php:152 msgid "Refetch contact data" msgstr "" -#: mod/crepair.php:170 mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 -#: mod/admin.php:1332 mod/settings.php:661 mod/settings.php:687 +#: mod/crepair.php:153 mod/admin.php:1328 mod/admin.php:1340 mod/admin.php:1341 +#: mod/admin.php:1354 mod/settings.php:662 mod/settings.php:688 msgid "Name" msgstr "" -#: mod/crepair.php:171 +#: mod/crepair.php:154 msgid "Account Nickname" msgstr "" -#: mod/crepair.php:172 +#: mod/crepair.php:155 msgid "@Tagname - overrides Name/Nickname" msgstr "" -#: mod/crepair.php:173 +#: mod/crepair.php:156 msgid "Account URL" msgstr "" -#: mod/crepair.php:174 +#: mod/crepair.php:157 msgid "Friend Request URL" msgstr "" -#: mod/crepair.php:175 +#: mod/crepair.php:158 msgid "Friend Confirm URL" msgstr "" -#: mod/crepair.php:176 +#: mod/crepair.php:159 msgid "Notification Endpoint URL" msgstr "" -#: mod/crepair.php:177 +#: mod/crepair.php:160 msgid "Poll/Feed URL" msgstr "" -#: mod/crepair.php:178 +#: mod/crepair.php:161 msgid "New photo from this URL" msgstr "" -#: mod/crepair.php:179 +#: mod/crepair.php:162 msgid "Remote Self" msgstr "" -#: mod/crepair.php:182 +#: mod/crepair.php:165 msgid "Mirror postings from this contact" msgstr "" -#: mod/crepair.php:184 +#: mod/crepair.php:167 msgid "" "Mark this contact as remote_self, this will cause friendica to repost new " "entries from this contact." msgstr "" -#: mod/bookmarklet.php:12 boot.php:1430 include/nav.php:91 +#: mod/bookmarklet.php:12 boot.php:1520 include/nav.php:91 msgid "Login" msgstr "" @@ -1864,8 +1874,8 @@ msgid "Connect" msgstr "" #: mod/dirfind.php:195 mod/allfriends.php:64 mod/match.php:70 -#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:283 -#: include/Contact.php:296 include/Contact.php:338 include/conversation.php:912 +#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:285 +#: include/Contact.php:298 include/Contact.php:340 include/conversation.php:912 #: include/conversation.php:926 msgid "View Profile" msgstr "" @@ -1879,14 +1889,14 @@ msgstr "" msgid "No matches" msgstr "" -#: mod/fbrowser.php:32 include/identity.php:702 include/nav.php:77 +#: mod/fbrowser.php:32 include/identity.php:703 include/nav.php:77 #: view/theme/diabook/theme.php:126 msgid "Photos" msgstr "" #: mod/fbrowser.php:41 mod/fbrowser.php:62 mod/photos.php:62 mod/photos.php:192 -#: mod/photos.php:1119 mod/photos.php:1245 mod/photos.php:1268 -#: mod/photos.php:1838 mod/photos.php:1850 view/theme/diabook/theme.php:499 +#: mod/photos.php:1105 mod/photos.php:1231 mod/photos.php:1254 +#: mod/photos.php:1824 mod/photos.php:1836 view/theme/diabook/theme.php:499 msgid "Contact Photos" msgstr "" @@ -1902,19 +1912,19 @@ msgstr "" msgid "Theme settings updated." msgstr "" -#: mod/admin.php:156 mod/admin.php:888 +#: mod/admin.php:156 mod/admin.php:904 msgid "Site" msgstr "" -#: mod/admin.php:157 mod/admin.php:832 mod/admin.php:1301 mod/admin.php:1316 +#: mod/admin.php:157 mod/admin.php:848 mod/admin.php:1323 mod/admin.php:1338 msgid "Users" msgstr "" -#: mod/admin.php:158 mod/admin.php:1416 mod/admin.php:1476 mod/settings.php:72 +#: mod/admin.php:158 mod/admin.php:1438 mod/admin.php:1498 mod/settings.php:72 msgid "Plugins" msgstr "" -#: mod/admin.php:159 mod/admin.php:1674 mod/admin.php:1724 +#: mod/admin.php:159 mod/admin.php:1696 mod/admin.php:1746 msgid "Themes" msgstr "" @@ -1926,19 +1936,19 @@ msgstr "" msgid "DB updates" msgstr "" -#: mod/admin.php:162 mod/admin.php:385 +#: mod/admin.php:162 mod/admin.php:394 msgid "Inspect Queue" msgstr "" -#: mod/admin.php:163 mod/admin.php:354 +#: mod/admin.php:163 mod/admin.php:363 msgid "Federation Statistics" msgstr "" -#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792 +#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1814 msgid "Logs" msgstr "" -#: mod/admin.php:178 mod/admin.php:1859 +#: mod/admin.php:178 mod/admin.php:1881 msgid "View Logs" msgstr "" @@ -1966,739 +1976,750 @@ msgstr "" msgid "User registrations waiting for confirmation" msgstr "" -#: mod/admin.php:347 +#: mod/admin.php:356 msgid "" "This page offers you some numbers to the known part of the federated social " "network your Friendica node is part of. These numbers are not complete but " "only reflect the part of the network your node is aware of." msgstr "" -#: mod/admin.php:348 +#: mod/admin.php:357 msgid "" "The Auto Discovered Contact Directory feature is not enabled, it " "will improve the data displayed here." msgstr "" -#: mod/admin.php:353 mod/admin.php:384 mod/admin.php:441 mod/admin.php:887 -#: mod/admin.php:1300 mod/admin.php:1415 mod/admin.php:1475 mod/admin.php:1673 -#: mod/admin.php:1723 mod/admin.php:1791 mod/admin.php:1858 +#: mod/admin.php:362 mod/admin.php:393 mod/admin.php:450 mod/admin.php:903 +#: mod/admin.php:1322 mod/admin.php:1437 mod/admin.php:1497 mod/admin.php:1695 +#: mod/admin.php:1745 mod/admin.php:1813 mod/admin.php:1880 msgid "Administration" msgstr "" -#: mod/admin.php:360 +#: mod/admin.php:369 #, php-format msgid "Currently this node is aware of %d nodes from the following platforms:" msgstr "" -#: mod/admin.php:387 +#: mod/admin.php:396 msgid "ID" msgstr "" -#: mod/admin.php:388 +#: mod/admin.php:397 msgid "Recipient Name" msgstr "" -#: mod/admin.php:389 +#: mod/admin.php:398 msgid "Recipient Profile" msgstr "" -#: mod/admin.php:391 +#: mod/admin.php:400 msgid "Created" msgstr "" -#: mod/admin.php:392 +#: mod/admin.php:401 msgid "Last Tried" msgstr "" -#: mod/admin.php:393 +#: mod/admin.php:402 msgid "" "This page lists the content of the queue for outgoing postings. These are " "postings the initial delivery failed for. They will be resend later and " "eventually deleted if the delivery fails permanently." msgstr "" -#: mod/admin.php:412 mod/admin.php:1254 +#: mod/admin.php:421 mod/admin.php:1276 msgid "Normal Account" msgstr "" -#: mod/admin.php:413 mod/admin.php:1255 +#: mod/admin.php:422 mod/admin.php:1277 msgid "Soapbox Account" msgstr "" -#: mod/admin.php:414 mod/admin.php:1256 +#: mod/admin.php:423 mod/admin.php:1278 msgid "Community/Celebrity Account" msgstr "" -#: mod/admin.php:415 mod/admin.php:1257 +#: mod/admin.php:424 mod/admin.php:1279 msgid "Automatic Friend Account" msgstr "" -#: mod/admin.php:416 +#: mod/admin.php:425 msgid "Blog Account" msgstr "" -#: mod/admin.php:417 +#: mod/admin.php:426 msgid "Private Forum" msgstr "" -#: mod/admin.php:436 +#: mod/admin.php:445 msgid "Message queues" msgstr "" -#: mod/admin.php:442 +#: mod/admin.php:451 msgid "Summary" msgstr "" -#: mod/admin.php:444 +#: mod/admin.php:453 msgid "Registered users" msgstr "" -#: mod/admin.php:446 +#: mod/admin.php:455 msgid "Pending registrations" msgstr "" -#: mod/admin.php:447 +#: mod/admin.php:456 msgid "Version" msgstr "" -#: mod/admin.php:452 +#: mod/admin.php:461 msgid "Active plugins" msgstr "" -#: mod/admin.php:475 +#: mod/admin.php:484 msgid "Can not parse base url. Must have at least ://" msgstr "" -#: mod/admin.php:760 +#: mod/admin.php:776 msgid "RINO2 needs mcrypt php extension to work." msgstr "" -#: mod/admin.php:768 +#: mod/admin.php:784 msgid "Site settings updated." msgstr "" -#: mod/admin.php:796 mod/settings.php:912 +#: mod/admin.php:812 mod/settings.php:916 msgid "No special theme for mobile devices" msgstr "" -#: mod/admin.php:815 +#: mod/admin.php:831 msgid "No community page" msgstr "" -#: mod/admin.php:816 +#: mod/admin.php:832 msgid "Public postings from users of this site" msgstr "" -#: mod/admin.php:817 +#: mod/admin.php:833 msgid "Global community page" msgstr "" -#: mod/admin.php:823 +#: mod/admin.php:839 msgid "At post arrival" msgstr "" -#: mod/admin.php:824 include/contact_selectors.php:56 +#: mod/admin.php:840 include/contact_selectors.php:56 msgid "Frequently" msgstr "" -#: mod/admin.php:825 include/contact_selectors.php:57 +#: mod/admin.php:841 include/contact_selectors.php:57 msgid "Hourly" msgstr "" -#: mod/admin.php:826 include/contact_selectors.php:58 +#: mod/admin.php:842 include/contact_selectors.php:58 msgid "Twice daily" msgstr "" -#: mod/admin.php:827 include/contact_selectors.php:59 +#: mod/admin.php:843 include/contact_selectors.php:59 msgid "Daily" msgstr "" -#: mod/admin.php:833 +#: mod/admin.php:849 msgid "Users, Global Contacts" msgstr "" -#: mod/admin.php:834 +#: mod/admin.php:850 msgid "Users, Global Contacts/fallback" msgstr "" -#: mod/admin.php:838 +#: mod/admin.php:854 msgid "One month" msgstr "" -#: mod/admin.php:839 +#: mod/admin.php:855 msgid "Three months" msgstr "" -#: mod/admin.php:840 +#: mod/admin.php:856 msgid "Half a year" msgstr "" -#: mod/admin.php:841 +#: mod/admin.php:857 msgid "One year" msgstr "" -#: mod/admin.php:846 +#: mod/admin.php:862 msgid "Multi user instance" msgstr "" -#: mod/admin.php:869 +#: mod/admin.php:885 msgid "Closed" msgstr "" -#: mod/admin.php:870 +#: mod/admin.php:886 msgid "Requires approval" msgstr "" -#: mod/admin.php:871 +#: mod/admin.php:887 msgid "Open" msgstr "" -#: mod/admin.php:875 +#: mod/admin.php:891 msgid "No SSL policy, links will track page SSL state" msgstr "" -#: mod/admin.php:876 +#: mod/admin.php:892 msgid "Force all links to use SSL" msgstr "" -#: mod/admin.php:877 +#: mod/admin.php:893 msgid "Self-signed certificate, use SSL for local links only (discouraged)" msgstr "" -#: mod/admin.php:889 mod/admin.php:1477 mod/admin.php:1725 mod/admin.php:1793 -#: mod/admin.php:1942 mod/settings.php:659 mod/settings.php:769 -#: mod/settings.php:813 mod/settings.php:882 mod/settings.php:969 -#: mod/settings.php:1204 +#: mod/admin.php:905 mod/admin.php:1499 mod/admin.php:1747 mod/admin.php:1815 +#: mod/admin.php:1964 mod/settings.php:660 mod/settings.php:770 +#: mod/settings.php:817 mod/settings.php:886 mod/settings.php:973 +#: mod/settings.php:1208 msgid "Save Settings" msgstr "" -#: mod/admin.php:890 mod/register.php:263 +#: mod/admin.php:906 mod/register.php:263 msgid "Registration" msgstr "" -#: mod/admin.php:891 +#: mod/admin.php:907 msgid "File upload" msgstr "" -#: mod/admin.php:892 +#: mod/admin.php:908 msgid "Policies" msgstr "" -#: mod/admin.php:893 -msgid "Advanced" -msgstr "" - -#: mod/admin.php:894 +#: mod/admin.php:910 msgid "Auto Discovered Contact Directory" msgstr "" -#: mod/admin.php:895 +#: mod/admin.php:911 msgid "Performance" msgstr "" -#: mod/admin.php:896 +#: mod/admin.php:912 +msgid "Worker" +msgstr "" + +#: mod/admin.php:913 msgid "" "Relocate - WARNING: advanced function. Could make this server unreachable." msgstr "" -#: mod/admin.php:899 +#: mod/admin.php:916 msgid "Site name" msgstr "" -#: mod/admin.php:900 +#: mod/admin.php:917 msgid "Host name" msgstr "" -#: mod/admin.php:901 +#: mod/admin.php:918 msgid "Sender Email" msgstr "" -#: mod/admin.php:901 +#: mod/admin.php:918 msgid "" "The email address your server shall use to send notification emails from." msgstr "" -#: mod/admin.php:902 +#: mod/admin.php:919 msgid "Banner/Logo" msgstr "" -#: mod/admin.php:903 +#: mod/admin.php:920 msgid "Shortcut icon" msgstr "" -#: mod/admin.php:903 +#: mod/admin.php:920 msgid "Link to an icon that will be used for browsers." msgstr "" -#: mod/admin.php:904 +#: mod/admin.php:921 msgid "Touch icon" msgstr "" -#: mod/admin.php:904 +#: mod/admin.php:921 msgid "Link to an icon that will be used for tablets and mobiles." msgstr "" -#: mod/admin.php:905 +#: mod/admin.php:922 msgid "Additional Info" msgstr "" -#: mod/admin.php:905 +#: mod/admin.php:922 #, php-format msgid "" "For public servers: you can add additional information here that will be " "listed at %s/siteinfo." msgstr "" -#: mod/admin.php:906 +#: mod/admin.php:923 msgid "System language" msgstr "" -#: mod/admin.php:907 +#: mod/admin.php:924 msgid "System theme" msgstr "" -#: mod/admin.php:907 +#: mod/admin.php:924 msgid "" "Default system theme - may be over-ridden by user profiles - change theme settings" msgstr "" -#: mod/admin.php:908 +#: mod/admin.php:925 msgid "Mobile system theme" msgstr "" -#: mod/admin.php:908 +#: mod/admin.php:925 msgid "Theme for mobile devices" msgstr "" -#: mod/admin.php:909 +#: mod/admin.php:926 msgid "SSL link policy" msgstr "" -#: mod/admin.php:909 +#: mod/admin.php:926 msgid "Determines whether generated links should be forced to use SSL" msgstr "" -#: mod/admin.php:910 +#: mod/admin.php:927 msgid "Force SSL" msgstr "" -#: mod/admin.php:910 +#: mod/admin.php:927 msgid "" "Force all Non-SSL requests to SSL - Attention: on some systems it could lead " "to endless loops." msgstr "" -#: mod/admin.php:911 +#: mod/admin.php:928 msgid "Old style 'Share'" msgstr "" -#: mod/admin.php:911 +#: mod/admin.php:928 msgid "Deactivates the bbcode element 'share' for repeating items." msgstr "" -#: mod/admin.php:912 +#: mod/admin.php:929 msgid "Hide help entry from navigation menu" msgstr "" -#: mod/admin.php:912 +#: mod/admin.php:929 msgid "" "Hides the menu entry for the Help pages from the navigation menu. You can " "still access it calling /help directly." msgstr "" -#: mod/admin.php:913 +#: mod/admin.php:930 msgid "Single user instance" msgstr "" -#: mod/admin.php:913 +#: mod/admin.php:930 msgid "Make this instance multi-user or single-user for the named user" msgstr "" -#: mod/admin.php:914 +#: mod/admin.php:931 msgid "Maximum image size" msgstr "" -#: mod/admin.php:914 +#: mod/admin.php:931 msgid "" "Maximum size in bytes of uploaded images. Default is 0, which means no " "limits." msgstr "" -#: mod/admin.php:915 +#: mod/admin.php:932 msgid "Maximum image length" msgstr "" -#: mod/admin.php:915 +#: mod/admin.php:932 msgid "" "Maximum length in pixels of the longest side of uploaded images. Default is " "-1, which means no limits." msgstr "" -#: mod/admin.php:916 +#: mod/admin.php:933 msgid "JPEG image quality" msgstr "" -#: mod/admin.php:916 +#: mod/admin.php:933 msgid "" "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is " "100, which is full quality." msgstr "" -#: mod/admin.php:918 +#: mod/admin.php:935 msgid "Register policy" msgstr "" -#: mod/admin.php:919 +#: mod/admin.php:936 msgid "Maximum Daily Registrations" msgstr "" -#: mod/admin.php:919 +#: mod/admin.php:936 msgid "" "If registration is permitted above, this sets the maximum number of new user " "registrations to accept per day. If register is set to closed, this setting " "has no effect." msgstr "" -#: mod/admin.php:920 +#: mod/admin.php:937 msgid "Register text" msgstr "" -#: mod/admin.php:920 +#: mod/admin.php:937 msgid "Will be displayed prominently on the registration page." msgstr "" -#: mod/admin.php:921 +#: mod/admin.php:938 msgid "Accounts abandoned after x days" msgstr "" -#: mod/admin.php:921 +#: mod/admin.php:938 msgid "" "Will not waste system resources polling external sites for abandonded " "accounts. Enter 0 for no time limit." msgstr "" -#: mod/admin.php:922 +#: mod/admin.php:939 msgid "Allowed friend domains" msgstr "" -#: mod/admin.php:922 +#: mod/admin.php:939 msgid "" "Comma separated list of domains which are allowed to establish friendships " "with this site. Wildcards are accepted. Empty to allow any domains" msgstr "" -#: mod/admin.php:923 +#: mod/admin.php:940 msgid "Allowed email domains" msgstr "" -#: mod/admin.php:923 +#: mod/admin.php:940 msgid "" "Comma separated list of domains which are allowed in email addresses for " "registrations to this site. Wildcards are accepted. Empty to allow any " "domains" msgstr "" -#: mod/admin.php:924 +#: mod/admin.php:941 msgid "Block public" msgstr "" -#: mod/admin.php:924 +#: mod/admin.php:941 msgid "" "Check to block public access to all otherwise public personal pages on this " "site unless you are currently logged in." msgstr "" -#: mod/admin.php:925 +#: mod/admin.php:942 msgid "Force publish" msgstr "" -#: mod/admin.php:925 +#: mod/admin.php:942 msgid "" "Check to force all profiles on this site to be listed in the site directory." msgstr "" -#: mod/admin.php:926 +#: mod/admin.php:943 msgid "Global directory URL" msgstr "" -#: mod/admin.php:926 +#: mod/admin.php:943 msgid "" "URL to the global directory. If this is not set, the global directory is " "completely unavailable to the application." msgstr "" -#: mod/admin.php:927 +#: mod/admin.php:944 msgid "Allow threaded items" msgstr "" -#: mod/admin.php:927 +#: mod/admin.php:944 msgid "Allow infinite level threading for items on this site." msgstr "" -#: mod/admin.php:928 +#: mod/admin.php:945 msgid "Private posts by default for new users" msgstr "" -#: mod/admin.php:928 +#: mod/admin.php:945 msgid "" "Set default post permissions for all new members to the default privacy " "group rather than public." msgstr "" -#: mod/admin.php:929 +#: mod/admin.php:946 msgid "Don't include post content in email notifications" msgstr "" -#: mod/admin.php:929 +#: mod/admin.php:946 msgid "" "Don't include the content of a post/comment/private message/etc. in the " "email notifications that are sent out from this site, as a privacy measure." msgstr "" -#: mod/admin.php:930 +#: mod/admin.php:947 msgid "Disallow public access to addons listed in the apps menu." msgstr "" -#: mod/admin.php:930 +#: mod/admin.php:947 msgid "" "Checking this box will restrict addons listed in the apps menu to members " "only." msgstr "" -#: mod/admin.php:931 +#: mod/admin.php:948 msgid "Don't embed private images in posts" msgstr "" -#: mod/admin.php:931 +#: mod/admin.php:948 msgid "" "Don't replace locally-hosted private photos in posts with an embedded copy " "of the image. This means that contacts who receive posts containing private " "photos will have to authenticate and load each image, which may take a while." msgstr "" -#: mod/admin.php:932 +#: mod/admin.php:949 msgid "Allow Users to set remote_self" msgstr "" -#: mod/admin.php:932 +#: mod/admin.php:949 msgid "" "With checking this, every user is allowed to mark every contact as a " "remote_self in the repair contact dialog. Setting this flag on a contact " "causes mirroring every posting of that contact in the users stream." msgstr "" -#: mod/admin.php:933 +#: mod/admin.php:950 msgid "Block multiple registrations" msgstr "" -#: mod/admin.php:933 +#: mod/admin.php:950 msgid "Disallow users to register additional accounts for use as pages." msgstr "" -#: mod/admin.php:934 +#: mod/admin.php:951 msgid "OpenID support" msgstr "" -#: mod/admin.php:934 +#: mod/admin.php:951 msgid "OpenID support for registration and logins." msgstr "" -#: mod/admin.php:935 +#: mod/admin.php:952 msgid "Fullname check" msgstr "" -#: mod/admin.php:935 +#: mod/admin.php:952 msgid "" "Force users to register with a space between firstname and lastname in Full " "name, as an antispam measure" msgstr "" -#: mod/admin.php:936 +#: mod/admin.php:953 msgid "UTF-8 Regular expressions" msgstr "" -#: mod/admin.php:936 +#: mod/admin.php:953 msgid "Use PHP UTF8 regular expressions" msgstr "" -#: mod/admin.php:937 +#: mod/admin.php:954 msgid "Community Page Style" msgstr "" -#: mod/admin.php:937 +#: mod/admin.php:954 msgid "" "Type of community page to show. 'Global community' shows every public " "posting from an open distributed network that arrived on this server." msgstr "" -#: mod/admin.php:938 +#: mod/admin.php:955 msgid "Posts per user on community page" msgstr "" -#: mod/admin.php:938 +#: mod/admin.php:955 msgid "" "The maximum number of posts per user on the community page. (Not valid for " "'Global Community')" msgstr "" -#: mod/admin.php:939 +#: mod/admin.php:956 msgid "Enable OStatus support" msgstr "" -#: mod/admin.php:939 +#: mod/admin.php:956 msgid "" "Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All " "communications in OStatus are public, so privacy warnings will be " "occasionally displayed." msgstr "" -#: mod/admin.php:940 +#: mod/admin.php:957 msgid "OStatus conversation completion interval" msgstr "" -#: mod/admin.php:940 +#: mod/admin.php:957 msgid "" "How often shall the poller check for new entries in OStatus conversations? " "This can be a very ressource task." msgstr "" -#: mod/admin.php:941 +#: mod/admin.php:958 +msgid "Only import OStatus threads from our contacts" +msgstr "" + +#: mod/admin.php:958 +msgid "" +"Normally we import every content from our OStatus contacts. With this option " +"we only store threads that are started by a contact that is known on our " +"system." +msgstr "" + +#: mod/admin.php:959 msgid "OStatus support can only be enabled if threading is enabled." msgstr "" -#: mod/admin.php:943 +#: mod/admin.php:961 msgid "" "Diaspora support can't be enabled because Friendica was installed into a sub " "directory." msgstr "" -#: mod/admin.php:944 +#: mod/admin.php:962 msgid "Enable Diaspora support" msgstr "" -#: mod/admin.php:944 +#: mod/admin.php:962 msgid "Provide built-in Diaspora network compatibility." msgstr "" -#: mod/admin.php:945 +#: mod/admin.php:963 msgid "Only allow Friendica contacts" msgstr "" -#: mod/admin.php:945 +#: mod/admin.php:963 msgid "" "All contacts must use Friendica protocols. All other built-in communication " "protocols disabled." msgstr "" -#: mod/admin.php:946 +#: mod/admin.php:964 msgid "Verify SSL" msgstr "" -#: mod/admin.php:946 +#: mod/admin.php:964 msgid "" "If you wish, you can turn on strict certificate checking. This will mean you " "cannot connect (at all) to self-signed SSL sites." msgstr "" -#: mod/admin.php:947 +#: mod/admin.php:965 msgid "Proxy user" msgstr "" -#: mod/admin.php:948 +#: mod/admin.php:966 msgid "Proxy URL" msgstr "" -#: mod/admin.php:949 +#: mod/admin.php:967 msgid "Network timeout" msgstr "" -#: mod/admin.php:949 +#: mod/admin.php:967 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." msgstr "" -#: mod/admin.php:950 +#: mod/admin.php:968 msgid "Delivery interval" msgstr "" -#: mod/admin.php:950 +#: mod/admin.php:968 msgid "" "Delay background delivery processes by this many seconds to reduce system " "load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 " "for large dedicated servers." msgstr "" -#: mod/admin.php:951 +#: mod/admin.php:969 msgid "Poll interval" msgstr "" -#: mod/admin.php:951 +#: mod/admin.php:969 msgid "" "Delay background polling processes by this many seconds to reduce system " "load. If 0, use delivery interval." msgstr "" -#: mod/admin.php:952 +#: mod/admin.php:970 msgid "Maximum Load Average" msgstr "" -#: mod/admin.php:952 +#: mod/admin.php:970 msgid "" "Maximum system load before delivery and poll processes are deferred - " "default 50." msgstr "" -#: mod/admin.php:953 +#: mod/admin.php:971 msgid "Maximum Load Average (Frontend)" msgstr "" -#: mod/admin.php:953 +#: mod/admin.php:971 msgid "Maximum system load before the frontend quits service - default 50." msgstr "" -#: mod/admin.php:954 +#: mod/admin.php:972 msgid "Maximum table size for optimization" msgstr "" -#: mod/admin.php:954 +#: mod/admin.php:972 msgid "" "Maximum table size (in MB) for the automatic optimization - default 100 MB. " "Enter -1 to disable it." msgstr "" -#: mod/admin.php:955 +#: mod/admin.php:973 msgid "Minimum level of fragmentation" msgstr "" -#: mod/admin.php:955 +#: mod/admin.php:973 msgid "" "Minimum fragmenation level to start the automatic optimization - default " "value is 30%." msgstr "" -#: mod/admin.php:957 +#: mod/admin.php:975 msgid "Periodical check of global contacts" msgstr "" -#: mod/admin.php:957 +#: mod/admin.php:975 msgid "" "If enabled, the global contacts are checked periodically for missing or " "outdated data and the vitality of the contacts and servers." msgstr "" -#: mod/admin.php:958 +#: mod/admin.php:976 msgid "Days between requery" msgstr "" -#: mod/admin.php:958 +#: mod/admin.php:976 msgid "Number of days after which a server is requeried for his contacts." msgstr "" -#: mod/admin.php:959 +#: mod/admin.php:977 msgid "Discover contacts from other servers" msgstr "" -#: mod/admin.php:959 +#: mod/admin.php:977 msgid "" "Periodically query other servers for contacts. You can choose between " "'users': the users on the remote system, 'Global Contacts': active contacts " @@ -2708,32 +2729,32 @@ msgid "" "Global Contacts'." msgstr "" -#: mod/admin.php:960 +#: mod/admin.php:978 msgid "Timeframe for fetching global contacts" msgstr "" -#: mod/admin.php:960 +#: mod/admin.php:978 msgid "" "When the discovery is activated, this value defines the timeframe for the " "activity of the global contacts that are fetched from other servers." msgstr "" -#: mod/admin.php:961 +#: mod/admin.php:979 msgid "Search the local directory" msgstr "" -#: mod/admin.php:961 +#: mod/admin.php:979 msgid "" "Search the local directory instead of the global directory. When searching " "locally, every search will be executed on the global directory in the " "background. This improves the search results when the search is repeated." msgstr "" -#: mod/admin.php:963 +#: mod/admin.php:981 msgid "Publish server information" msgstr "" -#: mod/admin.php:963 +#: mod/admin.php:981 msgid "" "If enabled, general server and usage data will be published. The data " "contains the name and version of the server, number of users with public " @@ -2741,204 +2762,235 @@ msgid "" "href='http://the-federation.info/'>the-federation.info for details." msgstr "" -#: mod/admin.php:965 +#: mod/admin.php:983 msgid "Use MySQL full text engine" msgstr "" -#: mod/admin.php:965 +#: mod/admin.php:983 msgid "" "Activates the full text engine. Speeds up search - but can only search for " "four and more characters." msgstr "" -#: mod/admin.php:966 +#: mod/admin.php:984 msgid "Suppress Language" msgstr "" -#: mod/admin.php:966 +#: mod/admin.php:984 msgid "Suppress language information in meta information about a posting." msgstr "" -#: mod/admin.php:967 +#: mod/admin.php:985 msgid "Suppress Tags" msgstr "" -#: mod/admin.php:967 +#: mod/admin.php:985 msgid "Suppress showing a list of hashtags at the end of the posting." msgstr "" -#: mod/admin.php:968 +#: mod/admin.php:986 msgid "Path to item cache" msgstr "" -#: mod/admin.php:968 +#: mod/admin.php:986 msgid "The item caches buffers generated bbcode and external images." msgstr "" -#: mod/admin.php:969 +#: mod/admin.php:987 msgid "Cache duration in seconds" msgstr "" -#: mod/admin.php:969 +#: mod/admin.php:987 msgid "" "How long should the cache files be hold? Default value is 86400 seconds (One " "day). To disable the item cache, set the value to -1." msgstr "" -#: mod/admin.php:970 +#: mod/admin.php:988 msgid "Maximum numbers of comments per post" msgstr "" -#: mod/admin.php:970 +#: mod/admin.php:988 msgid "How much comments should be shown for each post? Default value is 100." msgstr "" -#: mod/admin.php:971 +#: mod/admin.php:989 msgid "Path for lock file" msgstr "" -#: mod/admin.php:971 +#: mod/admin.php:989 msgid "" "The lock file is used to avoid multiple pollers at one time. Only define a " "folder here." msgstr "" -#: mod/admin.php:972 +#: mod/admin.php:990 msgid "Temp path" msgstr "" -#: mod/admin.php:972 +#: mod/admin.php:990 msgid "" "If you have a restricted system where the webserver can't access the system " "temp path, enter another path here." msgstr "" -#: mod/admin.php:973 +#: mod/admin.php:991 msgid "Base path to installation" msgstr "" -#: mod/admin.php:973 +#: mod/admin.php:991 msgid "" "If the system cannot detect the correct path to your installation, enter the " "correct path here. This setting should only be set if you are using a " "restricted system and symbolic links to your webroot." msgstr "" -#: mod/admin.php:974 +#: mod/admin.php:992 msgid "Disable picture proxy" msgstr "" -#: mod/admin.php:974 +#: mod/admin.php:992 msgid "" "The picture proxy increases performance and privacy. It shouldn't be used on " "systems with very low bandwith." msgstr "" -#: mod/admin.php:975 +#: mod/admin.php:993 msgid "Enable old style pager" msgstr "" -#: mod/admin.php:975 +#: mod/admin.php:993 msgid "" "The old style pager has page numbers but slows down massively the page speed." msgstr "" -#: mod/admin.php:976 +#: mod/admin.php:994 msgid "Only search in tags" msgstr "" -#: mod/admin.php:976 +#: mod/admin.php:994 msgid "On large systems the text search can slow down the system extremely." msgstr "" -#: mod/admin.php:978 +#: mod/admin.php:996 msgid "New base url" msgstr "" -#: mod/admin.php:978 +#: mod/admin.php:996 msgid "" "Change base url for this server. Sends relocate message to all DFRN contacts " "of all users." msgstr "" -#: mod/admin.php:980 +#: mod/admin.php:998 msgid "RINO Encryption" msgstr "" -#: mod/admin.php:980 +#: mod/admin.php:998 msgid "Encryption layer between nodes." msgstr "" -#: mod/admin.php:981 +#: mod/admin.php:999 msgid "Embedly API key" msgstr "" -#: mod/admin.php:981 +#: mod/admin.php:999 msgid "" "Embedly is used to fetch additional data for " "web pages. This is an optional parameter." msgstr "" -#: mod/admin.php:1010 +#: mod/admin.php:1001 +msgid "Enable 'worker' background processing" +msgstr "" + +#: mod/admin.php:1001 +msgid "" +"The worker background processing limits the number of parallel background " +"jobs to a maximum number and respects the system load." +msgstr "" + +#: mod/admin.php:1002 +msgid "Maximum number of parallel workers" +msgstr "" + +#: mod/admin.php:1002 +msgid "" +"On shared hosters set this to 2. On larger systems, values of 10 are great. " +"Default value is 4." +msgstr "" + +#: mod/admin.php:1003 +msgid "Don't use 'proc_open' with the worker" +msgstr "" + +#: mod/admin.php:1003 +msgid "" +"Enable this if your system doesn't allow the use of 'proc_open'. This can " +"happen on shared hosters. If this is enabled you should increase the " +"frequency of poller calls in your crontab." +msgstr "" + +#: mod/admin.php:1032 msgid "Update has been marked successful" msgstr "" -#: mod/admin.php:1018 -#, php-format -msgid "Database structure update %s was successfully applied." -msgstr "" - -#: mod/admin.php:1021 -#, php-format -msgid "Executing of database structure update %s failed with error: %s" -msgstr "" - -#: mod/admin.php:1033 -#, php-format -msgid "Executing %s failed with error: %s" -msgstr "" - -#: mod/admin.php:1036 -#, php-format -msgid "Update %s was successfully applied." -msgstr "" - #: mod/admin.php:1040 #, php-format +msgid "Database structure update %s was successfully applied." +msgstr "" + +#: mod/admin.php:1043 +#, php-format +msgid "Executing of database structure update %s failed with error: %s" +msgstr "" + +#: mod/admin.php:1055 +#, php-format +msgid "Executing %s failed with error: %s" +msgstr "" + +#: mod/admin.php:1058 +#, php-format +msgid "Update %s was successfully applied." +msgstr "" + +#: mod/admin.php:1062 +#, php-format msgid "Update %s did not return a status. Unknown if it succeeded." msgstr "" -#: mod/admin.php:1042 +#: mod/admin.php:1064 #, php-format msgid "There was no additional update function %s that needed to be called." msgstr "" -#: mod/admin.php:1061 +#: mod/admin.php:1083 msgid "No failed updates." msgstr "" -#: mod/admin.php:1062 +#: mod/admin.php:1084 msgid "Check database structure" msgstr "" -#: mod/admin.php:1067 +#: mod/admin.php:1089 msgid "Failed Updates" msgstr "" -#: mod/admin.php:1068 +#: mod/admin.php:1090 msgid "" "This does not include updates prior to 1139, which did not return a status." msgstr "" -#: mod/admin.php:1069 +#: mod/admin.php:1091 msgid "Mark success (if update was manually applied)" msgstr "" -#: mod/admin.php:1070 +#: mod/admin.php:1092 msgid "Attempt to execute this update step automatically" msgstr "" -#: mod/admin.php:1102 +#: mod/admin.php:1124 #, php-format msgid "" "\n" @@ -2946,7 +2998,7 @@ msgid "" "\t\t\t\tthe administrator of %2$s has set up an account for you." msgstr "" -#: mod/admin.php:1105 +#: mod/admin.php:1127 #, php-format msgid "" "\n" @@ -2982,168 +3034,168 @@ msgid "" "\t\t\tThank you and welcome to %4$s." msgstr "" -#: mod/admin.php:1137 include/user.php:423 +#: mod/admin.php:1159 include/user.php:423 #, php-format msgid "Registration details for %s" msgstr "" -#: mod/admin.php:1149 +#: mod/admin.php:1171 #, php-format msgid "%s user blocked/unblocked" msgid_plural "%s users blocked/unblocked" msgstr[0] "" msgstr[1] "" -#: mod/admin.php:1156 +#: mod/admin.php:1178 #, php-format msgid "%s user deleted" msgid_plural "%s users deleted" msgstr[0] "" msgstr[1] "" -#: mod/admin.php:1203 +#: mod/admin.php:1225 #, php-format msgid "User '%s' deleted" msgstr "" -#: mod/admin.php:1211 +#: mod/admin.php:1233 #, php-format msgid "User '%s' unblocked" msgstr "" -#: mod/admin.php:1211 +#: mod/admin.php:1233 #, php-format msgid "User '%s' blocked" msgstr "" -#: mod/admin.php:1302 +#: mod/admin.php:1324 msgid "Add User" msgstr "" -#: mod/admin.php:1303 +#: mod/admin.php:1325 msgid "select all" msgstr "" -#: mod/admin.php:1304 +#: mod/admin.php:1326 msgid "User registrations waiting for confirm" msgstr "" -#: mod/admin.php:1305 +#: mod/admin.php:1327 msgid "User waiting for permanent deletion" msgstr "" -#: mod/admin.php:1306 +#: mod/admin.php:1328 msgid "Request date" msgstr "" -#: mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 mod/admin.php:1334 +#: mod/admin.php:1328 mod/admin.php:1340 mod/admin.php:1341 mod/admin.php:1356 #: include/contact_selectors.php:79 include/contact_selectors.php:86 msgid "Email" msgstr "" -#: mod/admin.php:1307 +#: mod/admin.php:1329 msgid "No registrations." msgstr "" -#: mod/admin.php:1309 +#: mod/admin.php:1331 msgid "Deny" msgstr "" -#: mod/admin.php:1313 +#: mod/admin.php:1335 msgid "Site admin" msgstr "" -#: mod/admin.php:1314 +#: mod/admin.php:1336 msgid "Account expired" msgstr "" -#: mod/admin.php:1317 +#: mod/admin.php:1339 msgid "New User" msgstr "" -#: mod/admin.php:1318 mod/admin.php:1319 +#: mod/admin.php:1340 mod/admin.php:1341 msgid "Register date" msgstr "" -#: mod/admin.php:1318 mod/admin.php:1319 +#: mod/admin.php:1340 mod/admin.php:1341 msgid "Last login" msgstr "" -#: mod/admin.php:1318 mod/admin.php:1319 +#: mod/admin.php:1340 mod/admin.php:1341 msgid "Last item" msgstr "" -#: mod/admin.php:1318 +#: mod/admin.php:1340 msgid "Deleted since" msgstr "" -#: mod/admin.php:1319 mod/settings.php:41 +#: mod/admin.php:1341 mod/settings.php:41 msgid "Account" msgstr "" -#: mod/admin.php:1321 +#: mod/admin.php:1343 msgid "" "Selected users will be deleted!\\n\\nEverything these users had posted on " "this site will be permanently deleted!\\n\\nAre you sure?" msgstr "" -#: mod/admin.php:1322 +#: mod/admin.php:1344 msgid "" "The user {0} will be deleted!\\n\\nEverything this user has posted on this " "site will be permanently deleted!\\n\\nAre you sure?" msgstr "" -#: mod/admin.php:1332 +#: mod/admin.php:1354 msgid "Name of the new user." msgstr "" -#: mod/admin.php:1333 +#: mod/admin.php:1355 msgid "Nickname" msgstr "" -#: mod/admin.php:1333 +#: mod/admin.php:1355 msgid "Nickname of the new user." msgstr "" -#: mod/admin.php:1334 +#: mod/admin.php:1356 msgid "Email address of the new user." msgstr "" -#: mod/admin.php:1377 +#: mod/admin.php:1399 #, php-format msgid "Plugin %s disabled." msgstr "" -#: mod/admin.php:1381 +#: mod/admin.php:1403 #, php-format msgid "Plugin %s enabled." msgstr "" -#: mod/admin.php:1392 mod/admin.php:1628 +#: mod/admin.php:1414 mod/admin.php:1650 msgid "Disable" msgstr "" -#: mod/admin.php:1394 mod/admin.php:1630 +#: mod/admin.php:1416 mod/admin.php:1652 msgid "Enable" msgstr "" -#: mod/admin.php:1417 mod/admin.php:1675 +#: mod/admin.php:1439 mod/admin.php:1697 msgid "Toggle" msgstr "" -#: mod/admin.php:1425 mod/admin.php:1684 +#: mod/admin.php:1447 mod/admin.php:1706 msgid "Author: " msgstr "" -#: mod/admin.php:1426 mod/admin.php:1685 +#: mod/admin.php:1448 mod/admin.php:1707 msgid "Maintainer: " msgstr "" -#: mod/admin.php:1478 +#: mod/admin.php:1500 msgid "Reload active plugins" msgstr "" -#: mod/admin.php:1483 +#: mod/admin.php:1505 #, php-format msgid "" "There are currently no plugins available on your node. You can find the " @@ -3151,62 +3203,62 @@ msgid "" "in the open plugin registry at %2$s" msgstr "" -#: mod/admin.php:1588 +#: mod/admin.php:1610 msgid "No themes found." msgstr "" -#: mod/admin.php:1666 +#: mod/admin.php:1688 msgid "Screenshot" msgstr "" -#: mod/admin.php:1726 +#: mod/admin.php:1748 msgid "Reload active themes" msgstr "" -#: mod/admin.php:1731 +#: mod/admin.php:1753 #, php-format msgid "No themes found on the system. They should be paced in %1$s" msgstr "" -#: mod/admin.php:1732 +#: mod/admin.php:1754 msgid "[Experimental]" msgstr "" -#: mod/admin.php:1733 +#: mod/admin.php:1755 msgid "[Unsupported]" msgstr "" -#: mod/admin.php:1757 +#: mod/admin.php:1779 msgid "Log settings updated." msgstr "" -#: mod/admin.php:1794 +#: mod/admin.php:1816 msgid "Clear" msgstr "" -#: mod/admin.php:1799 +#: mod/admin.php:1821 msgid "Enable Debugging" msgstr "" -#: mod/admin.php:1800 +#: mod/admin.php:1822 msgid "Log file" msgstr "" -#: mod/admin.php:1800 +#: mod/admin.php:1822 msgid "" "Must be writable by web server. Relative to your Friendica top-level " "directory." msgstr "" -#: mod/admin.php:1801 +#: mod/admin.php:1823 msgid "Log level" msgstr "" -#: mod/admin.php:1804 +#: mod/admin.php:1826 msgid "PHP logging" msgstr "" -#: mod/admin.php:1805 +#: mod/admin.php:1827 msgid "" "To enable logging of PHP errors and warnings you can add the following to " "the .htconfig.php file of your installation. The filename set in the " @@ -3215,20 +3267,20 @@ msgid "" "'display_errors' is to enable these options, set to '0' to disable them." msgstr "" -#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +#: mod/admin.php:1953 mod/admin.php:1954 mod/settings.php:760 msgid "Off" msgstr "" -#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +#: mod/admin.php:1953 mod/admin.php:1954 mod/settings.php:760 msgid "On" msgstr "" -#: mod/admin.php:1932 +#: mod/admin.php:1954 #, php-format msgid "Lock feature %s" msgstr "" -#: mod/admin.php:1940 +#: mod/admin.php:1962 msgid "Manage Additional Features" msgstr "" @@ -3245,7 +3297,7 @@ msgstr "" msgid "Saved Searches" msgstr "" -#: mod/network.php:201 include/group.php:293 +#: mod/network.php:201 include/group.php:296 msgid "add" msgstr "" @@ -3362,31 +3414,31 @@ msgstr "" msgid "Sat" msgstr "" -#: mod/events.php:208 mod/settings.php:948 include/text.php:1274 +#: mod/events.php:208 mod/settings.php:952 include/text.php:1245 msgid "Sunday" msgstr "" -#: mod/events.php:209 mod/settings.php:948 include/text.php:1274 +#: mod/events.php:209 mod/settings.php:952 include/text.php:1245 msgid "Monday" msgstr "" -#: mod/events.php:210 include/text.php:1274 +#: mod/events.php:210 include/text.php:1245 msgid "Tuesday" msgstr "" -#: mod/events.php:211 include/text.php:1274 +#: mod/events.php:211 include/text.php:1245 msgid "Wednesday" msgstr "" -#: mod/events.php:212 include/text.php:1274 +#: mod/events.php:212 include/text.php:1245 msgid "Thursday" msgstr "" -#: mod/events.php:213 include/text.php:1274 +#: mod/events.php:213 include/text.php:1245 msgid "Friday" msgstr "" -#: mod/events.php:214 include/text.php:1274 +#: mod/events.php:214 include/text.php:1245 msgid "Saturday" msgstr "" @@ -3406,7 +3458,7 @@ msgstr "" msgid "Apr" msgstr "" -#: mod/events.php:219 mod/events.php:231 include/text.php:1278 +#: mod/events.php:219 mod/events.php:231 include/text.php:1249 msgid "May" msgstr "" @@ -3438,47 +3490,47 @@ msgstr "" msgid "Dec" msgstr "" -#: mod/events.php:227 include/text.php:1278 +#: mod/events.php:227 include/text.php:1249 msgid "January" msgstr "" -#: mod/events.php:228 include/text.php:1278 +#: mod/events.php:228 include/text.php:1249 msgid "February" msgstr "" -#: mod/events.php:229 include/text.php:1278 +#: mod/events.php:229 include/text.php:1249 msgid "March" msgstr "" -#: mod/events.php:230 include/text.php:1278 +#: mod/events.php:230 include/text.php:1249 msgid "April" msgstr "" -#: mod/events.php:232 include/text.php:1278 +#: mod/events.php:232 include/text.php:1249 msgid "June" msgstr "" -#: mod/events.php:233 include/text.php:1278 +#: mod/events.php:233 include/text.php:1249 msgid "July" msgstr "" -#: mod/events.php:234 include/text.php:1278 +#: mod/events.php:234 include/text.php:1249 msgid "August" msgstr "" -#: mod/events.php:235 include/text.php:1278 +#: mod/events.php:235 include/text.php:1249 msgid "September" msgstr "" -#: mod/events.php:236 include/text.php:1278 +#: mod/events.php:236 include/text.php:1249 msgid "October" msgstr "" -#: mod/events.php:237 include/text.php:1278 +#: mod/events.php:237 include/text.php:1249 msgid "November" msgstr "" -#: mod/events.php:238 include/text.php:1278 +#: mod/events.php:238 include/text.php:1249 msgid "December" msgstr "" @@ -3486,15 +3538,15 @@ msgstr "" msgid "today" msgstr "" -#: mod/events.php:240 include/datetime.php:288 +#: mod/events.php:240 include/datetime.php:344 msgid "month" msgstr "" -#: mod/events.php:241 include/datetime.php:289 +#: mod/events.php:241 include/datetime.php:345 msgid "week" msgstr "" -#: mod/events.php:242 include/datetime.php:290 +#: mod/events.php:242 include/datetime.php:346 msgid "day" msgstr "" @@ -3506,11 +3558,11 @@ msgstr "" msgid "Edit event" msgstr "" -#: mod/events.php:421 include/text.php:1728 include/text.php:1735 +#: mod/events.php:421 include/text.php:1651 include/text.php:1658 msgid "link to source" msgstr "" -#: mod/events.php:456 include/identity.php:722 include/nav.php:79 +#: mod/events.php:456 include/identity.php:723 include/nav.php:79 #: include/nav.php:140 view/theme/diabook/theme.php:127 msgid "Events" msgstr "" @@ -3568,7 +3620,7 @@ msgid "Share this event" msgstr "" #: mod/events.php:572 mod/content.php:721 mod/editpost.php:145 -#: mod/photos.php:1631 mod/photos.php:1679 mod/photos.php:1767 +#: mod/photos.php:1617 mod/photos.php:1665 mod/photos.php:1753 #: object/Item.php:719 include/conversation.php:1216 msgid "Preview" msgstr "" @@ -3584,7 +3636,7 @@ msgid "" "code or the translation of Friendica. Thank you all!" msgstr "" -#: mod/content.php:439 mod/content.php:742 mod/photos.php:1722 +#: mod/content.php:439 mod/content.php:742 mod/photos.php:1708 #: object/Item.php:133 include/conversation.php:634 msgid "Select" msgstr "" @@ -3613,23 +3665,23 @@ msgstr[0] "" msgstr[1] "" #: mod/content.php:607 object/Item.php:421 object/Item.php:434 -#: include/text.php:2004 +#: include/text.php:1927 msgid "comment" msgid_plural "comments" msgstr[0] "" msgstr[1] "" -#: mod/content.php:608 boot.php:870 object/Item.php:422 -#: include/contact_widgets.php:242 include/forums.php:110 -#: include/items.php:5207 view/theme/vier/theme.php:264 +#: mod/content.php:608 boot.php:872 object/Item.php:422 +#: include/contact_widgets.php:242 include/ForumManager.php:117 +#: include/items.php:2113 view/theme/vier/theme.php:260 msgid "show more" msgstr "" -#: mod/content.php:622 mod/photos.php:1418 object/Item.php:117 +#: mod/content.php:622 mod/photos.php:1404 object/Item.php:117 msgid "Private Message" msgstr "" -#: mod/content.php:686 mod/photos.php:1607 object/Item.php:253 +#: mod/content.php:686 mod/photos.php:1593 object/Item.php:253 msgid "I like this (toggle)" msgstr "" @@ -3637,7 +3689,7 @@ msgstr "" msgid "like" msgstr "" -#: mod/content.php:687 mod/photos.php:1608 object/Item.php:254 +#: mod/content.php:687 mod/photos.php:1594 object/Item.php:254 msgid "I don't like this (toggle)" msgstr "" @@ -3653,13 +3705,13 @@ msgstr "" msgid "share" msgstr "" -#: mod/content.php:709 mod/photos.php:1627 mod/photos.php:1675 -#: mod/photos.php:1763 object/Item.php:707 +#: mod/content.php:709 mod/photos.php:1613 mod/photos.php:1661 +#: mod/photos.php:1749 object/Item.php:707 msgid "This is you" msgstr "" -#: mod/content.php:711 mod/photos.php:1629 mod/photos.php:1677 -#: mod/photos.php:1765 boot.php:869 object/Item.php:393 object/Item.php:709 +#: mod/content.php:711 mod/photos.php:1615 mod/photos.php:1663 +#: mod/photos.php:1751 boot.php:871 object/Item.php:393 object/Item.php:709 msgid "Comment" msgstr "" @@ -3695,7 +3747,7 @@ msgstr "" msgid "Video" msgstr "" -#: mod/content.php:730 mod/settings.php:721 object/Item.php:122 +#: mod/content.php:730 mod/settings.php:722 object/Item.php:122 #: object/Item.php:124 msgid "Edit" msgstr "" @@ -4094,19 +4146,19 @@ msgstr "" msgid "Help:" msgstr "" -#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:302 +#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:298 msgid "Help" msgstr "" -#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:270 +#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:271 msgid "Not Found" msgstr "" -#: mod/help.php:56 index.php:273 +#: mod/help.php:56 index.php:274 msgid "Page not found." msgstr "" -#: mod/dfrn_poll.php:103 mod/dfrn_poll.php:536 +#: mod/dfrn_poll.php:101 mod/dfrn_poll.php:534 #, php-format msgid "%1$s welcomes %2$s" msgstr "" @@ -4170,7 +4222,7 @@ msgstr "" msgid "Display" msgstr "" -#: mod/settings.php:65 mod/settings.php:864 +#: mod/settings.php:65 mod/settings.php:868 msgid "Social Networks" msgstr "" @@ -4182,7 +4234,7 @@ msgstr "" msgid "Connected apps" msgstr "" -#: mod/settings.php:93 mod/uexport.php:85 +#: mod/settings.php:93 mod/uexport.php:37 msgid "Export personal data" msgstr "" @@ -4194,685 +4246,689 @@ msgstr "" msgid "Missing some important data!" msgstr "" -#: mod/settings.php:266 +#: mod/settings.php:267 msgid "Failed to connect with email account using the settings provided." msgstr "" -#: mod/settings.php:271 +#: mod/settings.php:272 msgid "Email settings updated." msgstr "" -#: mod/settings.php:286 +#: mod/settings.php:287 msgid "Features updated" msgstr "" -#: mod/settings.php:353 +#: mod/settings.php:354 msgid "Relocate message has been send to your contacts" msgstr "" -#: mod/settings.php:367 include/user.php:39 +#: mod/settings.php:368 include/user.php:39 msgid "Passwords do not match. Password unchanged." msgstr "" -#: mod/settings.php:372 +#: mod/settings.php:373 msgid "Empty passwords are not allowed. Password unchanged." msgstr "" -#: mod/settings.php:380 +#: mod/settings.php:381 msgid "Wrong password." msgstr "" -#: mod/settings.php:391 +#: mod/settings.php:392 msgid "Password changed." msgstr "" -#: mod/settings.php:393 +#: mod/settings.php:394 msgid "Password update failed. Please try again." msgstr "" -#: mod/settings.php:462 +#: mod/settings.php:463 msgid " Please use a shorter name." msgstr "" -#: mod/settings.php:464 +#: mod/settings.php:465 msgid " Name too short." msgstr "" -#: mod/settings.php:473 +#: mod/settings.php:474 msgid "Wrong Password" msgstr "" -#: mod/settings.php:478 +#: mod/settings.php:479 msgid " Not valid email." msgstr "" -#: mod/settings.php:484 +#: mod/settings.php:485 msgid " Cannot change to that email." msgstr "" -#: mod/settings.php:540 +#: mod/settings.php:541 msgid "Private forum has no privacy permissions. Using default privacy group." msgstr "" -#: mod/settings.php:544 +#: mod/settings.php:545 msgid "Private forum has no privacy permissions and no default privacy group." msgstr "" -#: mod/settings.php:583 +#: mod/settings.php:584 msgid "Settings updated." msgstr "" -#: mod/settings.php:658 mod/settings.php:684 mod/settings.php:720 +#: mod/settings.php:659 mod/settings.php:685 mod/settings.php:721 msgid "Add application" msgstr "" -#: mod/settings.php:662 mod/settings.php:688 +#: mod/settings.php:663 mod/settings.php:689 msgid "Consumer Key" msgstr "" -#: mod/settings.php:663 mod/settings.php:689 +#: mod/settings.php:664 mod/settings.php:690 msgid "Consumer Secret" msgstr "" -#: mod/settings.php:664 mod/settings.php:690 +#: mod/settings.php:665 mod/settings.php:691 msgid "Redirect" msgstr "" -#: mod/settings.php:665 mod/settings.php:691 +#: mod/settings.php:666 mod/settings.php:692 msgid "Icon url" msgstr "" -#: mod/settings.php:676 +#: mod/settings.php:677 msgid "You can't edit this application." msgstr "" -#: mod/settings.php:719 +#: mod/settings.php:720 msgid "Connected Apps" msgstr "" -#: mod/settings.php:723 +#: mod/settings.php:724 msgid "Client key starts with" msgstr "" -#: mod/settings.php:724 +#: mod/settings.php:725 msgid "No name" msgstr "" -#: mod/settings.php:725 +#: mod/settings.php:726 msgid "Remove authorization" msgstr "" -#: mod/settings.php:737 +#: mod/settings.php:738 msgid "No Plugin settings configured" msgstr "" -#: mod/settings.php:745 +#: mod/settings.php:746 msgid "Plugin Settings" msgstr "" -#: mod/settings.php:767 +#: mod/settings.php:768 msgid "Additional Features" msgstr "" -#: mod/settings.php:777 mod/settings.php:781 +#: mod/settings.php:778 mod/settings.php:782 msgid "General Social Media Settings" msgstr "" -#: mod/settings.php:787 +#: mod/settings.php:788 msgid "Disable intelligent shortening" msgstr "" -#: mod/settings.php:789 +#: mod/settings.php:790 msgid "" "Normally the system tries to find the best link to add to shortened posts. " "If this option is enabled then every shortened post will always point to the " "original friendica post." msgstr "" -#: mod/settings.php:795 +#: mod/settings.php:796 msgid "Automatically follow any GNU Social (OStatus) followers/mentioners" msgstr "" -#: mod/settings.php:797 +#: mod/settings.php:798 msgid "" "If you receive a message from an unknown OStatus user, this option decides " "what to do. If it is checked, a new contact will be created for every " "unknown user." msgstr "" -#: mod/settings.php:806 +#: mod/settings.php:804 +msgid "Default group for OStatus contacts" +msgstr "" + +#: mod/settings.php:810 msgid "Your legacy GNU Social account" msgstr "" -#: mod/settings.php:808 +#: mod/settings.php:812 msgid "" "If you enter your old GNU Social/Statusnet account name here (in the format " "user@domain.tld), your contacts will be added automatically. The field will " "be emptied when done." msgstr "" -#: mod/settings.php:811 +#: mod/settings.php:815 msgid "Repair OStatus subscriptions" msgstr "" -#: mod/settings.php:820 mod/settings.php:821 +#: mod/settings.php:824 mod/settings.php:825 #, php-format msgid "Built-in support for %s connectivity is %s" msgstr "" -#: mod/settings.php:820 mod/dfrn_request.php:865 +#: mod/settings.php:824 mod/dfrn_request.php:863 #: include/contact_selectors.php:80 msgid "Diaspora" msgstr "" -#: mod/settings.php:820 mod/settings.php:821 +#: mod/settings.php:824 mod/settings.php:825 msgid "enabled" msgstr "" -#: mod/settings.php:820 mod/settings.php:821 +#: mod/settings.php:824 mod/settings.php:825 msgid "disabled" msgstr "" -#: mod/settings.php:821 +#: mod/settings.php:825 msgid "GNU Social (OStatus)" msgstr "" -#: mod/settings.php:857 +#: mod/settings.php:861 msgid "Email access is disabled on this site." msgstr "" -#: mod/settings.php:869 +#: mod/settings.php:873 msgid "Email/Mailbox Setup" msgstr "" -#: mod/settings.php:870 +#: mod/settings.php:874 msgid "" "If you wish to communicate with email contacts using this service " "(optional), please specify how to connect to your mailbox." msgstr "" -#: mod/settings.php:871 +#: mod/settings.php:875 msgid "Last successful email check:" msgstr "" -#: mod/settings.php:873 +#: mod/settings.php:877 msgid "IMAP server name:" msgstr "" -#: mod/settings.php:874 +#: mod/settings.php:878 msgid "IMAP port:" msgstr "" -#: mod/settings.php:875 +#: mod/settings.php:879 msgid "Security:" msgstr "" -#: mod/settings.php:875 mod/settings.php:880 +#: mod/settings.php:879 mod/settings.php:884 msgid "None" msgstr "" -#: mod/settings.php:876 +#: mod/settings.php:880 msgid "Email login name:" msgstr "" -#: mod/settings.php:877 +#: mod/settings.php:881 msgid "Email password:" msgstr "" -#: mod/settings.php:878 +#: mod/settings.php:882 msgid "Reply-to address:" msgstr "" -#: mod/settings.php:879 +#: mod/settings.php:883 msgid "Send public posts to all email contacts:" msgstr "" -#: mod/settings.php:880 +#: mod/settings.php:884 msgid "Action after import:" msgstr "" -#: mod/settings.php:880 +#: mod/settings.php:884 msgid "Mark as seen" msgstr "" -#: mod/settings.php:880 +#: mod/settings.php:884 msgid "Move to folder" msgstr "" -#: mod/settings.php:881 +#: mod/settings.php:885 msgid "Move to folder:" msgstr "" -#: mod/settings.php:967 +#: mod/settings.php:971 msgid "Display Settings" msgstr "" -#: mod/settings.php:973 mod/settings.php:991 +#: mod/settings.php:977 mod/settings.php:995 msgid "Display Theme:" msgstr "" -#: mod/settings.php:974 +#: mod/settings.php:978 msgid "Mobile Theme:" msgstr "" -#: mod/settings.php:975 +#: mod/settings.php:979 msgid "Update browser every xx seconds" msgstr "" -#: mod/settings.php:975 +#: mod/settings.php:979 msgid "Minimum of 10 seconds. Enter -1 to disable it." msgstr "" -#: mod/settings.php:976 +#: mod/settings.php:980 msgid "Number of items to display per page:" msgstr "" -#: mod/settings.php:976 mod/settings.php:977 +#: mod/settings.php:980 mod/settings.php:981 msgid "Maximum of 100 items" msgstr "" -#: mod/settings.php:977 +#: mod/settings.php:981 msgid "Number of items to display per page when viewed from mobile device:" msgstr "" -#: mod/settings.php:978 +#: mod/settings.php:982 msgid "Don't show emoticons" msgstr "" -#: mod/settings.php:979 +#: mod/settings.php:983 msgid "Calendar" msgstr "" -#: mod/settings.php:980 +#: mod/settings.php:984 msgid "Beginning of week:" msgstr "" -#: mod/settings.php:981 +#: mod/settings.php:985 msgid "Don't show notices" msgstr "" -#: mod/settings.php:982 +#: mod/settings.php:986 msgid "Infinite scroll" msgstr "" -#: mod/settings.php:983 +#: mod/settings.php:987 msgid "Automatic updates only at the top of the network page" msgstr "" -#: mod/settings.php:985 view/theme/cleanzero/config.php:82 +#: mod/settings.php:989 view/theme/cleanzero/config.php:82 #: view/theme/dispy/config.php:72 view/theme/quattro/config.php:66 #: view/theme/diabook/config.php:150 view/theme/vier/config.php:109 #: view/theme/duepuntozero/config.php:61 msgid "Theme settings" msgstr "" -#: mod/settings.php:1062 +#: mod/settings.php:1066 msgid "User Types" msgstr "" -#: mod/settings.php:1063 +#: mod/settings.php:1067 msgid "Community Types" msgstr "" -#: mod/settings.php:1064 +#: mod/settings.php:1068 msgid "Normal Account Page" msgstr "" -#: mod/settings.php:1065 +#: mod/settings.php:1069 msgid "This account is a normal personal profile" msgstr "" -#: mod/settings.php:1068 +#: mod/settings.php:1072 msgid "Soapbox Page" msgstr "" -#: mod/settings.php:1069 +#: mod/settings.php:1073 msgid "Automatically approve all connection/friend requests as read-only fans" msgstr "" -#: mod/settings.php:1072 +#: mod/settings.php:1076 msgid "Community Forum/Celebrity Account" msgstr "" -#: mod/settings.php:1073 +#: mod/settings.php:1077 msgid "Automatically approve all connection/friend requests as read-write fans" msgstr "" -#: mod/settings.php:1076 +#: mod/settings.php:1080 msgid "Automatic Friend Page" msgstr "" -#: mod/settings.php:1077 +#: mod/settings.php:1081 msgid "Automatically approve all connection/friend requests as friends" msgstr "" -#: mod/settings.php:1080 +#: mod/settings.php:1084 msgid "Private Forum [Experimental]" msgstr "" -#: mod/settings.php:1081 +#: mod/settings.php:1085 msgid "Private forum - approved members only" msgstr "" -#: mod/settings.php:1093 +#: mod/settings.php:1097 msgid "OpenID:" msgstr "" -#: mod/settings.php:1093 +#: mod/settings.php:1097 msgid "(Optional) Allow this OpenID to login to this account." msgstr "" -#: mod/settings.php:1103 +#: mod/settings.php:1107 msgid "Publish your default profile in your local site directory?" msgstr "" -#: mod/settings.php:1109 +#: mod/settings.php:1113 msgid "Publish your default profile in the global social directory?" msgstr "" -#: mod/settings.php:1117 +#: mod/settings.php:1121 msgid "Hide your contact/friend list from viewers of your default profile?" msgstr "" -#: mod/settings.php:1121 include/acl_selectors.php:331 +#: mod/settings.php:1125 include/acl_selectors.php:331 msgid "Hide your profile details from unknown viewers?" msgstr "" -#: mod/settings.php:1121 +#: mod/settings.php:1125 msgid "" "If enabled, posting public messages to Diaspora and other networks isn't " "possible." msgstr "" -#: mod/settings.php:1126 +#: mod/settings.php:1130 msgid "Allow friends to post to your profile page?" msgstr "" -#: mod/settings.php:1132 +#: mod/settings.php:1136 msgid "Allow friends to tag your posts?" msgstr "" -#: mod/settings.php:1138 +#: mod/settings.php:1142 msgid "Allow us to suggest you as a potential friend to new members?" msgstr "" -#: mod/settings.php:1144 +#: mod/settings.php:1148 msgid "Permit unknown people to send you private mail?" msgstr "" -#: mod/settings.php:1152 +#: mod/settings.php:1156 msgid "Profile is not published." msgstr "" -#: mod/settings.php:1160 +#: mod/settings.php:1164 #, php-format msgid "Your Identity Address is '%s' or '%s'." msgstr "" -#: mod/settings.php:1167 +#: mod/settings.php:1171 msgid "Automatically expire posts after this many days:" msgstr "" -#: mod/settings.php:1167 +#: mod/settings.php:1171 msgid "If empty, posts will not expire. Expired posts will be deleted" msgstr "" -#: mod/settings.php:1168 +#: mod/settings.php:1172 msgid "Advanced expiration settings" msgstr "" -#: mod/settings.php:1169 +#: mod/settings.php:1173 msgid "Advanced Expiration" msgstr "" -#: mod/settings.php:1170 +#: mod/settings.php:1174 msgid "Expire posts:" msgstr "" -#: mod/settings.php:1171 +#: mod/settings.php:1175 msgid "Expire personal notes:" msgstr "" -#: mod/settings.php:1172 +#: mod/settings.php:1176 msgid "Expire starred posts:" msgstr "" -#: mod/settings.php:1173 +#: mod/settings.php:1177 msgid "Expire photos:" msgstr "" -#: mod/settings.php:1174 +#: mod/settings.php:1178 msgid "Only expire posts by others:" msgstr "" -#: mod/settings.php:1202 +#: mod/settings.php:1206 msgid "Account Settings" msgstr "" -#: mod/settings.php:1210 +#: mod/settings.php:1214 msgid "Password Settings" msgstr "" -#: mod/settings.php:1211 mod/register.php:274 +#: mod/settings.php:1215 mod/register.php:274 msgid "New Password:" msgstr "" -#: mod/settings.php:1212 mod/register.php:275 +#: mod/settings.php:1216 mod/register.php:275 msgid "Confirm:" msgstr "" -#: mod/settings.php:1212 +#: mod/settings.php:1216 msgid "Leave password fields blank unless changing" msgstr "" -#: mod/settings.php:1213 +#: mod/settings.php:1217 msgid "Current Password:" msgstr "" -#: mod/settings.php:1213 mod/settings.php:1214 +#: mod/settings.php:1217 mod/settings.php:1218 msgid "Your current password to confirm the changes" msgstr "" -#: mod/settings.php:1214 +#: mod/settings.php:1218 msgid "Password:" msgstr "" -#: mod/settings.php:1218 +#: mod/settings.php:1222 msgid "Basic Settings" msgstr "" -#: mod/settings.php:1219 include/identity.php:588 +#: mod/settings.php:1223 include/identity.php:589 msgid "Full Name:" msgstr "" -#: mod/settings.php:1220 +#: mod/settings.php:1224 msgid "Email Address:" msgstr "" -#: mod/settings.php:1221 +#: mod/settings.php:1225 msgid "Your Timezone:" msgstr "" -#: mod/settings.php:1222 +#: mod/settings.php:1226 msgid "Your Language:" msgstr "" -#: mod/settings.php:1222 +#: mod/settings.php:1226 msgid "" "Set the language we use to show you friendica interface and to send you " "emails" msgstr "" -#: mod/settings.php:1223 +#: mod/settings.php:1227 msgid "Default Post Location:" msgstr "" -#: mod/settings.php:1224 +#: mod/settings.php:1228 msgid "Use Browser Location:" msgstr "" -#: mod/settings.php:1227 +#: mod/settings.php:1231 msgid "Security and Privacy Settings" msgstr "" -#: mod/settings.php:1229 +#: mod/settings.php:1233 msgid "Maximum Friend Requests/Day:" msgstr "" -#: mod/settings.php:1229 mod/settings.php:1259 +#: mod/settings.php:1233 mod/settings.php:1263 msgid "(to prevent spam abuse)" msgstr "" -#: mod/settings.php:1230 +#: mod/settings.php:1234 msgid "Default Post Permissions" msgstr "" -#: mod/settings.php:1231 +#: mod/settings.php:1235 msgid "(click to open/close)" msgstr "" -#: mod/settings.php:1240 mod/photos.php:1199 mod/photos.php:1584 +#: mod/settings.php:1244 mod/photos.php:1185 mod/photos.php:1570 msgid "Show to Groups" msgstr "" -#: mod/settings.php:1241 mod/photos.php:1200 mod/photos.php:1585 +#: mod/settings.php:1245 mod/photos.php:1186 mod/photos.php:1571 msgid "Show to Contacts" msgstr "" -#: mod/settings.php:1242 +#: mod/settings.php:1246 msgid "Default Private Post" msgstr "" -#: mod/settings.php:1243 +#: mod/settings.php:1247 msgid "Default Public Post" msgstr "" -#: mod/settings.php:1247 +#: mod/settings.php:1251 msgid "Default Permissions for New Posts" msgstr "" -#: mod/settings.php:1259 +#: mod/settings.php:1263 msgid "Maximum private messages per day from unknown people:" msgstr "" -#: mod/settings.php:1262 +#: mod/settings.php:1266 msgid "Notification Settings" msgstr "" -#: mod/settings.php:1263 +#: mod/settings.php:1267 msgid "By default post a status message when:" msgstr "" -#: mod/settings.php:1264 +#: mod/settings.php:1268 msgid "accepting a friend request" msgstr "" -#: mod/settings.php:1265 +#: mod/settings.php:1269 msgid "joining a forum/community" msgstr "" -#: mod/settings.php:1266 +#: mod/settings.php:1270 msgid "making an interesting profile change" msgstr "" -#: mod/settings.php:1267 +#: mod/settings.php:1271 msgid "Send a notification email when:" msgstr "" -#: mod/settings.php:1268 +#: mod/settings.php:1272 msgid "You receive an introduction" msgstr "" -#: mod/settings.php:1269 +#: mod/settings.php:1273 msgid "Your introductions are confirmed" msgstr "" -#: mod/settings.php:1270 +#: mod/settings.php:1274 msgid "Someone writes on your profile wall" msgstr "" -#: mod/settings.php:1271 +#: mod/settings.php:1275 msgid "Someone writes a followup comment" msgstr "" -#: mod/settings.php:1272 +#: mod/settings.php:1276 msgid "You receive a private message" msgstr "" -#: mod/settings.php:1273 +#: mod/settings.php:1277 msgid "You receive a friend suggestion" msgstr "" -#: mod/settings.php:1274 +#: mod/settings.php:1278 msgid "You are tagged in a post" msgstr "" -#: mod/settings.php:1275 +#: mod/settings.php:1279 msgid "You are poked/prodded/etc. in a post" msgstr "" -#: mod/settings.php:1277 +#: mod/settings.php:1281 msgid "Activate desktop notifications" msgstr "" -#: mod/settings.php:1277 +#: mod/settings.php:1281 msgid "Show desktop popup on new notifications" msgstr "" -#: mod/settings.php:1279 +#: mod/settings.php:1283 msgid "Text-only notification emails" msgstr "" -#: mod/settings.php:1281 +#: mod/settings.php:1285 msgid "Send text only notification emails, without the html part" msgstr "" -#: mod/settings.php:1283 +#: mod/settings.php:1287 msgid "Advanced Account/Page Type Settings" msgstr "" -#: mod/settings.php:1284 +#: mod/settings.php:1288 msgid "Change the behaviour of this account for special situations" msgstr "" -#: mod/settings.php:1287 +#: mod/settings.php:1291 msgid "Relocate" msgstr "" -#: mod/settings.php:1288 +#: mod/settings.php:1292 msgid "" "If you have moved this profile from another server, and some of your " "contacts don't receive your updates, try pushing this button." msgstr "" -#: mod/settings.php:1289 +#: mod/settings.php:1293 msgid "Resend relocate message to contacts" msgstr "" -#: mod/dfrn_request.php:96 +#: mod/dfrn_request.php:98 msgid "This introduction has already been accepted." msgstr "" -#: mod/dfrn_request.php:119 mod/dfrn_request.php:516 +#: mod/dfrn_request.php:121 mod/dfrn_request.php:514 msgid "Profile location is not valid or does not contain profile information." msgstr "" -#: mod/dfrn_request.php:124 mod/dfrn_request.php:521 +#: mod/dfrn_request.php:126 mod/dfrn_request.php:519 msgid "Warning: profile location has no identifiable owner name." msgstr "" -#: mod/dfrn_request.php:126 mod/dfrn_request.php:523 +#: mod/dfrn_request.php:128 mod/dfrn_request.php:521 msgid "Warning: profile location has no profile photo." msgstr "" -#: mod/dfrn_request.php:129 mod/dfrn_request.php:526 +#: mod/dfrn_request.php:131 mod/dfrn_request.php:524 #, php-format msgid "%d required parameter was not found at the given location" msgid_plural "%d required parameters were not found at the given location" msgstr[0] "" msgstr[1] "" -#: mod/dfrn_request.php:172 +#: mod/dfrn_request.php:174 msgid "Introduction complete." msgstr "" @@ -4909,93 +4965,93 @@ msgstr "" msgid "This account has not been configured for email. Request failed." msgstr "" -#: mod/dfrn_request.php:474 +#: mod/dfrn_request.php:472 msgid "You have already introduced yourself here." msgstr "" -#: mod/dfrn_request.php:478 +#: mod/dfrn_request.php:476 #, php-format msgid "Apparently you are already friends with %s." msgstr "" -#: mod/dfrn_request.php:499 +#: mod/dfrn_request.php:497 msgid "Invalid profile URL." msgstr "" -#: mod/dfrn_request.php:505 include/follow.php:72 +#: mod/dfrn_request.php:503 include/follow.php:76 msgid "Disallowed profile URL." msgstr "" -#: mod/dfrn_request.php:596 +#: mod/dfrn_request.php:594 msgid "Your introduction has been sent." msgstr "" -#: mod/dfrn_request.php:636 +#: mod/dfrn_request.php:634 msgid "" "Remote subscription can't be done for your network. Please subscribe " "directly on your system." msgstr "" -#: mod/dfrn_request.php:659 +#: mod/dfrn_request.php:657 msgid "Please login to confirm introduction." msgstr "" -#: mod/dfrn_request.php:669 +#: mod/dfrn_request.php:667 msgid "" "Incorrect identity currently logged in. Please login to this profile." msgstr "" -#: mod/dfrn_request.php:683 mod/dfrn_request.php:700 +#: mod/dfrn_request.php:681 mod/dfrn_request.php:698 msgid "Confirm" msgstr "" -#: mod/dfrn_request.php:695 +#: mod/dfrn_request.php:693 msgid "Hide this contact" msgstr "" -#: mod/dfrn_request.php:698 +#: mod/dfrn_request.php:696 #, php-format msgid "Welcome home %s." msgstr "" -#: mod/dfrn_request.php:699 +#: mod/dfrn_request.php:697 #, php-format msgid "Please confirm your introduction/connection request to %s." msgstr "" -#: mod/dfrn_request.php:828 +#: mod/dfrn_request.php:826 msgid "" "Please enter your 'Identity Address' from one of the following supported " "communications networks:" msgstr "" -#: mod/dfrn_request.php:849 +#: mod/dfrn_request.php:847 #, php-format msgid "" "If you are not yet a member of the free social web, follow this link to find a public Friendica site and join us today." msgstr "" -#: mod/dfrn_request.php:854 +#: mod/dfrn_request.php:852 msgid "Friend/Connection Request" msgstr "" -#: mod/dfrn_request.php:855 +#: mod/dfrn_request.php:853 msgid "" "Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, " "testuser@identi.ca" msgstr "" -#: mod/dfrn_request.php:863 include/contact_selectors.php:76 +#: mod/dfrn_request.php:861 include/contact_selectors.php:76 msgid "Friendica" msgstr "" -#: mod/dfrn_request.php:864 +#: mod/dfrn_request.php:862 msgid "StatusNet/Federated Social Web" msgstr "" -#: mod/dfrn_request.php:866 +#: mod/dfrn_request.php:864 #, php-format msgid "" " - please do not use this form. Instead, enter %s into your Diaspora search " @@ -5083,7 +5139,7 @@ msgstr "" msgid "Choose a nickname: " msgstr "" -#: mod/register.php:280 boot.php:1405 include/nav.php:108 +#: mod/register.php:280 boot.php:1495 include/nav.php:108 msgid "Register" msgstr "" @@ -5111,7 +5167,7 @@ msgstr "" msgid "Only one search per minute is permitted for not logged in users." msgstr "" -#: mod/search.php:136 include/text.php:1003 include/nav.php:118 +#: mod/search.php:136 include/text.php:974 include/nav.php:118 msgid "Search" msgstr "" @@ -5125,16 +5181,16 @@ msgstr "" msgid "Search results for: %s" msgstr "" -#: mod/directory.php:149 include/identity.php:313 include/identity.php:610 +#: mod/directory.php:149 include/identity.php:314 include/identity.php:611 msgid "Status:" msgstr "" -#: mod/directory.php:151 include/identity.php:315 include/identity.php:621 +#: mod/directory.php:151 include/identity.php:316 include/identity.php:622 msgid "Homepage:" msgstr "" #: mod/directory.php:203 view/theme/diabook/theme.php:525 -#: view/theme/vier/theme.php:205 +#: view/theme/vier/theme.php:201 msgid "Global Directory" msgstr "" @@ -5193,21 +5249,21 @@ msgstr "" msgid "No contacts in common." msgstr "" -#: mod/uexport.php:77 +#: mod/uexport.php:29 msgid "Export account" msgstr "" -#: mod/uexport.php:77 +#: mod/uexport.php:29 msgid "" "Export your account info and contacts. Use this to make a backup of your " "account and/or to move it to another server." msgstr "" -#: mod/uexport.php:78 +#: mod/uexport.php:30 msgid "Export all" msgstr "" -#: mod/uexport.php:78 +#: mod/uexport.php:30 msgid "" "Export your accout info, contacts and all your items as json. Could be a " "very big file, and could take a lot of time. Use this to make a full backup " @@ -5242,7 +5298,7 @@ msgid "Ignore/Hide" msgstr "" #: mod/suggest.php:111 include/contact_widgets.php:35 -#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:207 +#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:203 msgid "Friend Suggestions" msgstr "" @@ -5274,11 +5330,11 @@ msgstr "" msgid "Romantic Partner" msgstr "" -#: mod/profiles.php:344 mod/photos.php:1647 include/conversation.php:508 +#: mod/profiles.php:344 mod/photos.php:1633 include/conversation.php:508 msgid "Likes" msgstr "" -#: mod/profiles.php:348 mod/photos.php:1647 include/conversation.php:508 +#: mod/profiles.php:348 mod/photos.php:1633 include/conversation.php:508 msgid "Dislikes" msgstr "" @@ -5306,7 +5362,7 @@ msgstr "" msgid "Homepage" msgstr "" -#: mod/profiles.php:375 mod/profiles.php:708 +#: mod/profiles.php:375 mod/profiles.php:686 msgid "Interests" msgstr "" @@ -5314,7 +5370,7 @@ msgstr "" msgid "Address" msgstr "" -#: mod/profiles.php:386 mod/profiles.php:704 +#: mod/profiles.php:386 mod/profiles.php:682 msgid "Location" msgstr "" @@ -5322,260 +5378,260 @@ msgstr "" msgid "Profile updated." msgstr "" -#: mod/profiles.php:565 +#: mod/profiles.php:551 msgid " and " msgstr "" -#: mod/profiles.php:573 +#: mod/profiles.php:559 msgid "public profile" msgstr "" -#: mod/profiles.php:576 +#: mod/profiles.php:562 #, php-format msgid "%1$s changed %2$s to “%3$s”" msgstr "" -#: mod/profiles.php:577 +#: mod/profiles.php:563 #, php-format msgid " - Visit %1$s's %2$s" msgstr "" -#: mod/profiles.php:580 +#: mod/profiles.php:566 #, php-format msgid "%1$s has an updated %2$s, changing %3$s." msgstr "" -#: mod/profiles.php:655 +#: mod/profiles.php:633 msgid "Hide contacts and friends:" msgstr "" -#: mod/profiles.php:660 +#: mod/profiles.php:638 msgid "Hide your contact/friend list from viewers of this profile?" msgstr "" -#: mod/profiles.php:684 +#: mod/profiles.php:662 msgid "Show more profile fields:" msgstr "" -#: mod/profiles.php:695 +#: mod/profiles.php:673 msgid "Edit Profile Details" msgstr "" -#: mod/profiles.php:697 +#: mod/profiles.php:675 msgid "Change Profile Photo" msgstr "" -#: mod/profiles.php:698 +#: mod/profiles.php:676 msgid "View this profile" msgstr "" -#: mod/profiles.php:699 +#: mod/profiles.php:677 msgid "Create a new profile using these settings" msgstr "" -#: mod/profiles.php:700 +#: mod/profiles.php:678 msgid "Clone this profile" msgstr "" -#: mod/profiles.php:701 +#: mod/profiles.php:679 msgid "Delete this profile" msgstr "" -#: mod/profiles.php:702 +#: mod/profiles.php:680 msgid "Basic information" msgstr "" -#: mod/profiles.php:703 +#: mod/profiles.php:681 msgid "Profile picture" msgstr "" -#: mod/profiles.php:705 +#: mod/profiles.php:683 msgid "Preferences" msgstr "" -#: mod/profiles.php:706 +#: mod/profiles.php:684 msgid "Status information" msgstr "" -#: mod/profiles.php:707 +#: mod/profiles.php:685 msgid "Additional information" msgstr "" -#: mod/profiles.php:710 +#: mod/profiles.php:688 msgid "Profile Name:" msgstr "" -#: mod/profiles.php:711 +#: mod/profiles.php:689 msgid "Your Full Name:" msgstr "" -#: mod/profiles.php:712 +#: mod/profiles.php:690 msgid "Title/Description:" msgstr "" -#: mod/profiles.php:713 +#: mod/profiles.php:691 msgid "Your Gender:" msgstr "" -#: mod/profiles.php:714 +#: mod/profiles.php:692 msgid "Birthday :" msgstr "" -#: mod/profiles.php:715 +#: mod/profiles.php:693 msgid "Street Address:" msgstr "" -#: mod/profiles.php:716 +#: mod/profiles.php:694 msgid "Locality/City:" msgstr "" -#: mod/profiles.php:717 +#: mod/profiles.php:695 msgid "Postal/Zip Code:" msgstr "" -#: mod/profiles.php:718 +#: mod/profiles.php:696 msgid "Country:" msgstr "" -#: mod/profiles.php:719 +#: mod/profiles.php:697 msgid "Region/State:" msgstr "" -#: mod/profiles.php:720 +#: mod/profiles.php:698 msgid " Marital Status:" msgstr "" -#: mod/profiles.php:721 +#: mod/profiles.php:699 msgid "Who: (if applicable)" msgstr "" -#: mod/profiles.php:722 +#: mod/profiles.php:700 msgid "Examples: cathy123, Cathy Williams, cathy@example.com" msgstr "" -#: mod/profiles.php:723 +#: mod/profiles.php:701 msgid "Since [date]:" msgstr "" -#: mod/profiles.php:724 include/identity.php:619 +#: mod/profiles.php:702 include/identity.php:620 msgid "Sexual Preference:" msgstr "" -#: mod/profiles.php:725 +#: mod/profiles.php:703 msgid "Homepage URL:" msgstr "" -#: mod/profiles.php:726 include/identity.php:623 +#: mod/profiles.php:704 include/identity.php:624 msgid "Hometown:" msgstr "" -#: mod/profiles.php:727 include/identity.php:627 +#: mod/profiles.php:705 include/identity.php:628 msgid "Political Views:" msgstr "" -#: mod/profiles.php:728 +#: mod/profiles.php:706 msgid "Religious Views:" msgstr "" -#: mod/profiles.php:729 +#: mod/profiles.php:707 msgid "Public Keywords:" msgstr "" -#: mod/profiles.php:730 +#: mod/profiles.php:708 msgid "Private Keywords:" msgstr "" -#: mod/profiles.php:731 include/identity.php:635 +#: mod/profiles.php:709 include/identity.php:636 msgid "Likes:" msgstr "" -#: mod/profiles.php:732 include/identity.php:637 +#: mod/profiles.php:710 include/identity.php:638 msgid "Dislikes:" msgstr "" -#: mod/profiles.php:733 +#: mod/profiles.php:711 msgid "Example: fishing photography software" msgstr "" -#: mod/profiles.php:734 +#: mod/profiles.php:712 msgid "(Used for suggesting potential friends, can be seen by others)" msgstr "" -#: mod/profiles.php:735 +#: mod/profiles.php:713 msgid "(Used for searching profiles, never shown to others)" msgstr "" -#: mod/profiles.php:736 +#: mod/profiles.php:714 msgid "Tell us about yourself..." msgstr "" -#: mod/profiles.php:737 +#: mod/profiles.php:715 msgid "Hobbies/Interests" msgstr "" -#: mod/profiles.php:738 +#: mod/profiles.php:716 msgid "Contact information and Social Networks" msgstr "" -#: mod/profiles.php:739 +#: mod/profiles.php:717 msgid "Musical interests" msgstr "" -#: mod/profiles.php:740 +#: mod/profiles.php:718 msgid "Books, literature" msgstr "" -#: mod/profiles.php:741 +#: mod/profiles.php:719 msgid "Television" msgstr "" -#: mod/profiles.php:742 +#: mod/profiles.php:720 msgid "Film/dance/culture/entertainment" msgstr "" -#: mod/profiles.php:743 +#: mod/profiles.php:721 msgid "Love/romance" msgstr "" -#: mod/profiles.php:744 +#: mod/profiles.php:722 msgid "Work/employment" msgstr "" -#: mod/profiles.php:745 +#: mod/profiles.php:723 msgid "School/education" msgstr "" -#: mod/profiles.php:750 +#: mod/profiles.php:728 msgid "" "This is your public profile.
            It may " "be visible to anybody using the internet." msgstr "" -#: mod/profiles.php:760 +#: mod/profiles.php:738 msgid "Age: " msgstr "" -#: mod/profiles.php:813 +#: mod/profiles.php:791 msgid "Edit/Manage Profiles" msgstr "" -#: mod/profiles.php:814 include/identity.php:260 include/identity.php:286 +#: mod/profiles.php:792 include/identity.php:261 include/identity.php:287 msgid "Change profile photo" msgstr "" -#: mod/profiles.php:815 include/identity.php:261 +#: mod/profiles.php:793 include/identity.php:262 msgid "Create New Profile" msgstr "" -#: mod/profiles.php:826 include/identity.php:271 +#: mod/profiles.php:804 include/identity.php:272 msgid "Profile Image" msgstr "" -#: mod/profiles.php:828 include/identity.php:274 +#: mod/profiles.php:806 include/identity.php:275 msgid "visible to everybody" msgstr "" -#: mod/profiles.php:829 include/identity.php:275 +#: mod/profiles.php:807 include/identity.php:276 msgid "Edit visibility" msgstr "" @@ -5721,7 +5777,7 @@ msgstr "" msgid "Visible to:" msgstr "" -#: mod/notes.php:46 include/identity.php:730 +#: mod/notes.php:46 include/identity.php:731 msgid "Personal Notes" msgstr "" @@ -5878,15 +5934,15 @@ msgid "" "important, please visit http://friendica.com" msgstr "" -#: mod/photos.php:99 include/identity.php:705 +#: mod/photos.php:99 include/identity.php:706 msgid "Photo Albums" msgstr "" -#: mod/photos.php:100 mod/photos.php:1899 +#: mod/photos.php:100 mod/photos.php:1885 msgid "Recent Photos" msgstr "" -#: mod/photos.php:103 mod/photos.php:1320 mod/photos.php:1901 +#: mod/photos.php:103 mod/photos.php:1306 mod/photos.php:1887 msgid "Upload New Photos" msgstr "" @@ -5898,7 +5954,7 @@ msgstr "" msgid "Album not found." msgstr "" -#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1262 +#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1248 msgid "Delete Album" msgstr "" @@ -5906,7 +5962,7 @@ msgstr "" msgid "Do you really want to delete this photo album and all its photos?" msgstr "" -#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1580 +#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1566 msgid "Delete Photo" msgstr "" @@ -5923,151 +5979,151 @@ msgstr "" msgid "a photo" msgstr "" -#: mod/photos.php:819 +#: mod/photos.php:813 msgid "Image file is empty." msgstr "" -#: mod/photos.php:986 +#: mod/photos.php:972 msgid "No photos selected" msgstr "" -#: mod/photos.php:1147 +#: mod/photos.php:1133 #, php-format msgid "You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage." msgstr "" -#: mod/photos.php:1182 +#: mod/photos.php:1168 msgid "Upload Photos" msgstr "" -#: mod/photos.php:1186 mod/photos.php:1257 +#: mod/photos.php:1172 mod/photos.php:1243 msgid "New album name: " msgstr "" -#: mod/photos.php:1187 +#: mod/photos.php:1173 msgid "or existing album name: " msgstr "" -#: mod/photos.php:1188 +#: mod/photos.php:1174 msgid "Do not show a status post for this upload" msgstr "" -#: mod/photos.php:1190 mod/photos.php:1575 include/acl_selectors.php:347 +#: mod/photos.php:1176 mod/photos.php:1561 include/acl_selectors.php:347 msgid "Permissions" msgstr "" -#: mod/photos.php:1201 +#: mod/photos.php:1187 msgid "Private Photo" msgstr "" -#: mod/photos.php:1202 +#: mod/photos.php:1188 msgid "Public Photo" msgstr "" -#: mod/photos.php:1270 +#: mod/photos.php:1256 msgid "Edit Album" msgstr "" -#: mod/photos.php:1276 +#: mod/photos.php:1262 msgid "Show Newest First" msgstr "" -#: mod/photos.php:1278 +#: mod/photos.php:1264 msgid "Show Oldest First" msgstr "" -#: mod/photos.php:1306 mod/photos.php:1884 +#: mod/photos.php:1292 mod/photos.php:1870 msgid "View Photo" msgstr "" -#: mod/photos.php:1353 +#: mod/photos.php:1339 msgid "Permission denied. Access to this item may be restricted." msgstr "" -#: mod/photos.php:1355 +#: mod/photos.php:1341 msgid "Photo not available" msgstr "" -#: mod/photos.php:1411 +#: mod/photos.php:1397 msgid "View photo" msgstr "" -#: mod/photos.php:1411 +#: mod/photos.php:1397 msgid "Edit photo" msgstr "" -#: mod/photos.php:1412 +#: mod/photos.php:1398 msgid "Use as profile photo" msgstr "" -#: mod/photos.php:1437 +#: mod/photos.php:1423 msgid "View Full Size" msgstr "" -#: mod/photos.php:1523 +#: mod/photos.php:1509 msgid "Tags: " msgstr "" -#: mod/photos.php:1526 +#: mod/photos.php:1512 msgid "[Remove any tag]" msgstr "" -#: mod/photos.php:1566 +#: mod/photos.php:1552 msgid "New album name" msgstr "" -#: mod/photos.php:1567 +#: mod/photos.php:1553 msgid "Caption" msgstr "" -#: mod/photos.php:1568 +#: mod/photos.php:1554 msgid "Add a Tag" msgstr "" -#: mod/photos.php:1568 +#: mod/photos.php:1554 msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" msgstr "" -#: mod/photos.php:1569 +#: mod/photos.php:1555 msgid "Do not rotate" msgstr "" -#: mod/photos.php:1570 +#: mod/photos.php:1556 msgid "Rotate CW (right)" msgstr "" -#: mod/photos.php:1571 +#: mod/photos.php:1557 msgid "Rotate CCW (left)" msgstr "" -#: mod/photos.php:1586 +#: mod/photos.php:1572 msgid "Private photo" msgstr "" -#: mod/photos.php:1587 +#: mod/photos.php:1573 msgid "Public photo" msgstr "" -#: mod/photos.php:1609 include/conversation.php:1182 +#: mod/photos.php:1595 include/conversation.php:1182 msgid "Share" msgstr "" -#: mod/photos.php:1648 include/conversation.php:509 +#: mod/photos.php:1634 include/conversation.php:509 #: include/conversation.php:1413 msgid "Attending" msgid_plural "Attending" msgstr[0] "" msgstr[1] "" -#: mod/photos.php:1648 include/conversation.php:509 +#: mod/photos.php:1634 include/conversation.php:509 msgid "Not attending" msgstr "" -#: mod/photos.php:1648 include/conversation.php:509 +#: mod/photos.php:1634 include/conversation.php:509 msgid "Might attend" msgstr "" -#: mod/photos.php:1813 +#: mod/photos.php:1799 msgid "Map" msgstr "" @@ -6127,60 +6183,60 @@ msgstr "" msgid "Item was not found." msgstr "" -#: boot.php:868 +#: boot.php:870 msgid "Delete this item?" msgstr "" -#: boot.php:871 +#: boot.php:873 msgid "show fewer" msgstr "" -#: boot.php:1292 +#: boot.php:1382 #, php-format msgid "Update %s failed. See error logs." msgstr "" -#: boot.php:1404 +#: boot.php:1494 msgid "Create a New Account" msgstr "" -#: boot.php:1429 include/nav.php:72 +#: boot.php:1519 include/nav.php:72 msgid "Logout" msgstr "" -#: boot.php:1432 +#: boot.php:1522 msgid "Nickname or Email address: " msgstr "" -#: boot.php:1433 +#: boot.php:1523 msgid "Password: " msgstr "" -#: boot.php:1434 +#: boot.php:1524 msgid "Remember me" msgstr "" -#: boot.php:1437 +#: boot.php:1527 msgid "Or login using OpenID: " msgstr "" -#: boot.php:1443 +#: boot.php:1533 msgid "Forgot your password?" msgstr "" -#: boot.php:1446 +#: boot.php:1536 msgid "Website Terms of Service" msgstr "" -#: boot.php:1447 +#: boot.php:1537 msgid "terms of service" msgstr "" -#: boot.php:1449 +#: boot.php:1539 msgid "Website Privacy Policy" msgstr "" -#: boot.php:1450 +#: boot.php:1540 msgid "privacy policy" msgstr "" @@ -6224,6 +6280,16 @@ msgstr "" msgid "via" msgstr "" +#: include/dfrn.php:1092 +#, php-format +msgid "%s\\'s birthday" +msgstr "" + +#: include/dfrn.php:1093 include/datetime.php:565 +#, php-format +msgid "Happy Birthday %s" +msgstr "" + #: include/dbstructure.php:26 #, php-format msgid "" @@ -6296,7 +6362,7 @@ msgid "Examples: Robert Morgenstein, Fishing" msgstr "" #: include/contact_widgets.php:36 view/theme/diabook/theme.php:526 -#: view/theme/vier/theme.php:206 +#: view/theme/vier/theme.php:202 msgid "Similar Interests" msgstr "" @@ -6305,7 +6371,7 @@ msgid "Random Profile" msgstr "" #: include/contact_widgets.php:38 view/theme/diabook/theme.php:528 -#: view/theme/vier/theme.php:208 +#: view/theme/vier/theme.php:204 msgid "Invite Friends" msgstr "" @@ -6527,58 +6593,58 @@ msgstr "" msgid "Show visitors public community forums at the Advanced Profile Page" msgstr "" -#: include/follow.php:77 +#: include/follow.php:81 msgid "Connect URL missing." msgstr "" -#: include/follow.php:104 +#: include/follow.php:108 msgid "" "This site is not configured to allow communications with other networks." msgstr "" -#: include/follow.php:105 include/follow.php:125 +#: include/follow.php:109 include/follow.php:129 msgid "No compatible communication protocols or feeds were discovered." msgstr "" -#: include/follow.php:123 +#: include/follow.php:127 msgid "The profile address specified does not provide adequate information." msgstr "" -#: include/follow.php:127 +#: include/follow.php:131 msgid "An author or name was not found." msgstr "" -#: include/follow.php:129 +#: include/follow.php:133 msgid "No browser URL could be matched to this address." msgstr "" -#: include/follow.php:131 +#: include/follow.php:135 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: include/follow.php:132 +#: include/follow.php:136 msgid "Use mailto: in front of address to force email check." msgstr "" -#: include/follow.php:138 +#: include/follow.php:142 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "" -#: include/follow.php:148 +#: include/follow.php:152 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." msgstr "" -#: include/follow.php:249 +#: include/follow.php:253 msgid "Unable to retrieve contact information." msgstr "" -#: include/follow.php:302 +#: include/follow.php:288 msgid "following" msgstr "" @@ -6593,245 +6659,240 @@ msgstr "" msgid "Default privacy group for new contacts" msgstr "" -#: include/group.php:239 +#: include/group.php:242 msgid "Everybody" msgstr "" -#: include/group.php:262 +#: include/group.php:265 msgid "edit" msgstr "" -#: include/group.php:285 +#: include/group.php:288 msgid "Edit groups" msgstr "" -#: include/group.php:287 +#: include/group.php:290 msgid "Edit group" msgstr "" -#: include/group.php:288 +#: include/group.php:291 msgid "Create a new group" msgstr "" -#: include/group.php:291 +#: include/group.php:294 msgid "Contacts not in any group" msgstr "" -#: include/datetime.php:43 include/datetime.php:45 +#: include/datetime.php:57 include/datetime.php:59 msgid "Miscellaneous" msgstr "" -#: include/datetime.php:141 +#: include/datetime.php:178 msgid "YYYY-MM-DD or MM-DD" msgstr "" -#: include/datetime.php:271 +#: include/datetime.php:327 msgid "never" msgstr "" -#: include/datetime.php:277 +#: include/datetime.php:333 msgid "less than a second ago" msgstr "" -#: include/datetime.php:287 +#: include/datetime.php:343 msgid "year" msgstr "" -#: include/datetime.php:287 +#: include/datetime.php:343 msgid "years" msgstr "" -#: include/datetime.php:288 +#: include/datetime.php:344 msgid "months" msgstr "" -#: include/datetime.php:289 +#: include/datetime.php:345 msgid "weeks" msgstr "" -#: include/datetime.php:290 +#: include/datetime.php:346 msgid "days" msgstr "" -#: include/datetime.php:291 +#: include/datetime.php:347 msgid "hour" msgstr "" -#: include/datetime.php:291 +#: include/datetime.php:347 msgid "hours" msgstr "" -#: include/datetime.php:292 +#: include/datetime.php:348 msgid "minute" msgstr "" -#: include/datetime.php:292 +#: include/datetime.php:348 msgid "minutes" msgstr "" -#: include/datetime.php:293 +#: include/datetime.php:349 msgid "second" msgstr "" -#: include/datetime.php:293 +#: include/datetime.php:349 msgid "seconds" msgstr "" -#: include/datetime.php:302 +#: include/datetime.php:358 #, php-format msgid "%1$d %2$s ago" msgstr "" -#: include/datetime.php:474 include/items.php:2500 +#: include/datetime.php:564 #, php-format msgid "%s's birthday" msgstr "" -#: include/datetime.php:475 include/items.php:2501 -#, php-format -msgid "Happy Birthday %s" -msgstr "" - #: include/identity.php:42 msgid "Requested account is not available." msgstr "" -#: include/identity.php:95 include/identity.php:284 include/identity.php:662 +#: include/identity.php:95 include/identity.php:285 include/identity.php:663 msgid "Edit profile" msgstr "" -#: include/identity.php:244 +#: include/identity.php:245 msgid "Atom feed" msgstr "" -#: include/identity.php:249 +#: include/identity.php:250 msgid "Message" msgstr "" -#: include/identity.php:255 include/nav.php:185 +#: include/identity.php:256 include/nav.php:185 msgid "Profiles" msgstr "" -#: include/identity.php:255 +#: include/identity.php:256 msgid "Manage/edit profiles" msgstr "" -#: include/identity.php:425 include/identity.php:509 +#: include/identity.php:426 include/identity.php:510 msgid "g A l F d" msgstr "" -#: include/identity.php:426 include/identity.php:510 +#: include/identity.php:427 include/identity.php:511 msgid "F d" msgstr "" -#: include/identity.php:471 include/identity.php:556 +#: include/identity.php:472 include/identity.php:557 msgid "[today]" msgstr "" -#: include/identity.php:483 +#: include/identity.php:484 msgid "Birthday Reminders" msgstr "" -#: include/identity.php:484 +#: include/identity.php:485 msgid "Birthdays this week:" msgstr "" -#: include/identity.php:543 +#: include/identity.php:544 msgid "[No description]" msgstr "" -#: include/identity.php:567 +#: include/identity.php:568 msgid "Event Reminders" msgstr "" -#: include/identity.php:568 +#: include/identity.php:569 msgid "Events this week:" msgstr "" -#: include/identity.php:595 +#: include/identity.php:596 msgid "j F, Y" msgstr "" -#: include/identity.php:596 +#: include/identity.php:597 msgid "j F" msgstr "" -#: include/identity.php:603 +#: include/identity.php:604 msgid "Birthday:" msgstr "" -#: include/identity.php:607 +#: include/identity.php:608 msgid "Age:" msgstr "" -#: include/identity.php:616 +#: include/identity.php:617 #, php-format msgid "for %1$d %2$s" msgstr "" -#: include/identity.php:629 +#: include/identity.php:630 msgid "Religion:" msgstr "" -#: include/identity.php:633 +#: include/identity.php:634 msgid "Hobbies/Interests:" msgstr "" -#: include/identity.php:640 +#: include/identity.php:641 msgid "Contact information and Social Networks:" msgstr "" -#: include/identity.php:642 +#: include/identity.php:643 msgid "Musical interests:" msgstr "" -#: include/identity.php:644 +#: include/identity.php:645 msgid "Books, literature:" msgstr "" -#: include/identity.php:646 +#: include/identity.php:647 msgid "Television:" msgstr "" -#: include/identity.php:648 +#: include/identity.php:649 msgid "Film/dance/culture/entertainment:" msgstr "" -#: include/identity.php:650 +#: include/identity.php:651 msgid "Love/Romance:" msgstr "" -#: include/identity.php:652 +#: include/identity.php:653 msgid "Work/employment:" msgstr "" -#: include/identity.php:654 +#: include/identity.php:655 msgid "School/education:" msgstr "" -#: include/identity.php:658 +#: include/identity.php:659 msgid "Forums:" msgstr "" -#: include/identity.php:710 include/identity.php:713 include/nav.php:78 +#: include/identity.php:711 include/identity.php:714 include/nav.php:78 msgid "Videos" msgstr "" -#: include/identity.php:725 include/nav.php:140 +#: include/identity.php:726 include/nav.php:140 msgid "Events and Calendar" msgstr "" -#: include/identity.php:733 +#: include/identity.php:734 msgid "Only You Can See This" msgstr "" #: include/like.php:167 include/conversation.php:122 -#: include/conversation.php:258 include/text.php:1998 +#: include/conversation.php:258 include/text.php:1921 #: view/theme/diabook/theme.php:463 msgid "event" msgstr "" -#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2185 +#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2133 #: view/theme/diabook/theme.php:480 #, php-format msgid "%1$s likes %2$s's %3$s" @@ -6892,31 +6953,31 @@ msgstr "" msgid "stopped following" msgstr "" -#: include/Contact.php:337 include/conversation.php:911 +#: include/Contact.php:339 include/conversation.php:911 msgid "View Status" msgstr "" -#: include/Contact.php:339 include/conversation.php:913 +#: include/Contact.php:341 include/conversation.php:913 msgid "View Photos" msgstr "" -#: include/Contact.php:340 include/conversation.php:914 +#: include/Contact.php:342 include/conversation.php:914 msgid "Network Posts" msgstr "" -#: include/Contact.php:341 include/conversation.php:915 +#: include/Contact.php:343 include/conversation.php:915 msgid "Edit Contact" msgstr "" -#: include/Contact.php:342 +#: include/Contact.php:344 msgid "Drop Contact" msgstr "" -#: include/Contact.php:343 include/conversation.php:916 +#: include/Contact.php:345 include/conversation.php:916 msgid "Send PM" msgstr "" -#: include/Contact.php:344 include/conversation.php:920 +#: include/Contact.php:346 include/conversation.php:920 msgid "Poke" msgstr "" @@ -7131,16 +7192,7 @@ msgid_plural "Undecided" msgstr[0] "" msgstr[1] "" -#: include/forums.php:105 include/text.php:1015 include/nav.php:126 -#: view/theme/vier/theme.php:259 -msgid "Forums" -msgstr "" - -#: include/forums.php:107 view/theme/vier/theme.php:261 -msgid "External link to forum" -msgstr "" - -#: include/network.php:967 +#: include/network.php:975 msgid "view full size" msgstr "" @@ -7176,186 +7228,191 @@ msgstr "" msgid "The end" msgstr "" -#: include/text.php:894 +#: include/text.php:865 msgid "No contacts" msgstr "" -#: include/text.php:909 +#: include/text.php:880 #, php-format msgid "%d Contact" msgid_plural "%d Contacts" msgstr[0] "" msgstr[1] "" -#: include/text.php:921 +#: include/text.php:892 msgid "View Contacts" msgstr "" -#: include/text.php:1010 include/nav.php:121 +#: include/text.php:981 include/nav.php:121 msgid "Full Text" msgstr "" -#: include/text.php:1011 include/nav.php:122 +#: include/text.php:982 include/nav.php:122 msgid "Tags" msgstr "" -#: include/text.php:1066 +#: include/text.php:986 include/ForumManager.php:112 include/nav.php:126 +#: view/theme/vier/theme.php:255 +msgid "Forums" +msgstr "" + +#: include/text.php:1037 msgid "poke" msgstr "" -#: include/text.php:1066 +#: include/text.php:1037 msgid "poked" msgstr "" -#: include/text.php:1067 +#: include/text.php:1038 msgid "ping" msgstr "" -#: include/text.php:1067 +#: include/text.php:1038 msgid "pinged" msgstr "" -#: include/text.php:1068 +#: include/text.php:1039 msgid "prod" msgstr "" -#: include/text.php:1068 +#: include/text.php:1039 msgid "prodded" msgstr "" -#: include/text.php:1069 +#: include/text.php:1040 msgid "slap" msgstr "" -#: include/text.php:1069 +#: include/text.php:1040 msgid "slapped" msgstr "" -#: include/text.php:1070 +#: include/text.php:1041 msgid "finger" msgstr "" -#: include/text.php:1070 +#: include/text.php:1041 msgid "fingered" msgstr "" -#: include/text.php:1071 +#: include/text.php:1042 msgid "rebuff" msgstr "" -#: include/text.php:1071 +#: include/text.php:1042 msgid "rebuffed" msgstr "" -#: include/text.php:1085 +#: include/text.php:1056 msgid "happy" msgstr "" -#: include/text.php:1086 +#: include/text.php:1057 msgid "sad" msgstr "" -#: include/text.php:1087 +#: include/text.php:1058 msgid "mellow" msgstr "" -#: include/text.php:1088 +#: include/text.php:1059 msgid "tired" msgstr "" -#: include/text.php:1089 +#: include/text.php:1060 msgid "perky" msgstr "" -#: include/text.php:1090 +#: include/text.php:1061 msgid "angry" msgstr "" -#: include/text.php:1091 +#: include/text.php:1062 msgid "stupified" msgstr "" -#: include/text.php:1092 +#: include/text.php:1063 msgid "puzzled" msgstr "" -#: include/text.php:1093 +#: include/text.php:1064 msgid "interested" msgstr "" -#: include/text.php:1094 +#: include/text.php:1065 msgid "bitter" msgstr "" -#: include/text.php:1095 +#: include/text.php:1066 msgid "cheerful" msgstr "" -#: include/text.php:1096 +#: include/text.php:1067 msgid "alive" msgstr "" -#: include/text.php:1097 +#: include/text.php:1068 msgid "annoyed" msgstr "" -#: include/text.php:1098 +#: include/text.php:1069 msgid "anxious" msgstr "" -#: include/text.php:1099 +#: include/text.php:1070 msgid "cranky" msgstr "" -#: include/text.php:1100 +#: include/text.php:1071 msgid "disturbed" msgstr "" -#: include/text.php:1101 +#: include/text.php:1072 msgid "frustrated" msgstr "" -#: include/text.php:1102 +#: include/text.php:1073 msgid "motivated" msgstr "" -#: include/text.php:1103 +#: include/text.php:1074 msgid "relaxed" msgstr "" -#: include/text.php:1104 +#: include/text.php:1075 msgid "surprised" msgstr "" -#: include/text.php:1504 +#: include/text.php:1475 msgid "bytes" msgstr "" -#: include/text.php:1536 include/text.php:1548 +#: include/text.php:1507 include/text.php:1519 msgid "Click to open/close" msgstr "" -#: include/text.php:1722 +#: include/text.php:1645 msgid "View on separate page" msgstr "" -#: include/text.php:1723 +#: include/text.php:1646 msgid "view on separate page" msgstr "" -#: include/text.php:2002 +#: include/text.php:1925 msgid "activity" msgstr "" -#: include/text.php:2005 +#: include/text.php:1928 msgid "post" msgstr "" -#: include/text.php:2173 +#: include/text.php:2096 msgid "Item filed" msgstr "" -#: include/bbcode.php:482 include/bbcode.php:1157 include/bbcode.php:1158 +#: include/bbcode.php:482 include/bbcode.php:1159 include/bbcode.php:1160 msgid "Image/photo" msgstr "" @@ -7371,11 +7428,11 @@ msgid "" "\"%s\" target=\"_blank\">post" msgstr "" -#: include/bbcode.php:1117 include/bbcode.php:1137 +#: include/bbcode.php:1119 include/bbcode.php:1139 msgid "$1 wrote:" msgstr "" -#: include/bbcode.php:1166 include/bbcode.php:1167 +#: include/bbcode.php:1168 include/bbcode.php:1169 msgid "Encrypted content" msgstr "" @@ -7469,10 +7526,10 @@ msgid "App.net" msgstr "" #: include/contact_selectors.php:103 -msgid "Redmatrix" +msgid "Hubzilla/Redmatrix" msgstr "" -#: include/Scrape.php:624 +#: include/Scrape.php:623 msgid " on Last.fm" msgstr "" @@ -7496,6 +7553,10 @@ msgstr "" msgid "This action is not available under your subscription plan." msgstr "" +#: include/ForumManager.php:114 view/theme/vier/theme.php:257 +msgid "External link to forum" +msgstr "" + #: include/nav.php:72 msgid "End this session" msgstr "" @@ -7648,17 +7709,17 @@ msgstr "" msgid "Site map" msgstr "" -#: include/api.php:878 +#: include/api.php:906 #, php-format msgid "Daily posting limit of %d posts reached. The post was rejected." msgstr "" -#: include/api.php:897 +#: include/api.php:926 #, php-format msgid "Weekly posting limit of %d posts reached. The post was rejected." msgstr "" -#: include/api.php:916 +#: include/api.php:947 #, php-format msgid "Monthly posting limit of %d posts reached. The post was rejected." msgstr "" @@ -7781,27 +7842,27 @@ msgid "" "\t\tThank you and welcome to %2$s." msgstr "" -#: include/diaspora.php:720 +#: include/diaspora.php:719 msgid "Sharing notification from Diaspora network" msgstr "" -#: include/diaspora.php:2625 +#: include/diaspora.php:2570 msgid "Attachments:" msgstr "" -#: include/delivery.php:533 +#: include/delivery.php:438 msgid "(no subject)" msgstr "" -#: include/delivery.php:544 include/enotify.php:37 +#: include/delivery.php:449 include/enotify.php:37 msgid "noreply" msgstr "" -#: include/items.php:4926 +#: include/items.php:1832 msgid "Do you really want to delete this item?" msgstr "" -#: include/items.php:5201 +#: include/items.php:2107 msgid "Archives" msgstr "" @@ -8361,7 +8422,7 @@ msgstr[1] "" msgid "Done. You can now login with your username and password" msgstr "" -#: index.php:442 +#: index.php:434 msgid "toggle mobile" msgstr "" @@ -8443,7 +8504,7 @@ msgstr "" #: view/theme/diabook/config.php:160 view/theme/diabook/theme.php:391 #: view/theme/diabook/theme.php:626 view/theme/vier/config.php:112 -#: view/theme/vier/theme.php:156 +#: view/theme/vier/theme.php:152 msgid "Community Profiles" msgstr "" @@ -8454,19 +8515,19 @@ msgstr "" #: view/theme/diabook/config.php:162 view/theme/diabook/theme.php:606 #: view/theme/diabook/theme.php:628 view/theme/vier/config.php:114 -#: view/theme/vier/theme.php:377 +#: view/theme/vier/theme.php:373 msgid "Connect Services" msgstr "" #: view/theme/diabook/config.php:163 view/theme/diabook/theme.php:523 #: view/theme/diabook/theme.php:629 view/theme/vier/config.php:115 -#: view/theme/vier/theme.php:203 +#: view/theme/vier/theme.php:199 msgid "Find Friends" msgstr "" #: view/theme/diabook/config.php:164 view/theme/diabook/theme.php:412 #: view/theme/diabook/theme.php:630 view/theme/vier/config.php:116 -#: view/theme/vier/theme.php:185 +#: view/theme/vier/theme.php:181 msgid "Last users" msgstr "" @@ -8488,7 +8549,7 @@ msgstr "" msgid "Your personal photos" msgstr "" -#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:204 +#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:200 msgid "Local Directory" msgstr "" @@ -8508,7 +8569,7 @@ msgstr "" msgid "Set style" msgstr "" -#: view/theme/vier/theme.php:295 +#: view/theme/vier/theme.php:291 msgid "Quick Start" msgstr "" diff --git a/view/default.php b/view/default.php index 78ca97ac94..df9adbc392 100644 --- a/view/default.php +++ b/view/default.php @@ -1,5 +1,5 @@ - + <?php if(x($page,'title')) echo $page['title'] ?> @@ -8,11 +8,12 @@ -
            +
            + +
            - diff --git a/view/global.css b/view/global.css index 7fa9947d30..f375e811b2 100644 --- a/view/global.css +++ b/view/global.css @@ -1,6 +1,32 @@ /* General style rules .*/ .pull-right { float: right } +/* General designing elements */ +.btn { + outline: none; + -moz-box-shadow: inset 0px 1px 0px 0px #ffffff; + -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff; + box-shadow: inset 0px 1px 0px 0px #ffffff; + background-color: #ededed; + text-indent: 0; + border: 1px solid #dcdcdc; + display: inline-block; + color: #777777; + padding: 5px 10px; + text-align: center; +} +a.btn, a.btn:hover { + text-decoration: none; + color: inherit; +} + +.menu-popup .divider { + height: 1px; + margin: 3px 0; + overflow: hidden; + background-color: #2d2d2d; +} + /* List of social Networks */ img.connector, img.connector-disabled { height: 40px; @@ -330,20 +356,20 @@ a { margin: 10px 0 10px; } .version-match { - font-weight: bold; - color: #00a700; + font-weight: bold; + color: #00a700; } .federation-graph { - width: 400px; - height: 400px; - float: right; - margin: 20px; + width: 400px; + height: 400px; + float: right; + margin: 20px; } .federation-network-graph { - width: 240px; - height: 240px; - float: left; - margin: 20px; + width: 240px; + height: 240px; + float: left; + margin: 20px; } ul.federation-stats, ul.credits { @@ -355,10 +381,10 @@ ul.credits li { width: 240px; } table#federation-stats { - width: 100%; + width: 100%; } td.federation-data { - border-bottom: 1px solid #000; + border-bottom: 1px solid #000; } .contact-entry-photo img { @@ -382,25 +408,48 @@ td.federation-data { } .crepair-label { - margin-top: 10px; - float: left; - width: 250px; + margin-top: 10px; + float: left; + width: 250px; } .crepair-input { - margin-top: 10px; - float: left; - width: 200px; + margin-top: 10px; + float: left; + width: 200px; } .renderinfo { - clear: both; + clear: both; } .p-addr { - clear: both; + clear: both; } #live-community { - clear: both; + clear: both; +} + +/* contact-edit */ +#contact-edit-status-wrapper { + border: 1px solid; + padding: 10px; +} +#contact-edit-actions { + float: right; + display: inline-block; + position: relative; +} +#contact-edit-actions > .menu-popup { + right: 0; + left: auto; +} + +#contact-edit-settings-label:after { + content: ' »'; +} + +#contact-edit-settings { + display: none; } diff --git a/view/it/messages.po b/view/it/messages.po index 5f0129fb0a..a9df298ca9 100644 --- a/view/it/messages.po +++ b/view/it/messages.po @@ -9,15 +9,15 @@ # fabrixxm , 2011-2012 # Francesco Apruzzese , 2012-2013 # ufic , 2012 -# tuscanhobbit , 2012 -# Sandro Santilli , 2015 +# Paolo Wave , 2012 +# Sandro Santilli , 2015-2016 msgid "" msgstr "" "Project-Id-Version: friendica\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-14 07:48+0100\n" -"PO-Revision-Date: 2015-12-14 13:05+0000\n" -"Last-Translator: fabrixxm \n" +"POT-Creation-Date: 2016-01-24 06:49+0100\n" +"PO-Revision-Date: 2016-02-16 10:29+0000\n" +"Last-Translator: Sandro Santilli \n" "Language-Team: Italian (http://www.transifex.com/Friendica/friendica/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,26 +25,26 @@ msgstr "" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: mod/contacts.php:50 include/identity.php:380 +#: mod/contacts.php:50 include/identity.php:395 msgid "Network:" msgstr "Rete:" -#: mod/contacts.php:51 mod/contacts.php:986 mod/videos.php:37 -#: mod/viewcontacts.php:105 mod/dirfind.php:208 mod/network.php:596 -#: mod/allfriends.php:72 mod/match.php:82 mod/directory.php:172 -#: mod/common.php:124 mod/suggest.php:95 mod/photos.php:41 -#: include/identity.php:295 +#: mod/contacts.php:51 mod/contacts.php:961 mod/videos.php:37 +#: mod/viewcontacts.php:105 mod/dirfind.php:214 mod/network.php:598 +#: mod/allfriends.php:77 mod/match.php:82 mod/directory.php:172 +#: mod/common.php:123 mod/suggest.php:95 mod/photos.php:41 +#: include/identity.php:298 msgid "Forum" msgstr "Forum" #: mod/contacts.php:128 #, php-format msgid "%d contact edited." -msgid_plural "%d contacts edited" -msgstr[0] "%d contatto modificato" +msgid_plural "%d contacts edited." +msgstr[0] "%d contatto modificato." msgstr[1] "%d contatti modificati" -#: mod/contacts.php:159 mod/contacts.php:382 +#: mod/contacts.php:159 mod/contacts.php:383 msgid "Could not access contact record." msgstr "Non è possibile accedere al contatto." @@ -56,15 +56,15 @@ msgstr "Non riesco a trovare il profilo selezionato." msgid "Contact updated." msgstr "Contatto aggiornato." -#: mod/contacts.php:208 mod/dfrn_request.php:578 +#: mod/contacts.php:208 mod/dfrn_request.php:575 msgid "Failed to update contact record." msgstr "Errore nell'aggiornamento del contatto." -#: mod/contacts.php:364 mod/manage.php:96 mod/display.php:496 +#: mod/contacts.php:365 mod/manage.php:96 mod/display.php:509 #: mod/profile_photo.php:19 mod/profile_photo.php:175 #: mod/profile_photo.php:186 mod/profile_photo.php:199 -#: mod/ostatus_subscribe.php:9 mod/follow.php:10 mod/follow.php:72 -#: mod/follow.php:137 mod/item.php:169 mod/item.php:185 mod/group.php:19 +#: mod/ostatus_subscribe.php:9 mod/follow.php:11 mod/follow.php:73 +#: mod/follow.php:155 mod/item.php:180 mod/item.php:192 mod/group.php:19 #: mod/dfrn_confirm.php:55 mod/fsuggest.php:78 mod/wall_upload.php:77 #: mod/wall_upload.php:80 mod/viewcontacts.php:40 mod/notifications.php:69 #: mod/message.php:45 mod/message.php:181 mod/crepair.php:117 @@ -72,129 +72,129 @@ msgstr "Errore nell'aggiornamento del contatto." #: mod/allfriends.php:12 mod/events.php:165 mod/wallmessage.php:9 #: mod/wallmessage.php:33 mod/wallmessage.php:79 mod/wallmessage.php:103 #: mod/wall_attach.php:67 mod/wall_attach.php:70 mod/settings.php:20 -#: mod/settings.php:116 mod/settings.php:637 mod/register.php:42 +#: mod/settings.php:126 mod/settings.php:646 mod/register.php:42 #: mod/delegate.php:12 mod/common.php:18 mod/mood.php:114 mod/suggest.php:58 #: mod/profiles.php:165 mod/profiles.php:615 mod/editpost.php:10 #: mod/api.php:26 mod/api.php:31 mod/notes.php:22 mod/poke.php:149 #: mod/repair_ostatus.php:9 mod/invite.php:15 mod/invite.php:101 #: mod/photos.php:171 mod/photos.php:1105 mod/regmod.php:110 -#: mod/uimport.php:23 mod/attach.php:33 include/items.php:5067 index.php:382 +#: mod/uimport.php:23 mod/attach.php:33 include/items.php:5096 index.php:383 msgid "Permission denied." msgstr "Permesso negato." -#: mod/contacts.php:403 +#: mod/contacts.php:404 msgid "Contact has been blocked" msgstr "Il contatto è stato bloccato" -#: mod/contacts.php:403 +#: mod/contacts.php:404 msgid "Contact has been unblocked" msgstr "Il contatto è stato sbloccato" -#: mod/contacts.php:414 +#: mod/contacts.php:415 msgid "Contact has been ignored" msgstr "Il contatto è ignorato" -#: mod/contacts.php:414 +#: mod/contacts.php:415 msgid "Contact has been unignored" msgstr "Il contatto non è più ignorato" -#: mod/contacts.php:426 +#: mod/contacts.php:427 msgid "Contact has been archived" msgstr "Il contatto è stato archiviato" -#: mod/contacts.php:426 +#: mod/contacts.php:427 msgid "Contact has been unarchived" msgstr "Il contatto è stato dearchiviato" -#: mod/contacts.php:453 mod/contacts.php:801 +#: mod/contacts.php:454 mod/contacts.php:802 msgid "Do you really want to delete this contact?" msgstr "Vuoi veramente cancellare questo contatto?" -#: mod/contacts.php:455 mod/follow.php:105 mod/message.php:216 -#: mod/settings.php:1094 mod/settings.php:1100 mod/settings.php:1108 -#: mod/settings.php:1112 mod/settings.php:1117 mod/settings.php:1123 -#: mod/settings.php:1129 mod/settings.php:1135 mod/settings.php:1161 -#: mod/settings.php:1162 mod/settings.php:1163 mod/settings.php:1164 -#: mod/settings.php:1165 mod/dfrn_request.php:850 mod/register.php:238 +#: mod/contacts.php:456 mod/follow.php:110 mod/message.php:216 +#: mod/settings.php:1103 mod/settings.php:1109 mod/settings.php:1117 +#: mod/settings.php:1121 mod/settings.php:1126 mod/settings.php:1132 +#: mod/settings.php:1138 mod/settings.php:1144 mod/settings.php:1170 +#: mod/settings.php:1171 mod/settings.php:1172 mod/settings.php:1173 +#: mod/settings.php:1174 mod/dfrn_request.php:857 mod/register.php:238 #: mod/suggest.php:29 mod/profiles.php:658 mod/profiles.php:661 -#: mod/profiles.php:687 mod/api.php:105 include/items.php:4899 +#: mod/profiles.php:687 mod/api.php:105 include/items.php:4928 msgid "Yes" msgstr "Si" -#: mod/contacts.php:458 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:116 +#: mod/contacts.php:459 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121 #: mod/videos.php:131 mod/message.php:219 mod/fbrowser.php:93 -#: mod/fbrowser.php:128 mod/settings.php:651 mod/settings.php:677 -#: mod/dfrn_request.php:864 mod/suggest.php:32 mod/editpost.php:148 -#: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1221 -#: include/items.php:4902 +#: mod/fbrowser.php:128 mod/settings.php:660 mod/settings.php:686 +#: mod/dfrn_request.php:871 mod/suggest.php:32 mod/editpost.php:148 +#: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1220 +#: include/items.php:4931 msgid "Cancel" msgstr "Annulla" -#: mod/contacts.php:470 +#: mod/contacts.php:471 msgid "Contact has been removed." msgstr "Il contatto è stato rimosso." -#: mod/contacts.php:511 +#: mod/contacts.php:512 #, php-format msgid "You are mutual friends with %s" msgstr "Sei amico reciproco con %s" -#: mod/contacts.php:515 +#: mod/contacts.php:516 #, php-format msgid "You are sharing with %s" msgstr "Stai condividendo con %s" -#: mod/contacts.php:520 +#: mod/contacts.php:521 #, php-format msgid "%s is sharing with you" msgstr "%s sta condividendo con te" -#: mod/contacts.php:540 +#: mod/contacts.php:541 msgid "Private communications are not available for this contact." msgstr "Le comunicazioni private non sono disponibili per questo contatto." -#: mod/contacts.php:543 mod/admin.php:645 +#: mod/contacts.php:544 mod/admin.php:822 msgid "Never" msgstr "Mai" -#: mod/contacts.php:547 +#: mod/contacts.php:548 msgid "(Update was successful)" msgstr "(L'aggiornamento è stato completato)" -#: mod/contacts.php:547 +#: mod/contacts.php:548 msgid "(Update was not successful)" msgstr "(L'aggiornamento non è stato completato)" -#: mod/contacts.php:549 +#: mod/contacts.php:550 msgid "Suggest friends" msgstr "Suggerisci amici" -#: mod/contacts.php:553 +#: mod/contacts.php:554 #, php-format msgid "Network type: %s" msgstr "Tipo di rete: %s" -#: mod/contacts.php:566 +#: mod/contacts.php:567 msgid "Communications lost with this contact!" msgstr "Comunicazione con questo contatto persa!" -#: mod/contacts.php:569 +#: mod/contacts.php:570 msgid "Fetch further information for feeds" msgstr "Recupera maggiori infomazioni per i feed" -#: mod/contacts.php:570 mod/admin.php:654 +#: mod/contacts.php:571 mod/admin.php:831 msgid "Disabled" msgstr "Disabilitato" -#: mod/contacts.php:570 +#: mod/contacts.php:571 msgid "Fetch information" msgstr "Recupera informazioni" -#: mod/contacts.php:570 +#: mod/contacts.php:571 msgid "Fetch information and keywords" msgstr "Recupera informazioni e parole chiave" -#: mod/contacts.php:586 mod/manage.php:143 mod/fsuggest.php:107 +#: mod/contacts.php:587 mod/manage.php:143 mod/fsuggest.php:107 #: mod/message.php:342 mod/message.php:525 mod/crepair.php:196 #: mod/events.php:574 mod/content.php:712 mod/install.php:261 #: mod/install.php:299 mod/mood.php:137 mod/profiles.php:696 @@ -204,308 +204,308 @@ msgstr "Recupera informazioni e parole chiave" #: object/Item.php:710 view/theme/cleanzero/config.php:80 #: view/theme/dispy/config.php:70 view/theme/quattro/config.php:64 #: view/theme/diabook/config.php:148 view/theme/diabook/theme.php:633 -#: view/theme/clean/config.php:83 view/theme/vier/config.php:107 -#: view/theme/duepuntozero/config.php:59 +#: view/theme/vier/config.php:107 view/theme/duepuntozero/config.php:59 msgid "Submit" msgstr "Invia" -#: mod/contacts.php:587 +#: mod/contacts.php:588 msgid "Profile Visibility" msgstr "Visibilità del profilo" -#: mod/contacts.php:588 +#: mod/contacts.php:589 #, php-format msgid "" "Please choose the profile you would like to display to %s when viewing your " "profile securely." msgstr "Seleziona il profilo che vuoi mostrare a %s quando visita il tuo profilo in modo sicuro." -#: mod/contacts.php:589 +#: mod/contacts.php:590 msgid "Contact Information / Notes" msgstr "Informazioni / Note sul contatto" -#: mod/contacts.php:590 +#: mod/contacts.php:591 msgid "Edit contact notes" msgstr "Modifica note contatto" -#: mod/contacts.php:595 mod/contacts.php:977 mod/viewcontacts.php:97 +#: mod/contacts.php:596 mod/contacts.php:952 mod/viewcontacts.php:97 #: mod/nogroup.php:41 #, php-format msgid "Visit %s's profile [%s]" msgstr "Visita il profilo di %s [%s]" -#: mod/contacts.php:596 +#: mod/contacts.php:597 msgid "Block/Unblock contact" msgstr "Blocca/Sblocca contatto" -#: mod/contacts.php:597 +#: mod/contacts.php:598 msgid "Ignore contact" msgstr "Ignora il contatto" -#: mod/contacts.php:598 +#: mod/contacts.php:599 msgid "Repair URL settings" msgstr "Impostazioni riparazione URL" -#: mod/contacts.php:599 +#: mod/contacts.php:600 msgid "View conversations" msgstr "Vedi conversazioni" -#: mod/contacts.php:601 +#: mod/contacts.php:602 msgid "Delete contact" msgstr "Rimuovi contatto" -#: mod/contacts.php:605 +#: mod/contacts.php:606 msgid "Last update:" msgstr "Ultimo aggiornamento:" -#: mod/contacts.php:607 +#: mod/contacts.php:608 msgid "Update public posts" msgstr "Aggiorna messaggi pubblici" -#: mod/contacts.php:609 mod/admin.php:1653 +#: mod/contacts.php:610 msgid "Update now" msgstr "Aggiorna adesso" -#: mod/contacts.php:611 mod/dirfind.php:190 mod/allfriends.php:60 -#: mod/match.php:71 mod/suggest.php:82 include/contact_widgets.php:32 -#: include/Contact.php:321 include/conversation.php:924 +#: mod/contacts.php:612 mod/follow.php:103 mod/dirfind.php:196 +#: mod/allfriends.php:65 mod/match.php:71 mod/suggest.php:82 +#: include/contact_widgets.php:32 include/Contact.php:297 +#: include/conversation.php:924 msgid "Connect/Follow" msgstr "Connetti/segui" -#: mod/contacts.php:614 mod/contacts.php:805 mod/contacts.php:864 -#: mod/admin.php:1117 +#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 +#: mod/admin.php:1312 msgid "Unblock" msgstr "Sblocca" -#: mod/contacts.php:614 mod/contacts.php:805 mod/contacts.php:864 -#: mod/admin.php:1116 +#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 +#: mod/admin.php:1311 msgid "Block" msgstr "Blocca" -#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:871 +#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 msgid "Unignore" msgstr "Non ignorare" -#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:871 +#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 #: mod/notifications.php:54 mod/notifications.php:179 #: mod/notifications.php:259 msgid "Ignore" msgstr "Ignora" -#: mod/contacts.php:618 +#: mod/contacts.php:619 msgid "Currently blocked" msgstr "Bloccato" -#: mod/contacts.php:619 +#: mod/contacts.php:620 msgid "Currently ignored" msgstr "Ignorato" -#: mod/contacts.php:620 +#: mod/contacts.php:621 msgid "Currently archived" msgstr "Al momento archiviato" -#: mod/contacts.php:621 mod/notifications.php:172 mod/notifications.php:251 +#: mod/contacts.php:622 mod/notifications.php:172 mod/notifications.php:251 msgid "Hide this contact from others" msgstr "Nascondi questo contatto agli altri" -#: mod/contacts.php:621 +#: mod/contacts.php:622 msgid "" "Replies/likes to your public posts may still be visible" msgstr "Risposte ai tuoi post pubblici possono essere comunque visibili" -#: mod/contacts.php:622 +#: mod/contacts.php:623 msgid "Notification for new posts" msgstr "Notifica per i nuovi messaggi" -#: mod/contacts.php:622 +#: mod/contacts.php:623 msgid "Send a notification of every new post of this contact" msgstr "Invia una notifica per ogni nuovo messaggio di questo contatto" -#: mod/contacts.php:625 +#: mod/contacts.php:626 msgid "Blacklisted keywords" msgstr "Parole chiave in blacklist" -#: mod/contacts.php:625 +#: mod/contacts.php:626 msgid "" "Comma separated list of keywords that should not be converted to hashtags, " "when \"Fetch information and keywords\" is selected" msgstr "Lista separata da virgola di parole chiave che non dovranno essere convertite in hastag, quando \"Recupera informazioni e parole chiave\" è selezionato" -#: mod/contacts.php:632 mod/follow.php:121 mod/notifications.php:255 +#: mod/contacts.php:633 mod/follow.php:126 mod/notifications.php:255 msgid "Profile URL" msgstr "URL Profilo" -#: mod/contacts.php:635 mod/follow.php:125 mod/notifications.php:244 -#: mod/events.php:566 mod/directory.php:145 include/identity.php:304 -#: include/bb2diaspora.php:170 include/event.php:36 include/event.php:60 +#: mod/contacts.php:636 mod/notifications.php:244 mod/events.php:566 +#: mod/directory.php:145 include/identity.php:308 include/bb2diaspora.php:170 +#: include/event.php:36 include/event.php:60 msgid "Location:" msgstr "Posizione:" -#: mod/contacts.php:637 mod/follow.php:127 mod/notifications.php:246 -#: mod/directory.php:153 include/identity.php:313 include/identity.php:621 +#: mod/contacts.php:638 mod/notifications.php:246 mod/directory.php:153 +#: include/identity.php:317 include/identity.php:631 msgid "About:" msgstr "Informazioni:" -#: mod/contacts.php:639 mod/follow.php:129 mod/notifications.php:248 -#: include/identity.php:615 +#: mod/contacts.php:640 mod/follow.php:134 mod/notifications.php:248 +#: include/identity.php:625 msgid "Tags:" msgstr "Tag:" -#: mod/contacts.php:684 +#: mod/contacts.php:685 msgid "Suggestions" msgstr "Suggerimenti" -#: mod/contacts.php:687 +#: mod/contacts.php:688 msgid "Suggest potential friends" msgstr "Suggerisci potenziali amici" -#: mod/contacts.php:692 mod/group.php:192 +#: mod/contacts.php:693 mod/group.php:192 msgid "All Contacts" msgstr "Tutti i contatti" -#: mod/contacts.php:695 +#: mod/contacts.php:696 msgid "Show all contacts" msgstr "Mostra tutti i contatti" -#: mod/contacts.php:700 +#: mod/contacts.php:701 msgid "Unblocked" msgstr "Sbloccato" -#: mod/contacts.php:703 +#: mod/contacts.php:704 msgid "Only show unblocked contacts" msgstr "Mostra solo contatti non bloccati" -#: mod/contacts.php:709 +#: mod/contacts.php:710 msgid "Blocked" msgstr "Bloccato" -#: mod/contacts.php:712 +#: mod/contacts.php:713 msgid "Only show blocked contacts" msgstr "Mostra solo contatti bloccati" -#: mod/contacts.php:718 +#: mod/contacts.php:719 msgid "Ignored" msgstr "Ignorato" -#: mod/contacts.php:721 +#: mod/contacts.php:722 msgid "Only show ignored contacts" msgstr "Mostra solo contatti ignorati" -#: mod/contacts.php:727 +#: mod/contacts.php:728 msgid "Archived" msgstr "Achiviato" -#: mod/contacts.php:730 +#: mod/contacts.php:731 msgid "Only show archived contacts" msgstr "Mostra solo contatti archiviati" -#: mod/contacts.php:736 +#: mod/contacts.php:737 msgid "Hidden" msgstr "Nascosto" -#: mod/contacts.php:739 +#: mod/contacts.php:740 msgid "Only show hidden contacts" msgstr "Mostra solo contatti nascosti" -#: mod/contacts.php:792 mod/contacts.php:840 mod/viewcontacts.php:116 -#: include/identity.php:732 include/identity.php:735 include/text.php:1012 +#: mod/contacts.php:793 mod/contacts.php:841 mod/viewcontacts.php:116 +#: include/identity.php:741 include/identity.php:744 include/text.php:1012 #: include/nav.php:123 include/nav.php:187 view/theme/diabook/theme.php:125 msgid "Contacts" msgstr "Contatti" -#: mod/contacts.php:796 +#: mod/contacts.php:797 msgid "Search your contacts" msgstr "Cerca nei tuoi contatti" -#: mod/contacts.php:797 +#: mod/contacts.php:798 msgid "Finding: " msgstr "Ricerca: " -#: mod/contacts.php:798 mod/directory.php:210 include/contact_widgets.php:34 +#: mod/contacts.php:799 mod/directory.php:210 include/contact_widgets.php:34 msgid "Find" msgstr "Trova" -#: mod/contacts.php:804 mod/settings.php:146 mod/settings.php:676 +#: mod/contacts.php:805 mod/settings.php:156 mod/settings.php:685 msgid "Update" msgstr "Aggiorna" -#: mod/contacts.php:807 mod/contacts.php:878 +#: mod/contacts.php:808 mod/contacts.php:879 msgid "Archive" msgstr "Archivia" -#: mod/contacts.php:807 mod/contacts.php:878 +#: mod/contacts.php:808 mod/contacts.php:879 msgid "Unarchive" msgstr "Dearchivia" -#: mod/contacts.php:808 mod/group.php:171 mod/admin.php:1115 -#: mod/content.php:440 mod/content.php:743 mod/settings.php:713 +#: mod/contacts.php:809 mod/group.php:171 mod/admin.php:1310 +#: mod/content.php:440 mod/content.php:743 mod/settings.php:722 #: mod/photos.php:1723 object/Item.php:134 include/conversation.php:635 msgid "Delete" msgstr "Rimuovi" -#: mod/contacts.php:821 include/identity.php:677 include/nav.php:75 +#: mod/contacts.php:822 include/identity.php:686 include/nav.php:75 msgid "Status" msgstr "Stato" -#: mod/contacts.php:824 include/identity.php:680 +#: mod/contacts.php:825 mod/follow.php:143 include/identity.php:689 msgid "Status Messages and Posts" msgstr "Messaggi di stato e post" -#: mod/contacts.php:829 mod/profperm.php:104 mod/newmember.php:32 -#: include/identity.php:569 include/identity.php:655 include/identity.php:685 +#: mod/contacts.php:830 mod/profperm.php:104 mod/newmember.php:32 +#: include/identity.php:579 include/identity.php:665 include/identity.php:694 #: include/nav.php:76 view/theme/diabook/theme.php:124 msgid "Profile" msgstr "Profilo" -#: mod/contacts.php:832 include/identity.php:688 +#: mod/contacts.php:833 include/identity.php:697 msgid "Profile Details" msgstr "Dettagli del profilo" -#: mod/contacts.php:843 +#: mod/contacts.php:844 msgid "View all contacts" msgstr "Vedi tutti i contatti" -#: mod/contacts.php:849 mod/common.php:135 +#: mod/contacts.php:850 mod/common.php:134 msgid "Common Friends" msgstr "Amici in comune" -#: mod/contacts.php:852 +#: mod/contacts.php:853 msgid "View all common friends" msgstr "Vedi tutti gli amici in comune" -#: mod/contacts.php:856 +#: mod/contacts.php:857 msgid "Repair" msgstr "Ripara" -#: mod/contacts.php:859 +#: mod/contacts.php:860 msgid "Advanced Contact Settings" msgstr "Impostazioni avanzate Contatto" -#: mod/contacts.php:867 +#: mod/contacts.php:868 msgid "Toggle Blocked status" msgstr "Inverti stato \"Blocca\"" -#: mod/contacts.php:874 +#: mod/contacts.php:875 msgid "Toggle Ignored status" msgstr "Inverti stato \"Ignora\"" -#: mod/contacts.php:881 +#: mod/contacts.php:882 msgid "Toggle Archive status" msgstr "Inverti stato \"Archiviato\"" -#: mod/contacts.php:949 +#: mod/contacts.php:924 msgid "Mutual Friendship" msgstr "Amicizia reciproca" -#: mod/contacts.php:953 +#: mod/contacts.php:928 msgid "is a fan of yours" msgstr "è un tuo fan" -#: mod/contacts.php:957 +#: mod/contacts.php:932 msgid "you are a fan of" msgstr "sei un fan di" -#: mod/contacts.php:978 mod/nogroup.php:42 +#: mod/contacts.php:953 mod/nogroup.php:42 msgid "Edit contact" msgstr "Modifca contatto" @@ -531,7 +531,7 @@ msgstr "Seleziona un'identità da gestire:" msgid "Post successful." msgstr "Inviato!" -#: mod/profperm.php:19 mod/group.php:72 index.php:381 +#: mod/profperm.php:19 mod/group.php:72 index.php:382 msgid "Permission denied" msgstr "Permesso negato" @@ -555,23 +555,23 @@ msgstr "Visibile a" msgid "All Contacts (with secure profile access)" msgstr "Tutti i contatti (con profilo ad accesso sicuro)" -#: mod/display.php:82 mod/display.php:283 mod/display.php:500 -#: mod/viewsrc.php:15 mod/admin.php:196 mod/admin.php:1160 mod/admin.php:1381 -#: mod/notice.php:15 include/items.php:4858 +#: mod/display.php:82 mod/display.php:291 mod/display.php:513 +#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1365 mod/admin.php:1599 +#: mod/notice.php:15 include/items.php:4887 msgid "Item not found." msgstr "Elemento non trovato." -#: mod/display.php:211 mod/videos.php:197 mod/viewcontacts.php:35 -#: mod/community.php:18 mod/dfrn_request.php:779 mod/search.php:93 +#: mod/display.php:220 mod/videos.php:197 mod/viewcontacts.php:35 +#: mod/community.php:22 mod/dfrn_request.php:786 mod/search.php:93 #: mod/search.php:99 mod/directory.php:37 mod/photos.php:976 msgid "Public access denied." msgstr "Accesso negato." -#: mod/display.php:331 mod/profile.php:155 +#: mod/display.php:339 mod/profile.php:155 msgid "Access to this profile has been restricted." msgstr "L'accesso a questo profilo è stato limitato." -#: mod/display.php:493 +#: mod/display.php:506 msgid "Item has been removed." msgstr "L'oggetto è stato rimosso." @@ -606,8 +606,8 @@ msgid "" " join." msgstr "Sulla tua pagina Quick Start - veloce introduzione alla tua pagina profilo e alla pagina Rete, fai qualche nuova amicizia, e trova qualche gruppo a cui unirti." -#: mod/newmember.php:22 mod/admin.php:1212 mod/admin.php:1457 -#: mod/settings.php:99 include/nav.php:182 view/theme/diabook/theme.php:544 +#: mod/newmember.php:22 mod/admin.php:1418 mod/admin.php:1676 +#: mod/settings.php:109 include/nav.php:182 view/theme/diabook/theme.php:544 #: view/theme/diabook/theme.php:648 msgid "Settings" msgstr "Impostazioni" @@ -668,60 +668,44 @@ msgstr "Inserisci qualche parola chiave pubblica nel tuo profilo predefinito che msgid "Connecting" msgstr "Collegarsi" -#: mod/newmember.php:49 mod/newmember.php:51 include/contact_selectors.php:81 -msgid "Facebook" -msgstr "Facebook" - -#: mod/newmember.php:49 -msgid "" -"Authorise the Facebook Connector if you currently have a Facebook account " -"and we will (optionally) import all your Facebook friends and conversations." -msgstr "Autorizza il Facebook Connector se hai un account Facebook, e noi (opzionalmente) importeremo tuti i tuoi amici e le tue conversazioni da Facebook." - #: mod/newmember.php:51 -msgid "" -"If this is your own personal server, installing the Facebook addon " -"may ease your transition to the free social web." -msgstr "SeAdd New Contact dialog." msgstr "La tua pagina Contatti è il mezzo per gestire le amicizie e collegarsi con amici su altre reti. Di solito, basta inserire l'indirizzo nel campo Aggiungi Nuovo Contatto" -#: mod/newmember.php:60 +#: mod/newmember.php:55 msgid "Go to Your Site's Directory" msgstr "Vai all'Elenco del tuo sito" -#: mod/newmember.php:60 +#: mod/newmember.php:55 msgid "" "The Directory page lets you find other people in this network or other " "federated sites. Look for a Connect or Follow link on " "their profile page. Provide your own Identity Address if requested." msgstr "La pagina Elenco ti permette di trovare altre persone in questa rete o in altri siti. Cerca un link Connetti o Segui nella loro pagina del profilo. Inserisci il tuo Indirizzo Identità, se richiesto." -#: mod/newmember.php:62 +#: mod/newmember.php:57 msgid "Finding New People" msgstr "Trova nuove persone" -#: mod/newmember.php:62 +#: mod/newmember.php:57 msgid "" "On the side panel of the Contacts page are several tools to find new " "friends. We can match people by interest, look up people by name or " @@ -730,41 +714,41 @@ msgid "" "hours." msgstr "Nel pannello laterale nella pagina \"Contatti\", ci sono diversi strumenti per trovare nuovi amici. Possiamo confrontare le persone per interessi, cercare le persone per nome e fornire suggerimenti basati sui tuoi contatti esistenti. Su un sito nuovo, i suggerimenti sono di solito presenti dopo 24 ore." -#: mod/newmember.php:66 include/group.php:283 +#: mod/newmember.php:61 include/group.php:283 msgid "Groups" msgstr "Gruppi" -#: mod/newmember.php:70 +#: mod/newmember.php:65 msgid "Group Your Contacts" msgstr "Raggruppa i tuoi contatti" -#: mod/newmember.php:70 +#: mod/newmember.php:65 msgid "" "Once you have made some friends, organize them into private conversation " "groups from the sidebar of your Contacts page and then you can interact with" " each group privately on your Network page." msgstr "Quando avrai alcuni amici, organizzali in gruppi di conversazioni private dalla barra laterale della tua pagina Contatti. Potrai interagire privatamente con ogni gruppo nella tua pagina Rete" -#: mod/newmember.php:73 +#: mod/newmember.php:68 msgid "Why Aren't My Posts Public?" msgstr "Perchè i miei post non sono pubblici?" -#: mod/newmember.php:73 +#: mod/newmember.php:68 msgid "" "Friendica respects your privacy. By default, your posts will only show up to" " people you've added as friends. For more information, see the help section " "from the link above." msgstr "Friendica rispetta la tua provacy. Per impostazione predefinita, i tuoi post sono mostrati solo alle persone che hai aggiunto come amici. Per maggiori informazioni guarda la sezione della guida dal link qui sopra." -#: mod/newmember.php:78 +#: mod/newmember.php:73 msgid "Getting Help" msgstr "Ottenere Aiuto" -#: mod/newmember.php:82 +#: mod/newmember.php:77 msgid "Go to the Help Section" msgstr "Vai alla sezione Guida" -#: mod/newmember.php:82 +#: mod/newmember.php:77 msgid "" "Our help pages may be consulted for detail on other program" " features and resources." @@ -779,7 +763,7 @@ msgid "" "Account not found and OpenID registration is not permitted on this site." msgstr "L'account non è stato trovato, e la registrazione via OpenID non è permessa su questo sito." -#: mod/openid.php:93 include/auth.php:112 include/auth.php:175 +#: mod/openid.php:93 include/auth.php:118 include/auth.php:181 msgid "Login failed." msgstr "Accesso fallito." @@ -865,18 +849,18 @@ msgstr "Immagine caricata con successo." msgid "Image upload failed." msgstr "Caricamento immagine fallito." -#: mod/subthread.php:87 mod/tagger.php:62 mod/like.php:168 +#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 #: include/conversation.php:130 include/conversation.php:266 -#: include/text.php:1993 include/diaspora.php:2146 +#: include/text.php:2000 include/diaspora.php:2169 #: view/theme/diabook/theme.php:471 msgid "photo" msgstr "foto" -#: mod/subthread.php:87 mod/tagger.php:62 mod/like.php:168 mod/like.php:346 -#: include/conversation.php:125 include/conversation.php:134 -#: include/conversation.php:261 include/conversation.php:270 -#: include/diaspora.php:2146 view/theme/diabook/theme.php:466 -#: view/theme/diabook/theme.php:475 +#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 +#: include/like.php:334 include/conversation.php:125 +#: include/conversation.php:134 include/conversation.php:261 +#: include/conversation.php:270 include/diaspora.php:2169 +#: view/theme/diabook/theme.php:466 view/theme/diabook/theme.php:475 msgid "status" msgstr "stato" @@ -902,7 +886,7 @@ msgid "Remove" msgstr "Rimuovi" #: mod/ostatus_subscribe.php:14 -msgid "Subsribing to OStatus contacts" +msgid "Subscribing to OStatus contacts" msgstr "Iscrizione a contatti OStatus" #: mod/ostatus_subscribe.php:25 @@ -937,8 +921,8 @@ msgstr "ignorato" msgid "Keep this window open until done." msgstr "Tieni questa finestra aperta fino a che ha finito." -#: mod/filer.php:30 include/conversation.php:1133 -#: include/conversation.php:1151 +#: mod/filer.php:30 include/conversation.php:1132 +#: include/conversation.php:1150 msgid "Save to Folder:" msgstr "Salva nella Cartella:" @@ -951,54 +935,54 @@ msgstr "- seleziona -" msgid "Save" msgstr "Salva" -#: mod/follow.php:18 mod/dfrn_request.php:863 +#: mod/follow.php:19 mod/dfrn_request.php:870 msgid "Submit Request" msgstr "Invia richiesta" -#: mod/follow.php:29 +#: mod/follow.php:30 msgid "You already added this contact." msgstr "Hai già aggiunto questo contatto." -#: mod/follow.php:38 +#: mod/follow.php:39 msgid "Diaspora support isn't enabled. Contact can't be added." msgstr "Il supporto Diaspora non è abilitato. Il contatto non puo' essere aggiunto." -#: mod/follow.php:45 +#: mod/follow.php:46 msgid "OStatus support is disabled. Contact can't be added." msgstr "Il supporto OStatus non è abilitato. Il contatto non puo' essere aggiunto." -#: mod/follow.php:52 +#: mod/follow.php:53 msgid "The network type couldn't be detected. Contact can't be added." msgstr "Non è possibile rilevare il tipo di rete. Il contatto non puo' essere aggiunto." -#: mod/follow.php:104 mod/dfrn_request.php:849 +#: mod/follow.php:109 mod/dfrn_request.php:856 msgid "Please answer the following:" msgstr "Rispondi:" -#: mod/follow.php:105 mod/dfrn_request.php:850 +#: mod/follow.php:110 mod/dfrn_request.php:857 #, php-format msgid "Does %s know you?" msgstr "%s ti conosce?" -#: mod/follow.php:105 mod/settings.php:1094 mod/settings.php:1100 -#: mod/settings.php:1108 mod/settings.php:1112 mod/settings.php:1117 -#: mod/settings.php:1123 mod/settings.php:1129 mod/settings.php:1135 -#: mod/settings.php:1161 mod/settings.php:1162 mod/settings.php:1163 -#: mod/settings.php:1164 mod/settings.php:1165 mod/dfrn_request.php:850 +#: mod/follow.php:110 mod/settings.php:1103 mod/settings.php:1109 +#: mod/settings.php:1117 mod/settings.php:1121 mod/settings.php:1126 +#: mod/settings.php:1132 mod/settings.php:1138 mod/settings.php:1144 +#: mod/settings.php:1170 mod/settings.php:1171 mod/settings.php:1172 +#: mod/settings.php:1173 mod/settings.php:1174 mod/dfrn_request.php:857 #: mod/register.php:239 mod/profiles.php:658 mod/profiles.php:662 #: mod/profiles.php:687 mod/api.php:106 msgid "No" msgstr "No" -#: mod/follow.php:106 mod/dfrn_request.php:854 +#: mod/follow.php:111 mod/dfrn_request.php:861 msgid "Add a personal note:" msgstr "Aggiungi una nota personale:" -#: mod/follow.php:112 mod/dfrn_request.php:860 +#: mod/follow.php:117 mod/dfrn_request.php:867 msgid "Your Identity Address:" msgstr "L'indirizzo della tua identità:" -#: mod/follow.php:162 +#: mod/follow.php:180 msgid "Contact added" msgstr "Contatto aggiunto" @@ -1006,39 +990,39 @@ msgstr "Contatto aggiunto" msgid "Unable to locate original post." msgstr "Impossibile trovare il messaggio originale." -#: mod/item.php:322 +#: mod/item.php:329 msgid "Empty post discarded." msgstr "Messaggio vuoto scartato." -#: mod/item.php:460 mod/wall_upload.php:213 mod/wall_upload.php:227 -#: mod/wall_upload.php:234 include/Photo.php:954 include/Photo.php:969 -#: include/Photo.php:976 include/Photo.php:998 include/message.php:145 +#: mod/item.php:467 mod/wall_upload.php:213 mod/wall_upload.php:227 +#: mod/wall_upload.php:234 include/Photo.php:958 include/Photo.php:973 +#: include/Photo.php:980 include/Photo.php:1002 include/message.php:145 msgid "Wall Photos" msgstr "Foto della bacheca" -#: mod/item.php:834 +#: mod/item.php:842 msgid "System error. Post not saved." msgstr "Errore di sistema. Messaggio non salvato." -#: mod/item.php:963 +#: mod/item.php:971 #, php-format msgid "" "This message was sent to you by %s, a member of the Friendica social " "network." msgstr "Questo messaggio ti è stato inviato da %s, un membro del social network Friendica." -#: mod/item.php:965 +#: mod/item.php:973 #, php-format msgid "You may visit them online at %s" msgstr "Puoi visitarli online su %s" -#: mod/item.php:966 +#: mod/item.php:974 msgid "" "Please contact the sender by replying to this post if you do not wish to " "receive these messages." msgstr "Contatta il mittente rispondendo a questo post se non vuoi ricevere questi messaggi." -#: mod/item.php:970 +#: mod/item.php:978 #, php-format msgid "%s posted an update." msgstr "%s ha inviato un aggiornamento." @@ -1087,11 +1071,11 @@ msgstr "Modifica gruppo" msgid "Members" msgstr "Membri" -#: mod/group.php:193 mod/network.php:563 mod/content.php:130 +#: mod/group.php:193 mod/network.php:576 mod/content.php:130 msgid "Group is empty" msgstr "Il gruppo è vuoto" -#: mod/apps.php:7 index.php:225 +#: mod/apps.php:7 index.php:226 msgid "You must be logged in to use addons. " msgstr "Devi aver effettuato il login per usare gli addons." @@ -1148,7 +1132,7 @@ msgid "Unable to set contact photo." msgstr "Impossibile impostare la foto del contatto." #: mod/dfrn_confirm.php:487 include/conversation.php:185 -#: include/diaspora.php:636 +#: include/diaspora.php:637 #, php-format msgid "%1$s is now friends with %2$s" msgstr "%1$s e %2$s adesso sono amici" @@ -1189,7 +1173,7 @@ msgstr "Impossibile impostare le credenziali del tuo contatto sul nostro sistema msgid "Unable to update your contact profile details on our system" msgstr "Impossibile aggiornare i dettagli del tuo contatto sul nostro sistema" -#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:734 include/items.php:4270 +#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:741 include/items.php:4299 msgid "[Name Withheld]" msgstr "[Nome Nascosto]" @@ -1198,7 +1182,7 @@ msgstr "[Nome Nascosto]" msgid "%1$s has joined %2$s" msgstr "%1$s si è unito a %2$s" -#: mod/profile.php:21 include/identity.php:52 +#: mod/profile.php:21 include/identity.php:51 msgid "Requested profile is not available." msgstr "Profilo richiesto non disponibile." @@ -1222,7 +1206,7 @@ msgstr "Nessun video selezionato" msgid "Access to this item is restricted." msgstr "Questo oggetto non è visibile a tutti." -#: mod/videos.php:383 include/text.php:1465 +#: mod/videos.php:383 include/text.php:1472 msgid "View Video" msgstr "Guarda Video" @@ -1258,7 +1242,7 @@ msgstr "Suggerisci un amico a %s" #: mod/wall_upload.php:20 mod/wall_upload.php:33 mod/wall_upload.php:86 #: mod/wall_upload.php:122 mod/wall_upload.php:125 mod/wall_attach.php:17 -#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1733 +#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1781 msgid "Invalid request." msgstr "Richiesta non valida." @@ -1314,7 +1298,7 @@ msgid "" "Password reset failed." msgstr "La richiesta non può essere verificata. (Puoi averla già richiesta precendentemente). Reimpostazione password fallita." -#: mod/lostpass.php:109 boot.php:1307 +#: mod/lostpass.php:109 boot.php:1444 msgid "Password Reset" msgstr "Reimpostazione password" @@ -1388,37 +1372,6 @@ msgstr "Nome utente o email: " msgid "Reset" msgstr "Reimposta" -#: mod/like.php:170 include/conversation.php:122 include/conversation.php:258 -#: include/text.php:1991 view/theme/diabook/theme.php:463 -msgid "event" -msgstr "l'evento" - -#: mod/like.php:187 include/conversation.php:141 include/diaspora.php:2162 -#: view/theme/diabook/theme.php:480 -#, php-format -msgid "%1$s likes %2$s's %3$s" -msgstr "A %1$s piace %3$s di %2$s" - -#: mod/like.php:189 include/conversation.php:144 -#, php-format -msgid "%1$s doesn't like %2$s's %3$s" -msgstr "A %1$s non piace %3$s di %2$s" - -#: mod/like.php:191 -#, php-format -msgid "%1$s is attending %2$s's %3$s" -msgstr "%1$s parteciperà a %3$s di %2$s" - -#: mod/like.php:193 -#, php-format -msgid "%1$s is not attending %2$s's %3$s" -msgstr "%1$s non parteciperà a %3$s di %2$s" - -#: mod/like.php:195 -#, php-format -msgid "%1$s may attend %2$s's %3$s" -msgstr "%1$s forse parteciperà a %3$s di %2$s" - #: mod/ping.php:265 msgid "{0} wants to be your friend" msgstr "{0} vuole essere tuo amico" @@ -1448,11 +1401,11 @@ msgstr "Scarta" msgid "System" msgstr "Sistema" -#: mod/notifications.php:87 mod/admin.php:228 include/nav.php:154 +#: mod/notifications.php:87 mod/admin.php:390 include/nav.php:154 msgid "Network" msgstr "Rete" -#: mod/notifications.php:93 mod/network.php:381 +#: mod/notifications.php:93 mod/network.php:384 msgid "Personal" msgstr "Personale" @@ -1494,7 +1447,7 @@ msgstr "Invia una attività \"è ora amico con\"" msgid "if applicable" msgstr "se applicabile" -#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1113 +#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1308 msgid "Approve" msgstr "Approva" @@ -1544,8 +1497,8 @@ msgstr "Richiesta amicizia/connessione" msgid "New Follower" msgstr "Qualcuno inizia a seguirti" -#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:306 -#: include/identity.php:580 +#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:310 +#: include/identity.php:590 msgid "Gender:" msgstr "Genere:" @@ -1716,7 +1669,7 @@ msgstr "Conversazione rimossa." #: mod/message.php:290 mod/message.php:298 mod/message.php:427 #: mod/message.php:435 mod/wallmessage.php:127 mod/wallmessage.php:135 -#: include/conversation.php:1129 include/conversation.php:1147 +#: include/conversation.php:1128 include/conversation.php:1146 msgid "Please enter a link URL:" msgstr "Inserisci l'indirizzo del link:" @@ -1738,19 +1691,19 @@ msgid "Your message:" msgstr "Il tuo messaggio:" #: mod/message.php:339 mod/message.php:523 mod/wallmessage.php:154 -#: mod/editpost.php:110 include/conversation.php:1184 +#: mod/editpost.php:110 include/conversation.php:1183 msgid "Upload photo" msgstr "Carica foto" #: mod/message.php:340 mod/message.php:524 mod/wallmessage.php:155 -#: mod/editpost.php:114 include/conversation.php:1188 +#: mod/editpost.php:114 include/conversation.php:1187 msgid "Insert web link" msgstr "Inserisci link" #: mod/message.php:341 mod/message.php:526 mod/content.php:501 #: mod/content.php:885 mod/wallmessage.php:156 mod/editpost.php:124 #: mod/photos.php:1610 object/Item.php:396 include/conversation.php:713 -#: include/conversation.php:1202 +#: include/conversation.php:1201 msgid "Please wait" msgstr "Attendi" @@ -1766,7 +1719,7 @@ msgstr "Messaggio non disponibile." msgid "Delete message" msgstr "Elimina il messaggio" -#: mod/message.php:507 mod/message.php:582 +#: mod/message.php:507 mod/message.php:584 msgid "Delete conversation" msgstr "Elimina la conversazione" @@ -1780,26 +1733,26 @@ msgstr "Nessuna comunicazione sicura disponibile, Potresti esse msgid "Send Reply" msgstr "Invia la risposta" -#: mod/message.php:555 +#: mod/message.php:557 #, php-format msgid "Unknown sender - %s" msgstr "Mittente sconosciuto - %s" -#: mod/message.php:558 +#: mod/message.php:560 #, php-format msgid "You and %s" msgstr "Tu e %s" -#: mod/message.php:561 +#: mod/message.php:563 #, php-format msgid "%s and You" msgstr "%s e Tu" -#: mod/message.php:585 +#: mod/message.php:587 msgid "D, d M Y - g:i A" msgstr "D d M Y - G:i" -#: mod/message.php:588 +#: mod/message.php:590 #, php-format msgid "%d message" msgid_plural "%d messages" @@ -1851,9 +1804,9 @@ msgstr "Ritorna alla modifica contatto" msgid "Refetch contact data" msgstr "Ricarica dati contatto" -#: mod/crepair.php:170 mod/admin.php:1111 mod/admin.php:1123 -#: mod/admin.php:1124 mod/admin.php:1137 mod/settings.php:652 -#: mod/settings.php:678 +#: mod/crepair.php:170 mod/admin.php:1306 mod/admin.php:1318 +#: mod/admin.php:1319 mod/admin.php:1332 mod/settings.php:661 +#: mod/settings.php:687 msgid "Name" msgstr "Nome" @@ -1903,7 +1856,7 @@ msgid "" "entries from this contact." msgstr "Imposta questo contatto come 'io remoto', questo farà si che friendica reinvii i nuovi messaggi da questo contatto." -#: mod/bookmarklet.php:12 boot.php:1293 include/nav.php:91 +#: mod/bookmarklet.php:12 boot.php:1430 include/nav.php:91 msgid "Login" msgstr "Accedi" @@ -1915,28 +1868,28 @@ msgstr "Il messaggio è stato creato" msgid "Access denied." msgstr "Accesso negato." -#: mod/dirfind.php:188 mod/allfriends.php:75 mod/match.php:85 -#: mod/suggest.php:98 include/contact_widgets.php:10 include/identity.php:209 +#: mod/dirfind.php:194 mod/allfriends.php:80 mod/match.php:85 +#: mod/suggest.php:98 include/contact_widgets.php:10 include/identity.php:212 msgid "Connect" msgstr "Connetti" -#: mod/dirfind.php:189 mod/allfriends.php:59 mod/match.php:70 -#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:307 -#: include/Contact.php:320 include/Contact.php:362 +#: mod/dirfind.php:195 mod/allfriends.php:64 mod/match.php:70 +#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:283 +#: include/Contact.php:296 include/Contact.php:338 #: include/conversation.php:912 include/conversation.php:926 msgid "View Profile" msgstr "Visualizza profilo" -#: mod/dirfind.php:218 +#: mod/dirfind.php:224 #, php-format msgid "People Search - %s" msgstr "Cerca persone - %s" -#: mod/dirfind.php:225 mod/match.php:105 +#: mod/dirfind.php:231 mod/match.php:105 msgid "No matches" msgstr "Nessun risultato" -#: mod/fbrowser.php:32 include/identity.php:693 include/nav.php:77 +#: mod/fbrowser.php:32 include/identity.php:702 include/nav.php:77 #: view/theme/diabook/theme.php:126 msgid "Photos" msgstr "Foto" @@ -1956,548 +1909,579 @@ msgstr "File" msgid "Contacts who are not members of a group" msgstr "Contatti che non sono membri di un gruppo" -#: mod/admin.php:80 +#: mod/admin.php:92 msgid "Theme settings updated." msgstr "Impostazioni del tema aggiornate." -#: mod/admin.php:127 mod/admin.php:711 +#: mod/admin.php:156 mod/admin.php:888 msgid "Site" msgstr "Sito" -#: mod/admin.php:128 mod/admin.php:655 mod/admin.php:1106 mod/admin.php:1121 +#: mod/admin.php:157 mod/admin.php:832 mod/admin.php:1301 mod/admin.php:1316 msgid "Users" msgstr "Utenti" -#: mod/admin.php:129 mod/admin.php:1210 mod/admin.php:1270 mod/settings.php:66 +#: mod/admin.php:158 mod/admin.php:1416 mod/admin.php:1476 mod/settings.php:72 msgid "Plugins" msgstr "Plugin" -#: mod/admin.php:130 mod/admin.php:1455 mod/admin.php:1506 +#: mod/admin.php:159 mod/admin.php:1674 mod/admin.php:1724 msgid "Themes" msgstr "Temi" -#: mod/admin.php:131 +#: mod/admin.php:160 mod/settings.php:50 +msgid "Additional features" +msgstr "Funzionalità aggiuntive" + +#: mod/admin.php:161 msgid "DB updates" msgstr "Aggiornamenti Database" -#: mod/admin.php:132 mod/admin.php:223 +#: mod/admin.php:162 mod/admin.php:385 msgid "Inspect Queue" msgstr "Ispeziona Coda di invio" -#: mod/admin.php:147 mod/admin.php:156 mod/admin.php:1594 +#: mod/admin.php:163 mod/admin.php:354 +msgid "Federation Statistics" +msgstr "Statistiche sulla Federazione" + +#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792 msgid "Logs" msgstr "Log" -#: mod/admin.php:148 +#: mod/admin.php:178 mod/admin.php:1859 +msgid "View Logs" +msgstr "Vedi i log" + +#: mod/admin.php:179 msgid "probe address" msgstr "controlla indirizzo" -#: mod/admin.php:149 +#: mod/admin.php:180 msgid "check webfinger" msgstr "verifica webfinger" -#: mod/admin.php:154 include/nav.php:194 +#: mod/admin.php:186 include/nav.php:194 msgid "Admin" msgstr "Amministrazione" -#: mod/admin.php:155 +#: mod/admin.php:187 msgid "Plugin Features" msgstr "Impostazioni Plugins" -#: mod/admin.php:157 +#: mod/admin.php:189 msgid "diagnostics" msgstr "diagnostiche" -#: mod/admin.php:158 +#: mod/admin.php:190 msgid "User registrations waiting for confirmation" msgstr "Utenti registrati in attesa di conferma" -#: mod/admin.php:222 mod/admin.php:272 mod/admin.php:710 mod/admin.php:1105 -#: mod/admin.php:1209 mod/admin.php:1269 mod/admin.php:1454 mod/admin.php:1505 -#: mod/admin.php:1593 +#: mod/admin.php:347 +msgid "" +"This page offers you some numbers to the known part of the federated social " +"network your Friendica node is part of. These numbers are not complete but " +"only reflect the part of the network your node is aware of." +msgstr "Questa pagina offre alcuni numeri riguardo la porzione del social network federato di cui il tuo nodo Friendica fa parte. Questi numeri non sono completi ma riflettono esclusivamente la porzione di rete di cui il tuo nodo e' a conoscenza." + +#: mod/admin.php:348 +msgid "" +"The Auto Discovered Contact Directory feature is not enabled, it " +"will improve the data displayed here." +msgstr "" + +#: mod/admin.php:353 mod/admin.php:384 mod/admin.php:441 mod/admin.php:887 +#: mod/admin.php:1300 mod/admin.php:1415 mod/admin.php:1475 mod/admin.php:1673 +#: mod/admin.php:1723 mod/admin.php:1791 mod/admin.php:1858 msgid "Administration" msgstr "Amministrazione" -#: mod/admin.php:225 +#: mod/admin.php:360 +#, php-format +msgid "Currently this node is aware of %d nodes from the following platforms:" +msgstr "" + +#: mod/admin.php:387 msgid "ID" msgstr "ID" -#: mod/admin.php:226 +#: mod/admin.php:388 msgid "Recipient Name" msgstr "Nome Destinatario" -#: mod/admin.php:227 +#: mod/admin.php:389 msgid "Recipient Profile" msgstr "Profilo Destinatario" -#: mod/admin.php:229 +#: mod/admin.php:391 msgid "Created" msgstr "Creato" -#: mod/admin.php:230 +#: mod/admin.php:392 msgid "Last Tried" msgstr "Ultimo Tentativo" -#: mod/admin.php:231 +#: mod/admin.php:393 msgid "" "This page lists the content of the queue for outgoing postings. These are " "postings the initial delivery failed for. They will be resend later and " "eventually deleted if the delivery fails permanently." msgstr "Questa pagina elenca il contenuto della coda di invo dei post. Questi sono post la cui consegna è fallita. Verranno reinviati più tardi ed eventualmente cancellati se la consegna continua a fallire." -#: mod/admin.php:243 mod/admin.php:1059 +#: mod/admin.php:412 mod/admin.php:1254 msgid "Normal Account" msgstr "Account normale" -#: mod/admin.php:244 mod/admin.php:1060 +#: mod/admin.php:413 mod/admin.php:1255 msgid "Soapbox Account" msgstr "Account per comunicati e annunci" -#: mod/admin.php:245 mod/admin.php:1061 +#: mod/admin.php:414 mod/admin.php:1256 msgid "Community/Celebrity Account" msgstr "Account per celebrità o per comunità" -#: mod/admin.php:246 mod/admin.php:1062 +#: mod/admin.php:415 mod/admin.php:1257 msgid "Automatic Friend Account" msgstr "Account per amicizia automatizzato" -#: mod/admin.php:247 +#: mod/admin.php:416 msgid "Blog Account" msgstr "Account Blog" -#: mod/admin.php:248 +#: mod/admin.php:417 msgid "Private Forum" msgstr "Forum Privato" -#: mod/admin.php:267 +#: mod/admin.php:436 msgid "Message queues" msgstr "Code messaggi" -#: mod/admin.php:273 +#: mod/admin.php:442 msgid "Summary" msgstr "Sommario" -#: mod/admin.php:275 +#: mod/admin.php:444 msgid "Registered users" msgstr "Utenti registrati" -#: mod/admin.php:277 +#: mod/admin.php:446 msgid "Pending registrations" msgstr "Registrazioni in attesa" -#: mod/admin.php:278 +#: mod/admin.php:447 msgid "Version" msgstr "Versione" -#: mod/admin.php:283 +#: mod/admin.php:452 msgid "Active plugins" msgstr "Plugin attivi" -#: mod/admin.php:306 +#: mod/admin.php:475 msgid "Can not parse base url. Must have at least ://" msgstr "Impossibile analizzare l'url base. Deve avere almeno [schema]://[dominio]" -#: mod/admin.php:587 +#: mod/admin.php:760 msgid "RINO2 needs mcrypt php extension to work." msgstr "RINO2 necessita dell'estensione php mcrypt per funzionare." -#: mod/admin.php:595 +#: mod/admin.php:768 msgid "Site settings updated." msgstr "Impostazioni del sito aggiornate." -#: mod/admin.php:619 mod/settings.php:903 +#: mod/admin.php:796 mod/settings.php:912 msgid "No special theme for mobile devices" msgstr "Nessun tema speciale per i dispositivi mobili" -#: mod/admin.php:638 +#: mod/admin.php:815 msgid "No community page" msgstr "Nessuna pagina Comunità" -#: mod/admin.php:639 +#: mod/admin.php:816 msgid "Public postings from users of this site" msgstr "Messaggi pubblici dagli utenti di questo sito" -#: mod/admin.php:640 +#: mod/admin.php:817 msgid "Global community page" msgstr "Pagina Comunità globale" -#: mod/admin.php:646 +#: mod/admin.php:823 msgid "At post arrival" msgstr "All'arrivo di un messaggio" -#: mod/admin.php:647 include/contact_selectors.php:56 +#: mod/admin.php:824 include/contact_selectors.php:56 msgid "Frequently" msgstr "Frequentemente" -#: mod/admin.php:648 include/contact_selectors.php:57 +#: mod/admin.php:825 include/contact_selectors.php:57 msgid "Hourly" msgstr "Ogni ora" -#: mod/admin.php:649 include/contact_selectors.php:58 +#: mod/admin.php:826 include/contact_selectors.php:58 msgid "Twice daily" msgstr "Due volte al dì" -#: mod/admin.php:650 include/contact_selectors.php:59 +#: mod/admin.php:827 include/contact_selectors.php:59 msgid "Daily" msgstr "Giornalmente" -#: mod/admin.php:656 +#: mod/admin.php:833 msgid "Users, Global Contacts" msgstr "Utenti, Contatti Globali" -#: mod/admin.php:657 +#: mod/admin.php:834 msgid "Users, Global Contacts/fallback" msgstr "Utenti, Contatti Globali/fallback" -#: mod/admin.php:661 +#: mod/admin.php:838 msgid "One month" msgstr "Un mese" -#: mod/admin.php:662 +#: mod/admin.php:839 msgid "Three months" msgstr "Tre mesi" -#: mod/admin.php:663 +#: mod/admin.php:840 msgid "Half a year" msgstr "Sei mesi" -#: mod/admin.php:664 +#: mod/admin.php:841 msgid "One year" msgstr "Un anno" -#: mod/admin.php:669 +#: mod/admin.php:846 msgid "Multi user instance" msgstr "Istanza multi utente" -#: mod/admin.php:692 +#: mod/admin.php:869 msgid "Closed" msgstr "Chiusa" -#: mod/admin.php:693 +#: mod/admin.php:870 msgid "Requires approval" msgstr "Richiede l'approvazione" -#: mod/admin.php:694 +#: mod/admin.php:871 msgid "Open" msgstr "Aperta" -#: mod/admin.php:698 +#: mod/admin.php:875 msgid "No SSL policy, links will track page SSL state" msgstr "Nessuna gestione SSL, i link seguiranno lo stato SSL della pagina" -#: mod/admin.php:699 +#: mod/admin.php:876 msgid "Force all links to use SSL" msgstr "Forza tutti i linki ad usare SSL" -#: mod/admin.php:700 +#: mod/admin.php:877 msgid "Self-signed certificate, use SSL for local links only (discouraged)" msgstr "Certificato auto-firmato, usa SSL solo per i link locali (sconsigliato)" -#: mod/admin.php:712 mod/admin.php:1271 mod/admin.php:1507 mod/admin.php:1595 -#: mod/settings.php:650 mod/settings.php:760 mod/settings.php:804 -#: mod/settings.php:873 mod/settings.php:960 mod/settings.php:1195 +#: mod/admin.php:889 mod/admin.php:1477 mod/admin.php:1725 mod/admin.php:1793 +#: mod/admin.php:1942 mod/settings.php:659 mod/settings.php:769 +#: mod/settings.php:813 mod/settings.php:882 mod/settings.php:969 +#: mod/settings.php:1204 msgid "Save Settings" msgstr "Salva Impostazioni" -#: mod/admin.php:713 mod/register.php:263 +#: mod/admin.php:890 mod/register.php:263 msgid "Registration" msgstr "Registrazione" -#: mod/admin.php:714 +#: mod/admin.php:891 msgid "File upload" msgstr "Caricamento file" -#: mod/admin.php:715 +#: mod/admin.php:892 msgid "Policies" msgstr "Politiche" -#: mod/admin.php:716 +#: mod/admin.php:893 msgid "Advanced" msgstr "Avanzate" -#: mod/admin.php:717 +#: mod/admin.php:894 msgid "Auto Discovered Contact Directory" msgstr "Elenco Contatti Scoperto Automaticamente" -#: mod/admin.php:718 +#: mod/admin.php:895 msgid "Performance" msgstr "Performance" -#: mod/admin.php:719 +#: mod/admin.php:896 msgid "" "Relocate - WARNING: advanced function. Could make this server unreachable." msgstr "Trasloca - ATTENZIONE: funzione avanzata! Puo' rendere questo server irraggiungibile." -#: mod/admin.php:722 +#: mod/admin.php:899 msgid "Site name" msgstr "Nome del sito" -#: mod/admin.php:723 +#: mod/admin.php:900 msgid "Host name" msgstr "Nome host" -#: mod/admin.php:724 +#: mod/admin.php:901 msgid "Sender Email" msgstr "Mittente email" -#: mod/admin.php:724 +#: mod/admin.php:901 msgid "" "The email address your server shall use to send notification emails from." msgstr "L'indirizzo email che il tuo server dovrà usare per inviare notifiche via email." -#: mod/admin.php:725 +#: mod/admin.php:902 msgid "Banner/Logo" msgstr "Banner/Logo" -#: mod/admin.php:726 +#: mod/admin.php:903 msgid "Shortcut icon" msgstr "Icona shortcut" -#: mod/admin.php:726 +#: mod/admin.php:903 msgid "Link to an icon that will be used for browsers." msgstr "Link verso un'icona che verrà usata dai browsers." -#: mod/admin.php:727 +#: mod/admin.php:904 msgid "Touch icon" msgstr "Icona touch" -#: mod/admin.php:727 +#: mod/admin.php:904 msgid "Link to an icon that will be used for tablets and mobiles." msgstr "Link verso un'icona che verrà usata dai tablet e i telefonini." -#: mod/admin.php:728 +#: mod/admin.php:905 msgid "Additional Info" msgstr "Informazioni aggiuntive" -#: mod/admin.php:728 +#: mod/admin.php:905 #, php-format msgid "" "For public servers: you can add additional information here that will be " "listed at %s/siteinfo." msgstr "Per server pubblici: puoi aggiungere informazioni extra che verrano mostrate su %s/siteinfo." -#: mod/admin.php:729 +#: mod/admin.php:906 msgid "System language" msgstr "Lingua di sistema" -#: mod/admin.php:730 +#: mod/admin.php:907 msgid "System theme" msgstr "Tema di sistema" -#: mod/admin.php:730 +#: mod/admin.php:907 msgid "" "Default system theme - may be over-ridden by user profiles - change theme settings" msgstr "Tema di sistema - puo' essere sovrascritto dalle impostazioni utente - cambia le impostazioni del tema" -#: mod/admin.php:731 +#: mod/admin.php:908 msgid "Mobile system theme" msgstr "Tema mobile di sistema" -#: mod/admin.php:731 +#: mod/admin.php:908 msgid "Theme for mobile devices" msgstr "Tema per dispositivi mobili" -#: mod/admin.php:732 +#: mod/admin.php:909 msgid "SSL link policy" msgstr "Gestione link SSL" -#: mod/admin.php:732 +#: mod/admin.php:909 msgid "Determines whether generated links should be forced to use SSL" msgstr "Determina se i link generati devono essere forzati a usare SSL" -#: mod/admin.php:733 +#: mod/admin.php:910 msgid "Force SSL" msgstr "Forza SSL" -#: mod/admin.php:733 +#: mod/admin.php:910 msgid "" "Force all Non-SSL requests to SSL - Attention: on some systems it could lead" " to endless loops." msgstr "Forza tutte le richieste non SSL su SSL - Attenzione: su alcuni sistemi puo' portare a loop senza fine" -#: mod/admin.php:734 +#: mod/admin.php:911 msgid "Old style 'Share'" msgstr "Ricondivisione vecchio stile" -#: mod/admin.php:734 +#: mod/admin.php:911 msgid "Deactivates the bbcode element 'share' for repeating items." msgstr "Disattiva l'elemento bbcode 'share' con elementi ripetuti" -#: mod/admin.php:735 +#: mod/admin.php:912 msgid "Hide help entry from navigation menu" msgstr "Nascondi la voce 'Guida' dal menu di navigazione" -#: mod/admin.php:735 +#: mod/admin.php:912 msgid "" "Hides the menu entry for the Help pages from the navigation menu. You can " "still access it calling /help directly." msgstr "Nasconde la voce per le pagine della guida dal menu di navigazione. E' comunque possibile accedervi richiamando /help direttamente." -#: mod/admin.php:736 +#: mod/admin.php:913 msgid "Single user instance" msgstr "Instanza a singolo utente" -#: mod/admin.php:736 +#: mod/admin.php:913 msgid "Make this instance multi-user or single-user for the named user" msgstr "Rendi questa istanza multi utente o a singolo utente per l'utente selezionato" -#: mod/admin.php:737 +#: mod/admin.php:914 msgid "Maximum image size" msgstr "Massima dimensione immagini" -#: mod/admin.php:737 +#: mod/admin.php:914 msgid "" "Maximum size in bytes of uploaded images. Default is 0, which means no " "limits." msgstr "Massima dimensione in byte delle immagini caricate. Il default è 0, cioè nessun limite." -#: mod/admin.php:738 +#: mod/admin.php:915 msgid "Maximum image length" msgstr "Massima lunghezza immagine" -#: mod/admin.php:738 +#: mod/admin.php:915 msgid "" "Maximum length in pixels of the longest side of uploaded images. Default is " "-1, which means no limits." msgstr "Massima lunghezza in pixel del lato più lungo delle immagini caricate. Predefinito a -1, ovvero nessun limite." -#: mod/admin.php:739 +#: mod/admin.php:916 msgid "JPEG image quality" msgstr "Qualità immagini JPEG" -#: mod/admin.php:739 +#: mod/admin.php:916 msgid "" "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is " "100, which is full quality." msgstr "Le immagini JPEG caricate verranno salvate con questa qualità [0-100]. Predefinito è 100, ovvero qualità piena." -#: mod/admin.php:741 +#: mod/admin.php:918 msgid "Register policy" msgstr "Politica di registrazione" -#: mod/admin.php:742 +#: mod/admin.php:919 msgid "Maximum Daily Registrations" msgstr "Massime registrazioni giornaliere" -#: mod/admin.php:742 +#: mod/admin.php:919 msgid "" "If registration is permitted above, this sets the maximum number of new user" " registrations to accept per day. If register is set to closed, this " "setting has no effect." msgstr "Se la registrazione è permessa, qui si definisce il massimo numero di nuovi utenti registrati da accettare giornalmente. Se la registrazione è chiusa, questa impostazione non ha effetto." -#: mod/admin.php:743 +#: mod/admin.php:920 msgid "Register text" msgstr "Testo registrazione" -#: mod/admin.php:743 +#: mod/admin.php:920 msgid "Will be displayed prominently on the registration page." msgstr "Sarà mostrato ben visibile nella pagina di registrazione." -#: mod/admin.php:744 +#: mod/admin.php:921 msgid "Accounts abandoned after x days" msgstr "Account abbandonati dopo x giorni" -#: mod/admin.php:744 +#: mod/admin.php:921 msgid "" "Will not waste system resources polling external sites for abandonded " "accounts. Enter 0 for no time limit." msgstr "Non spreca risorse di sistema controllando siti esterni per gli account abbandonati. Immettere 0 per nessun limite di tempo." -#: mod/admin.php:745 +#: mod/admin.php:922 msgid "Allowed friend domains" msgstr "Domini amici consentiti" -#: mod/admin.php:745 +#: mod/admin.php:922 msgid "" "Comma separated list of domains which are allowed to establish friendships " "with this site. Wildcards are accepted. Empty to allow any domains" msgstr "Elenco separato da virglola dei domini che possono stabilire amicizie con questo sito. Sono accettati caratteri jolly. Lascalo vuoto per accettare qualsiasi dominio." -#: mod/admin.php:746 +#: mod/admin.php:923 msgid "Allowed email domains" msgstr "Domini email consentiti" -#: mod/admin.php:746 +#: mod/admin.php:923 msgid "" "Comma separated list of domains which are allowed in email addresses for " "registrations to this site. Wildcards are accepted. Empty to allow any " "domains" msgstr "Elenco separato da virgola dei domini permessi come indirizzi email in fase di registrazione a questo sito. Sono accettati caratteri jolly. Lascalo vuoto per accettare qualsiasi dominio." -#: mod/admin.php:747 +#: mod/admin.php:924 msgid "Block public" msgstr "Blocca pagine pubbliche" -#: mod/admin.php:747 +#: mod/admin.php:924 msgid "" "Check to block public access to all otherwise public personal pages on this " "site unless you are currently logged in." msgstr "Seleziona per bloccare l'accesso pubblico a tutte le pagine personali di questo sito, a meno di essere loggato." -#: mod/admin.php:748 +#: mod/admin.php:925 msgid "Force publish" msgstr "Forza publicazione" -#: mod/admin.php:748 +#: mod/admin.php:925 msgid "" "Check to force all profiles on this site to be listed in the site directory." msgstr "Seleziona per forzare tutti i profili di questo sito ad essere compresi nell'elenco di questo sito." -#: mod/admin.php:749 +#: mod/admin.php:926 msgid "Global directory URL" msgstr "URL della directory globale" -#: mod/admin.php:749 +#: mod/admin.php:926 msgid "" "URL to the global directory. If this is not set, the global directory is " "completely unavailable to the application." msgstr "URL dell'elenco globale. Se vuoto, l'elenco globale sarà completamente disabilitato." -#: mod/admin.php:750 +#: mod/admin.php:927 msgid "Allow threaded items" msgstr "Permetti commenti nidificati" -#: mod/admin.php:750 +#: mod/admin.php:927 msgid "Allow infinite level threading for items on this site." msgstr "Permette un infinito livello di nidificazione dei commenti su questo sito." -#: mod/admin.php:751 +#: mod/admin.php:928 msgid "Private posts by default for new users" msgstr "Post privati di default per i nuovi utenti" -#: mod/admin.php:751 +#: mod/admin.php:928 msgid "" "Set default post permissions for all new members to the default privacy " "group rather than public." msgstr "Imposta i permessi predefiniti dei post per tutti i nuovi utenti come privati per il gruppo predefinito, invece che pubblici." -#: mod/admin.php:752 +#: mod/admin.php:929 msgid "Don't include post content in email notifications" msgstr "Non includere il contenuto dei post nelle notifiche via email" -#: mod/admin.php:752 +#: mod/admin.php:929 msgid "" "Don't include the content of a post/comment/private message/etc. in the " "email notifications that are sent out from this site, as a privacy measure." msgstr "Non include il contenuti del post/commento/messaggio privato/etc. nelle notifiche email che sono inviate da questo sito, per privacy" -#: mod/admin.php:753 +#: mod/admin.php:930 msgid "Disallow public access to addons listed in the apps menu." msgstr "Disabilita l'accesso pubblico ai plugin raccolti nel menu apps." -#: mod/admin.php:753 +#: mod/admin.php:930 msgid "" "Checking this box will restrict addons listed in the apps menu to members " "only." msgstr "Selezionando questo box si limiterà ai soli membri l'accesso agli addon nel menu applicazioni" -#: mod/admin.php:754 +#: mod/admin.php:931 msgid "Don't embed private images in posts" msgstr "Non inglobare immagini private nei post" -#: mod/admin.php:754 +#: mod/admin.php:931 msgid "" "Don't replace locally-hosted private photos in posts with an embedded copy " "of the image. This means that contacts who receive posts containing private " @@ -2505,218 +2489,228 @@ msgid "" "while." msgstr "Non sostituire le foto locali nei post con una copia incorporata dell'immagine. Questo significa che i contatti che riceveranno i post contenenti foto private dovranno autenticarsi e caricare ogni immagine, cosa che puo' richiedere un po' di tempo." -#: mod/admin.php:755 +#: mod/admin.php:932 msgid "Allow Users to set remote_self" msgstr "Permetti agli utenti di impostare 'io remoto'" -#: mod/admin.php:755 +#: mod/admin.php:932 msgid "" "With checking this, every user is allowed to mark every contact as a " "remote_self in the repair contact dialog. Setting this flag on a contact " "causes mirroring every posting of that contact in the users stream." msgstr "Selezionando questo, a tutti gli utenti sarà permesso di impostare qualsiasi contatto come 'io remoto' nella pagina di modifica del contatto. Impostare questa opzione fa si che tutti i messaggi di quel contatto vengano ripetuti nello stream del'utente." -#: mod/admin.php:756 +#: mod/admin.php:933 msgid "Block multiple registrations" msgstr "Blocca registrazioni multiple" -#: mod/admin.php:756 +#: mod/admin.php:933 msgid "Disallow users to register additional accounts for use as pages." msgstr "Non permette all'utente di registrare account extra da usare come pagine." -#: mod/admin.php:757 +#: mod/admin.php:934 msgid "OpenID support" msgstr "Supporto OpenID" -#: mod/admin.php:757 +#: mod/admin.php:934 msgid "OpenID support for registration and logins." msgstr "Supporta OpenID per la registrazione e il login" -#: mod/admin.php:758 +#: mod/admin.php:935 msgid "Fullname check" msgstr "Controllo nome completo" -#: mod/admin.php:758 +#: mod/admin.php:935 msgid "" "Force users to register with a space between firstname and lastname in Full " "name, as an antispam measure" msgstr "Forza gli utenti a registrarsi con uno spazio tra il nome e il cognome in \"Nome completo\", come misura antispam" -#: mod/admin.php:759 +#: mod/admin.php:936 msgid "UTF-8 Regular expressions" msgstr "Espressioni regolari UTF-8" -#: mod/admin.php:759 +#: mod/admin.php:936 msgid "Use PHP UTF8 regular expressions" msgstr "Usa le espressioni regolari PHP in UTF8" -#: mod/admin.php:760 +#: mod/admin.php:937 msgid "Community Page Style" msgstr "Stile pagina Comunità" -#: mod/admin.php:760 +#: mod/admin.php:937 msgid "" "Type of community page to show. 'Global community' shows every public " "posting from an open distributed network that arrived on this server." msgstr "Tipo di pagina Comunità da mostrare. 'Comunità Globale' mostra tutti i messaggi pubblici arrivati su questo server da network aperti distribuiti." -#: mod/admin.php:761 +#: mod/admin.php:938 msgid "Posts per user on community page" msgstr "Messaggi per utente nella pagina Comunità" -#: mod/admin.php:761 +#: mod/admin.php:938 msgid "" "The maximum number of posts per user on the community page. (Not valid for " "'Global Community')" msgstr "Il numero massimo di messaggi per utente mostrato nella pagina Comuntà (non valido per 'Comunità globale')" -#: mod/admin.php:762 +#: mod/admin.php:939 msgid "Enable OStatus support" msgstr "Abilita supporto OStatus" -#: mod/admin.php:762 +#: mod/admin.php:939 msgid "" "Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All " "communications in OStatus are public, so privacy warnings will be " "occasionally displayed." msgstr "Fornisce la compatibilità integrata a OStatus (StatusNet, Gnu Social, etc.). Tutte le comunicazioni su OStatus sono pubbliche, quindi un avviso di privacy verrà mostrato occasionalmente." -#: mod/admin.php:763 +#: mod/admin.php:940 msgid "OStatus conversation completion interval" msgstr "Intervallo completamento conversazioni OStatus" -#: mod/admin.php:763 +#: mod/admin.php:940 msgid "" "How often shall the poller check for new entries in OStatus conversations? " "This can be a very ressource task." msgstr "quanto spesso il poller deve controllare se esistono nuovi commenti in una conversazione OStatus? Questo è un lavoro che puo' richiedere molte risorse." -#: mod/admin.php:764 +#: mod/admin.php:941 msgid "OStatus support can only be enabled if threading is enabled." msgstr "Il supporto OStatus puo' essere abilitato solo se è abilitato il threading." -#: mod/admin.php:766 +#: mod/admin.php:943 msgid "" "Diaspora support can't be enabled because Friendica was installed into a sub" " directory." msgstr "Il supporto a Diaspora non puo' essere abilitato perchè Friendica è stato installato in una sotto directory." -#: mod/admin.php:767 +#: mod/admin.php:944 msgid "Enable Diaspora support" msgstr "Abilita il supporto a Diaspora" -#: mod/admin.php:767 +#: mod/admin.php:944 msgid "Provide built-in Diaspora network compatibility." msgstr "Fornisce compatibilità con il network Diaspora." -#: mod/admin.php:768 +#: mod/admin.php:945 msgid "Only allow Friendica contacts" msgstr "Permetti solo contatti Friendica" -#: mod/admin.php:768 +#: mod/admin.php:945 msgid "" "All contacts must use Friendica protocols. All other built-in communication " "protocols disabled." msgstr "Tutti i contatti devono usare il protocollo di Friendica. Tutti gli altri protocolli sono disabilitati." -#: mod/admin.php:769 +#: mod/admin.php:946 msgid "Verify SSL" msgstr "Verifica SSL" -#: mod/admin.php:769 +#: mod/admin.php:946 msgid "" "If you wish, you can turn on strict certificate checking. This will mean you" " cannot connect (at all) to self-signed SSL sites." msgstr "Se vuoi, puoi abilitare il controllo rigoroso dei certificati.Questo significa che non potrai collegarti (del tutto) con siti con certificati SSL auto-firmati." -#: mod/admin.php:770 +#: mod/admin.php:947 msgid "Proxy user" msgstr "Utente Proxy" -#: mod/admin.php:771 +#: mod/admin.php:948 msgid "Proxy URL" msgstr "URL Proxy" -#: mod/admin.php:772 +#: mod/admin.php:949 msgid "Network timeout" msgstr "Timeout rete" -#: mod/admin.php:772 +#: mod/admin.php:949 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." msgstr "Valore in secondi. Imposta a 0 per illimitato (non raccomandato)." -#: mod/admin.php:773 +#: mod/admin.php:950 msgid "Delivery interval" msgstr "Intervallo di invio" -#: mod/admin.php:773 +#: mod/admin.php:950 msgid "" "Delay background delivery processes by this many seconds to reduce system " "load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 " "for large dedicated servers." msgstr "Ritarda il processo di invio in background di n secondi per ridurre il carico di sistema. Raccomandato: 4-5 per host condivisit, 2-3 per VPS. 0-1 per grandi server dedicati." -#: mod/admin.php:774 +#: mod/admin.php:951 msgid "Poll interval" msgstr "Intervallo di poll" -#: mod/admin.php:774 +#: mod/admin.php:951 msgid "" "Delay background polling processes by this many seconds to reduce system " "load. If 0, use delivery interval." msgstr "Ritarda il processo di poll in background di n secondi per ridurre il carico di sistema. Se 0, usa l'intervallo di invio." -#: mod/admin.php:775 +#: mod/admin.php:952 msgid "Maximum Load Average" msgstr "Massimo carico medio" -#: mod/admin.php:775 +#: mod/admin.php:952 msgid "" "Maximum system load before delivery and poll processes are deferred - " "default 50." msgstr "Massimo carico di sistema prima che i processi di invio e di poll siano ritardati. Predefinito a 50." -#: mod/admin.php:776 +#: mod/admin.php:953 msgid "Maximum Load Average (Frontend)" msgstr "Media Massimo Carico (Frontend)" -#: mod/admin.php:776 +#: mod/admin.php:953 msgid "Maximum system load before the frontend quits service - default 50." msgstr "Massimo carico di sistema prima che il frontend fermi il servizio - default 50." -#: mod/admin.php:777 +#: mod/admin.php:954 msgid "Maximum table size for optimization" msgstr "Dimensione massima della tabella per l'ottimizzazione" -#: mod/admin.php:777 +#: mod/admin.php:954 msgid "" "Maximum table size (in MB) for the automatic optimization - default 100 MB. " "Enter -1 to disable it." msgstr "La dimensione massima (in MB) per l'ottimizzazione automatica - default 100 MB. Inserisci -1 per disabilitarlo." -#: mod/admin.php:779 +#: mod/admin.php:955 +msgid "Minimum level of fragmentation" +msgstr "" + +#: mod/admin.php:955 +msgid "" +"Minimum fragmenation level to start the automatic optimization - default " +"value is 30%." +msgstr "" + +#: mod/admin.php:957 msgid "Periodical check of global contacts" msgstr "Check periodico dei contatti globali" -#: mod/admin.php:779 +#: mod/admin.php:957 msgid "" "If enabled, the global contacts are checked periodically for missing or " "outdated data and the vitality of the contacts and servers." msgstr "Se abilitato, i contatti globali sono controllati periodicamente per verificare dati mancanti o sorpassati e la vitaltà dei contatti e dei server." -#: mod/admin.php:780 +#: mod/admin.php:958 msgid "Days between requery" msgstr "Giorni tra le richieste" -#: mod/admin.php:780 +#: mod/admin.php:958 msgid "Number of days after which a server is requeried for his contacts." msgstr "Numero di giorni dopo i quali al server vengono richiesti i suoi contatti." -#: mod/admin.php:781 +#: mod/admin.php:959 msgid "Discover contacts from other servers" msgstr "Trova contatti dagli altri server" -#: mod/admin.php:781 +#: mod/admin.php:959 msgid "" "Periodically query other servers for contacts. You can choose between " "'users': the users on the remote system, 'Global Contacts': active contacts " @@ -2726,32 +2720,32 @@ msgid "" "Global Contacts'." msgstr "Richiede periodicamente contatti agli altri server. Puoi scegliere tra 'utenti', gli uenti sul sistema remoto, o 'contatti globali', i contatti attivi che sono conosciuti dal sistema. Il fallback è pensato per i server Redmatrix e i vecchi server Friendica, dove i contatti globali non sono disponibili. Il fallback incrementa il carico di sistema, per cui l'impostazione consigliata è \"Utenti, Contatti Globali\"." -#: mod/admin.php:782 +#: mod/admin.php:960 msgid "Timeframe for fetching global contacts" msgstr "Termine per il recupero contatti globali" -#: mod/admin.php:782 +#: mod/admin.php:960 msgid "" "When the discovery is activated, this value defines the timeframe for the " "activity of the global contacts that are fetched from other servers." msgstr "Quando si attiva la scoperta, questo valore definisce il periodo di tempo per l'attività dei contatti globali che vengono prelevati da altri server." -#: mod/admin.php:783 +#: mod/admin.php:961 msgid "Search the local directory" msgstr "Cerca la directory locale" -#: mod/admin.php:783 +#: mod/admin.php:961 msgid "" "Search the local directory instead of the global directory. When searching " "locally, every search will be executed on the global directory in the " "background. This improves the search results when the search is repeated." msgstr "Cerca nella directory locale invece che nella directory globale. Durante la ricerca a livello locale, ogni ricerca verrà eseguita sulla directory globale in background. Ciò migliora i risultati della ricerca quando la ricerca viene ripetuta." -#: mod/admin.php:785 +#: mod/admin.php:963 msgid "Publish server information" msgstr "Pubblica informazioni server" -#: mod/admin.php:785 +#: mod/admin.php:963 msgid "" "If enabled, general server and usage data will be published. The data " "contains the name and version of the server, number of users with public " @@ -2759,205 +2753,205 @@ msgid "" " href='http://the-federation.info/'>the-federation.info for details." msgstr "Se abilitata, saranno pubblicati i dati generali del server e i dati di utilizzo. I dati contengono il nome e la versione del server, il numero di utenti con profili pubblici, numero dei posti e dei protocolli e connettori attivati. Per informazioni, vedere the-federation.info ." -#: mod/admin.php:787 +#: mod/admin.php:965 msgid "Use MySQL full text engine" msgstr "Usa il motore MySQL full text" -#: mod/admin.php:787 +#: mod/admin.php:965 msgid "" "Activates the full text engine. Speeds up search - but can only search for " "four and more characters." msgstr "Attiva il motore full text. Velocizza la ricerca, ma puo' cercare solo per quattro o più caratteri." -#: mod/admin.php:788 +#: mod/admin.php:966 msgid "Suppress Language" msgstr "Disattiva lingua" -#: mod/admin.php:788 +#: mod/admin.php:966 msgid "Suppress language information in meta information about a posting." msgstr "Disattiva le informazioni sulla lingua nei meta di un post." -#: mod/admin.php:789 +#: mod/admin.php:967 msgid "Suppress Tags" msgstr "Sopprimi Tags" -#: mod/admin.php:789 +#: mod/admin.php:967 msgid "Suppress showing a list of hashtags at the end of the posting." msgstr "Non mostra la lista di hashtag in coda al messaggio" -#: mod/admin.php:790 +#: mod/admin.php:968 msgid "Path to item cache" msgstr "Percorso cache elementi" -#: mod/admin.php:790 +#: mod/admin.php:968 msgid "The item caches buffers generated bbcode and external images." msgstr "La cache degli elementi memorizza il bbcode generato e le immagini esterne." -#: mod/admin.php:791 +#: mod/admin.php:969 msgid "Cache duration in seconds" msgstr "Durata della cache in secondi" -#: mod/admin.php:791 +#: mod/admin.php:969 msgid "" "How long should the cache files be hold? Default value is 86400 seconds (One" " day). To disable the item cache, set the value to -1." msgstr "Quanto a lungo devono essere mantenuti i file di cache? Il valore predefinito è 86400 secondi (un giorno). Per disabilitare la cache, imposta il valore a -1." -#: mod/admin.php:792 +#: mod/admin.php:970 msgid "Maximum numbers of comments per post" msgstr "Numero massimo di commenti per post" -#: mod/admin.php:792 +#: mod/admin.php:970 msgid "How much comments should be shown for each post? Default value is 100." msgstr "Quanti commenti devono essere mostrati per ogni post? Default : 100." -#: mod/admin.php:793 +#: mod/admin.php:971 msgid "Path for lock file" msgstr "Percorso al file di lock" -#: mod/admin.php:793 +#: mod/admin.php:971 msgid "" "The lock file is used to avoid multiple pollers at one time. Only define a " "folder here." msgstr "Il file di lock è usato per evitare l'avvio di poller multipli allo stesso tempo. Inserisci solo la cartella, qui." -#: mod/admin.php:794 +#: mod/admin.php:972 msgid "Temp path" msgstr "Percorso file temporanei" -#: mod/admin.php:794 +#: mod/admin.php:972 msgid "" "If you have a restricted system where the webserver can't access the system " "temp path, enter another path here." msgstr "Se si dispone di un sistema ristretto in cui il server web non può accedere al percorso temporaneo di sistema, inserire un altro percorso qui." -#: mod/admin.php:795 +#: mod/admin.php:973 msgid "Base path to installation" msgstr "Percorso base all'installazione" -#: mod/admin.php:795 +#: mod/admin.php:973 msgid "" "If the system cannot detect the correct path to your installation, enter the" " correct path here. This setting should only be set if you are using a " "restricted system and symbolic links to your webroot." msgstr "Se il sistema non è in grado di rilevare il percorso corretto per l'installazione, immettere il percorso corretto qui. Questa impostazione deve essere inserita solo se si utilizza un sistema limitato e/o collegamenti simbolici al tuo webroot." -#: mod/admin.php:796 +#: mod/admin.php:974 msgid "Disable picture proxy" msgstr "Disabilita il proxy immagini" -#: mod/admin.php:796 +#: mod/admin.php:974 msgid "" "The picture proxy increases performance and privacy. It shouldn't be used on" " systems with very low bandwith." msgstr "Il proxy immagini aumenta le performace e la privacy. Non dovrebbe essere usato su server con poca banda disponibile." -#: mod/admin.php:797 +#: mod/admin.php:975 msgid "Enable old style pager" msgstr "Abilita la paginazione vecchio stile" -#: mod/admin.php:797 +#: mod/admin.php:975 msgid "" "The old style pager has page numbers but slows down massively the page " "speed." msgstr "La paginazione vecchio stile mostra i numeri delle pagine, ma rallenta la velocità di caricamento della pagina." -#: mod/admin.php:798 +#: mod/admin.php:976 msgid "Only search in tags" msgstr "Cerca solo nei tag" -#: mod/admin.php:798 +#: mod/admin.php:976 msgid "On large systems the text search can slow down the system extremely." msgstr "Su server con molti dati, la ricerca nel testo può estremamente rallentare il sistema." -#: mod/admin.php:800 +#: mod/admin.php:978 msgid "New base url" msgstr "Nuovo url base" -#: mod/admin.php:800 +#: mod/admin.php:978 msgid "" "Change base url for this server. Sends relocate message to all DFRN contacts" " of all users." msgstr "Cambia l'url base di questo server. Invia il messaggio di trasloco a tutti i contatti DFRN di tutti gli utenti." -#: mod/admin.php:802 +#: mod/admin.php:980 msgid "RINO Encryption" msgstr "Crittografia RINO" -#: mod/admin.php:802 +#: mod/admin.php:980 msgid "Encryption layer between nodes." msgstr "Crittografia delle comunicazioni tra nodi." -#: mod/admin.php:803 +#: mod/admin.php:981 msgid "Embedly API key" msgstr "Embedly API key" -#: mod/admin.php:803 +#: mod/admin.php:981 msgid "" "Embedly is used to fetch additional data for " "web pages. This is an optional parameter." msgstr "Embedly è usato per recuperate informazioni addizionali dalle pagine web. Questo parametro è opzionale." -#: mod/admin.php:821 +#: mod/admin.php:1010 msgid "Update has been marked successful" msgstr "L'aggiornamento è stato segnato come di successo" -#: mod/admin.php:829 +#: mod/admin.php:1018 #, php-format msgid "Database structure update %s was successfully applied." msgstr "Aggiornamento struttura database %s applicata con successo." -#: mod/admin.php:832 +#: mod/admin.php:1021 #, php-format msgid "Executing of database structure update %s failed with error: %s" msgstr "Aggiornamento struttura database %s fallita con errore: %s" -#: mod/admin.php:844 +#: mod/admin.php:1033 #, php-format msgid "Executing %s failed with error: %s" msgstr "Esecuzione di %s fallita con errore: %s" -#: mod/admin.php:847 +#: mod/admin.php:1036 #, php-format msgid "Update %s was successfully applied." msgstr "L'aggiornamento %s è stato applicato con successo" -#: mod/admin.php:851 +#: mod/admin.php:1040 #, php-format msgid "Update %s did not return a status. Unknown if it succeeded." msgstr "L'aggiornamento %s non ha riportato uno stato. Non so se è andato a buon fine." -#: mod/admin.php:853 +#: mod/admin.php:1042 #, php-format msgid "There was no additional update function %s that needed to be called." msgstr "Non ci sono altre funzioni di aggiornamento %s da richiamare." -#: mod/admin.php:872 +#: mod/admin.php:1061 msgid "No failed updates." msgstr "Nessun aggiornamento fallito." -#: mod/admin.php:873 +#: mod/admin.php:1062 msgid "Check database structure" msgstr "Controlla struttura database" -#: mod/admin.php:878 +#: mod/admin.php:1067 msgid "Failed Updates" msgstr "Aggiornamenti falliti" -#: mod/admin.php:879 +#: mod/admin.php:1068 msgid "" "This does not include updates prior to 1139, which did not return a status." msgstr "Questo non include gli aggiornamenti prima del 1139, che non ritornano lo stato." -#: mod/admin.php:880 +#: mod/admin.php:1069 msgid "Mark success (if update was manually applied)" msgstr "Segna completato (se l'update è stato applicato manualmente)" -#: mod/admin.php:881 +#: mod/admin.php:1070 msgid "Attempt to execute this update step automatically" msgstr "Cerco di eseguire questo aggiornamento in automatico" -#: mod/admin.php:913 +#: mod/admin.php:1102 #, php-format msgid "" "\n" @@ -2965,7 +2959,7 @@ msgid "" "\t\t\t\tthe administrator of %2$s has set up an account for you." msgstr "\nGentile %1$s,\n l'amministratore di %2$s ha impostato un account per te." -#: mod/admin.php:916 +#: mod/admin.php:1105 #, php-format msgid "" "\n" @@ -2995,233 +2989,255 @@ msgid "" "\t\t\tThank you and welcome to %4$s." msgstr "\nI dettagli del tuo utente sono:\n Indirizzo del sito: %1$s\n Nome utente: %2$s\n Password: %3$s\n\nPuoi cambiare la tua password dalla pagina delle impostazioni del tuo account dopo esserti autenticato.\n\nPer favore, prenditi qualche momento per esaminare tutte le impostazioni presenti.\n\nPotresti voler aggiungere qualche informazione di base al tuo profilo predefinito (nella pagina \"Profili\"), così che le altre persone possano trovarti più facilmente.\n\nTi raccomandiamo di inserire il tuo nome completo, aggiungere una foto, aggiungere qualche parola chiave del profilo (molto utili per trovare nuovi contatti), e magari in quale nazione vivi, se non vuoi essere più specifico di così.\n\nNoi rispettiamo appieno la tua privacy, e nessuna di queste informazioni è necessaria o obbligatoria.\nSe sei nuovo e non conosci nessuno qui, possono aiutarti a trovare qualche nuovo e interessante contatto.\n\nGrazie e benvenuto su %4$s" -#: mod/admin.php:948 include/user.php:423 +#: mod/admin.php:1137 include/user.php:423 #, php-format msgid "Registration details for %s" msgstr "Dettagli della registrazione di %s" -#: mod/admin.php:960 +#: mod/admin.php:1149 #, php-format msgid "%s user blocked/unblocked" msgid_plural "%s users blocked/unblocked" msgstr[0] "%s utente bloccato/sbloccato" msgstr[1] "%s utenti bloccati/sbloccati" -#: mod/admin.php:967 +#: mod/admin.php:1156 #, php-format msgid "%s user deleted" msgid_plural "%s users deleted" msgstr[0] "%s utente cancellato" msgstr[1] "%s utenti cancellati" -#: mod/admin.php:1006 +#: mod/admin.php:1203 #, php-format msgid "User '%s' deleted" msgstr "Utente '%s' cancellato" -#: mod/admin.php:1014 +#: mod/admin.php:1211 #, php-format msgid "User '%s' unblocked" msgstr "Utente '%s' sbloccato" -#: mod/admin.php:1014 +#: mod/admin.php:1211 #, php-format msgid "User '%s' blocked" msgstr "Utente '%s' bloccato" -#: mod/admin.php:1107 +#: mod/admin.php:1302 msgid "Add User" msgstr "Aggiungi utente" -#: mod/admin.php:1108 +#: mod/admin.php:1303 msgid "select all" msgstr "seleziona tutti" -#: mod/admin.php:1109 +#: mod/admin.php:1304 msgid "User registrations waiting for confirm" msgstr "Richieste di registrazione in attesa di conferma" -#: mod/admin.php:1110 +#: mod/admin.php:1305 msgid "User waiting for permanent deletion" msgstr "Utente in attesa di cancellazione definitiva" -#: mod/admin.php:1111 +#: mod/admin.php:1306 msgid "Request date" msgstr "Data richiesta" -#: mod/admin.php:1111 mod/admin.php:1123 mod/admin.php:1124 mod/admin.php:1139 +#: mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 mod/admin.php:1334 #: include/contact_selectors.php:79 include/contact_selectors.php:86 msgid "Email" msgstr "Email" -#: mod/admin.php:1112 +#: mod/admin.php:1307 msgid "No registrations." msgstr "Nessuna registrazione." -#: mod/admin.php:1114 +#: mod/admin.php:1309 msgid "Deny" msgstr "Nega" -#: mod/admin.php:1118 +#: mod/admin.php:1313 msgid "Site admin" msgstr "Amministrazione sito" -#: mod/admin.php:1119 +#: mod/admin.php:1314 msgid "Account expired" msgstr "Account scaduto" -#: mod/admin.php:1122 +#: mod/admin.php:1317 msgid "New User" msgstr "Nuovo Utente" -#: mod/admin.php:1123 mod/admin.php:1124 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Register date" msgstr "Data registrazione" -#: mod/admin.php:1123 mod/admin.php:1124 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Last login" msgstr "Ultimo accesso" -#: mod/admin.php:1123 mod/admin.php:1124 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Last item" msgstr "Ultimo elemento" -#: mod/admin.php:1123 +#: mod/admin.php:1318 msgid "Deleted since" msgstr "Rimosso da" -#: mod/admin.php:1124 mod/settings.php:41 +#: mod/admin.php:1319 mod/settings.php:41 msgid "Account" msgstr "Account" -#: mod/admin.php:1126 +#: mod/admin.php:1321 msgid "" "Selected users will be deleted!\\n\\nEverything these users had posted on " "this site will be permanently deleted!\\n\\nAre you sure?" msgstr "Gli utenti selezionati saranno cancellati!\\n\\nTutto quello che gli utenti hanno inviato su questo sito sarà permanentemente canellato!\\n\\nSei sicuro?" -#: mod/admin.php:1127 +#: mod/admin.php:1322 msgid "" "The user {0} will be deleted!\\n\\nEverything this user has posted on this " "site will be permanently deleted!\\n\\nAre you sure?" msgstr "L'utente {0} sarà cancellato!\\n\\nTutto quello che ha inviato su questo sito sarà permanentemente cancellato!\\n\\nSei sicuro?" -#: mod/admin.php:1137 +#: mod/admin.php:1332 msgid "Name of the new user." msgstr "Nome del nuovo utente." -#: mod/admin.php:1138 +#: mod/admin.php:1333 msgid "Nickname" msgstr "Nome utente" -#: mod/admin.php:1138 +#: mod/admin.php:1333 msgid "Nickname of the new user." msgstr "Nome utente del nuovo utente." -#: mod/admin.php:1139 +#: mod/admin.php:1334 msgid "Email address of the new user." msgstr "Indirizzo Email del nuovo utente." -#: mod/admin.php:1172 +#: mod/admin.php:1377 #, php-format msgid "Plugin %s disabled." msgstr "Plugin %s disabilitato." -#: mod/admin.php:1176 +#: mod/admin.php:1381 #, php-format msgid "Plugin %s enabled." msgstr "Plugin %s abilitato." -#: mod/admin.php:1186 mod/admin.php:1410 +#: mod/admin.php:1392 mod/admin.php:1628 msgid "Disable" msgstr "Disabilita" -#: mod/admin.php:1188 mod/admin.php:1412 +#: mod/admin.php:1394 mod/admin.php:1630 msgid "Enable" msgstr "Abilita" -#: mod/admin.php:1211 mod/admin.php:1456 +#: mod/admin.php:1417 mod/admin.php:1675 msgid "Toggle" msgstr "Inverti" -#: mod/admin.php:1219 mod/admin.php:1466 +#: mod/admin.php:1425 mod/admin.php:1684 msgid "Author: " msgstr "Autore: " -#: mod/admin.php:1220 mod/admin.php:1467 +#: mod/admin.php:1426 mod/admin.php:1685 msgid "Maintainer: " msgstr "Manutentore: " -#: mod/admin.php:1272 -#: view/smarty3/compiled/f835364006028b1061f37be121c9bd9db5fa50a9.file.admin_plugins.tpl.php:42 +#: mod/admin.php:1478 msgid "Reload active plugins" msgstr "Ricarica i plugin attivi" -#: mod/admin.php:1370 +#: mod/admin.php:1483 +#, php-format +msgid "" +"There are currently no plugins available on your node. You can find the " +"official plugin repository at %1$s and might find other interesting plugins " +"in the open plugin registry at %2$s" +msgstr "" + +#: mod/admin.php:1588 msgid "No themes found." msgstr "Nessun tema trovato." -#: mod/admin.php:1448 +#: mod/admin.php:1666 msgid "Screenshot" msgstr "Anteprima" -#: mod/admin.php:1508 +#: mod/admin.php:1726 msgid "Reload active themes" msgstr "Ricarica i temi attivi" -#: mod/admin.php:1512 +#: mod/admin.php:1731 +#, php-format +msgid "No themes found on the system. They should be paced in %1$s" +msgstr "" + +#: mod/admin.php:1732 msgid "[Experimental]" msgstr "[Sperimentale]" -#: mod/admin.php:1513 +#: mod/admin.php:1733 msgid "[Unsupported]" msgstr "[Non supportato]" -#: mod/admin.php:1540 +#: mod/admin.php:1757 msgid "Log settings updated." msgstr "Impostazioni Log aggiornate." -#: mod/admin.php:1596 +#: mod/admin.php:1794 msgid "Clear" msgstr "Pulisci" -#: mod/admin.php:1602 +#: mod/admin.php:1799 msgid "Enable Debugging" msgstr "Abilita Debugging" -#: mod/admin.php:1603 +#: mod/admin.php:1800 msgid "Log file" msgstr "File di Log" -#: mod/admin.php:1603 +#: mod/admin.php:1800 msgid "" "Must be writable by web server. Relative to your Friendica top-level " "directory." msgstr "Deve essere scrivibile dal server web. Relativo alla tua directory Friendica." -#: mod/admin.php:1604 +#: mod/admin.php:1801 msgid "Log level" msgstr "Livello di Log" -#: mod/admin.php:1654 include/acl_selectors.php:348 -msgid "Close" -msgstr "Chiudi" +#: mod/admin.php:1804 +msgid "PHP logging" +msgstr "" -#: mod/admin.php:1660 -msgid "FTP Host" -msgstr "Indirizzo FTP" +#: mod/admin.php:1805 +msgid "" +"To enable logging of PHP errors and warnings you can add the following to " +"the .htconfig.php file of your installation. The filename set in the " +"'error_log' line is relative to the friendica top-level directory and must " +"be writeable by the web server. The option '1' for 'log_errors' and " +"'display_errors' is to enable these options, set to '0' to disable them." +msgstr "" -#: mod/admin.php:1661 -msgid "FTP Path" -msgstr "Percorso FTP" +#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +msgid "Off" +msgstr "Spento" -#: mod/admin.php:1662 -msgid "FTP User" -msgstr "Utente FTP" +#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +msgid "On" +msgstr "Acceso" -#: mod/admin.php:1663 -msgid "FTP Password" -msgstr "Pasword FTP" +#: mod/admin.php:1932 +#, php-format +msgid "Lock feature %s" +msgstr "" + +#: mod/admin.php:1940 +msgid "Manage Additional Features" +msgstr "" #: mod/network.php:146 #, php-format @@ -3232,7 +3248,7 @@ msgstr "Risultato della ricerca per: %s" msgid "Remove term" msgstr "Rimuovi termine" -#: mod/network.php:200 mod/search.php:34 include/features.php:79 +#: mod/network.php:200 mod/search.php:34 include/features.php:84 msgid "Saved Searches" msgstr "Ricerche salvate" @@ -3240,51 +3256,51 @@ msgstr "Ricerche salvate" msgid "add" msgstr "aggiungi" -#: mod/network.php:362 +#: mod/network.php:365 msgid "Commented Order" msgstr "Ordina per commento" -#: mod/network.php:365 +#: mod/network.php:368 msgid "Sort by Comment Date" msgstr "Ordina per data commento" -#: mod/network.php:370 +#: mod/network.php:373 msgid "Posted Order" msgstr "Ordina per invio" -#: mod/network.php:373 +#: mod/network.php:376 msgid "Sort by Post Date" msgstr "Ordina per data messaggio" -#: mod/network.php:384 +#: mod/network.php:387 msgid "Posts that mention or involve you" msgstr "Messaggi che ti citano o coinvolgono" -#: mod/network.php:392 +#: mod/network.php:395 msgid "New" msgstr "Nuovo" -#: mod/network.php:395 +#: mod/network.php:398 msgid "Activity Stream - by date" msgstr "Activity Stream - per data" -#: mod/network.php:403 +#: mod/network.php:406 msgid "Shared Links" msgstr "Links condivisi" -#: mod/network.php:406 +#: mod/network.php:409 msgid "Interesting Links" msgstr "Link Interessanti" -#: mod/network.php:414 +#: mod/network.php:417 msgid "Starred" msgstr "Preferiti" -#: mod/network.php:417 +#: mod/network.php:420 msgid "Favourite Posts" msgstr "Messaggi preferiti" -#: mod/network.php:476 +#: mod/network.php:479 #, php-format msgid "Warning: This group contains %s member from an insecure network." msgid_plural "" @@ -3292,28 +3308,28 @@ msgid_plural "" msgstr[0] "Attenzione: questo gruppo contiene %s membro da un network insicuro." msgstr[1] "Attenzione: questo gruppo contiene %s membri da un network insicuro." -#: mod/network.php:479 +#: mod/network.php:482 msgid "Private messages to this group are at risk of public disclosure." msgstr "I messaggi privati su questo gruppo potrebbero risultare visibili anche pubblicamente." -#: mod/network.php:546 mod/content.php:119 +#: mod/network.php:549 mod/content.php:119 msgid "No such group" msgstr "Nessun gruppo" -#: mod/network.php:574 mod/content.php:135 +#: mod/network.php:580 mod/content.php:135 #, php-format msgid "Group: %s" msgstr "Gruppo: %s" -#: mod/network.php:606 +#: mod/network.php:608 msgid "Private messages to this person are at risk of public disclosure." msgstr "I messaggi privati a questa persona potrebbero risultare visibili anche pubblicamente." -#: mod/network.php:611 +#: mod/network.php:613 msgid "Invalid contact." msgstr "Contatto non valido." -#: mod/allfriends.php:38 +#: mod/allfriends.php:43 msgid "No friends to display." msgstr "Nessun amico da visualizzare." @@ -3353,11 +3369,11 @@ msgstr "Ven" msgid "Sat" msgstr "Sab" -#: mod/events.php:208 mod/settings.php:939 include/text.php:1274 +#: mod/events.php:208 mod/settings.php:948 include/text.php:1274 msgid "Sunday" msgstr "Domenica" -#: mod/events.php:209 mod/settings.php:939 include/text.php:1274 +#: mod/events.php:209 mod/settings.php:948 include/text.php:1274 msgid "Monday" msgstr "Lunedì" @@ -3497,11 +3513,11 @@ msgstr "l j F" msgid "Edit event" msgstr "Modifca l'evento" -#: mod/events.php:421 include/text.php:1721 include/text.php:1728 +#: mod/events.php:421 include/text.php:1728 include/text.php:1735 msgid "link to source" msgstr "Collegamento all'originale" -#: mod/events.php:456 include/identity.php:713 include/nav.php:79 +#: mod/events.php:456 include/identity.php:722 include/nav.php:79 #: include/nav.php:140 view/theme/diabook/theme.php:127 msgid "Events" msgstr "Eventi" @@ -3560,7 +3576,7 @@ msgstr "Condividi questo evento" #: mod/events.php:572 mod/content.php:721 mod/editpost.php:145 #: mod/photos.php:1631 mod/photos.php:1679 mod/photos.php:1767 -#: object/Item.php:719 include/conversation.php:1217 +#: object/Item.php:719 include/conversation.php:1216 msgid "Preview" msgstr "Anteprima" @@ -3604,15 +3620,15 @@ msgstr[0] "%d commento" msgstr[1] "%d commenti" #: mod/content.php:607 object/Item.php:421 object/Item.php:434 -#: include/text.php:1997 +#: include/text.php:2004 msgid "comment" msgid_plural "comments" msgstr[0] "" msgstr[1] "commento" -#: mod/content.php:608 boot.php:785 object/Item.php:422 +#: mod/content.php:608 boot.php:870 object/Item.php:422 #: include/contact_widgets.php:242 include/forums.php:110 -#: include/items.php:5178 view/theme/vier/theme.php:264 +#: include/items.php:5207 view/theme/vier/theme.php:264 msgid "show more" msgstr "mostra di più" @@ -3650,7 +3666,7 @@ msgid "This is you" msgstr "Questo sei tu" #: mod/content.php:711 mod/photos.php:1629 mod/photos.php:1677 -#: mod/photos.php:1765 boot.php:784 object/Item.php:393 object/Item.php:709 +#: mod/photos.php:1765 boot.php:869 object/Item.php:393 object/Item.php:709 msgid "Comment" msgstr "Commento" @@ -3686,7 +3702,7 @@ msgstr "Link" msgid "Video" msgstr "Video" -#: mod/content.php:730 mod/settings.php:712 object/Item.php:122 +#: mod/content.php:730 mod/settings.php:721 object/Item.php:122 #: object/Item.php:124 msgid "Edit" msgstr "Modifica" @@ -4082,19 +4098,19 @@ msgid "" "your site allow private mail from unknown senders." msgstr "Se vuoi che %s ti risponda, controlla che le tue impostazioni di privacy permettano la ricezione di messaggi privati da mittenti sconosciuti." -#: mod/help.php:31 +#: mod/help.php:41 msgid "Help:" msgstr "Guida:" -#: mod/help.php:36 include/nav.php:113 view/theme/vier/theme.php:302 +#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:302 msgid "Help" msgstr "Guida" -#: mod/help.php:42 mod/p.php:16 mod/p.php:25 index.php:269 +#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:270 msgid "Not Found" msgstr "Non trovato" -#: mod/help.php:45 index.php:272 +#: mod/help.php:56 index.php:273 msgid "Page not found." msgstr "Pagina non trovata." @@ -4141,16 +4157,16 @@ msgstr "Profili corrispondenti" msgid "link" msgstr "collegamento" -#: mod/community.php:23 +#: mod/community.php:27 msgid "Not available." msgstr "Non disponibile." -#: mod/community.php:32 include/nav.php:136 include/nav.php:138 +#: mod/community.php:36 include/nav.php:136 include/nav.php:138 #: view/theme/diabook/theme.php:129 msgid "Community" msgstr "Comunità" -#: mod/community.php:62 mod/community.php:71 mod/search.php:228 +#: mod/community.php:66 mod/community.php:75 mod/search.php:228 msgid "No results." msgstr "Nessun risultato." @@ -4158,822 +4174,812 @@ msgstr "Nessun risultato." msgid "everybody" msgstr "tutti" -#: mod/settings.php:47 -msgid "Additional features" -msgstr "Funzionalità aggiuntive" - -#: mod/settings.php:53 +#: mod/settings.php:58 msgid "Display" msgstr "Visualizzazione" -#: mod/settings.php:60 mod/settings.php:855 +#: mod/settings.php:65 mod/settings.php:864 msgid "Social Networks" msgstr "Social Networks" -#: mod/settings.php:72 include/nav.php:180 +#: mod/settings.php:79 include/nav.php:180 msgid "Delegations" msgstr "Delegazioni" -#: mod/settings.php:78 +#: mod/settings.php:86 msgid "Connected apps" msgstr "Applicazioni collegate" -#: mod/settings.php:84 mod/uexport.php:85 +#: mod/settings.php:93 mod/uexport.php:85 msgid "Export personal data" msgstr "Esporta dati personali" -#: mod/settings.php:90 +#: mod/settings.php:100 msgid "Remove account" msgstr "Rimuovi account" -#: mod/settings.php:143 +#: mod/settings.php:153 msgid "Missing some important data!" msgstr "Mancano alcuni dati importanti!" -#: mod/settings.php:256 +#: mod/settings.php:266 msgid "Failed to connect with email account using the settings provided." msgstr "Impossibile collegarsi all'account email con i parametri forniti." -#: mod/settings.php:261 +#: mod/settings.php:271 msgid "Email settings updated." msgstr "Impostazioni e-mail aggiornate." -#: mod/settings.php:276 +#: mod/settings.php:286 msgid "Features updated" msgstr "Funzionalità aggiornate" -#: mod/settings.php:343 +#: mod/settings.php:353 msgid "Relocate message has been send to your contacts" msgstr "Il messaggio di trasloco è stato inviato ai tuoi contatti" -#: mod/settings.php:357 include/user.php:39 +#: mod/settings.php:367 include/user.php:39 msgid "Passwords do not match. Password unchanged." msgstr "Le password non corrispondono. Password non cambiata." -#: mod/settings.php:362 +#: mod/settings.php:372 msgid "Empty passwords are not allowed. Password unchanged." msgstr "Le password non possono essere vuote. Password non cambiata." -#: mod/settings.php:370 +#: mod/settings.php:380 msgid "Wrong password." msgstr "Password sbagliata." -#: mod/settings.php:381 +#: mod/settings.php:391 msgid "Password changed." msgstr "Password cambiata." -#: mod/settings.php:383 +#: mod/settings.php:393 msgid "Password update failed. Please try again." msgstr "Aggiornamento password fallito. Prova ancora." -#: mod/settings.php:452 +#: mod/settings.php:462 msgid " Please use a shorter name." msgstr " Usa un nome più corto." -#: mod/settings.php:454 +#: mod/settings.php:464 msgid " Name too short." msgstr " Nome troppo corto." -#: mod/settings.php:463 +#: mod/settings.php:473 msgid "Wrong Password" msgstr "Password Sbagliata" -#: mod/settings.php:468 +#: mod/settings.php:478 msgid " Not valid email." msgstr " Email non valida." -#: mod/settings.php:474 +#: mod/settings.php:484 msgid " Cannot change to that email." msgstr "Non puoi usare quella email." -#: mod/settings.php:530 +#: mod/settings.php:540 msgid "Private forum has no privacy permissions. Using default privacy group." msgstr "Il forum privato non ha permessi di privacy. Uso il gruppo di privacy predefinito." -#: mod/settings.php:534 +#: mod/settings.php:544 msgid "Private forum has no privacy permissions and no default privacy group." msgstr "Il gruppo privato non ha permessi di privacy e nessun gruppo di privacy predefinito." -#: mod/settings.php:573 +#: mod/settings.php:583 msgid "Settings updated." msgstr "Impostazioni aggiornate." -#: mod/settings.php:649 mod/settings.php:675 mod/settings.php:711 +#: mod/settings.php:658 mod/settings.php:684 mod/settings.php:720 msgid "Add application" msgstr "Aggiungi applicazione" -#: mod/settings.php:653 mod/settings.php:679 +#: mod/settings.php:662 mod/settings.php:688 msgid "Consumer Key" msgstr "Consumer Key" -#: mod/settings.php:654 mod/settings.php:680 +#: mod/settings.php:663 mod/settings.php:689 msgid "Consumer Secret" msgstr "Consumer Secret" -#: mod/settings.php:655 mod/settings.php:681 +#: mod/settings.php:664 mod/settings.php:690 msgid "Redirect" msgstr "Redirect" -#: mod/settings.php:656 mod/settings.php:682 +#: mod/settings.php:665 mod/settings.php:691 msgid "Icon url" msgstr "Url icona" -#: mod/settings.php:667 +#: mod/settings.php:676 msgid "You can't edit this application." msgstr "Non puoi modificare questa applicazione." -#: mod/settings.php:710 +#: mod/settings.php:719 msgid "Connected Apps" msgstr "Applicazioni Collegate" -#: mod/settings.php:714 +#: mod/settings.php:723 msgid "Client key starts with" msgstr "Chiave del client inizia con" -#: mod/settings.php:715 +#: mod/settings.php:724 msgid "No name" msgstr "Nessun nome" -#: mod/settings.php:716 +#: mod/settings.php:725 msgid "Remove authorization" msgstr "Rimuovi l'autorizzazione" -#: mod/settings.php:728 +#: mod/settings.php:737 msgid "No Plugin settings configured" msgstr "Nessun plugin ha impostazioni modificabili" -#: mod/settings.php:736 +#: mod/settings.php:745 msgid "Plugin Settings" msgstr "Impostazioni plugin" -#: mod/settings.php:750 -msgid "Off" -msgstr "Spento" - -#: mod/settings.php:750 -msgid "On" -msgstr "Acceso" - -#: mod/settings.php:758 +#: mod/settings.php:767 msgid "Additional Features" msgstr "Funzionalità aggiuntive" -#: mod/settings.php:768 mod/settings.php:772 +#: mod/settings.php:777 mod/settings.php:781 msgid "General Social Media Settings" msgstr "Impostazioni Media Sociali" -#: mod/settings.php:778 +#: mod/settings.php:787 msgid "Disable intelligent shortening" msgstr "Disabilita accorciamento intelligente" -#: mod/settings.php:780 +#: mod/settings.php:789 msgid "" "Normally the system tries to find the best link to add to shortened posts. " "If this option is enabled then every shortened post will always point to the" " original friendica post." msgstr "Normalmente il sistema tenta di trovare il migliore link da aggiungere a un post accorciato. Se questa opzione è abilitata, ogni post accorciato conterrà sempre un link al post originale su Friendica." -#: mod/settings.php:786 +#: mod/settings.php:795 msgid "Automatically follow any GNU Social (OStatus) followers/mentioners" msgstr "Segui automanticamente chiunque da GNU Social (OStatus) ti segua o ti menzioni" -#: mod/settings.php:788 +#: mod/settings.php:797 msgid "" "If you receive a message from an unknown OStatus user, this option decides " "what to do. If it is checked, a new contact will be created for every " "unknown user." msgstr "Se ricevi un messaggio da un utente OStatus sconosciuto, questa opzione decide cosa fare. Se selezionato, un nuovo contatto verrà creato per ogni utente sconosciuto." -#: mod/settings.php:797 +#: mod/settings.php:806 msgid "Your legacy GNU Social account" msgstr "Il tuo vecchio account GNU Social" -#: mod/settings.php:799 +#: mod/settings.php:808 msgid "" "If you enter your old GNU Social/Statusnet account name here (in the format " "user@domain.tld), your contacts will be added automatically. The field will " "be emptied when done." msgstr "Se inserisci il nome del tuo vecchio account GNU Social/Statusnet qui (nel formato utente@dominio.tld), i tuoi contatti verranno automaticamente aggiunti. Il campo verrà svuotato una volta terminato." -#: mod/settings.php:802 +#: mod/settings.php:811 msgid "Repair OStatus subscriptions" msgstr "Ripara le iscrizioni OStatus" -#: mod/settings.php:811 mod/settings.php:812 +#: mod/settings.php:820 mod/settings.php:821 #, php-format msgid "Built-in support for %s connectivity is %s" msgstr "Il supporto integrato per la connettività con %s è %s" -#: mod/settings.php:811 mod/dfrn_request.php:858 +#: mod/settings.php:820 mod/dfrn_request.php:865 #: include/contact_selectors.php:80 msgid "Diaspora" msgstr "Diaspora" -#: mod/settings.php:811 mod/settings.php:812 +#: mod/settings.php:820 mod/settings.php:821 msgid "enabled" msgstr "abilitato" -#: mod/settings.php:811 mod/settings.php:812 +#: mod/settings.php:820 mod/settings.php:821 msgid "disabled" msgstr "disabilitato" -#: mod/settings.php:812 +#: mod/settings.php:821 msgid "GNU Social (OStatus)" msgstr "GNU Social (OStatus)" -#: mod/settings.php:848 +#: mod/settings.php:857 msgid "Email access is disabled on this site." msgstr "L'accesso email è disabilitato su questo sito." -#: mod/settings.php:860 +#: mod/settings.php:869 msgid "Email/Mailbox Setup" msgstr "Impostazioni email" -#: mod/settings.php:861 +#: mod/settings.php:870 msgid "" "If you wish to communicate with email contacts using this service " "(optional), please specify how to connect to your mailbox." msgstr "Se vuoi comunicare con i contatti email usando questo servizio, specifica come collegarti alla tua casella di posta. (opzionale)" -#: mod/settings.php:862 +#: mod/settings.php:871 msgid "Last successful email check:" msgstr "Ultimo controllo email eseguito con successo:" -#: mod/settings.php:864 +#: mod/settings.php:873 msgid "IMAP server name:" msgstr "Nome server IMAP:" -#: mod/settings.php:865 +#: mod/settings.php:874 msgid "IMAP port:" msgstr "Porta IMAP:" -#: mod/settings.php:866 +#: mod/settings.php:875 msgid "Security:" msgstr "Sicurezza:" -#: mod/settings.php:866 mod/settings.php:871 +#: mod/settings.php:875 mod/settings.php:880 msgid "None" msgstr "Nessuna" -#: mod/settings.php:867 +#: mod/settings.php:876 msgid "Email login name:" msgstr "Nome utente email:" -#: mod/settings.php:868 +#: mod/settings.php:877 msgid "Email password:" msgstr "Password email:" -#: mod/settings.php:869 +#: mod/settings.php:878 msgid "Reply-to address:" msgstr "Indirizzo di risposta:" -#: mod/settings.php:870 +#: mod/settings.php:879 msgid "Send public posts to all email contacts:" msgstr "Invia i messaggi pubblici ai contatti email:" -#: mod/settings.php:871 +#: mod/settings.php:880 msgid "Action after import:" msgstr "Azione post importazione:" -#: mod/settings.php:871 +#: mod/settings.php:880 msgid "Mark as seen" msgstr "Segna come letto" -#: mod/settings.php:871 +#: mod/settings.php:880 msgid "Move to folder" msgstr "Sposta nella cartella" -#: mod/settings.php:872 +#: mod/settings.php:881 msgid "Move to folder:" msgstr "Sposta nella cartella:" -#: mod/settings.php:958 +#: mod/settings.php:967 msgid "Display Settings" msgstr "Impostazioni Grafiche" -#: mod/settings.php:964 mod/settings.php:982 +#: mod/settings.php:973 mod/settings.php:991 msgid "Display Theme:" msgstr "Tema:" -#: mod/settings.php:965 +#: mod/settings.php:974 msgid "Mobile Theme:" msgstr "Tema mobile:" -#: mod/settings.php:966 +#: mod/settings.php:975 msgid "Update browser every xx seconds" msgstr "Aggiorna il browser ogni x secondi" -#: mod/settings.php:966 +#: mod/settings.php:975 msgid "Minimum of 10 seconds. Enter -1 to disable it." msgstr "Minimo 10 secondi. Inserisci -1 per disabilitarlo" -#: mod/settings.php:967 +#: mod/settings.php:976 msgid "Number of items to display per page:" msgstr "Numero di elementi da mostrare per pagina:" -#: mod/settings.php:967 mod/settings.php:968 +#: mod/settings.php:976 mod/settings.php:977 msgid "Maximum of 100 items" msgstr "Massimo 100 voci" -#: mod/settings.php:968 +#: mod/settings.php:977 msgid "Number of items to display per page when viewed from mobile device:" msgstr "Numero di voci da visualizzare per pagina quando si utilizza un dispositivo mobile:" -#: mod/settings.php:969 +#: mod/settings.php:978 msgid "Don't show emoticons" msgstr "Non mostrare le emoticons" -#: mod/settings.php:970 +#: mod/settings.php:979 msgid "Calendar" msgstr "Calendario" -#: mod/settings.php:971 +#: mod/settings.php:980 msgid "Beginning of week:" msgstr "Inizio della settimana:" -#: mod/settings.php:972 +#: mod/settings.php:981 msgid "Don't show notices" msgstr "Non mostrare gli avvisi" -#: mod/settings.php:973 +#: mod/settings.php:982 msgid "Infinite scroll" msgstr "Scroll infinito" -#: mod/settings.php:974 +#: mod/settings.php:983 msgid "Automatic updates only at the top of the network page" msgstr "Aggiornamenti automatici solo in cima alla pagina \"rete\"" -#: mod/settings.php:976 view/theme/cleanzero/config.php:82 +#: mod/settings.php:985 view/theme/cleanzero/config.php:82 #: view/theme/dispy/config.php:72 view/theme/quattro/config.php:66 -#: view/theme/diabook/config.php:150 view/theme/clean/config.php:85 -#: view/theme/vier/config.php:109 view/theme/duepuntozero/config.php:61 +#: view/theme/diabook/config.php:150 view/theme/vier/config.php:109 +#: view/theme/duepuntozero/config.php:61 msgid "Theme settings" msgstr "Impostazioni tema" -#: mod/settings.php:1053 +#: mod/settings.php:1062 msgid "User Types" msgstr "Tipi di Utenti" -#: mod/settings.php:1054 +#: mod/settings.php:1063 msgid "Community Types" msgstr "Tipi di Comunità" -#: mod/settings.php:1055 +#: mod/settings.php:1064 msgid "Normal Account Page" msgstr "Pagina Account Normale" -#: mod/settings.php:1056 +#: mod/settings.php:1065 msgid "This account is a normal personal profile" msgstr "Questo account è un normale profilo personale" -#: mod/settings.php:1059 +#: mod/settings.php:1068 msgid "Soapbox Page" msgstr "Pagina Sandbox" -#: mod/settings.php:1060 +#: mod/settings.php:1069 msgid "Automatically approve all connection/friend requests as read-only fans" msgstr "Chi richiede la connessione/amicizia sarà accettato automaticamente come fan che potrà solamente leggere la bacheca" -#: mod/settings.php:1063 +#: mod/settings.php:1072 msgid "Community Forum/Celebrity Account" msgstr "Account Celebrità/Forum comunitario" -#: mod/settings.php:1064 +#: mod/settings.php:1073 msgid "" "Automatically approve all connection/friend requests as read-write fans" msgstr "Chi richiede la connessione/amicizia sarà accettato automaticamente come fan che potrà leggere e scrivere sulla bacheca" -#: mod/settings.php:1067 +#: mod/settings.php:1076 msgid "Automatic Friend Page" msgstr "Pagina con amicizia automatica" -#: mod/settings.php:1068 +#: mod/settings.php:1077 msgid "Automatically approve all connection/friend requests as friends" msgstr "Chi richiede la connessione/amicizia sarà accettato automaticamente come amico" -#: mod/settings.php:1071 +#: mod/settings.php:1080 msgid "Private Forum [Experimental]" msgstr "Forum privato [sperimentale]" -#: mod/settings.php:1072 +#: mod/settings.php:1081 msgid "Private forum - approved members only" msgstr "Forum privato - solo membri approvati" -#: mod/settings.php:1084 +#: mod/settings.php:1093 msgid "OpenID:" msgstr "OpenID:" -#: mod/settings.php:1084 +#: mod/settings.php:1093 msgid "(Optional) Allow this OpenID to login to this account." msgstr "(Opzionale) Consente di loggarti in questo account con questo OpenID" -#: mod/settings.php:1094 +#: mod/settings.php:1103 msgid "Publish your default profile in your local site directory?" msgstr "Pubblica il tuo profilo predefinito nell'elenco locale del sito" -#: mod/settings.php:1100 +#: mod/settings.php:1109 msgid "Publish your default profile in the global social directory?" msgstr "Pubblica il tuo profilo predefinito nell'elenco sociale globale" -#: mod/settings.php:1108 +#: mod/settings.php:1117 msgid "Hide your contact/friend list from viewers of your default profile?" msgstr "Nascondi la lista dei tuoi contatti/amici dai visitatori del tuo profilo predefinito" -#: mod/settings.php:1112 include/acl_selectors.php:331 +#: mod/settings.php:1121 include/acl_selectors.php:331 msgid "Hide your profile details from unknown viewers?" msgstr "Nascondi i dettagli del tuo profilo ai visitatori sconosciuti?" -#: mod/settings.php:1112 +#: mod/settings.php:1121 msgid "" "If enabled, posting public messages to Diaspora and other networks isn't " "possible." msgstr "Se abilitato, l'invio di messaggi pubblici verso Diaspora e altri network non sarà possibile" -#: mod/settings.php:1117 +#: mod/settings.php:1126 msgid "Allow friends to post to your profile page?" msgstr "Permetti agli amici di scrivere sulla tua pagina profilo?" -#: mod/settings.php:1123 +#: mod/settings.php:1132 msgid "Allow friends to tag your posts?" msgstr "Permetti agli amici di taggare i tuoi messaggi?" -#: mod/settings.php:1129 +#: mod/settings.php:1138 msgid "Allow us to suggest you as a potential friend to new members?" msgstr "Ci permetti di suggerirti come potenziale amico ai nuovi membri?" -#: mod/settings.php:1135 +#: mod/settings.php:1144 msgid "Permit unknown people to send you private mail?" msgstr "Permetti a utenti sconosciuti di inviarti messaggi privati?" -#: mod/settings.php:1143 +#: mod/settings.php:1152 msgid "Profile is not published." msgstr "Il profilo non è pubblicato." -#: mod/settings.php:1151 +#: mod/settings.php:1160 #, php-format msgid "Your Identity Address is '%s' or '%s'." msgstr "L'indirizzo della tua identità è '%s' or '%s'." -#: mod/settings.php:1158 +#: mod/settings.php:1167 msgid "Automatically expire posts after this many days:" msgstr "Fai scadere i post automaticamente dopo x giorni:" -#: mod/settings.php:1158 +#: mod/settings.php:1167 msgid "If empty, posts will not expire. Expired posts will be deleted" msgstr "Se lasciato vuoto, i messaggi non verranno cancellati." -#: mod/settings.php:1159 +#: mod/settings.php:1168 msgid "Advanced expiration settings" msgstr "Impostazioni avanzate di scandenza" -#: mod/settings.php:1160 +#: mod/settings.php:1169 msgid "Advanced Expiration" msgstr "Scadenza avanzata" -#: mod/settings.php:1161 +#: mod/settings.php:1170 msgid "Expire posts:" msgstr "Fai scadere i post:" -#: mod/settings.php:1162 +#: mod/settings.php:1171 msgid "Expire personal notes:" msgstr "Fai scadere le Note personali:" -#: mod/settings.php:1163 +#: mod/settings.php:1172 msgid "Expire starred posts:" msgstr "Fai scadere i post Speciali:" -#: mod/settings.php:1164 +#: mod/settings.php:1173 msgid "Expire photos:" msgstr "Fai scadere le foto:" -#: mod/settings.php:1165 +#: mod/settings.php:1174 msgid "Only expire posts by others:" msgstr "Fai scadere solo i post degli altri:" -#: mod/settings.php:1193 +#: mod/settings.php:1202 msgid "Account Settings" msgstr "Impostazioni account" -#: mod/settings.php:1201 +#: mod/settings.php:1210 msgid "Password Settings" msgstr "Impostazioni password" -#: mod/settings.php:1202 mod/register.php:274 +#: mod/settings.php:1211 mod/register.php:274 msgid "New Password:" msgstr "Nuova password:" -#: mod/settings.php:1203 mod/register.php:275 +#: mod/settings.php:1212 mod/register.php:275 msgid "Confirm:" msgstr "Conferma:" -#: mod/settings.php:1203 +#: mod/settings.php:1212 msgid "Leave password fields blank unless changing" msgstr "Lascia questi campi in bianco per non effettuare variazioni alla password" -#: mod/settings.php:1204 +#: mod/settings.php:1213 msgid "Current Password:" msgstr "Password Attuale:" -#: mod/settings.php:1204 mod/settings.php:1205 +#: mod/settings.php:1213 mod/settings.php:1214 msgid "Your current password to confirm the changes" msgstr "La tua password attuale per confermare le modifiche" -#: mod/settings.php:1205 +#: mod/settings.php:1214 msgid "Password:" msgstr "Password:" -#: mod/settings.php:1209 +#: mod/settings.php:1218 msgid "Basic Settings" msgstr "Impostazioni base" -#: mod/settings.php:1210 include/identity.php:578 +#: mod/settings.php:1219 include/identity.php:588 msgid "Full Name:" msgstr "Nome completo:" -#: mod/settings.php:1211 +#: mod/settings.php:1220 msgid "Email Address:" msgstr "Indirizzo Email:" -#: mod/settings.php:1212 +#: mod/settings.php:1221 msgid "Your Timezone:" msgstr "Il tuo fuso orario:" -#: mod/settings.php:1213 +#: mod/settings.php:1222 msgid "Your Language:" msgstr "La tua lingua:" -#: mod/settings.php:1213 +#: mod/settings.php:1222 msgid "" "Set the language we use to show you friendica interface and to send you " "emails" msgstr "Imposta la lingua che sarà usata per mostrarti l'interfaccia di Friendica e per inviarti le email" -#: mod/settings.php:1214 +#: mod/settings.php:1223 msgid "Default Post Location:" msgstr "Località predefinita:" -#: mod/settings.php:1215 +#: mod/settings.php:1224 msgid "Use Browser Location:" msgstr "Usa la località rilevata dal browser:" -#: mod/settings.php:1218 +#: mod/settings.php:1227 msgid "Security and Privacy Settings" msgstr "Impostazioni di sicurezza e privacy" -#: mod/settings.php:1220 +#: mod/settings.php:1229 msgid "Maximum Friend Requests/Day:" msgstr "Numero massimo di richieste di amicizia al giorno:" -#: mod/settings.php:1220 mod/settings.php:1250 +#: mod/settings.php:1229 mod/settings.php:1259 msgid "(to prevent spam abuse)" msgstr "(per prevenire lo spam)" -#: mod/settings.php:1221 +#: mod/settings.php:1230 msgid "Default Post Permissions" msgstr "Permessi predefiniti per i messaggi" -#: mod/settings.php:1222 +#: mod/settings.php:1231 msgid "(click to open/close)" msgstr "(clicca per aprire/chiudere)" -#: mod/settings.php:1231 mod/photos.php:1199 mod/photos.php:1584 +#: mod/settings.php:1240 mod/photos.php:1199 mod/photos.php:1584 msgid "Show to Groups" msgstr "Mostra ai gruppi" -#: mod/settings.php:1232 mod/photos.php:1200 mod/photos.php:1585 +#: mod/settings.php:1241 mod/photos.php:1200 mod/photos.php:1585 msgid "Show to Contacts" msgstr "Mostra ai contatti" -#: mod/settings.php:1233 +#: mod/settings.php:1242 msgid "Default Private Post" msgstr "Default Post Privato" -#: mod/settings.php:1234 +#: mod/settings.php:1243 msgid "Default Public Post" msgstr "Default Post Pubblico" -#: mod/settings.php:1238 +#: mod/settings.php:1247 msgid "Default Permissions for New Posts" msgstr "Permessi predefiniti per i nuovi post" -#: mod/settings.php:1250 +#: mod/settings.php:1259 msgid "Maximum private messages per day from unknown people:" msgstr "Numero massimo di messaggi privati da utenti sconosciuti per giorno:" -#: mod/settings.php:1253 +#: mod/settings.php:1262 msgid "Notification Settings" msgstr "Impostazioni notifiche" -#: mod/settings.php:1254 +#: mod/settings.php:1263 msgid "By default post a status message when:" msgstr "Invia un messaggio di stato quando:" -#: mod/settings.php:1255 +#: mod/settings.php:1264 msgid "accepting a friend request" msgstr "accetti una richiesta di amicizia" -#: mod/settings.php:1256 +#: mod/settings.php:1265 msgid "joining a forum/community" msgstr "ti unisci a un forum/comunità" -#: mod/settings.php:1257 +#: mod/settings.php:1266 msgid "making an interesting profile change" msgstr "fai un interessante modifica al profilo" -#: mod/settings.php:1258 +#: mod/settings.php:1267 msgid "Send a notification email when:" msgstr "Invia una mail di notifica quando:" -#: mod/settings.php:1259 +#: mod/settings.php:1268 msgid "You receive an introduction" msgstr "Ricevi una presentazione" -#: mod/settings.php:1260 +#: mod/settings.php:1269 msgid "Your introductions are confirmed" msgstr "Le tue presentazioni sono confermate" -#: mod/settings.php:1261 +#: mod/settings.php:1270 msgid "Someone writes on your profile wall" msgstr "Qualcuno scrive sulla bacheca del tuo profilo" -#: mod/settings.php:1262 +#: mod/settings.php:1271 msgid "Someone writes a followup comment" msgstr "Qualcuno scrive un commento a un tuo messaggio" -#: mod/settings.php:1263 +#: mod/settings.php:1272 msgid "You receive a private message" msgstr "Ricevi un messaggio privato" -#: mod/settings.php:1264 +#: mod/settings.php:1273 msgid "You receive a friend suggestion" msgstr "Hai ricevuto un suggerimento di amicizia" -#: mod/settings.php:1265 +#: mod/settings.php:1274 msgid "You are tagged in a post" msgstr "Sei stato taggato in un post" -#: mod/settings.php:1266 +#: mod/settings.php:1275 msgid "You are poked/prodded/etc. in a post" msgstr "Sei 'toccato'/'spronato'/ecc. in un post" -#: mod/settings.php:1268 +#: mod/settings.php:1277 msgid "Activate desktop notifications" msgstr "Attiva notifiche desktop" -#: mod/settings.php:1268 +#: mod/settings.php:1277 msgid "Show desktop popup on new notifications" msgstr "Mostra un popup di notifica sul desktop all'arrivo di nuove notifiche" -#: mod/settings.php:1270 +#: mod/settings.php:1279 msgid "Text-only notification emails" msgstr "Email di notifica in solo testo" -#: mod/settings.php:1272 +#: mod/settings.php:1281 msgid "Send text only notification emails, without the html part" msgstr "Invia le email di notifica in solo testo, senza la parte in html" -#: mod/settings.php:1274 +#: mod/settings.php:1283 msgid "Advanced Account/Page Type Settings" msgstr "Impostazioni avanzate Account/Tipo di pagina" -#: mod/settings.php:1275 +#: mod/settings.php:1284 msgid "Change the behaviour of this account for special situations" msgstr "Modifica il comportamento di questo account in situazioni speciali" -#: mod/settings.php:1278 +#: mod/settings.php:1287 msgid "Relocate" msgstr "Trasloca" -#: mod/settings.php:1279 +#: mod/settings.php:1288 msgid "" "If you have moved this profile from another server, and some of your " "contacts don't receive your updates, try pushing this button." msgstr "Se hai spostato questo profilo da un'altro server, e alcuni dei tuoi contatti non ricevono i tuoi aggiornamenti, prova a premere questo bottone." -#: mod/settings.php:1280 +#: mod/settings.php:1289 msgid "Resend relocate message to contacts" msgstr "Reinvia il messaggio di trasloco" -#: mod/dfrn_request.php:95 +#: mod/dfrn_request.php:96 msgid "This introduction has already been accepted." msgstr "Questa presentazione è già stata accettata." -#: mod/dfrn_request.php:120 mod/dfrn_request.php:519 +#: mod/dfrn_request.php:119 mod/dfrn_request.php:516 msgid "Profile location is not valid or does not contain profile information." msgstr "L'indirizzo del profilo non è valido o non contiene un profilo." -#: mod/dfrn_request.php:125 mod/dfrn_request.php:524 +#: mod/dfrn_request.php:124 mod/dfrn_request.php:521 msgid "Warning: profile location has no identifiable owner name." msgstr "Attenzione: l'indirizzo del profilo non riporta il nome del proprietario." -#: mod/dfrn_request.php:127 mod/dfrn_request.php:526 +#: mod/dfrn_request.php:126 mod/dfrn_request.php:523 msgid "Warning: profile location has no profile photo." msgstr "Attenzione: l'indirizzo del profilo non ha una foto." -#: mod/dfrn_request.php:130 mod/dfrn_request.php:529 +#: mod/dfrn_request.php:129 mod/dfrn_request.php:526 #, php-format msgid "%d required parameter was not found at the given location" msgid_plural "%d required parameters were not found at the given location" msgstr[0] "%d parametro richiesto non è stato trovato all'indirizzo dato" msgstr[1] "%d parametri richiesti non sono stati trovati all'indirizzo dato" -#: mod/dfrn_request.php:173 +#: mod/dfrn_request.php:172 msgid "Introduction complete." msgstr "Presentazione completa." -#: mod/dfrn_request.php:215 +#: mod/dfrn_request.php:214 msgid "Unrecoverable protocol error." msgstr "Errore di comunicazione." -#: mod/dfrn_request.php:243 +#: mod/dfrn_request.php:242 msgid "Profile unavailable." msgstr "Profilo non disponibile." -#: mod/dfrn_request.php:268 +#: mod/dfrn_request.php:267 #, php-format msgid "%s has received too many connection requests today." msgstr "%s ha ricevuto troppe richieste di connessione per oggi." -#: mod/dfrn_request.php:269 +#: mod/dfrn_request.php:268 msgid "Spam protection measures have been invoked." msgstr "Sono state attivate le misure di protezione contro lo spam." -#: mod/dfrn_request.php:270 +#: mod/dfrn_request.php:269 msgid "Friends are advised to please try again in 24 hours." msgstr "Gli amici sono pregati di riprovare tra 24 ore." -#: mod/dfrn_request.php:332 +#: mod/dfrn_request.php:331 msgid "Invalid locator" msgstr "Invalid locator" -#: mod/dfrn_request.php:341 +#: mod/dfrn_request.php:340 msgid "Invalid email address." msgstr "Indirizzo email non valido." -#: mod/dfrn_request.php:368 +#: mod/dfrn_request.php:367 msgid "This account has not been configured for email. Request failed." msgstr "Questo account non è stato configurato per l'email. Richiesta fallita." -#: mod/dfrn_request.php:464 -msgid "Unable to resolve your name at the provided location." -msgstr "Impossibile risolvere il tuo nome nella posizione indicata." - -#: mod/dfrn_request.php:477 +#: mod/dfrn_request.php:474 msgid "You have already introduced yourself here." msgstr "Ti sei già presentato qui." -#: mod/dfrn_request.php:481 +#: mod/dfrn_request.php:478 #, php-format msgid "Apparently you are already friends with %s." msgstr "Pare che tu e %s siate già amici." -#: mod/dfrn_request.php:502 +#: mod/dfrn_request.php:499 msgid "Invalid profile URL." msgstr "Indirizzo profilo non valido." -#: mod/dfrn_request.php:508 include/follow.php:72 +#: mod/dfrn_request.php:505 include/follow.php:72 msgid "Disallowed profile URL." msgstr "Indirizzo profilo non permesso." -#: mod/dfrn_request.php:599 +#: mod/dfrn_request.php:596 msgid "Your introduction has been sent." msgstr "La tua presentazione è stata inviata." -#: mod/dfrn_request.php:652 +#: mod/dfrn_request.php:636 +msgid "" +"Remote subscription can't be done for your network. Please subscribe " +"directly on your system." +msgstr "" + +#: mod/dfrn_request.php:659 msgid "Please login to confirm introduction." msgstr "Accedi per confermare la presentazione." -#: mod/dfrn_request.php:662 +#: mod/dfrn_request.php:669 msgid "" "Incorrect identity currently logged in. Please login to " "this profile." msgstr "Non hai fatto accesso con l'identità corretta. Accedi a questo profilo." -#: mod/dfrn_request.php:676 mod/dfrn_request.php:693 +#: mod/dfrn_request.php:683 mod/dfrn_request.php:700 msgid "Confirm" msgstr "Conferma" -#: mod/dfrn_request.php:688 +#: mod/dfrn_request.php:695 msgid "Hide this contact" msgstr "Nascondi questo contatto" -#: mod/dfrn_request.php:691 +#: mod/dfrn_request.php:698 #, php-format msgid "Welcome home %s." msgstr "Bentornato a casa %s." -#: mod/dfrn_request.php:692 +#: mod/dfrn_request.php:699 #, php-format msgid "Please confirm your introduction/connection request to %s." msgstr "Conferma la tua richiesta di connessione con %s." -#: mod/dfrn_request.php:821 +#: mod/dfrn_request.php:828 msgid "" "Please enter your 'Identity Address' from one of the following supported " "communications networks:" msgstr "Inserisci il tuo 'Indirizzo Identità' da uno dei seguenti network supportati:" -#: mod/dfrn_request.php:842 +#: mod/dfrn_request.php:849 #, php-format msgid "" "If you are not yet a member of the free social web, ." msgstr "Se non sei un membro del web sociale libero, segui questo link per trovare un sito Friendica pubblico e unisciti a noi oggi" -#: mod/dfrn_request.php:847 +#: mod/dfrn_request.php:854 msgid "Friend/Connection Request" msgstr "Richieste di amicizia/connessione" -#: mod/dfrn_request.php:848 +#: mod/dfrn_request.php:855 msgid "" "Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, " "testuser@identi.ca" msgstr "Esempi: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca" -#: mod/dfrn_request.php:856 include/contact_selectors.php:76 +#: mod/dfrn_request.php:863 include/contact_selectors.php:76 msgid "Friendica" msgstr "Friendica" -#: mod/dfrn_request.php:857 +#: mod/dfrn_request.php:864 msgid "StatusNet/Federated Social Web" msgstr "StatusNet/Federated Social Web" -#: mod/dfrn_request.php:859 +#: mod/dfrn_request.php:866 #, php-format msgid "" " - please do not use this form. Instead, enter %s into your Diaspora search" @@ -5087,7 +5093,7 @@ msgstr "Scegli un nome utente. Deve cominciare con una lettera. L'indirizzo del msgid "Choose a nickname: " msgstr "Scegli un nome utente: " -#: mod/register.php:280 boot.php:1268 include/nav.php:108 +#: mod/register.php:280 boot.php:1405 include/nav.php:108 msgid "Register" msgstr "Registrati" @@ -5129,11 +5135,11 @@ msgstr "Elementi taggati con: %s" msgid "Search results for: %s" msgstr "Risultato della ricerca per: %s" -#: mod/directory.php:149 include/identity.php:309 include/identity.php:600 +#: mod/directory.php:149 include/identity.php:313 include/identity.php:610 msgid "Status:" msgstr "Stato:" -#: mod/directory.php:151 include/identity.php:311 include/identity.php:611 +#: mod/directory.php:151 include/identity.php:315 include/identity.php:621 msgid "Homepage:" msgstr "Homepage:" @@ -5193,7 +5199,7 @@ msgstr "Aggiungi" msgid "No entries." msgstr "Nessuna voce." -#: mod/common.php:87 +#: mod/common.php:86 msgid "No contacts in common." msgstr "Nessun contatto in comune." @@ -5461,7 +5467,7 @@ msgstr "Esempio: cathy123, Cathy Williams, cathy@example.com" msgid "Since [date]:" msgstr "Dal [data]:" -#: mod/profiles.php:724 include/identity.php:609 +#: mod/profiles.php:724 include/identity.php:619 msgid "Sexual Preference:" msgstr "Preferenze sessuali:" @@ -5469,11 +5475,11 @@ msgstr "Preferenze sessuali:" msgid "Homepage URL:" msgstr "Homepage:" -#: mod/profiles.php:726 include/identity.php:613 +#: mod/profiles.php:726 include/identity.php:623 msgid "Hometown:" msgstr "Paese natale:" -#: mod/profiles.php:727 include/identity.php:617 +#: mod/profiles.php:727 include/identity.php:627 msgid "Political Views:" msgstr "Orientamento politico:" @@ -5489,11 +5495,11 @@ msgstr "Parole chiave visibili a tutti:" msgid "Private Keywords:" msgstr "Parole chiave private:" -#: mod/profiles.php:731 include/identity.php:625 +#: mod/profiles.php:731 include/identity.php:635 msgid "Likes:" msgstr "Mi piace:" -#: mod/profiles.php:732 include/identity.php:627 +#: mod/profiles.php:732 include/identity.php:637 msgid "Dislikes:" msgstr "Non mi piace:" @@ -5563,23 +5569,23 @@ msgstr "Età : " msgid "Edit/Manage Profiles" msgstr "Modifica / Gestisci profili" -#: mod/profiles.php:814 include/identity.php:257 include/identity.php:283 +#: mod/profiles.php:814 include/identity.php:260 include/identity.php:286 msgid "Change profile photo" msgstr "Cambia la foto del profilo" -#: mod/profiles.php:815 include/identity.php:258 +#: mod/profiles.php:815 include/identity.php:261 msgid "Create New Profile" msgstr "Crea un nuovo profilo" -#: mod/profiles.php:826 include/identity.php:268 +#: mod/profiles.php:826 include/identity.php:271 msgid "Profile Image" msgstr "Immagine del Profilo" -#: mod/profiles.php:828 include/identity.php:271 +#: mod/profiles.php:828 include/identity.php:274 msgid "visible to everybody" msgstr "visibile a tutti" -#: mod/profiles.php:829 include/identity.php:272 +#: mod/profiles.php:829 include/identity.php:275 msgid "Edit visibility" msgstr "Modifica visibilità" @@ -5591,55 +5597,55 @@ msgstr "Oggetto non trovato" msgid "Edit post" msgstr "Modifica messaggio" -#: mod/editpost.php:111 include/conversation.php:1185 +#: mod/editpost.php:111 include/conversation.php:1184 msgid "upload photo" msgstr "carica foto" -#: mod/editpost.php:112 include/conversation.php:1186 +#: mod/editpost.php:112 include/conversation.php:1185 msgid "Attach file" msgstr "Allega file" -#: mod/editpost.php:113 include/conversation.php:1187 +#: mod/editpost.php:113 include/conversation.php:1186 msgid "attach file" msgstr "allega file" -#: mod/editpost.php:115 include/conversation.php:1189 +#: mod/editpost.php:115 include/conversation.php:1188 msgid "web link" msgstr "link web" -#: mod/editpost.php:116 include/conversation.php:1190 +#: mod/editpost.php:116 include/conversation.php:1189 msgid "Insert video link" msgstr "Inserire collegamento video" -#: mod/editpost.php:117 include/conversation.php:1191 +#: mod/editpost.php:117 include/conversation.php:1190 msgid "video link" msgstr "link video" -#: mod/editpost.php:118 include/conversation.php:1192 +#: mod/editpost.php:118 include/conversation.php:1191 msgid "Insert audio link" msgstr "Inserisci collegamento audio" -#: mod/editpost.php:119 include/conversation.php:1193 +#: mod/editpost.php:119 include/conversation.php:1192 msgid "audio link" msgstr "link audio" -#: mod/editpost.php:120 include/conversation.php:1194 +#: mod/editpost.php:120 include/conversation.php:1193 msgid "Set your location" msgstr "La tua posizione" -#: mod/editpost.php:121 include/conversation.php:1195 +#: mod/editpost.php:121 include/conversation.php:1194 msgid "set location" msgstr "posizione" -#: mod/editpost.php:122 include/conversation.php:1196 +#: mod/editpost.php:122 include/conversation.php:1195 msgid "Clear browser location" msgstr "Rimuovi la localizzazione data dal browser" -#: mod/editpost.php:123 include/conversation.php:1197 +#: mod/editpost.php:123 include/conversation.php:1196 msgid "clear location" msgstr "canc. pos." -#: mod/editpost.php:125 include/conversation.php:1203 +#: mod/editpost.php:125 include/conversation.php:1202 msgid "Permission settings" msgstr "Impostazioni permessi" @@ -5647,15 +5653,15 @@ msgstr "Impostazioni permessi" msgid "CC: email addresses" msgstr "CC: indirizzi email" -#: mod/editpost.php:134 include/conversation.php:1212 +#: mod/editpost.php:134 include/conversation.php:1211 msgid "Public post" msgstr "Messaggio pubblico" -#: mod/editpost.php:137 include/conversation.php:1199 +#: mod/editpost.php:137 include/conversation.php:1198 msgid "Set title" msgstr "Scegli un titolo" -#: mod/editpost.php:139 include/conversation.php:1201 +#: mod/editpost.php:139 include/conversation.php:1200 msgid "Categories (comma-separated list)" msgstr "Categorie (lista separata da virgola)" @@ -5663,39 +5669,39 @@ msgstr "Categorie (lista separata da virgola)" msgid "Example: bob@example.com, mary@example.com" msgstr "Esempio: bob@example.com, mary@example.com" -#: mod/friendica.php:59 +#: mod/friendica.php:70 msgid "This is Friendica, version" msgstr "Questo è Friendica, versione" -#: mod/friendica.php:60 +#: mod/friendica.php:71 msgid "running at web location" msgstr "in esecuzione all'indirizzo web" -#: mod/friendica.php:62 +#: mod/friendica.php:73 msgid "" "Please visit Friendica.com to learn " "more about the Friendica project." msgstr "Visita Friendica.com per saperne di più sul progetto Friendica." -#: mod/friendica.php:64 +#: mod/friendica.php:75 msgid "Bug reports and issues: please visit" msgstr "Segnalazioni di bug e problemi: visita" -#: mod/friendica.php:64 +#: mod/friendica.php:75 msgid "the bugtracker at github" msgstr "il bugtracker su github" -#: mod/friendica.php:65 +#: mod/friendica.php:76 msgid "" "Suggestions, praise, donations, etc. - please email \"Info\" at Friendica - " "dot com" msgstr "Suggerimenti, lodi, donazioni, ecc - e-mail a \"Info\" at Friendica punto com" -#: mod/friendica.php:79 +#: mod/friendica.php:90 msgid "Installed plugins/addons/apps:" msgstr "Plugin/addon/applicazioni instalate" -#: mod/friendica.php:92 +#: mod/friendica.php:103 msgid "No installed plugins/addons/apps" msgstr "Nessun plugin/addons/applicazione installata" @@ -5725,7 +5731,7 @@ msgstr "Informazioni remote sulla privacy non disponibili." msgid "Visible to:" msgstr "Visibile a:" -#: mod/notes.php:46 include/identity.php:721 +#: mod/notes.php:46 include/identity.php:730 msgid "Personal Notes" msgstr "Note personali" @@ -5783,8 +5789,8 @@ msgid "Make this post private" msgstr "Rendi questo post privato" #: mod/repair_ostatus.php:14 -msgid "Resubsribing to OStatus contacts" -msgstr "Reiscrizione a contatti OStatus" +msgid "Resubscribing to OStatus contacts" +msgstr "" #: mod/repair_ostatus.php:30 msgid "Error" @@ -5836,7 +5842,7 @@ msgstr "Visita %s per una lista di siti pubblici a cui puoi iscriverti. I membri msgid "" "To accept this invitation, please visit and register at %s or any other " "public Friendica website." -msgstr "Per accettare questo invito, visita e resitrati su %s o su un'altro sito web Friendica aperto al pubblico." +msgstr "Per accettare questo invito, visita e registrati su %s o su un'altro sito web Friendica aperto al pubblico." #: mod/invite.php:123 #, php-format @@ -5865,7 +5871,7 @@ msgstr "Inserisci gli indirizzi email, uno per riga:" msgid "" "You are cordially invited to join me and other close friends on Friendica - " "and help us to create a better social web." -msgstr "Sei cordialmente invitato a unirti a me ed ad altri amici su Friendica, e ad aiutarci a creare una rete sociale migliore." +msgstr "Sei cordialmente invitato/a ad unirti a me e ad altri amici su Friendica, e ad aiutarci a creare una rete sociale migliore." #: mod/invite.php:137 msgid "You will need to supply this invitation code: $invite_code" @@ -5882,7 +5888,7 @@ msgid "" "important, please visit http://friendica.com" msgstr "Per maggiori informazioni sul progetto Friendica e perchè pensiamo sia importante, visita http://friendica.com" -#: mod/photos.php:99 include/identity.php:696 +#: mod/photos.php:99 include/identity.php:705 msgid "Photo Albums" msgstr "Album foto" @@ -6053,12 +6059,12 @@ msgstr "Foto privata" msgid "Public photo" msgstr "Foto pubblica" -#: mod/photos.php:1609 include/conversation.php:1183 +#: mod/photos.php:1609 include/conversation.php:1182 msgid "Share" msgstr "Condividi" #: mod/photos.php:1648 include/conversation.php:509 -#: include/conversation.php:1414 +#: include/conversation.php:1413 msgid "Attending" msgid_plural "Attending" msgstr[0] "Partecipa" @@ -6132,60 +6138,60 @@ msgstr "Oggetto non disponibile." msgid "Item was not found." msgstr "Oggetto non trovato." -#: boot.php:783 +#: boot.php:868 msgid "Delete this item?" msgstr "Cancellare questo elemento?" -#: boot.php:786 +#: boot.php:871 msgid "show fewer" msgstr "mostra di meno" -#: boot.php:1160 +#: boot.php:1292 #, php-format msgid "Update %s failed. See error logs." msgstr "aggiornamento %s fallito. Guarda i log di errore." -#: boot.php:1267 +#: boot.php:1404 msgid "Create a New Account" msgstr "Crea un nuovo account" -#: boot.php:1292 include/nav.php:72 +#: boot.php:1429 include/nav.php:72 msgid "Logout" msgstr "Esci" -#: boot.php:1295 +#: boot.php:1432 msgid "Nickname or Email address: " msgstr "Nome utente o indirizzo email: " -#: boot.php:1296 +#: boot.php:1433 msgid "Password: " msgstr "Password: " -#: boot.php:1297 +#: boot.php:1434 msgid "Remember me" msgstr "Ricordati di me" -#: boot.php:1300 +#: boot.php:1437 msgid "Or login using OpenID: " msgstr "O entra con OpenID:" -#: boot.php:1306 +#: boot.php:1443 msgid "Forgot your password?" msgstr "Hai dimenticato la password?" -#: boot.php:1309 +#: boot.php:1446 msgid "Website Terms of Service" msgstr "Condizioni di servizio del sito web " -#: boot.php:1310 +#: boot.php:1447 msgid "terms of service" msgstr "condizioni del servizio" -#: boot.php:1312 +#: boot.php:1449 msgid "Website Privacy Policy" msgstr "Politiche di privacy del sito" -#: boot.php:1313 +#: boot.php:1450 msgid "privacy policy" msgstr "politiche di privacy" @@ -6246,25 +6252,25 @@ msgid "" "[pre]%s[/pre]" msgstr "Il messaggio di errore è\n[pre]%s[/pre]" -#: include/dbstructure.php:151 +#: include/dbstructure.php:153 msgid "Errors encountered creating database tables." msgstr "La creazione delle tabelle del database ha generato errori." -#: include/dbstructure.php:209 +#: include/dbstructure.php:230 msgid "Errors encountered performing database changes." msgstr "Riscontrati errori applicando le modifiche al database." -#: include/auth.php:38 +#: include/auth.php:44 msgid "Logged out." msgstr "Uscita effettuata." -#: include/auth.php:128 include/user.php:75 +#: include/auth.php:134 include/user.php:75 msgid "" "We encountered a problem while logging in with the OpenID you provided. " "Please check the correct spelling of the ID." msgstr "Abbiamo incontrato un problema mentre contattavamo il server OpenID che ci hai fornito. Controlla di averlo scritto giusto." -#: include/auth.php:128 include/user.php:75 +#: include/auth.php:134 include/user.php:75 msgid "The error message was:" msgstr "Il messaggio riportato era:" @@ -6321,7 +6327,7 @@ msgstr "Reti" msgid "All Networks" msgstr "Tutte le Reti" -#: include/contact_widgets.php:141 include/features.php:97 +#: include/contact_widgets.php:141 include/features.php:102 msgid "Saved Folders" msgstr "Cartelle Salvate" @@ -6340,194 +6346,194 @@ msgid_plural "%d contacts in common" msgstr[0] "%d contatto in comune" msgstr[1] "%d contatti in comune" -#: include/features.php:58 +#: include/features.php:63 msgid "General Features" msgstr "Funzionalità generali" -#: include/features.php:60 +#: include/features.php:65 msgid "Multiple Profiles" msgstr "Profili multipli" -#: include/features.php:60 +#: include/features.php:65 msgid "Ability to create multiple profiles" msgstr "Possibilità di creare profili multipli" -#: include/features.php:61 +#: include/features.php:66 msgid "Photo Location" msgstr "Località Foto" -#: include/features.php:61 +#: include/features.php:66 msgid "" "Photo metadata is normally stripped. This extracts the location (if present)" " prior to stripping metadata and links it to a map." msgstr "I metadati delle foto vengono rimossi. Questa opzione estrae la località (se presenta) prima di rimuovere i metadati e la collega a una mappa." -#: include/features.php:66 +#: include/features.php:71 msgid "Post Composition Features" msgstr "Funzionalità di composizione dei post" -#: include/features.php:67 +#: include/features.php:72 msgid "Richtext Editor" msgstr "Editor visuale" -#: include/features.php:67 +#: include/features.php:72 msgid "Enable richtext editor" msgstr "Abilita l'editor visuale" -#: include/features.php:68 +#: include/features.php:73 msgid "Post Preview" msgstr "Anteprima dei post" -#: include/features.php:68 +#: include/features.php:73 msgid "Allow previewing posts and comments before publishing them" msgstr "Permetti di avere un'anteprima di messaggi e commenti prima di pubblicarli" -#: include/features.php:69 +#: include/features.php:74 msgid "Auto-mention Forums" msgstr "Auto-cita i Forum" -#: include/features.php:69 +#: include/features.php:74 msgid "" "Add/remove mention when a fourm page is selected/deselected in ACL window." msgstr "Aggiunge o rimuove una citazione quando un forum è selezionato o deselezionato nella finestra dei permessi." -#: include/features.php:74 +#: include/features.php:79 msgid "Network Sidebar Widgets" msgstr "Widget della barra laterale nella pagina Rete" -#: include/features.php:75 +#: include/features.php:80 msgid "Search by Date" msgstr "Cerca per data" -#: include/features.php:75 +#: include/features.php:80 msgid "Ability to select posts by date ranges" msgstr "Permette di filtrare i post per data" -#: include/features.php:76 include/features.php:106 +#: include/features.php:81 include/features.php:111 msgid "List Forums" msgstr "Elenco forum" -#: include/features.php:76 +#: include/features.php:81 msgid "Enable widget to display the forums your are connected with" msgstr "Abilita il widget che mostra i forum ai quali sei connesso" -#: include/features.php:77 +#: include/features.php:82 msgid "Group Filter" msgstr "Filtra gruppi" -#: include/features.php:77 +#: include/features.php:82 msgid "Enable widget to display Network posts only from selected group" msgstr "Abilita il widget per filtrare i post solo per il gruppo selezionato" -#: include/features.php:78 +#: include/features.php:83 msgid "Network Filter" msgstr "Filtro reti" -#: include/features.php:78 +#: include/features.php:83 msgid "Enable widget to display Network posts only from selected network" msgstr "Abilita il widget per mostare i post solo per la rete selezionata" -#: include/features.php:79 +#: include/features.php:84 msgid "Save search terms for re-use" msgstr "Salva i termini cercati per riutilizzarli" -#: include/features.php:84 +#: include/features.php:89 msgid "Network Tabs" msgstr "Schede pagina Rete" -#: include/features.php:85 +#: include/features.php:90 msgid "Network Personal Tab" msgstr "Scheda Personali" -#: include/features.php:85 +#: include/features.php:90 msgid "Enable tab to display only Network posts that you've interacted on" msgstr "Abilita la scheda per mostrare solo i post a cui hai partecipato" -#: include/features.php:86 +#: include/features.php:91 msgid "Network New Tab" msgstr "Scheda Nuovi" -#: include/features.php:86 +#: include/features.php:91 msgid "Enable tab to display only new Network posts (from the last 12 hours)" msgstr "Abilita la scheda per mostrare solo i post nuovi (nelle ultime 12 ore)" -#: include/features.php:87 +#: include/features.php:92 msgid "Network Shared Links Tab" msgstr "Scheda Link Condivisi" -#: include/features.php:87 +#: include/features.php:92 msgid "Enable tab to display only Network posts with links in them" msgstr "Abilita la scheda per mostrare solo i post che contengono link" -#: include/features.php:92 +#: include/features.php:97 msgid "Post/Comment Tools" msgstr "Strumenti per messaggi/commenti" -#: include/features.php:93 +#: include/features.php:98 msgid "Multiple Deletion" msgstr "Eliminazione multipla" -#: include/features.php:93 +#: include/features.php:98 msgid "Select and delete multiple posts/comments at once" msgstr "Seleziona ed elimina vari messagi e commenti in una volta sola" -#: include/features.php:94 +#: include/features.php:99 msgid "Edit Sent Posts" msgstr "Modifica i post inviati" -#: include/features.php:94 +#: include/features.php:99 msgid "Edit and correct posts and comments after sending" msgstr "Modifica e correggi messaggi e commenti dopo averli inviati" -#: include/features.php:95 +#: include/features.php:100 msgid "Tagging" msgstr "Aggiunta tag" -#: include/features.php:95 +#: include/features.php:100 msgid "Ability to tag existing posts" msgstr "Permette di aggiungere tag ai post già esistenti" -#: include/features.php:96 +#: include/features.php:101 msgid "Post Categories" msgstr "Cateorie post" -#: include/features.php:96 +#: include/features.php:101 msgid "Add categories to your posts" msgstr "Aggiungi categorie ai tuoi post" -#: include/features.php:97 +#: include/features.php:102 msgid "Ability to file posts under folders" msgstr "Permette di archiviare i post in cartelle" -#: include/features.php:98 +#: include/features.php:103 msgid "Dislike Posts" msgstr "Non mi piace" -#: include/features.php:98 +#: include/features.php:103 msgid "Ability to dislike posts/comments" msgstr "Permetti di inviare \"non mi piace\" ai messaggi" -#: include/features.php:99 +#: include/features.php:104 msgid "Star Posts" msgstr "Post preferiti" -#: include/features.php:99 +#: include/features.php:104 msgid "Ability to mark special posts with a star indicator" msgstr "Permette di segnare i post preferiti con una stella" -#: include/features.php:100 +#: include/features.php:105 msgid "Mute Post Notifications" msgstr "Silenzia le notifiche di nuovi post" -#: include/features.php:100 +#: include/features.php:105 msgid "Ability to mute notifications for a thread" msgstr "Permette di silenziare le notifiche di nuovi post in una discussione" -#: include/features.php:105 +#: include/features.php:110 msgid "Advanced Profile Settings" msgstr "Impostazioni Avanzate Profilo" -#: include/features.php:106 +#: include/features.php:111 msgid "Show visitors public community forums at the Advanced Profile Page" msgstr "Mostra ai visitatori i forum nella pagina Profilo Avanzato" @@ -6686,149 +6692,181 @@ msgstr "secondi" msgid "%1$d %2$s ago" msgstr "%1$d %2$s fa" -#: include/datetime.php:474 include/items.php:2470 +#: include/datetime.php:474 include/items.php:2500 #, php-format msgid "%s's birthday" msgstr "Compleanno di %s" -#: include/datetime.php:475 include/items.php:2471 +#: include/datetime.php:475 include/items.php:2501 #, php-format msgid "Happy Birthday %s" msgstr "Buon compleanno %s" -#: include/identity.php:43 +#: include/identity.php:42 msgid "Requested account is not available." msgstr "L'account richiesto non è disponibile." -#: include/identity.php:96 include/identity.php:281 include/identity.php:652 +#: include/identity.php:95 include/identity.php:284 include/identity.php:662 msgid "Edit profile" msgstr "Modifica il profilo" -#: include/identity.php:241 +#: include/identity.php:244 msgid "Atom feed" msgstr "Feed Atom" -#: include/identity.php:246 +#: include/identity.php:249 msgid "Message" msgstr "Messaggio" -#: include/identity.php:252 include/nav.php:185 +#: include/identity.php:255 include/nav.php:185 msgid "Profiles" msgstr "Profili" -#: include/identity.php:252 +#: include/identity.php:255 msgid "Manage/edit profiles" msgstr "Gestisci/modifica i profili" -#: include/identity.php:412 include/identity.php:498 +#: include/identity.php:425 include/identity.php:509 msgid "g A l F d" msgstr "g A l d F" -#: include/identity.php:413 include/identity.php:499 +#: include/identity.php:426 include/identity.php:510 msgid "F d" msgstr "d F" -#: include/identity.php:458 include/identity.php:545 +#: include/identity.php:471 include/identity.php:556 msgid "[today]" msgstr "[oggi]" -#: include/identity.php:470 +#: include/identity.php:483 msgid "Birthday Reminders" msgstr "Promemoria compleanni" -#: include/identity.php:471 +#: include/identity.php:484 msgid "Birthdays this week:" msgstr "Compleanni questa settimana:" -#: include/identity.php:532 +#: include/identity.php:543 msgid "[No description]" msgstr "[Nessuna descrizione]" -#: include/identity.php:556 +#: include/identity.php:567 msgid "Event Reminders" msgstr "Promemoria" -#: include/identity.php:557 +#: include/identity.php:568 msgid "Events this week:" msgstr "Eventi di questa settimana:" -#: include/identity.php:585 +#: include/identity.php:595 msgid "j F, Y" msgstr "j F Y" -#: include/identity.php:586 +#: include/identity.php:596 msgid "j F" msgstr "j F" -#: include/identity.php:593 +#: include/identity.php:603 msgid "Birthday:" msgstr "Compleanno:" -#: include/identity.php:597 +#: include/identity.php:607 msgid "Age:" msgstr "Età:" -#: include/identity.php:606 +#: include/identity.php:616 #, php-format msgid "for %1$d %2$s" msgstr "per %1$d %2$s" -#: include/identity.php:619 +#: include/identity.php:629 msgid "Religion:" msgstr "Religione:" -#: include/identity.php:623 +#: include/identity.php:633 msgid "Hobbies/Interests:" msgstr "Hobby/Interessi:" -#: include/identity.php:630 +#: include/identity.php:640 msgid "Contact information and Social Networks:" msgstr "Informazioni su contatti e social network:" -#: include/identity.php:632 +#: include/identity.php:642 msgid "Musical interests:" msgstr "Interessi musicali:" -#: include/identity.php:634 +#: include/identity.php:644 msgid "Books, literature:" msgstr "Libri, letteratura:" -#: include/identity.php:636 +#: include/identity.php:646 msgid "Television:" msgstr "Televisione:" -#: include/identity.php:638 +#: include/identity.php:648 msgid "Film/dance/culture/entertainment:" msgstr "Film/danza/cultura/intrattenimento:" -#: include/identity.php:640 +#: include/identity.php:650 msgid "Love/Romance:" msgstr "Amore:" -#: include/identity.php:642 +#: include/identity.php:652 msgid "Work/employment:" msgstr "Lavoro:" -#: include/identity.php:644 +#: include/identity.php:654 msgid "School/education:" msgstr "Scuola:" -#: include/identity.php:648 +#: include/identity.php:658 msgid "Forums:" msgstr "Forum:" -#: include/identity.php:701 include/identity.php:704 include/nav.php:78 +#: include/identity.php:710 include/identity.php:713 include/nav.php:78 msgid "Videos" msgstr "Video" -#: include/identity.php:716 include/nav.php:140 +#: include/identity.php:725 include/nav.php:140 msgid "Events and Calendar" msgstr "Eventi e calendario" -#: include/identity.php:724 +#: include/identity.php:733 msgid "Only You Can See This" msgstr "Solo tu puoi vedere questo" +#: include/like.php:167 include/conversation.php:122 +#: include/conversation.php:258 include/text.php:1998 +#: view/theme/diabook/theme.php:463 +msgid "event" +msgstr "l'evento" + +#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2185 +#: view/theme/diabook/theme.php:480 +#, php-format +msgid "%1$s likes %2$s's %3$s" +msgstr "A %1$s piace %3$s di %2$s" + +#: include/like.php:186 include/conversation.php:144 +#, php-format +msgid "%1$s doesn't like %2$s's %3$s" +msgstr "A %1$s non piace %3$s di %2$s" + +#: include/like.php:188 +#, php-format +msgid "%1$s is attending %2$s's %3$s" +msgstr "%1$s parteciperà a %3$s di %2$s" + +#: include/like.php:190 +#, php-format +msgid "%1$s is not attending %2$s's %3$s" +msgstr "%1$s non parteciperà a %3$s di %2$s" + +#: include/like.php:192 +#, php-format +msgid "%1$s may attend %2$s's %3$s" +msgstr "%1$s forse parteciperà a %3$s di %2$s" + #: include/acl_selectors.php:325 msgid "Post to Email" msgstr "Invia a email" @@ -6852,6 +6890,10 @@ msgstr "mostra" msgid "don't show" msgstr "non mostrare" +#: include/acl_selectors.php:348 +msgid "Close" +msgstr "Chiudi" + #: include/message.php:15 include/message.php:173 msgid "[no subject]" msgstr "[nessun oggetto]" @@ -6860,31 +6902,31 @@ msgstr "[nessun oggetto]" msgid "stopped following" msgstr "tolto dai seguiti" -#: include/Contact.php:361 include/conversation.php:911 +#: include/Contact.php:337 include/conversation.php:911 msgid "View Status" msgstr "Visualizza stato" -#: include/Contact.php:363 include/conversation.php:913 +#: include/Contact.php:339 include/conversation.php:913 msgid "View Photos" msgstr "Visualizza foto" -#: include/Contact.php:364 include/conversation.php:914 +#: include/Contact.php:340 include/conversation.php:914 msgid "Network Posts" msgstr "Post della Rete" -#: include/Contact.php:365 include/conversation.php:915 +#: include/Contact.php:341 include/conversation.php:915 msgid "Edit Contact" msgstr "Modifica contatto" -#: include/Contact.php:366 +#: include/Contact.php:342 msgid "Drop Contact" msgstr "Rimuovi contatto" -#: include/Contact.php:367 include/conversation.php:916 +#: include/Contact.php:343 include/conversation.php:916 msgid "Send PM" msgstr "Invia messaggio privato" -#: include/Contact.php:368 include/conversation.php:920 +#: include/Contact.php:344 include/conversation.php:920 msgid "Poke" msgstr "Stuzzica" @@ -6947,153 +6989,153 @@ msgstr "Cancella elementi selezionati" msgid "Follow Thread" msgstr "Segui la discussione" -#: include/conversation.php:1035 +#: include/conversation.php:1034 #, php-format msgid "%s likes this." msgstr "Piace a %s." -#: include/conversation.php:1038 +#: include/conversation.php:1037 #, php-format msgid "%s doesn't like this." msgstr "Non piace a %s." -#: include/conversation.php:1041 +#: include/conversation.php:1040 #, php-format msgid "%s attends." msgstr "%s partecipa." -#: include/conversation.php:1044 +#: include/conversation.php:1043 #, php-format msgid "%s doesn't attend." msgstr "%s non partecipa." -#: include/conversation.php:1047 +#: include/conversation.php:1046 #, php-format msgid "%s attends maybe." msgstr "%s forse partecipa." -#: include/conversation.php:1057 +#: include/conversation.php:1056 msgid "and" msgstr "e" -#: include/conversation.php:1063 +#: include/conversation.php:1062 #, php-format msgid ", and %d other people" msgstr "e altre %d persone" -#: include/conversation.php:1072 +#: include/conversation.php:1071 #, php-format msgid "%2$d people like this" msgstr "Piace a %2$d persone." -#: include/conversation.php:1073 +#: include/conversation.php:1072 #, php-format msgid "%s like this." msgstr "a %s piace." -#: include/conversation.php:1076 +#: include/conversation.php:1075 #, php-format msgid "%2$d people don't like this" msgstr "Non piace a %2$d persone." -#: include/conversation.php:1077 +#: include/conversation.php:1076 #, php-format msgid "%s don't like this." msgstr "a %s non piace." -#: include/conversation.php:1080 +#: include/conversation.php:1079 #, php-format msgid "%2$d people attend" msgstr "%2$d persone partecipano" -#: include/conversation.php:1081 +#: include/conversation.php:1080 #, php-format msgid "%s attend." msgstr "%s partecipa." -#: include/conversation.php:1084 +#: include/conversation.php:1083 #, php-format msgid "%2$d people don't attend" msgstr "%2$d persone non partecipano" -#: include/conversation.php:1085 +#: include/conversation.php:1084 #, php-format msgid "%s don't attend." msgstr "%s non partecipa." -#: include/conversation.php:1088 +#: include/conversation.php:1087 #, php-format msgid "%2$d people anttend maybe" msgstr "%2$d persone forse partecipano" -#: include/conversation.php:1089 +#: include/conversation.php:1088 #, php-format msgid "%s anttend maybe." msgstr "%s forse partecipano." -#: include/conversation.php:1128 include/conversation.php:1146 +#: include/conversation.php:1127 include/conversation.php:1145 msgid "Visible to everybody" msgstr "Visibile a tutti" -#: include/conversation.php:1130 include/conversation.php:1148 +#: include/conversation.php:1129 include/conversation.php:1147 msgid "Please enter a video link/URL:" msgstr "Inserisci un collegamento video / URL:" -#: include/conversation.php:1131 include/conversation.php:1149 +#: include/conversation.php:1130 include/conversation.php:1148 msgid "Please enter an audio link/URL:" msgstr "Inserisci un collegamento audio / URL:" -#: include/conversation.php:1132 include/conversation.php:1150 +#: include/conversation.php:1131 include/conversation.php:1149 msgid "Tag term:" msgstr "Tag:" -#: include/conversation.php:1134 include/conversation.php:1152 +#: include/conversation.php:1133 include/conversation.php:1151 msgid "Where are you right now?" msgstr "Dove sei ora?" -#: include/conversation.php:1135 +#: include/conversation.php:1134 msgid "Delete item(s)?" msgstr "Cancellare questo elemento/i?" -#: include/conversation.php:1204 +#: include/conversation.php:1203 msgid "permissions" msgstr "permessi" -#: include/conversation.php:1227 +#: include/conversation.php:1226 msgid "Post to Groups" msgstr "Invia ai Gruppi" -#: include/conversation.php:1228 +#: include/conversation.php:1227 msgid "Post to Contacts" msgstr "Invia ai Contatti" -#: include/conversation.php:1229 +#: include/conversation.php:1228 msgid "Private post" msgstr "Post privato" -#: include/conversation.php:1386 +#: include/conversation.php:1385 msgid "View all" msgstr "Mostra tutto" -#: include/conversation.php:1408 +#: include/conversation.php:1407 msgid "Like" msgid_plural "Likes" msgstr[0] "Mi piace" msgstr[1] "Mi piace" -#: include/conversation.php:1411 +#: include/conversation.php:1410 msgid "Dislike" msgid_plural "Dislikes" msgstr[0] "Non mi piace" msgstr[1] "Non mi piace" -#: include/conversation.php:1417 +#: include/conversation.php:1416 msgid "Not Attending" msgid_plural "Not Attending" msgstr[0] "Non partecipa" msgstr[1] "Non partecipano" -#: include/conversation.php:1420 include/profile_selectors.php:6 +#: include/conversation.php:1419 include/profile_selectors.php:6 msgid "Undecided" msgid_plural "Undecided" msgstr[0] "Indeciso" @@ -7295,67 +7337,59 @@ msgstr "rilassato" msgid "surprised" msgstr "sorpreso" -#: include/text.php:1497 +#: include/text.php:1504 msgid "bytes" msgstr "bytes" -#: include/text.php:1529 include/text.php:1541 +#: include/text.php:1536 include/text.php:1548 msgid "Click to open/close" msgstr "Clicca per aprire/chiudere" -#: include/text.php:1715 +#: include/text.php:1722 msgid "View on separate page" msgstr "Vedi in una pagina separata" -#: include/text.php:1716 +#: include/text.php:1723 msgid "view on separate page" msgstr "vedi in una pagina separata" -#: include/text.php:1995 +#: include/text.php:2002 msgid "activity" msgstr "attività" -#: include/text.php:1998 +#: include/text.php:2005 msgid "post" msgstr "messaggio" -#: include/text.php:2166 +#: include/text.php:2173 msgid "Item filed" msgstr "Messaggio salvato" -#: include/bbcode.php:483 include/bbcode.php:1143 include/bbcode.php:1144 +#: include/bbcode.php:482 include/bbcode.php:1157 include/bbcode.php:1158 msgid "Image/photo" msgstr "Immagine/foto" -#: include/bbcode.php:581 +#: include/bbcode.php:595 #, php-format msgid "%2$s %3$s" msgstr "%2$s %3$s" -#: include/bbcode.php:615 +#: include/bbcode.php:629 #, php-format msgid "" "%s wrote the following post" msgstr "%s ha scritto il seguente messaggio" -#: include/bbcode.php:1103 include/bbcode.php:1123 +#: include/bbcode.php:1117 include/bbcode.php:1137 msgid "$1 wrote:" msgstr "$1 ha scritto:" -#: include/bbcode.php:1152 include/bbcode.php:1153 +#: include/bbcode.php:1166 include/bbcode.php:1167 msgid "Encrypted content" msgstr "Contenuto criptato" -#: include/notifier.php:843 include/delivery.php:458 -msgid "(no subject)" -msgstr "(nessun oggetto)" - -#: include/notifier.php:853 include/delivery.php:469 include/enotify.php:37 -msgid "noreply" -msgstr "nessuna risposta" - -#: include/dba_pdo.php:72 include/dba.php:56 +#: include/dba_pdo.php:72 include/dba.php:55 #, php-format msgid "Cannot locate DNS info for database server '%s'" msgstr "Non trovo le informazioni DNS per il database server '%s'" @@ -7400,6 +7434,10 @@ msgstr "Ostatus" msgid "RSS/Atom" msgstr "RSS / Atom" +#: include/contact_selectors.php:81 +msgid "Facebook" +msgstr "Facebook" + #: include/contact_selectors.php:82 msgid "Zot!" msgstr "Zot!" @@ -7444,7 +7482,7 @@ msgstr "App.net" msgid "Redmatrix" msgstr "Redmatrix" -#: include/Scrape.php:610 +#: include/Scrape.php:624 msgid " on Last.fm" msgstr "su Last.fm" @@ -7620,46 +7658,21 @@ msgstr "Navigazione" msgid "Site map" msgstr "Mappa del sito" -#: include/api.php:343 include/api.php:354 include/api.php:463 -#: include/api.php:1182 include/api.php:1184 -msgid "User not found." -msgstr "Utente non trovato." - -#: include/api.php:830 +#: include/api.php:878 #, php-format msgid "Daily posting limit of %d posts reached. The post was rejected." msgstr "Limite giornaliero di %d messaggi raggiunto. Il messaggio è stato rifiutato" -#: include/api.php:849 +#: include/api.php:897 #, php-format msgid "Weekly posting limit of %d posts reached. The post was rejected." msgstr "Limite settimanale di %d messaggi raggiunto. Il messaggio è stato rifiutato" -#: include/api.php:868 +#: include/api.php:916 #, php-format msgid "Monthly posting limit of %d posts reached. The post was rejected." msgstr "Limite mensile di %d messaggi raggiunto. Il messaggio è stato rifiutato" -#: include/api.php:1391 -msgid "There is no status with this id." -msgstr "Non c'è nessuno status con questo id." - -#: include/api.php:1465 -msgid "There is no conversation with this id." -msgstr "Non c'è nessuna conversazione con questo id" - -#: include/api.php:1744 -msgid "Invalid item." -msgstr "Elemento non valido." - -#: include/api.php:1754 -msgid "Invalid action. " -msgstr "Azione non valida." - -#: include/api.php:1762 -msgid "DB error" -msgstr "Errore database" - #: include/user.php:48 msgid "An invitation is required." msgstr "E' richiesto un invito." @@ -7722,8 +7735,7 @@ msgstr "ERRORE GRAVE: La generazione delle chiavi di sicurezza è fallita." msgid "An error occurred during registration. Please try again." msgstr "C'è stato un errore durante la registrazione. Prova ancora." -#: include/user.php:256 view/theme/clean/config.php:56 -#: view/theme/duepuntozero/config.php:44 +#: include/user.php:256 view/theme/duepuntozero/config.php:44 msgid "default" msgstr "default" @@ -7774,19 +7786,27 @@ msgid "" "\t\tThank you and welcome to %2$s." msgstr "\nI dettagli del tuo utente sono:\n Indirizzo del sito: %3$s\n Nome utente: %1$s\n Password: %5$s\n\nPuoi cambiare la tua password dalla pagina delle impostazioni del tuo account dopo esserti autenticato.\n\nPer favore, prenditi qualche momento per esaminare tutte le impostazioni presenti.\n\nPotresti voler aggiungere qualche informazione di base al tuo profilo predefinito (nella pagina \"Profili\"), così che le altre persone possano trovarti più facilmente.\n\nTi raccomandiamo di inserire il tuo nome completo, aggiungere una foto, aggiungere qualche parola chiave del profilo (molto utili per trovare nuovi contatti), e magari in quale nazione vivi, se non vuoi essere più specifico di così.\n\nNoi rispettiamo appieno la tua privacy, e nessuna di queste informazioni è necessaria o obbligatoria.\nSe sei nuovo e non conosci nessuno qui, possono aiutarti a trovare qualche nuovo e interessante contatto.\n\nGrazie e benvenuto su %2$s" -#: include/diaspora.php:719 +#: include/diaspora.php:720 msgid "Sharing notification from Diaspora network" msgstr "Notifica di condivisione dal network Diaspora*" -#: include/diaspora.php:2606 +#: include/diaspora.php:2625 msgid "Attachments:" msgstr "Allegati:" -#: include/items.php:4897 +#: include/delivery.php:533 +msgid "(no subject)" +msgstr "(nessun oggetto)" + +#: include/delivery.php:544 include/enotify.php:37 +msgid "noreply" +msgstr "nessuna risposta" + +#: include/items.php:4926 msgid "Do you really want to delete this item?" msgstr "Vuoi veramente cancellare questo elemento?" -#: include/items.php:5172 +#: include/items.php:5201 msgid "Archives" msgstr "Archivi" @@ -8302,11 +8322,11 @@ msgstr "Nome completo: %1$s\nIndirizzo del sito: %2$s\nNome utente: %3$s (%4$s)" msgid "Please visit %s to approve or reject the request." msgstr "Visita %s per approvare o rifiutare la richiesta." -#: include/oembed.php:220 +#: include/oembed.php:226 msgid "Embedded content" msgstr "Contenuto incorporato" -#: include/oembed.php:229 +#: include/oembed.php:235 msgid "Embedding disabled" msgstr "Embed disabilitato" @@ -8346,7 +8366,7 @@ msgstr[1] "%d contatti non importati" msgid "Done. You can now login with your username and password" msgstr "Fatto. Ora puoi entrare con il tuo nome utente e la tua password" -#: index.php:441 +#: index.php:442 msgid "toggle mobile" msgstr "commuta tema mobile" @@ -8364,7 +8384,6 @@ msgid "Set theme width" msgstr "Imposta la larghezza del tema" #: view/theme/cleanzero/config.php:86 view/theme/quattro/config.php:68 -#: view/theme/clean/config.php:88 msgid "Color scheme" msgstr "Schema colori" @@ -8486,56 +8505,6 @@ msgstr "Livello di zoom per Earth Layers" msgid "Show/hide boxes at right-hand column:" msgstr "Mostra/Nascondi riquadri nella colonna destra" -#: view/theme/clean/config.php:57 -msgid "Midnight" -msgstr "Mezzanotte" - -#: view/theme/clean/config.php:58 -msgid "Zenburn" -msgstr "Zenburn" - -#: view/theme/clean/config.php:59 -msgid "Bootstrap" -msgstr "Bootstrap" - -#: view/theme/clean/config.php:60 -msgid "Shades of Pink" -msgstr "Sfumature di Rosa" - -#: view/theme/clean/config.php:61 -msgid "Lime and Orange" -msgstr "Lime e Arancia" - -#: view/theme/clean/config.php:62 -msgid "GeoCities Retro" -msgstr "GeoCities Retro" - -#: view/theme/clean/config.php:86 -msgid "Background Image" -msgstr "Immagine di sfondo" - -#: view/theme/clean/config.php:86 -msgid "" -"The URL to a picture (e.g. from your photo album) that should be used as " -"background image." -msgstr "L'URL a un'immagine (p.e. dal tuo album foto) che viene usata come immagine di sfondo." - -#: view/theme/clean/config.php:87 -msgid "Background Color" -msgstr "Colore di sfondo" - -#: view/theme/clean/config.php:87 -msgid "HEX value for the background color. Don't include the #" -msgstr "Valore esadecimale del colore di sfondo. Non includere il #" - -#: view/theme/clean/config.php:89 -msgid "font size" -msgstr "dimensione font" - -#: view/theme/clean/config.php:89 -msgid "base font size for your interface" -msgstr "dimensione di base dei font per la tua interfaccia" - #: view/theme/vier/config.php:64 msgid "Comma separated list of helper forums" msgstr "Lista separata da virgola di forum di aiuto" diff --git a/view/it/strings.php b/view/it/strings.php index fb30c73573..131e03080b 100644 --- a/view/it/strings.php +++ b/view/it/strings.php @@ -8,7 +8,7 @@ function string_plural_select_it($n){ $a->strings["Network:"] = "Rete:"; $a->strings["Forum"] = "Forum"; $a->strings["%d contact edited."] = array( - 0 => "%d contatto modificato", + 0 => "%d contatto modificato.", 1 => "%d contatti modificati", ); $a->strings["Could not access contact record."] = "Non è possibile accedere al contatto."; @@ -142,9 +142,6 @@ $a->strings["Edit your default profile to your liking. Review t $a->strings["Profile Keywords"] = "Parole chiave del profilo"; $a->strings["Set some public keywords for your default profile which describe your interests. We may be able to find other people with similar interests and suggest friendships."] = "Inserisci qualche parola chiave pubblica nel tuo profilo predefinito che descriva i tuoi interessi. Potremmo essere in grado di trovare altre persone con interessi similari e suggerirti delle amicizie."; $a->strings["Connecting"] = "Collegarsi"; -$a->strings["Facebook"] = "Facebook"; -$a->strings["Authorise the Facebook Connector if you currently have a Facebook account and we will (optionally) import all your Facebook friends and conversations."] = "Autorizza il Facebook Connector se hai un account Facebook, e noi (opzionalmente) importeremo tuti i tuoi amici e le tue conversazioni da Facebook."; -$a->strings["If this is your own personal server, installing the Facebook addon may ease your transition to the free social web."] = "Sestrings["Importing Emails"] = "Importare le Email"; $a->strings["Enter your email access information on your Connector Settings page if you wish to import and interact with friends or mailing lists from your email INBOX"] = "Inserisci i tuoi dati di accesso all'email nella tua pagina Impostazioni Connettori se vuoi importare e interagire con amici o mailing list dalla tua casella di posta in arrivo"; $a->strings["Go to Your Contacts Page"] = "Vai alla tua pagina Contatti"; @@ -189,7 +186,7 @@ $a->strings["Tag removed"] = "Tag rimosso"; $a->strings["Remove Item Tag"] = "Rimuovi il tag"; $a->strings["Select a tag to remove: "] = "Seleziona un tag da rimuovere: "; $a->strings["Remove"] = "Rimuovi"; -$a->strings["Subsribing to OStatus contacts"] = "Iscrizione a contatti OStatus"; +$a->strings["Subscribing to OStatus contacts"] = "Iscrizione a contatti OStatus"; $a->strings["No contact provided."] = "Nessun contatto disponibile."; $a->strings["Couldn't fetch information for contact."] = "Non è stato possibile recuperare le informazioni del contatto."; $a->strings["Couldn't fetch friends for contact."] = "Non è stato possibile recuperare gli amici del contatto."; @@ -290,12 +287,6 @@ $a->strings["Forgot your Password?"] = "Hai dimenticato la password?"; $a->strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = "Inserisci il tuo indirizzo email per reimpostare la password."; $a->strings["Nickname or Email: "] = "Nome utente o email: "; $a->strings["Reset"] = "Reimposta"; -$a->strings["event"] = "l'evento"; -$a->strings["%1\$s likes %2\$s's %3\$s"] = "A %1\$s piace %3\$s di %2\$s"; -$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "A %1\$s non piace %3\$s di %2\$s"; -$a->strings["%1\$s is attending %2\$s's %3\$s"] = "%1\$s parteciperà a %3\$s di %2\$s"; -$a->strings["%1\$s is not attending %2\$s's %3\$s"] = "%1\$s non parteciperà a %3\$s di %2\$s"; -$a->strings["%1\$s may attend %2\$s's %3\$s"] = "%1\$s forse parteciperà a %3\$s di %2\$s"; $a->strings["{0} wants to be your friend"] = "{0} vuole essere tuo amico"; $a->strings["{0} sent you a message"] = "{0} ti ha inviato un messaggio"; $a->strings["{0} requested registration"] = "{0} chiede la registrazione"; @@ -425,16 +416,22 @@ $a->strings["Site"] = "Sito"; $a->strings["Users"] = "Utenti"; $a->strings["Plugins"] = "Plugin"; $a->strings["Themes"] = "Temi"; +$a->strings["Additional features"] = "Funzionalità aggiuntive"; $a->strings["DB updates"] = "Aggiornamenti Database"; $a->strings["Inspect Queue"] = "Ispeziona Coda di invio"; +$a->strings["Federation Statistics"] = "Statistiche sulla Federazione"; $a->strings["Logs"] = "Log"; +$a->strings["View Logs"] = "Vedi i log"; $a->strings["probe address"] = "controlla indirizzo"; $a->strings["check webfinger"] = "verifica webfinger"; $a->strings["Admin"] = "Amministrazione"; $a->strings["Plugin Features"] = "Impostazioni Plugins"; $a->strings["diagnostics"] = "diagnostiche"; $a->strings["User registrations waiting for confirmation"] = "Utenti registrati in attesa di conferma"; +$a->strings["This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of."] = "Questa pagina offre alcuni numeri riguardo la porzione del social network federato di cui il tuo nodo Friendica fa parte. Questi numeri non sono completi ma riflettono esclusivamente la porzione di rete di cui il tuo nodo e' a conoscenza."; +$a->strings["The Auto Discovered Contact Directory feature is not enabled, it will improve the data displayed here."] = ""; $a->strings["Administration"] = "Amministrazione"; +$a->strings["Currently this node is aware of %d nodes from the following platforms:"] = ""; $a->strings["ID"] = "ID"; $a->strings["Recipient Name"] = "Nome Destinatario"; $a->strings["Recipient Profile"] = "Profilo Destinatario"; @@ -585,6 +582,8 @@ $a->strings["Maximum Load Average (Frontend)"] = "Media Massimo Carico (Frontend $a->strings["Maximum system load before the frontend quits service - default 50."] = "Massimo carico di sistema prima che il frontend fermi il servizio - default 50."; $a->strings["Maximum table size for optimization"] = "Dimensione massima della tabella per l'ottimizzazione"; $a->strings["Maximum table size (in MB) for the automatic optimization - default 100 MB. Enter -1 to disable it."] = "La dimensione massima (in MB) per l'ottimizzazione automatica - default 100 MB. Inserisci -1 per disabilitarlo."; +$a->strings["Minimum level of fragmentation"] = ""; +$a->strings["Minimum fragmenation level to start the automatic optimization - default value is 30%."] = ""; $a->strings["Periodical check of global contacts"] = "Check periodico dei contatti globali"; $a->strings["If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers."] = "Se abilitato, i contatti globali sono controllati periodicamente per verificare dati mancanti o sorpassati e la vitaltà dei contatti e dei server."; $a->strings["Days between requery"] = "Giorni tra le richieste"; @@ -684,9 +683,11 @@ $a->strings["Toggle"] = "Inverti"; $a->strings["Author: "] = "Autore: "; $a->strings["Maintainer: "] = "Manutentore: "; $a->strings["Reload active plugins"] = "Ricarica i plugin attivi"; +$a->strings["There are currently no plugins available on your node. You can find the official plugin repository at %1\$s and might find other interesting plugins in the open plugin registry at %2\$s"] = ""; $a->strings["No themes found."] = "Nessun tema trovato."; $a->strings["Screenshot"] = "Anteprima"; $a->strings["Reload active themes"] = "Ricarica i temi attivi"; +$a->strings["No themes found on the system. They should be paced in %1\$s"] = ""; $a->strings["[Experimental]"] = "[Sperimentale]"; $a->strings["[Unsupported]"] = "[Non supportato]"; $a->strings["Log settings updated."] = "Impostazioni Log aggiornate."; @@ -695,11 +696,12 @@ $a->strings["Enable Debugging"] = "Abilita Debugging"; $a->strings["Log file"] = "File di Log"; $a->strings["Must be writable by web server. Relative to your Friendica top-level directory."] = "Deve essere scrivibile dal server web. Relativo alla tua directory Friendica."; $a->strings["Log level"] = "Livello di Log"; -$a->strings["Close"] = "Chiudi"; -$a->strings["FTP Host"] = "Indirizzo FTP"; -$a->strings["FTP Path"] = "Percorso FTP"; -$a->strings["FTP User"] = "Utente FTP"; -$a->strings["FTP Password"] = "Pasword FTP"; +$a->strings["PHP logging"] = ""; +$a->strings["To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."] = ""; +$a->strings["Off"] = "Spento"; +$a->strings["On"] = "Acceso"; +$a->strings["Lock feature %s"] = ""; +$a->strings["Manage Additional Features"] = ""; $a->strings["Search Results For: %s"] = "Risultato della ricerca per: %s"; $a->strings["Remove term"] = "Rimuovi termine"; $a->strings["Saved Searches"] = "Ricerche salvate"; @@ -921,7 +923,6 @@ $a->strings["Not available."] = "Non disponibile."; $a->strings["Community"] = "Comunità"; $a->strings["No results."] = "Nessun risultato."; $a->strings["everybody"] = "tutti"; -$a->strings["Additional features"] = "Funzionalità aggiuntive"; $a->strings["Display"] = "Visualizzazione"; $a->strings["Social Networks"] = "Social Networks"; $a->strings["Delegations"] = "Delegazioni"; @@ -958,8 +959,6 @@ $a->strings["No name"] = "Nessun nome"; $a->strings["Remove authorization"] = "Rimuovi l'autorizzazione"; $a->strings["No Plugin settings configured"] = "Nessun plugin ha impostazioni modificabili"; $a->strings["Plugin Settings"] = "Impostazioni plugin"; -$a->strings["Off"] = "Spento"; -$a->strings["On"] = "Acceso"; $a->strings["Additional Features"] = "Funzionalità aggiuntive"; $a->strings["General Social Media Settings"] = "Impostazioni Media Sociali"; $a->strings["Disable intelligent shortening"] = "Disabilita accorciamento intelligente"; @@ -1106,12 +1105,12 @@ $a->strings["Friends are advised to please try again in 24 hours."] = "Gli amici $a->strings["Invalid locator"] = "Invalid locator"; $a->strings["Invalid email address."] = "Indirizzo email non valido."; $a->strings["This account has not been configured for email. Request failed."] = "Questo account non è stato configurato per l'email. Richiesta fallita."; -$a->strings["Unable to resolve your name at the provided location."] = "Impossibile risolvere il tuo nome nella posizione indicata."; $a->strings["You have already introduced yourself here."] = "Ti sei già presentato qui."; $a->strings["Apparently you are already friends with %s."] = "Pare che tu e %s siate già amici."; $a->strings["Invalid profile URL."] = "Indirizzo profilo non valido."; $a->strings["Disallowed profile URL."] = "Indirizzo profilo non permesso."; $a->strings["Your introduction has been sent."] = "La tua presentazione è stata inviata."; +$a->strings["Remote subscription can't be done for your network. Please subscribe directly on your system."] = ""; $a->strings["Please login to confirm introduction."] = "Accedi per confermare la presentazione."; $a->strings["Incorrect identity currently logged in. Please login to this profile."] = "Non hai fatto accesso con l'identità corretta. Accedi a questo profilo."; $a->strings["Confirm"] = "Conferma"; @@ -1308,7 +1307,7 @@ $a->strings["poke, prod or do other things to somebody"] = "tocca, pungola o fai $a->strings["Recipient"] = "Destinatario"; $a->strings["Choose what you wish to do to recipient"] = "Scegli cosa vuoi fare al destinatario"; $a->strings["Make this post private"] = "Rendi questo post privato"; -$a->strings["Resubsribing to OStatus contacts"] = "Reiscrizione a contatti OStatus"; +$a->strings["Resubscribing to OStatus contacts"] = ""; $a->strings["Error"] = "Errore"; $a->strings["Total invitation limit exceeded."] = "Limite totale degli inviti superato."; $a->strings["%s : Not a valid email address."] = "%s: non è un indirizzo email valido."; @@ -1321,12 +1320,12 @@ $a->strings["%d message sent."] = array( ); $a->strings["You have no more invitations available"] = "Non hai altri inviti disponibili"; $a->strings["Visit %s for a list of public sites that you can join. Friendica members on other sites can all connect with each other, as well as with members of many other social networks."] = "Visita %s per una lista di siti pubblici a cui puoi iscriverti. I membri Friendica su altri siti possono collegarsi uno con l'altro, come con membri di molti altri social network."; -$a->strings["To accept this invitation, please visit and register at %s or any other public Friendica website."] = "Per accettare questo invito, visita e resitrati su %s o su un'altro sito web Friendica aperto al pubblico."; +$a->strings["To accept this invitation, please visit and register at %s or any other public Friendica website."] = "Per accettare questo invito, visita e registrati su %s o su un'altro sito web Friendica aperto al pubblico."; $a->strings["Friendica sites all inter-connect to create a huge privacy-enhanced social web that is owned and controlled by its members. They can also connect with many traditional social networks. See %s for a list of alternate Friendica sites you can join."] = "I siti Friendica son tutti collegati tra loro per creare una grossa rete sociale rispettosa della privacy, posseduta e controllata dai suoi membri. I siti Friendica possono anche collegarsi a molti altri social network tradizionali. Vai su %s per una lista di siti Friendica alternativi a cui puoi iscriverti."; $a->strings["Our apologies. This system is not currently configured to connect with other public sites or invite members."] = "Ci scusiamo, questo sistema non è configurato per collegarsi con altri siti pubblici o per invitare membri."; $a->strings["Send invitations"] = "Invia inviti"; $a->strings["Enter email addresses, one per line:"] = "Inserisci gli indirizzi email, uno per riga:"; -$a->strings["You are cordially invited to join me and other close friends on Friendica - and help us to create a better social web."] = "Sei cordialmente invitato a unirti a me ed ad altri amici su Friendica, e ad aiutarci a creare una rete sociale migliore."; +$a->strings["You are cordially invited to join me and other close friends on Friendica - and help us to create a better social web."] = "Sei cordialmente invitato/a ad unirti a me e ad altri amici su Friendica, e ad aiutarci a creare una rete sociale migliore."; $a->strings["You will need to supply this invitation code: \$invite_code"] = "Sarà necessario fornire questo codice invito: \$invite_code"; $a->strings["Once you have registered, please connect with me via my profile page at:"] = "Una volta registrato, connettiti con me dal mio profilo:"; $a->strings["For more information about the Friendica project and why we feel it is important, please visit http://friendica.com"] = "Per maggiori informazioni sul progetto Friendica e perchè pensiamo sia importante, visita http://friendica.com"; @@ -1563,11 +1562,18 @@ $a->strings["Forums:"] = "Forum:"; $a->strings["Videos"] = "Video"; $a->strings["Events and Calendar"] = "Eventi e calendario"; $a->strings["Only You Can See This"] = "Solo tu puoi vedere questo"; +$a->strings["event"] = "l'evento"; +$a->strings["%1\$s likes %2\$s's %3\$s"] = "A %1\$s piace %3\$s di %2\$s"; +$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "A %1\$s non piace %3\$s di %2\$s"; +$a->strings["%1\$s is attending %2\$s's %3\$s"] = "%1\$s parteciperà a %3\$s di %2\$s"; +$a->strings["%1\$s is not attending %2\$s's %3\$s"] = "%1\$s non parteciperà a %3\$s di %2\$s"; +$a->strings["%1\$s may attend %2\$s's %3\$s"] = "%1\$s forse parteciperà a %3\$s di %2\$s"; $a->strings["Post to Email"] = "Invia a email"; $a->strings["Connectors disabled, since \"%s\" is enabled."] = "Connettore disabilitato, dato che \"%s\" è abilitato."; $a->strings["Visible to everybody"] = "Visibile a tutti"; $a->strings["show"] = "mostra"; $a->strings["don't show"] = "non mostrare"; +$a->strings["Close"] = "Chiudi"; $a->strings["[no subject]"] = "[nessun oggetto]"; $a->strings["stopped following"] = "tolto dai seguiti"; $a->strings["View Status"] = "Visualizza stato"; @@ -1697,8 +1703,6 @@ $a->strings["%2\$s %3\$s"] = "strings["%s wrote the following post"] = "%s ha scritto il seguente messaggio"; $a->strings["$1 wrote:"] = "$1 ha scritto:"; $a->strings["Encrypted content"] = "Contenuto criptato"; -$a->strings["(no subject)"] = "(nessun oggetto)"; -$a->strings["noreply"] = "nessuna risposta"; $a->strings["Cannot locate DNS info for database server '%s'"] = "Non trovo le informazioni DNS per il database server '%s'"; $a->strings["Unknown | Not categorised"] = "Sconosciuto | non categorizzato"; $a->strings["Block immediately"] = "Blocca immediatamente"; @@ -1710,6 +1714,7 @@ $a->strings["Weekly"] = "Settimanalmente"; $a->strings["Monthly"] = "Mensilmente"; $a->strings["OStatus"] = "Ostatus"; $a->strings["RSS/Atom"] = "RSS / Atom"; +$a->strings["Facebook"] = "Facebook"; $a->strings["Zot!"] = "Zot!"; $a->strings["LinkedIn"] = "LinkedIn"; $a->strings["XMPP/IM"] = "XMPP/IM"; @@ -1765,15 +1770,9 @@ $a->strings["Manage/edit friends and contacts"] = "Gestisci/modifica amici e con $a->strings["Site setup and configuration"] = "Configurazione del sito"; $a->strings["Navigation"] = "Navigazione"; $a->strings["Site map"] = "Mappa del sito"; -$a->strings["User not found."] = "Utente non trovato."; $a->strings["Daily posting limit of %d posts reached. The post was rejected."] = "Limite giornaliero di %d messaggi raggiunto. Il messaggio è stato rifiutato"; $a->strings["Weekly posting limit of %d posts reached. The post was rejected."] = "Limite settimanale di %d messaggi raggiunto. Il messaggio è stato rifiutato"; $a->strings["Monthly posting limit of %d posts reached. The post was rejected."] = "Limite mensile di %d messaggi raggiunto. Il messaggio è stato rifiutato"; -$a->strings["There is no status with this id."] = "Non c'è nessuno status con questo id."; -$a->strings["There is no conversation with this id."] = "Non c'è nessuna conversazione con questo id"; -$a->strings["Invalid item."] = "Elemento non valido."; -$a->strings["Invalid action. "] = "Azione non valida."; -$a->strings["DB error"] = "Errore database"; $a->strings["An invitation is required."] = "E' richiesto un invito."; $a->strings["Invitation could not be verified."] = "L'invito non puo' essere verificato."; $a->strings["Invalid OpenID url"] = "Url OpenID non valido"; @@ -1796,6 +1795,8 @@ $a->strings["\n\t\tDear %1\$s,\n\t\t\tThank you for registering at %2\$s. Your a $a->strings["\n\t\tThe login details are as follows:\n\t\t\tSite Location:\t%3\$s\n\t\t\tLogin Name:\t%1\$s\n\t\t\tPassword:\t%5\$s\n\n\t\tYou may change your password from your account \"Settings\" page after logging\n\t\tin.\n\n\t\tPlease take a few moments to review the other account settings on that page.\n\n\t\tYou may also wish to add some basic information to your default profile\n\t\t(on the \"Profiles\" page) so that other people can easily find you.\n\n\t\tWe recommend setting your full name, adding a profile photo,\n\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n\t\tperhaps what country you live in; if you do not wish to be more specific\n\t\tthan that.\n\n\t\tWe fully respect your right to privacy, and none of these items are necessary.\n\t\tIf you are new and do not know anybody here, they may help\n\t\tyou to make some new and interesting friends.\n\n\n\t\tThank you and welcome to %2\$s."] = "\nI dettagli del tuo utente sono:\n Indirizzo del sito: %3\$s\n Nome utente: %1\$s\n Password: %5\$s\n\nPuoi cambiare la tua password dalla pagina delle impostazioni del tuo account dopo esserti autenticato.\n\nPer favore, prenditi qualche momento per esaminare tutte le impostazioni presenti.\n\nPotresti voler aggiungere qualche informazione di base al tuo profilo predefinito (nella pagina \"Profili\"), così che le altre persone possano trovarti più facilmente.\n\nTi raccomandiamo di inserire il tuo nome completo, aggiungere una foto, aggiungere qualche parola chiave del profilo (molto utili per trovare nuovi contatti), e magari in quale nazione vivi, se non vuoi essere più specifico di così.\n\nNoi rispettiamo appieno la tua privacy, e nessuna di queste informazioni è necessaria o obbligatoria.\nSe sei nuovo e non conosci nessuno qui, possono aiutarti a trovare qualche nuovo e interessante contatto.\n\nGrazie e benvenuto su %2\$s"; $a->strings["Sharing notification from Diaspora network"] = "Notifica di condivisione dal network Diaspora*"; $a->strings["Attachments:"] = "Allegati:"; +$a->strings["(no subject)"] = "(nessun oggetto)"; +$a->strings["noreply"] = "nessuna risposta"; $a->strings["Do you really want to delete this item?"] = "Vuoi veramente cancellare questo elemento?"; $a->strings["Archives"] = "Archivi"; $a->strings["Male"] = "Maschio"; @@ -1956,18 +1957,6 @@ $a->strings["Your personal photos"] = "Le tue foto personali"; $a->strings["Local Directory"] = "Elenco Locale"; $a->strings["Set zoomfactor for Earth Layers"] = "Livello di zoom per Earth Layers"; $a->strings["Show/hide boxes at right-hand column:"] = "Mostra/Nascondi riquadri nella colonna destra"; -$a->strings["Midnight"] = "Mezzanotte"; -$a->strings["Zenburn"] = "Zenburn"; -$a->strings["Bootstrap"] = "Bootstrap"; -$a->strings["Shades of Pink"] = "Sfumature di Rosa"; -$a->strings["Lime and Orange"] = "Lime e Arancia"; -$a->strings["GeoCities Retro"] = "GeoCities Retro"; -$a->strings["Background Image"] = "Immagine di sfondo"; -$a->strings["The URL to a picture (e.g. from your photo album) that should be used as background image."] = "L'URL a un'immagine (p.e. dal tuo album foto) che viene usata come immagine di sfondo."; -$a->strings["Background Color"] = "Colore di sfondo"; -$a->strings["HEX value for the background color. Don't include the #"] = "Valore esadecimale del colore di sfondo. Non includere il #"; -$a->strings["font size"] = "dimensione font"; -$a->strings["base font size for your interface"] = "dimensione di base dei font per la tua interfaccia"; $a->strings["Comma separated list of helper forums"] = "Lista separata da virgola di forum di aiuto"; $a->strings["Set style"] = "Imposta stile"; $a->strings["Quick Start"] = "Quick Start"; diff --git a/view/ru/messages.po b/view/ru/messages.po index 4b0e372dd6..7f65e8f1be 100644 --- a/view/ru/messages.po +++ b/view/ru/messages.po @@ -1,8 +1,9 @@ # FRIENDICA Distributed Social Network # Copyright (C) 2010, 2011, 2012, 2013 the Friendica Project # This file is distributed under the same license as the Friendica package. -# +# # Translators: +# Aliaksei Sakalou , 2016 # Alex , 2013 # vislav , 2014 # Alex , 2013 @@ -15,1151 +16,1317 @@ msgid "" msgstr "" "Project-Id-Version: friendica\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-02-09 08:57+0100\n" -"PO-Revision-Date: 2015-02-09 09:46+0000\n" -"Last-Translator: fabrixxm \n" -"Language-Team: Russian (http://www.transifex.com/projects/p/friendica/language/ru/)\n" +"POT-Creation-Date: 2016-01-24 06:49+0100\n" +"PO-Revision-Date: 2016-02-16 18:28+0300\n" +"Last-Translator: Aliaksei Sakalou \n" +"Language-Team: Russian (http://www.transifex.com/Friendica/friendica/" +"language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" +"X-Generator: Poedit 1.5.4\n" -#: ../../mod/contacts.php:108 +#: mod/contacts.php:50 include/identity.php:395 +msgid "Network:" +msgstr "Сеть:" + +#: mod/contacts.php:51 mod/contacts.php:961 mod/videos.php:37 +#: mod/viewcontacts.php:105 mod/dirfind.php:214 mod/network.php:598 +#: mod/allfriends.php:77 mod/match.php:82 mod/directory.php:172 +#: mod/common.php:123 mod/suggest.php:95 mod/photos.php:41 +#: include/identity.php:298 +msgid "Forum" +msgstr "Форум" + +#: mod/contacts.php:128 #, php-format msgid "%d contact edited." -msgid_plural "%d contacts edited" -msgstr[0] "%d контакт изменён." -msgstr[1] "%d контакты изменены" -msgstr[2] "%d контакты изменены" +msgid_plural "%d contacts edited." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" -#: ../../mod/contacts.php:139 ../../mod/contacts.php:272 +#: mod/contacts.php:159 mod/contacts.php:383 msgid "Could not access contact record." msgstr "Не удалось получить доступ к записи контакта." -#: ../../mod/contacts.php:153 +#: mod/contacts.php:173 msgid "Could not locate selected profile." msgstr "Не удалось найти выбранный профиль." -#: ../../mod/contacts.php:186 +#: mod/contacts.php:206 msgid "Contact updated." msgstr "Контакт обновлен." -#: ../../mod/contacts.php:188 ../../mod/dfrn_request.php:576 +#: mod/contacts.php:208 mod/dfrn_request.php:575 msgid "Failed to update contact record." msgstr "Не удалось обновить запись контакта." -#: ../../mod/contacts.php:254 ../../mod/manage.php:96 -#: ../../mod/display.php:499 ../../mod/profile_photo.php:19 -#: ../../mod/profile_photo.php:169 ../../mod/profile_photo.php:180 -#: ../../mod/profile_photo.php:193 ../../mod/follow.php:9 -#: ../../mod/item.php:168 ../../mod/item.php:184 ../../mod/group.php:19 -#: ../../mod/dfrn_confirm.php:55 ../../mod/fsuggest.php:78 -#: ../../mod/wall_upload.php:66 ../../mod/viewcontacts.php:24 -#: ../../mod/notifications.php:66 ../../mod/message.php:38 -#: ../../mod/message.php:174 ../../mod/crepair.php:119 -#: ../../mod/nogroup.php:25 ../../mod/network.php:4 ../../mod/allfriends.php:9 -#: ../../mod/events.php:140 ../../mod/install.php:151 -#: ../../mod/wallmessage.php:9 ../../mod/wallmessage.php:33 -#: ../../mod/wallmessage.php:79 ../../mod/wallmessage.php:103 -#: ../../mod/wall_attach.php:55 ../../mod/settings.php:102 -#: ../../mod/settings.php:596 ../../mod/settings.php:601 -#: ../../mod/register.php:42 ../../mod/delegate.php:12 ../../mod/mood.php:114 -#: ../../mod/suggest.php:58 ../../mod/profiles.php:165 -#: ../../mod/profiles.php:618 ../../mod/editpost.php:10 ../../mod/api.php:26 -#: ../../mod/api.php:31 ../../mod/notes.php:20 ../../mod/poke.php:135 -#: ../../mod/invite.php:15 ../../mod/invite.php:101 ../../mod/photos.php:134 -#: ../../mod/photos.php:1050 ../../mod/regmod.php:110 ../../mod/uimport.php:23 -#: ../../mod/attach.php:33 ../../include/items.php:4712 ../../index.php:369 +#: mod/contacts.php:365 mod/manage.php:96 mod/display.php:509 +#: mod/profile_photo.php:19 mod/profile_photo.php:175 +#: mod/profile_photo.php:186 mod/profile_photo.php:199 +#: mod/ostatus_subscribe.php:9 mod/follow.php:11 mod/follow.php:73 +#: mod/follow.php:155 mod/item.php:180 mod/item.php:192 mod/group.php:19 +#: mod/dfrn_confirm.php:55 mod/fsuggest.php:78 mod/wall_upload.php:77 +#: mod/wall_upload.php:80 mod/viewcontacts.php:40 mod/notifications.php:69 +#: mod/message.php:45 mod/message.php:181 mod/crepair.php:117 +#: mod/dirfind.php:11 mod/nogroup.php:25 mod/network.php:4 +#: mod/allfriends.php:12 mod/events.php:165 mod/wallmessage.php:9 +#: mod/wallmessage.php:33 mod/wallmessage.php:79 mod/wallmessage.php:103 +#: mod/wall_attach.php:67 mod/wall_attach.php:70 mod/settings.php:20 +#: mod/settings.php:126 mod/settings.php:646 mod/register.php:42 +#: mod/delegate.php:12 mod/common.php:18 mod/mood.php:114 mod/suggest.php:58 +#: mod/profiles.php:165 mod/profiles.php:615 mod/editpost.php:10 +#: mod/api.php:26 mod/api.php:31 mod/notes.php:22 mod/poke.php:149 +#: mod/repair_ostatus.php:9 mod/invite.php:15 mod/invite.php:101 +#: mod/photos.php:171 mod/photos.php:1105 mod/regmod.php:110 +#: mod/uimport.php:23 mod/attach.php:33 include/items.php:5096 index.php:383 msgid "Permission denied." msgstr "Нет разрешения." -#: ../../mod/contacts.php:287 +#: mod/contacts.php:404 msgid "Contact has been blocked" msgstr "Контакт заблокирован" -#: ../../mod/contacts.php:287 +#: mod/contacts.php:404 msgid "Contact has been unblocked" msgstr "Контакт разблокирован" -#: ../../mod/contacts.php:298 +#: mod/contacts.php:415 msgid "Contact has been ignored" msgstr "Контакт проигнорирован" -#: ../../mod/contacts.php:298 +#: mod/contacts.php:415 msgid "Contact has been unignored" msgstr "У контакта отменено игнорирование" -#: ../../mod/contacts.php:310 +#: mod/contacts.php:427 msgid "Contact has been archived" msgstr "Контакт заархивирован" -#: ../../mod/contacts.php:310 +#: mod/contacts.php:427 msgid "Contact has been unarchived" msgstr "Контакт разархивирован" -#: ../../mod/contacts.php:335 ../../mod/contacts.php:711 +#: mod/contacts.php:454 mod/contacts.php:802 msgid "Do you really want to delete this contact?" msgstr "Вы действительно хотите удалить этот контакт?" -#: ../../mod/contacts.php:337 ../../mod/message.php:209 -#: ../../mod/settings.php:1010 ../../mod/settings.php:1016 -#: ../../mod/settings.php:1024 ../../mod/settings.php:1028 -#: ../../mod/settings.php:1033 ../../mod/settings.php:1039 -#: ../../mod/settings.php:1045 ../../mod/settings.php:1051 -#: ../../mod/settings.php:1081 ../../mod/settings.php:1082 -#: ../../mod/settings.php:1083 ../../mod/settings.php:1084 -#: ../../mod/settings.php:1085 ../../mod/dfrn_request.php:830 -#: ../../mod/register.php:233 ../../mod/suggest.php:29 -#: ../../mod/profiles.php:661 ../../mod/profiles.php:664 ../../mod/api.php:105 -#: ../../include/items.php:4557 +#: mod/contacts.php:456 mod/follow.php:110 mod/message.php:216 +#: mod/settings.php:1103 mod/settings.php:1109 mod/settings.php:1117 +#: mod/settings.php:1121 mod/settings.php:1126 mod/settings.php:1132 +#: mod/settings.php:1138 mod/settings.php:1144 mod/settings.php:1170 +#: mod/settings.php:1171 mod/settings.php:1172 mod/settings.php:1173 +#: mod/settings.php:1174 mod/dfrn_request.php:857 mod/register.php:238 +#: mod/suggest.php:29 mod/profiles.php:658 mod/profiles.php:661 +#: mod/profiles.php:687 mod/api.php:105 include/items.php:4928 msgid "Yes" msgstr "Да" -#: ../../mod/contacts.php:340 ../../mod/tagrm.php:11 ../../mod/tagrm.php:94 -#: ../../mod/message.php:212 ../../mod/fbrowser.php:81 -#: ../../mod/fbrowser.php:116 ../../mod/settings.php:615 -#: ../../mod/settings.php:641 ../../mod/dfrn_request.php:844 -#: ../../mod/suggest.php:32 ../../mod/editpost.php:148 -#: ../../mod/photos.php:203 ../../mod/photos.php:292 -#: ../../include/conversation.php:1129 ../../include/items.php:4560 +#: mod/contacts.php:459 mod/tagrm.php:11 mod/tagrm.php:94 mod/follow.php:121 +#: mod/videos.php:131 mod/message.php:219 mod/fbrowser.php:93 +#: mod/fbrowser.php:128 mod/settings.php:660 mod/settings.php:686 +#: mod/dfrn_request.php:871 mod/suggest.php:32 mod/editpost.php:148 +#: mod/photos.php:247 mod/photos.php:336 include/conversation.php:1220 +#: include/items.php:4931 msgid "Cancel" msgstr "Отмена" -#: ../../mod/contacts.php:352 +#: mod/contacts.php:471 msgid "Contact has been removed." msgstr "Контакт удален." -#: ../../mod/contacts.php:390 +#: mod/contacts.php:512 #, php-format msgid "You are mutual friends with %s" msgstr "У Вас взаимная дружба с %s" -#: ../../mod/contacts.php:394 +#: mod/contacts.php:516 #, php-format msgid "You are sharing with %s" msgstr "Вы делитесь с %s" -#: ../../mod/contacts.php:399 +#: mod/contacts.php:521 #, php-format msgid "%s is sharing with you" msgstr "%s делитса с Вами" -#: ../../mod/contacts.php:416 +#: mod/contacts.php:541 msgid "Private communications are not available for this contact." msgstr "Личные коммуникации недоступны для этого контакта." -#: ../../mod/contacts.php:419 ../../mod/admin.php:569 +#: mod/contacts.php:544 mod/admin.php:822 msgid "Never" msgstr "Никогда" -#: ../../mod/contacts.php:423 +#: mod/contacts.php:548 msgid "(Update was successful)" msgstr "(Обновление было успешно)" -#: ../../mod/contacts.php:423 +#: mod/contacts.php:548 msgid "(Update was not successful)" msgstr "(Обновление не удалось)" -#: ../../mod/contacts.php:425 +#: mod/contacts.php:550 msgid "Suggest friends" msgstr "Предложить друзей" -#: ../../mod/contacts.php:429 +#: mod/contacts.php:554 #, php-format msgid "Network type: %s" msgstr "Сеть: %s" -#: ../../mod/contacts.php:432 ../../include/contact_widgets.php:200 -#, php-format -msgid "%d contact in common" -msgid_plural "%d contacts in common" -msgstr[0] "%d Контакт" -msgstr[1] "%d Контактов" -msgstr[2] "%d Контактов" - -#: ../../mod/contacts.php:437 -msgid "View all contacts" -msgstr "Показать все контакты" - -#: ../../mod/contacts.php:442 ../../mod/contacts.php:501 -#: ../../mod/contacts.php:714 ../../mod/admin.php:1009 -msgid "Unblock" -msgstr "Разблокировать" - -#: ../../mod/contacts.php:442 ../../mod/contacts.php:501 -#: ../../mod/contacts.php:714 ../../mod/admin.php:1008 -msgid "Block" -msgstr "Заблокировать" - -#: ../../mod/contacts.php:445 -msgid "Toggle Blocked status" -msgstr "Изменить статус блокированности (заблокировать/разблокировать)" - -#: ../../mod/contacts.php:448 ../../mod/contacts.php:502 -#: ../../mod/contacts.php:715 -msgid "Unignore" -msgstr "Не игнорировать" - -#: ../../mod/contacts.php:448 ../../mod/contacts.php:502 -#: ../../mod/contacts.php:715 ../../mod/notifications.php:51 -#: ../../mod/notifications.php:164 ../../mod/notifications.php:210 -msgid "Ignore" -msgstr "Игнорировать" - -#: ../../mod/contacts.php:451 -msgid "Toggle Ignored status" -msgstr "Изменить статус игнорирования" - -#: ../../mod/contacts.php:455 ../../mod/contacts.php:716 -msgid "Unarchive" -msgstr "Разархивировать" - -#: ../../mod/contacts.php:455 ../../mod/contacts.php:716 -msgid "Archive" -msgstr "Архивировать" - -#: ../../mod/contacts.php:458 -msgid "Toggle Archive status" -msgstr "Сменить статус архивации (архивирова/не архивировать)" - -#: ../../mod/contacts.php:461 -msgid "Repair" -msgstr "Восстановить" - -#: ../../mod/contacts.php:464 -msgid "Advanced Contact Settings" -msgstr "Дополнительные Настройки Контакта" - -#: ../../mod/contacts.php:470 +#: mod/contacts.php:567 msgid "Communications lost with this contact!" msgstr "Связь с контактом утеряна!" -#: ../../mod/contacts.php:473 -msgid "Contact Editor" -msgstr "Редактор контакта" +#: mod/contacts.php:570 +msgid "Fetch further information for feeds" +msgstr "" -#: ../../mod/contacts.php:475 ../../mod/manage.php:110 -#: ../../mod/fsuggest.php:107 ../../mod/message.php:335 -#: ../../mod/message.php:564 ../../mod/crepair.php:186 -#: ../../mod/events.php:478 ../../mod/content.php:710 -#: ../../mod/install.php:248 ../../mod/install.php:286 ../../mod/mood.php:137 -#: ../../mod/profiles.php:686 ../../mod/localtime.php:45 -#: ../../mod/poke.php:199 ../../mod/invite.php:140 ../../mod/photos.php:1084 -#: ../../mod/photos.php:1203 ../../mod/photos.php:1514 -#: ../../mod/photos.php:1565 ../../mod/photos.php:1609 -#: ../../mod/photos.php:1697 ../../object/Item.php:678 -#: ../../view/theme/cleanzero/config.php:80 -#: ../../view/theme/dispy/config.php:70 ../../view/theme/quattro/config.php:64 -#: ../../view/theme/diabook/config.php:148 -#: ../../view/theme/diabook/theme.php:633 ../../view/theme/vier/config.php:53 -#: ../../view/theme/duepuntozero/config.php:59 +#: mod/contacts.php:571 mod/admin.php:831 +msgid "Disabled" +msgstr "Отключенный" + +#: mod/contacts.php:571 +msgid "Fetch information" +msgstr "" + +#: mod/contacts.php:571 +msgid "Fetch information and keywords" +msgstr "" + +#: mod/contacts.php:587 mod/manage.php:143 mod/fsuggest.php:107 +#: mod/message.php:342 mod/message.php:525 mod/crepair.php:196 +#: mod/events.php:574 mod/content.php:712 mod/install.php:261 +#: mod/install.php:299 mod/mood.php:137 mod/profiles.php:696 +#: mod/localtime.php:45 mod/poke.php:198 mod/invite.php:140 +#: mod/photos.php:1137 mod/photos.php:1261 mod/photos.php:1579 +#: mod/photos.php:1630 mod/photos.php:1678 mod/photos.php:1766 +#: object/Item.php:710 view/theme/cleanzero/config.php:80 +#: view/theme/dispy/config.php:70 view/theme/quattro/config.php:64 +#: view/theme/diabook/config.php:148 view/theme/diabook/theme.php:633 +#: view/theme/vier/config.php:107 view/theme/duepuntozero/config.php:59 msgid "Submit" -msgstr "Подтвердить" +msgstr "Добавить" -#: ../../mod/contacts.php:476 +#: mod/contacts.php:588 msgid "Profile Visibility" msgstr "Видимость профиля" -#: ../../mod/contacts.php:477 +#: mod/contacts.php:589 #, php-format msgid "" "Please choose the profile you would like to display to %s when viewing your " "profile securely." -msgstr "Пожалуйста, выберите профиль, который вы хотите отображать %s, когда просмотр вашего профиля безопасен." +msgstr "" +"Пожалуйста, выберите профиль, который вы хотите отображать %s, когда " +"просмотр вашего профиля безопасен." -#: ../../mod/contacts.php:478 +#: mod/contacts.php:590 msgid "Contact Information / Notes" msgstr "Информация о контакте / Заметки" -#: ../../mod/contacts.php:479 +#: mod/contacts.php:591 msgid "Edit contact notes" msgstr "Редактировать заметки контакта" -#: ../../mod/contacts.php:484 ../../mod/contacts.php:679 -#: ../../mod/viewcontacts.php:64 ../../mod/nogroup.php:40 +#: mod/contacts.php:596 mod/contacts.php:952 mod/viewcontacts.php:97 +#: mod/nogroup.php:41 #, php-format msgid "Visit %s's profile [%s]" msgstr "Посетить профиль %s [%s]" -#: ../../mod/contacts.php:485 +#: mod/contacts.php:597 msgid "Block/Unblock contact" msgstr "Блокировать / Разблокировать контакт" -#: ../../mod/contacts.php:486 +#: mod/contacts.php:598 msgid "Ignore contact" msgstr "Игнорировать контакт" -#: ../../mod/contacts.php:487 +#: mod/contacts.php:599 msgid "Repair URL settings" msgstr "Восстановить настройки URL" -#: ../../mod/contacts.php:488 +#: mod/contacts.php:600 msgid "View conversations" msgstr "Просмотр бесед" -#: ../../mod/contacts.php:490 +#: mod/contacts.php:602 msgid "Delete contact" msgstr "Удалить контакт" -#: ../../mod/contacts.php:494 +#: mod/contacts.php:606 msgid "Last update:" msgstr "Последнее обновление: " -#: ../../mod/contacts.php:496 +#: mod/contacts.php:608 msgid "Update public posts" msgstr "Обновить публичные сообщения" -#: ../../mod/contacts.php:498 ../../mod/admin.php:1503 +#: mod/contacts.php:610 msgid "Update now" msgstr "Обновить сейчас" -#: ../../mod/contacts.php:505 +#: mod/contacts.php:612 mod/follow.php:103 mod/dirfind.php:196 +#: mod/allfriends.php:65 mod/match.php:71 mod/suggest.php:82 +#: include/contact_widgets.php:32 include/Contact.php:297 +#: include/conversation.php:924 +msgid "Connect/Follow" +msgstr "Подключиться/Следовать" + +#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 +#: mod/admin.php:1312 +msgid "Unblock" +msgstr "Разблокировать" + +#: mod/contacts.php:615 mod/contacts.php:806 mod/contacts.php:865 +#: mod/admin.php:1311 +msgid "Block" +msgstr "Заблокировать" + +#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 +msgid "Unignore" +msgstr "Не игнорировать" + +#: mod/contacts.php:616 mod/contacts.php:807 mod/contacts.php:872 +#: mod/notifications.php:54 mod/notifications.php:179 +#: mod/notifications.php:259 +msgid "Ignore" +msgstr "Игнорировать" + +#: mod/contacts.php:619 msgid "Currently blocked" msgstr "В настоящее время заблокирован" -#: ../../mod/contacts.php:506 +#: mod/contacts.php:620 msgid "Currently ignored" msgstr "В настоящее время игнорируется" -#: ../../mod/contacts.php:507 +#: mod/contacts.php:621 msgid "Currently archived" msgstr "В данный момент архивирован" -#: ../../mod/contacts.php:508 ../../mod/notifications.php:157 -#: ../../mod/notifications.php:204 +#: mod/contacts.php:622 mod/notifications.php:172 mod/notifications.php:251 msgid "Hide this contact from others" msgstr "Скрыть этот контакт от других" -#: ../../mod/contacts.php:508 +#: mod/contacts.php:622 msgid "" "Replies/likes to your public posts may still be visible" msgstr "Ответы/лайки ваших публичных сообщений будут видимы." -#: ../../mod/contacts.php:509 +#: mod/contacts.php:623 msgid "Notification for new posts" msgstr "" -#: ../../mod/contacts.php:509 +#: mod/contacts.php:623 msgid "Send a notification of every new post of this contact" msgstr "" -#: ../../mod/contacts.php:510 -msgid "Fetch further information for feeds" -msgstr "" - -#: ../../mod/contacts.php:511 -msgid "Disabled" -msgstr "" - -#: ../../mod/contacts.php:511 -msgid "Fetch information" -msgstr "" - -#: ../../mod/contacts.php:511 -msgid "Fetch information and keywords" -msgstr "" - -#: ../../mod/contacts.php:513 +#: mod/contacts.php:626 msgid "Blacklisted keywords" msgstr "" -#: ../../mod/contacts.php:513 +#: mod/contacts.php:626 msgid "" "Comma separated list of keywords that should not be converted to hashtags, " "when \"Fetch information and keywords\" is selected" msgstr "" -#: ../../mod/contacts.php:564 +#: mod/contacts.php:633 mod/follow.php:126 mod/notifications.php:255 +msgid "Profile URL" +msgstr "URL профиля" + +#: mod/contacts.php:636 mod/notifications.php:244 mod/events.php:566 +#: mod/directory.php:145 include/identity.php:308 include/bb2diaspora.php:170 +#: include/event.php:36 include/event.php:60 +msgid "Location:" +msgstr "Откуда:" + +#: mod/contacts.php:638 mod/notifications.php:246 mod/directory.php:153 +#: include/identity.php:317 include/identity.php:631 +msgid "About:" +msgstr "О себе:" + +#: mod/contacts.php:640 mod/follow.php:134 mod/notifications.php:248 +#: include/identity.php:625 +msgid "Tags:" +msgstr "Ключевые слова: " + +#: mod/contacts.php:685 msgid "Suggestions" msgstr "Предложения" -#: ../../mod/contacts.php:567 +#: mod/contacts.php:688 msgid "Suggest potential friends" msgstr "Предложить потенциального знакомого" -#: ../../mod/contacts.php:570 ../../mod/group.php:194 +#: mod/contacts.php:693 mod/group.php:192 msgid "All Contacts" msgstr "Все контакты" -#: ../../mod/contacts.php:573 +#: mod/contacts.php:696 msgid "Show all contacts" msgstr "Показать все контакты" -#: ../../mod/contacts.php:576 +#: mod/contacts.php:701 msgid "Unblocked" msgstr "Не блокирован" -#: ../../mod/contacts.php:579 +#: mod/contacts.php:704 msgid "Only show unblocked contacts" msgstr "Показать только не блокированные контакты" -#: ../../mod/contacts.php:583 +#: mod/contacts.php:710 msgid "Blocked" msgstr "Заблокирован" -#: ../../mod/contacts.php:586 +#: mod/contacts.php:713 msgid "Only show blocked contacts" msgstr "Показать только блокированные контакты" -#: ../../mod/contacts.php:590 +#: mod/contacts.php:719 msgid "Ignored" msgstr "Игнорирован" -#: ../../mod/contacts.php:593 +#: mod/contacts.php:722 msgid "Only show ignored contacts" msgstr "Показать только игнорируемые контакты" -#: ../../mod/contacts.php:597 +#: mod/contacts.php:728 msgid "Archived" msgstr "Архивированные" -#: ../../mod/contacts.php:600 +#: mod/contacts.php:731 msgid "Only show archived contacts" msgstr "Показывать только архивные контакты" -#: ../../mod/contacts.php:604 +#: mod/contacts.php:737 msgid "Hidden" msgstr "Скрытые" -#: ../../mod/contacts.php:607 +#: mod/contacts.php:740 msgid "Only show hidden contacts" msgstr "Показывать только скрытые контакты" -#: ../../mod/contacts.php:655 -msgid "Mutual Friendship" -msgstr "Взаимная дружба" - -#: ../../mod/contacts.php:659 -msgid "is a fan of yours" -msgstr "является вашим поклонником" - -#: ../../mod/contacts.php:663 -msgid "you are a fan of" -msgstr "Вы - поклонник" - -#: ../../mod/contacts.php:680 ../../mod/nogroup.php:41 -msgid "Edit contact" -msgstr "Редактировать контакт" - -#: ../../mod/contacts.php:702 ../../include/nav.php:177 -#: ../../view/theme/diabook/theme.php:125 +#: mod/contacts.php:793 mod/contacts.php:841 mod/viewcontacts.php:116 +#: include/identity.php:741 include/identity.php:744 include/text.php:1012 +#: include/nav.php:123 include/nav.php:187 view/theme/diabook/theme.php:125 msgid "Contacts" msgstr "Контакты" -#: ../../mod/contacts.php:706 +#: mod/contacts.php:797 msgid "Search your contacts" msgstr "Поиск ваших контактов" -#: ../../mod/contacts.php:707 ../../mod/directory.php:61 +#: mod/contacts.php:798 msgid "Finding: " msgstr "Результат поиска: " -#: ../../mod/contacts.php:708 ../../mod/directory.php:63 -#: ../../include/contact_widgets.php:34 +#: mod/contacts.php:799 mod/directory.php:210 include/contact_widgets.php:34 msgid "Find" msgstr "Найти" -#: ../../mod/contacts.php:713 ../../mod/settings.php:132 -#: ../../mod/settings.php:640 +#: mod/contacts.php:805 mod/settings.php:156 mod/settings.php:685 msgid "Update" msgstr "Обновление" -#: ../../mod/contacts.php:717 ../../mod/group.php:171 ../../mod/admin.php:1007 -#: ../../mod/content.php:438 ../../mod/content.php:741 -#: ../../mod/settings.php:677 ../../mod/photos.php:1654 -#: ../../object/Item.php:130 ../../include/conversation.php:614 +#: mod/contacts.php:808 mod/contacts.php:879 +msgid "Archive" +msgstr "Архивировать" + +#: mod/contacts.php:808 mod/contacts.php:879 +msgid "Unarchive" +msgstr "Разархивировать" + +#: mod/contacts.php:809 mod/group.php:171 mod/admin.php:1310 +#: mod/content.php:440 mod/content.php:743 mod/settings.php:722 +#: mod/photos.php:1723 object/Item.php:134 include/conversation.php:635 msgid "Delete" msgstr "Удалить" -#: ../../mod/hcard.php:10 +#: mod/contacts.php:822 include/identity.php:686 include/nav.php:75 +msgid "Status" +msgstr "Посты" + +#: mod/contacts.php:825 mod/follow.php:143 include/identity.php:689 +msgid "Status Messages and Posts" +msgstr "Ваши посты" + +#: mod/contacts.php:830 mod/profperm.php:104 mod/newmember.php:32 +#: include/identity.php:579 include/identity.php:665 include/identity.php:694 +#: include/nav.php:76 view/theme/diabook/theme.php:124 +msgid "Profile" +msgstr "Информация" + +#: mod/contacts.php:833 include/identity.php:697 +msgid "Profile Details" +msgstr "Информация о вас" + +#: mod/contacts.php:844 +msgid "View all contacts" +msgstr "Показать все контакты" + +#: mod/contacts.php:850 mod/common.php:134 +msgid "Common Friends" +msgstr "Общие друзья" + +#: mod/contacts.php:853 +msgid "View all common friends" +msgstr "" + +#: mod/contacts.php:857 +msgid "Repair" +msgstr "Восстановить" + +#: mod/contacts.php:860 +msgid "Advanced Contact Settings" +msgstr "Дополнительные Настройки Контакта" + +#: mod/contacts.php:868 +msgid "Toggle Blocked status" +msgstr "Изменить статус блокированности (заблокировать/разблокировать)" + +#: mod/contacts.php:875 +msgid "Toggle Ignored status" +msgstr "Изменить статус игнорирования" + +#: mod/contacts.php:882 +msgid "Toggle Archive status" +msgstr "Сменить статус архивации (архивирова/не архивировать)" + +#: mod/contacts.php:924 +msgid "Mutual Friendship" +msgstr "Взаимная дружба" + +#: mod/contacts.php:928 +msgid "is a fan of yours" +msgstr "является вашим поклонником" + +#: mod/contacts.php:932 +msgid "you are a fan of" +msgstr "Вы - поклонник" + +#: mod/contacts.php:953 mod/nogroup.php:42 +msgid "Edit contact" +msgstr "Редактировать контакт" + +#: mod/hcard.php:10 msgid "No profile" msgstr "Нет профиля" -#: ../../mod/manage.php:106 +#: mod/manage.php:139 msgid "Manage Identities and/or Pages" msgstr "Управление идентификацией и / или страницами" -#: ../../mod/manage.php:107 +#: mod/manage.php:140 msgid "" "Toggle between different identities or community/group pages which share " "your account details or which you have been granted \"manage\" permissions" msgstr "" -#: ../../mod/manage.php:108 +#: mod/manage.php:141 msgid "Select an identity to manage: " msgstr "Выберите идентификацию для управления: " -#: ../../mod/oexchange.php:25 +#: mod/oexchange.php:25 msgid "Post successful." msgstr "Успешно добавлено." -#: ../../mod/profperm.php:19 ../../mod/group.php:72 ../../index.php:368 +#: mod/profperm.php:19 mod/group.php:72 index.php:382 msgid "Permission denied" msgstr "Доступ запрещен" -#: ../../mod/profperm.php:25 ../../mod/profperm.php:55 +#: mod/profperm.php:25 mod/profperm.php:56 msgid "Invalid profile identifier." msgstr "Недопустимый идентификатор профиля." -#: ../../mod/profperm.php:101 +#: mod/profperm.php:102 msgid "Profile Visibility Editor" msgstr "Редактор видимости профиля" -#: ../../mod/profperm.php:103 ../../mod/newmember.php:32 ../../boot.php:2119 -#: ../../include/profile_advanced.php:7 ../../include/profile_advanced.php:87 -#: ../../include/nav.php:77 ../../view/theme/diabook/theme.php:124 -msgid "Profile" -msgstr "Профиль" - -#: ../../mod/profperm.php:105 ../../mod/group.php:224 +#: mod/profperm.php:106 mod/group.php:223 msgid "Click on a contact to add or remove." msgstr "Нажмите на контакт, чтобы добавить или удалить." -#: ../../mod/profperm.php:114 +#: mod/profperm.php:115 msgid "Visible To" msgstr "Видимый для" -#: ../../mod/profperm.php:130 +#: mod/profperm.php:131 msgid "All Contacts (with secure profile access)" msgstr "Все контакты (с безопасным доступом к профилю)" -#: ../../mod/display.php:82 ../../mod/display.php:284 -#: ../../mod/display.php:503 ../../mod/viewsrc.php:15 ../../mod/admin.php:169 -#: ../../mod/admin.php:1052 ../../mod/admin.php:1265 ../../mod/notice.php:15 -#: ../../include/items.php:4516 +#: mod/display.php:82 mod/display.php:291 mod/display.php:513 +#: mod/viewsrc.php:15 mod/admin.php:234 mod/admin.php:1365 mod/admin.php:1599 +#: mod/notice.php:15 include/items.php:4887 msgid "Item not found." msgstr "Пункт не найден." -#: ../../mod/display.php:212 ../../mod/videos.php:115 -#: ../../mod/viewcontacts.php:19 ../../mod/community.php:18 -#: ../../mod/dfrn_request.php:762 ../../mod/search.php:89 -#: ../../mod/directory.php:33 ../../mod/photos.php:920 +#: mod/display.php:220 mod/videos.php:197 mod/viewcontacts.php:35 +#: mod/community.php:22 mod/dfrn_request.php:786 mod/search.php:93 +#: mod/search.php:99 mod/directory.php:37 mod/photos.php:976 msgid "Public access denied." msgstr "Свободный доступ закрыт." -#: ../../mod/display.php:332 ../../mod/profile.php:155 +#: mod/display.php:339 mod/profile.php:155 msgid "Access to this profile has been restricted." msgstr "Доступ к этому профилю ограничен." -#: ../../mod/display.php:496 +#: mod/display.php:506 msgid "Item has been removed." msgstr "Пункт был удален." -#: ../../mod/newmember.php:6 +#: mod/newmember.php:6 msgid "Welcome to Friendica" msgstr "Добро пожаловать в Friendica" -#: ../../mod/newmember.php:8 +#: mod/newmember.php:8 msgid "New Member Checklist" msgstr "Новый контрольный список участников" -#: ../../mod/newmember.php:12 +#: mod/newmember.php:12 msgid "" "We would like to offer some tips and links to help make your experience " "enjoyable. Click any item to visit the relevant page. A link to this page " "will be visible from your home page for two weeks after your initial " "registration and then will quietly disappear." -msgstr "Мы хотели бы предложить некоторые советы и ссылки, помогающие сделать вашу работу приятнее. Нажмите на любой элемент, чтобы посетить соответствующую страницу. Ссылка на эту страницу будет видна на вашей домашней странице в течение двух недель после первоначальной регистрации, а затем она исчезнет." +msgstr "" +"Мы хотели бы предложить некоторые советы и ссылки, помогающие сделать вашу " +"работу приятнее. Нажмите на любой элемент, чтобы посетить соответствующую " +"страницу. Ссылка на эту страницу будет видна на вашей домашней странице в " +"течение двух недель после первоначальной регистрации, а затем она исчезнет." -#: ../../mod/newmember.php:14 +#: mod/newmember.php:14 msgid "Getting Started" msgstr "Начало работы" -#: ../../mod/newmember.php:18 +#: mod/newmember.php:18 msgid "Friendica Walk-Through" msgstr "Friendica тур" -#: ../../mod/newmember.php:18 +#: mod/newmember.php:18 msgid "" "On your Quick Start page - find a brief introduction to your " -"profile and network tabs, make some new connections, and find some groups to" -" join." -msgstr "На вашей странице Быстрый старт - можно найти краткое введение в ваш профиль и сетевые закладки, создать новые связи, и найти группы, чтобы присоединиться к ним." +"profile and network tabs, make some new connections, and find some groups to " +"join." +msgstr "" +"На вашей странице Быстрый старт - можно найти краткое введение в " +"ваш профиль и сетевые закладки, создать новые связи, и найти группы, чтобы " +"присоединиться к ним." -#: ../../mod/newmember.php:22 ../../mod/admin.php:1104 -#: ../../mod/admin.php:1325 ../../mod/settings.php:85 -#: ../../include/nav.php:172 ../../view/theme/diabook/theme.php:544 -#: ../../view/theme/diabook/theme.php:648 +#: mod/newmember.php:22 mod/admin.php:1418 mod/admin.php:1676 +#: mod/settings.php:109 include/nav.php:182 view/theme/diabook/theme.php:544 +#: view/theme/diabook/theme.php:648 msgid "Settings" msgstr "Настройки" -#: ../../mod/newmember.php:26 +#: mod/newmember.php:26 msgid "Go to Your Settings" msgstr "Перейти к вашим настройкам" -#: ../../mod/newmember.php:26 +#: mod/newmember.php:26 msgid "" "On your Settings page - change your initial password. Also make a " "note of your Identity Address. This looks just like an email address - and " "will be useful in making friends on the free social web." -msgstr "На вашей странице Настройки - вы можете изменить свой первоначальный пароль. Также обратите внимание на ваш личный адрес. Он выглядит так же, как адрес электронной почты - и будет полезен для поиска друзей в свободной социальной сети." +msgstr "" +"На вашей странице Настройки - вы можете изменить свой " +"первоначальный пароль. Также обратите внимание на ваш личный адрес. Он " +"выглядит так же, как адрес электронной почты - и будет полезен для поиска " +"друзей в свободной социальной сети." -#: ../../mod/newmember.php:28 +#: mod/newmember.php:28 msgid "" -"Review the other settings, particularly the privacy settings. An unpublished" -" directory listing is like having an unlisted phone number. In general, you " +"Review the other settings, particularly the privacy settings. An unpublished " +"directory listing is like having an unlisted phone number. In general, you " "should probably publish your listing - unless all of your friends and " "potential friends know exactly how to find you." -msgstr "Просмотрите другие установки, в частности, параметры конфиденциальности. Неопубликованные пункты каталога с частными номерами телефона. В общем, вам, вероятно, следует опубликовать свою информацию - если все ваши друзья и потенциальные друзья точно знают, как вас найти." +msgstr "" +"Просмотрите другие установки, в частности, параметры конфиденциальности. " +"Неопубликованные пункты каталога с частными номерами телефона. В общем, вам, " +"вероятно, следует опубликовать свою информацию - если все ваши друзья и " +"потенциальные друзья точно знают, как вас найти." -#: ../../mod/newmember.php:36 ../../mod/profile_photo.php:244 -#: ../../mod/profiles.php:699 +#: mod/newmember.php:36 mod/profile_photo.php:250 mod/profiles.php:709 msgid "Upload Profile Photo" msgstr "Загрузить фото профиля" -#: ../../mod/newmember.php:36 +#: mod/newmember.php:36 msgid "" "Upload a profile photo if you have not done so already. Studies have shown " -"that people with real photos of themselves are ten times more likely to make" -" friends than people who do not." -msgstr "Загрузите фотографию профиля, если вы еще не сделали это. Исследования показали, что люди с реальными фотографиями имеют в десять раз больше шансов подружиться, чем люди, которые этого не делают." +"that people with real photos of themselves are ten times more likely to make " +"friends than people who do not." +msgstr "" +"Загрузите фотографию профиля, если вы еще не сделали это. Исследования " +"показали, что люди с реальными фотографиями имеют в десять раз больше шансов " +"подружиться, чем люди, которые этого не делают." -#: ../../mod/newmember.php:38 +#: mod/newmember.php:38 msgid "Edit Your Profile" msgstr "Редактировать профиль" -#: ../../mod/newmember.php:38 +#: mod/newmember.php:38 msgid "" "Edit your default profile to your liking. Review the " -"settings for hiding your list of friends and hiding the profile from unknown" -" visitors." -msgstr "Отредактируйте профиль по умолчанию на свой ​​вкус. Просмотрите установки для сокрытия вашего списка друзей и сокрытия профиля от неизвестных посетителей." +"settings for hiding your list of friends and hiding the profile from unknown " +"visitors." +msgstr "" +"Отредактируйте профиль по умолчанию на свой ​​вкус. " +"Просмотрите установки для сокрытия вашего списка друзей и сокрытия профиля " +"от неизвестных посетителей." -#: ../../mod/newmember.php:40 +#: mod/newmember.php:40 msgid "Profile Keywords" msgstr "Ключевые слова профиля" -#: ../../mod/newmember.php:40 +#: mod/newmember.php:40 msgid "" "Set some public keywords for your default profile which describe your " "interests. We may be able to find other people with similar interests and " "suggest friendships." -msgstr "Установите некоторые публичные ключевые слова для вашего профиля по умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти других людей со схожими интересами и предложить дружбу." +msgstr "" +"Установите некоторые публичные ключевые слова для вашего профиля по " +"умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти " +"других людей со схожими интересами и предложить дружбу." -#: ../../mod/newmember.php:44 +#: mod/newmember.php:44 msgid "Connecting" msgstr "Подключение" -#: ../../mod/newmember.php:49 ../../mod/newmember.php:51 -#: ../../include/contact_selectors.php:81 -msgid "Facebook" -msgstr "Facebook" - -#: ../../mod/newmember.php:49 -msgid "" -"Authorise the Facebook Connector if you currently have a Facebook account " -"and we will (optionally) import all your Facebook friends and conversations." -msgstr "Авторизуйте Facebook Connector , если у вас уже есть аккаунт на Facebook, и мы (по желанию) импортируем всех ваших друзей и беседы с Facebook." - -#: ../../mod/newmember.php:51 -msgid "" -"If this is your own personal server, installing the Facebook addon " -"may ease your transition to the free social web." -msgstr "Если это ваш личный сервер, установите дополнение Facebook, это может облегчить ваш переход на свободную социальную сеть." - -#: ../../mod/newmember.php:56 +#: mod/newmember.php:51 msgid "Importing Emails" msgstr "Импортирование Email-ов" -#: ../../mod/newmember.php:56 +#: mod/newmember.php:51 msgid "" "Enter your email access information on your Connector Settings page if you " "wish to import and interact with friends or mailing lists from your email " "INBOX" -msgstr "Введите информацию о доступе к вашему email на странице настроек вашего коннектора, если вы хотите импортировать, и общаться с друзьями или получать рассылки на ваш ящик электронной почты" +msgstr "" +"Введите информацию о доступе к вашему email на странице настроек вашего " +"коннектора, если вы хотите импортировать, и общаться с друзьями или получать " +"рассылки на ваш ящик электронной почты" -#: ../../mod/newmember.php:58 +#: mod/newmember.php:53 msgid "Go to Your Contacts Page" msgstr "Перейти на страницу ваших контактов" -#: ../../mod/newmember.php:58 +#: mod/newmember.php:53 msgid "" "Your Contacts page is your gateway to managing friendships and connecting " "with friends on other networks. Typically you enter their address or site " "URL in the Add New Contact dialog." -msgstr "Ваша страница контактов - это ваш шлюз к управлению дружбой и общением с друзьями в других сетях. Обычно вы вводите свой ​​адрес или адрес сайта в диалог Добавить новый контакт." +msgstr "" +"Ваша страница контактов - это ваш шлюз к управлению дружбой и общением с " +"друзьями в других сетях. Обычно вы вводите свой ​​адрес или адрес сайта в " +"диалог Добавить новый контакт." -#: ../../mod/newmember.php:60 +#: mod/newmember.php:55 msgid "Go to Your Site's Directory" msgstr "Перейти в каталог вашего сайта" -#: ../../mod/newmember.php:60 +#: mod/newmember.php:55 msgid "" "The Directory page lets you find other people in this network or other " "federated sites. Look for a Connect or Follow link on " "their profile page. Provide your own Identity Address if requested." -msgstr "На странице каталога вы можете найти других людей в этой сети или на других похожих сайтах. Ищите ссылки Подключить или Следовать на страницах их профилей. Укажите свой собственный адрес идентификации, если требуется." +msgstr "" +"На странице каталога вы можете найти других людей в этой сети или на других " +"похожих сайтах. Ищите ссылки Подключить или Следовать на " +"страницах их профилей. Укажите свой собственный адрес идентификации, если " +"требуется." -#: ../../mod/newmember.php:62 +#: mod/newmember.php:57 msgid "Finding New People" msgstr "Поиск людей" -#: ../../mod/newmember.php:62 +#: mod/newmember.php:57 msgid "" "On the side panel of the Contacts page are several tools to find new " "friends. We can match people by interest, look up people by name or " -"interest, and provide suggestions based on network relationships. On a brand" -" new site, friend suggestions will usually begin to be populated within 24 " +"interest, and provide suggestions based on network relationships. On a brand " +"new site, friend suggestions will usually begin to be populated within 24 " "hours." -msgstr "На боковой панели страницы Контакты есть несколько инструментов, чтобы найти новых друзей. Мы можем искать по соответствию интересам, посмотреть людей по имени или интересам, и внести предложения на основе сетевых отношений. На новом сайте, предложения дружбы, как правило, начинают заполняться в течение 24 часов." +msgstr "" +"На боковой панели страницы Контакты есть несколько инструментов, чтобы найти " +"новых друзей. Мы можем искать по соответствию интересам, посмотреть людей " +"по имени или интересам, и внести предложения на основе сетевых отношений. На " +"новом сайте, предложения дружбы, как правило, начинают заполняться в течение " +"24 часов." -#: ../../mod/newmember.php:66 ../../include/group.php:270 +#: mod/newmember.php:61 include/group.php:283 msgid "Groups" msgstr "Группы" -#: ../../mod/newmember.php:70 +#: mod/newmember.php:65 msgid "Group Your Contacts" msgstr "Группа \"ваши контакты\"" -#: ../../mod/newmember.php:70 +#: mod/newmember.php:65 msgid "" "Once you have made some friends, organize them into private conversation " -"groups from the sidebar of your Contacts page and then you can interact with" -" each group privately on your Network page." -msgstr "После того, как вы найдете несколько друзей, организуйте их в группы частных бесед в боковой панели на странице Контакты, а затем вы можете взаимодействовать с каждой группой приватно или на вашей странице Сеть." +"groups from the sidebar of your Contacts page and then you can interact with " +"each group privately on your Network page." +msgstr "" +"После того, как вы найдете несколько друзей, организуйте их в группы частных " +"бесед в боковой панели на странице Контакты, а затем вы можете " +"взаимодействовать с каждой группой приватно или на вашей странице Сеть." -#: ../../mod/newmember.php:73 +#: mod/newmember.php:68 msgid "Why Aren't My Posts Public?" msgstr "Почему мои посты не публичные?" -#: ../../mod/newmember.php:73 +#: mod/newmember.php:68 msgid "" -"Friendica respects your privacy. By default, your posts will only show up to" -" people you've added as friends. For more information, see the help section " +"Friendica respects your privacy. By default, your posts will only show up to " +"people you've added as friends. For more information, see the help section " "from the link above." -msgstr "Friendica уважает вашу приватность. По умолчанию, ваши сообщения будут показываться только для людей, которых вы добавили в список друзей. Для получения дополнительной информации см. раздел справки по ссылке выше." +msgstr "" +"Friendica уважает вашу приватность. По умолчанию, ваши сообщения будут " +"показываться только для людей, которых вы добавили в список друзей. Для " +"получения дополнительной информации см. раздел справки по ссылке выше." -#: ../../mod/newmember.php:78 +#: mod/newmember.php:73 msgid "Getting Help" msgstr "Получить помощь" -#: ../../mod/newmember.php:82 +#: mod/newmember.php:77 msgid "Go to the Help Section" msgstr "Перейти в раздел справки" -#: ../../mod/newmember.php:82 +#: mod/newmember.php:77 msgid "" -"Our help pages may be consulted for detail on other program" -" features and resources." -msgstr "Наши страницы помощи могут проконсультировать о подробностях и возможностях программы и ресурса." +"Our help pages may be consulted for detail on other program " +"features and resources." +msgstr "" +"Наши страницы помощи могут проконсультировать о " +"подробностях и возможностях программы и ресурса." -#: ../../mod/openid.php:24 +#: mod/openid.php:24 msgid "OpenID protocol error. No ID returned." msgstr "Ошибка протокола OpenID. Не возвращён ID." -#: ../../mod/openid.php:53 +#: mod/openid.php:53 msgid "" "Account not found and OpenID registration is not permitted on this site." msgstr "Аккаунт не найден и OpenID регистрация не допускается на этом сайте." -#: ../../mod/openid.php:93 ../../include/auth.php:112 -#: ../../include/auth.php:175 +#: mod/openid.php:93 include/auth.php:118 include/auth.php:181 msgid "Login failed." msgstr "Войти не удалось." -#: ../../mod/profile_photo.php:44 +#: mod/profile_photo.php:44 msgid "Image uploaded but image cropping failed." msgstr "Изображение загружено, но обрезка изображения не удалась." -#: ../../mod/profile_photo.php:74 ../../mod/profile_photo.php:81 -#: ../../mod/profile_photo.php:88 ../../mod/profile_photo.php:204 -#: ../../mod/profile_photo.php:296 ../../mod/profile_photo.php:305 -#: ../../mod/photos.php:155 ../../mod/photos.php:731 ../../mod/photos.php:1187 -#: ../../mod/photos.php:1210 ../../include/user.php:335 -#: ../../include/user.php:342 ../../include/user.php:349 -#: ../../view/theme/diabook/theme.php:500 +#: mod/profile_photo.php:74 mod/profile_photo.php:81 mod/profile_photo.php:88 +#: mod/profile_photo.php:210 mod/profile_photo.php:302 +#: mod/profile_photo.php:311 mod/photos.php:78 mod/photos.php:192 +#: mod/photos.php:775 mod/photos.php:1245 mod/photos.php:1268 +#: mod/photos.php:1862 include/user.php:345 include/user.php:352 +#: include/user.php:359 view/theme/diabook/theme.php:500 msgid "Profile Photos" msgstr "Фотографии профиля" -#: ../../mod/profile_photo.php:77 ../../mod/profile_photo.php:84 -#: ../../mod/profile_photo.php:91 ../../mod/profile_photo.php:308 +#: mod/profile_photo.php:77 mod/profile_photo.php:84 mod/profile_photo.php:91 +#: mod/profile_photo.php:314 #, php-format msgid "Image size reduction [%s] failed." msgstr "Уменьшение размера изображения [%s] не удалось." -#: ../../mod/profile_photo.php:118 +#: mod/profile_photo.php:124 msgid "" "Shift-reload the page or clear browser cache if the new photo does not " "display immediately." -msgstr "Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть свое новое фото немедленно." +msgstr "" +"Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть " +"свое новое фото немедленно." -#: ../../mod/profile_photo.php:128 +#: mod/profile_photo.php:134 msgid "Unable to process image" msgstr "Не удается обработать изображение" -#: ../../mod/profile_photo.php:144 ../../mod/wall_upload.php:122 +#: mod/profile_photo.php:150 mod/wall_upload.php:151 mod/photos.php:811 #, php-format -msgid "Image exceeds size limit of %d" -msgstr "Изображение превышает предельный размер %d" +msgid "Image exceeds size limit of %s" +msgstr "" -#: ../../mod/profile_photo.php:153 ../../mod/wall_upload.php:144 -#: ../../mod/photos.php:807 +#: mod/profile_photo.php:159 mod/wall_upload.php:183 mod/photos.php:851 msgid "Unable to process image." msgstr "Невозможно обработать фото." -#: ../../mod/profile_photo.php:242 +#: mod/profile_photo.php:248 msgid "Upload File:" msgstr "Загрузить файл:" -#: ../../mod/profile_photo.php:243 +#: mod/profile_photo.php:249 msgid "Select a profile:" msgstr "Выбрать этот профиль:" -#: ../../mod/profile_photo.php:245 +#: mod/profile_photo.php:251 msgid "Upload" msgstr "Загрузить" -#: ../../mod/profile_photo.php:248 ../../mod/settings.php:1062 +#: mod/profile_photo.php:254 msgid "or" msgstr "или" -#: ../../mod/profile_photo.php:248 +#: mod/profile_photo.php:254 msgid "skip this step" msgstr "пропустить этот шаг" -#: ../../mod/profile_photo.php:248 +#: mod/profile_photo.php:254 msgid "select a photo from your photo albums" msgstr "выберите фото из ваших фотоальбомов" -#: ../../mod/profile_photo.php:262 +#: mod/profile_photo.php:268 msgid "Crop Image" msgstr "Обрезать изображение" -#: ../../mod/profile_photo.php:263 +#: mod/profile_photo.php:269 msgid "Please adjust the image cropping for optimum viewing." msgstr "Пожалуйста, настройте обрезку изображения для оптимального просмотра." -#: ../../mod/profile_photo.php:265 +#: mod/profile_photo.php:271 msgid "Done Editing" msgstr "Редактирование выполнено" -#: ../../mod/profile_photo.php:299 +#: mod/profile_photo.php:305 msgid "Image uploaded successfully." msgstr "Изображение загружено успешно." -#: ../../mod/profile_photo.php:301 ../../mod/wall_upload.php:172 -#: ../../mod/photos.php:834 +#: mod/profile_photo.php:307 mod/wall_upload.php:216 mod/photos.php:878 msgid "Image upload failed." msgstr "Загрузка фото неудачная." -#: ../../mod/subthread.php:87 ../../mod/tagger.php:62 ../../mod/like.php:149 -#: ../../include/conversation.php:126 ../../include/conversation.php:254 -#: ../../include/text.php:1968 ../../include/diaspora.php:2087 -#: ../../view/theme/diabook/theme.php:471 +#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 +#: include/conversation.php:130 include/conversation.php:266 +#: include/text.php:2000 include/diaspora.php:2169 +#: view/theme/diabook/theme.php:471 msgid "photo" msgstr "фото" -#: ../../mod/subthread.php:87 ../../mod/tagger.php:62 ../../mod/like.php:149 -#: ../../mod/like.php:319 ../../include/conversation.php:121 -#: ../../include/conversation.php:130 ../../include/conversation.php:249 -#: ../../include/conversation.php:258 ../../include/diaspora.php:2087 -#: ../../view/theme/diabook/theme.php:466 -#: ../../view/theme/diabook/theme.php:475 +#: mod/subthread.php:87 mod/tagger.php:62 include/like.php:165 +#: include/like.php:334 include/conversation.php:125 +#: include/conversation.php:134 include/conversation.php:261 +#: include/conversation.php:270 include/diaspora.php:2169 +#: view/theme/diabook/theme.php:466 view/theme/diabook/theme.php:475 msgid "status" msgstr "статус" -#: ../../mod/subthread.php:103 +#: mod/subthread.php:103 #, php-format msgid "%1$s is following %2$s's %3$s" msgstr "" -#: ../../mod/tagrm.php:41 +#: mod/tagrm.php:41 msgid "Tag removed" msgstr "Ключевое слово удалено" -#: ../../mod/tagrm.php:79 +#: mod/tagrm.php:79 msgid "Remove Item Tag" msgstr "Удалить ключевое слово" -#: ../../mod/tagrm.php:81 +#: mod/tagrm.php:81 msgid "Select a tag to remove: " msgstr "Выберите ключевое слово для удаления: " -#: ../../mod/tagrm.php:93 ../../mod/delegate.php:139 +#: mod/tagrm.php:93 mod/delegate.php:139 msgid "Remove" msgstr "Удалить" -#: ../../mod/filer.php:30 ../../include/conversation.php:1006 -#: ../../include/conversation.php:1024 +#: mod/ostatus_subscribe.php:14 +msgid "Subscribing to OStatus contacts" +msgstr "" + +#: mod/ostatus_subscribe.php:25 +msgid "No contact provided." +msgstr "" + +#: mod/ostatus_subscribe.php:30 +msgid "Couldn't fetch information for contact." +msgstr "" + +#: mod/ostatus_subscribe.php:38 +msgid "Couldn't fetch friends for contact." +msgstr "" + +#: mod/ostatus_subscribe.php:51 mod/repair_ostatus.php:44 +msgid "Done" +msgstr "Готово" + +#: mod/ostatus_subscribe.php:65 +msgid "success" +msgstr "удачно" + +#: mod/ostatus_subscribe.php:67 +msgid "failed" +msgstr "неудача" + +#: mod/ostatus_subscribe.php:69 object/Item.php:235 +msgid "ignored" +msgstr "" + +#: mod/ostatus_subscribe.php:73 mod/repair_ostatus.php:50 +msgid "Keep this window open until done." +msgstr "" + +#: mod/filer.php:30 include/conversation.php:1132 +#: include/conversation.php:1150 msgid "Save to Folder:" msgstr "Сохранить в папку:" -#: ../../mod/filer.php:30 +#: mod/filer.php:30 msgid "- select -" msgstr "- выбрать -" -#: ../../mod/filer.php:31 ../../mod/editpost.php:109 ../../mod/notes.php:63 -#: ../../include/text.php:956 +#: mod/filer.php:31 mod/editpost.php:109 mod/notes.php:61 +#: include/text.php:1004 msgid "Save" msgstr "Сохранить" -#: ../../mod/follow.php:27 +#: mod/follow.php:19 mod/dfrn_request.php:870 +msgid "Submit Request" +msgstr "Отправить запрос" + +#: mod/follow.php:30 +msgid "You already added this contact." +msgstr "" + +#: mod/follow.php:39 +msgid "Diaspora support isn't enabled. Contact can't be added." +msgstr "" + +#: mod/follow.php:46 +msgid "OStatus support is disabled. Contact can't be added." +msgstr "" + +#: mod/follow.php:53 +msgid "The network type couldn't be detected. Contact can't be added." +msgstr "" + +#: mod/follow.php:109 mod/dfrn_request.php:856 +msgid "Please answer the following:" +msgstr "Пожалуйста, ответьте следующее:" + +#: mod/follow.php:110 mod/dfrn_request.php:857 +#, php-format +msgid "Does %s know you?" +msgstr "%s знает вас?" + +#: mod/follow.php:110 mod/settings.php:1103 mod/settings.php:1109 +#: mod/settings.php:1117 mod/settings.php:1121 mod/settings.php:1126 +#: mod/settings.php:1132 mod/settings.php:1138 mod/settings.php:1144 +#: mod/settings.php:1170 mod/settings.php:1171 mod/settings.php:1172 +#: mod/settings.php:1173 mod/settings.php:1174 mod/dfrn_request.php:857 +#: mod/register.php:239 mod/profiles.php:658 mod/profiles.php:662 +#: mod/profiles.php:687 mod/api.php:106 +msgid "No" +msgstr "Нет" + +#: mod/follow.php:111 mod/dfrn_request.php:861 +msgid "Add a personal note:" +msgstr "Добавить личную заметку:" + +#: mod/follow.php:117 mod/dfrn_request.php:867 +msgid "Your Identity Address:" +msgstr "Ваш идентификационный адрес:" + +#: mod/follow.php:180 msgid "Contact added" msgstr "Контакт добавлен" -#: ../../mod/item.php:113 +#: mod/item.php:114 msgid "Unable to locate original post." msgstr "Не удалось найти оригинальный пост." -#: ../../mod/item.php:345 +#: mod/item.php:329 msgid "Empty post discarded." msgstr "Пустое сообщение отбрасывается." -#: ../../mod/item.php:484 ../../mod/wall_upload.php:169 -#: ../../mod/wall_upload.php:178 ../../mod/wall_upload.php:185 -#: ../../include/Photo.php:916 ../../include/Photo.php:931 -#: ../../include/Photo.php:938 ../../include/Photo.php:960 -#: ../../include/message.php:144 +#: mod/item.php:467 mod/wall_upload.php:213 mod/wall_upload.php:227 +#: mod/wall_upload.php:234 include/Photo.php:958 include/Photo.php:973 +#: include/Photo.php:980 include/Photo.php:1002 include/message.php:145 msgid "Wall Photos" msgstr "Фото стены" -#: ../../mod/item.php:938 +#: mod/item.php:842 msgid "System error. Post not saved." msgstr "Системная ошибка. Сообщение не сохранено." -#: ../../mod/item.php:964 +#: mod/item.php:971 #, php-format msgid "" -"This message was sent to you by %s, a member of the Friendica social " -"network." -msgstr "Это сообщение было отправлено вам %s, участником социальной сети Friendica." +"This message was sent to you by %s, a member of the Friendica social network." +msgstr "" +"Это сообщение было отправлено вам %s, участником социальной сети Friendica." -#: ../../mod/item.php:966 +#: mod/item.php:973 #, php-format msgid "You may visit them online at %s" msgstr "Вы можете посетить их в онлайне на %s" -#: ../../mod/item.php:967 +#: mod/item.php:974 msgid "" "Please contact the sender by replying to this post if you do not wish to " "receive these messages." -msgstr "Пожалуйста, свяжитесь с отправителем, ответив на это сообщение, если вы не хотите получать эти сообщения." +msgstr "" +"Пожалуйста, свяжитесь с отправителем, ответив на это сообщение, если вы не " +"хотите получать эти сообщения." -#: ../../mod/item.php:971 +#: mod/item.php:978 #, php-format msgid "%s posted an update." msgstr "%s отправил/а/ обновление." -#: ../../mod/group.php:29 +#: mod/group.php:29 msgid "Group created." msgstr "Группа создана." -#: ../../mod/group.php:35 +#: mod/group.php:35 msgid "Could not create group." msgstr "Не удалось создать группу." -#: ../../mod/group.php:47 ../../mod/group.php:140 +#: mod/group.php:47 mod/group.php:140 msgid "Group not found." msgstr "Группа не найдена." -#: ../../mod/group.php:60 +#: mod/group.php:60 msgid "Group name changed." msgstr "Название группы изменено." -#: ../../mod/group.php:87 +#: mod/group.php:87 msgid "Save Group" msgstr "Сохранить группу" -#: ../../mod/group.php:93 +#: mod/group.php:93 msgid "Create a group of contacts/friends." msgstr "Создать группу контактов / друзей." -#: ../../mod/group.php:94 ../../mod/group.php:180 +#: mod/group.php:94 mod/group.php:178 include/group.php:289 msgid "Group Name: " msgstr "Название группы: " -#: ../../mod/group.php:113 +#: mod/group.php:113 msgid "Group removed." msgstr "Группа удалена." -#: ../../mod/group.php:115 +#: mod/group.php:115 msgid "Unable to remove group." msgstr "Не удается удалить группу." -#: ../../mod/group.php:179 +#: mod/group.php:177 msgid "Group Editor" msgstr "Редактор групп" -#: ../../mod/group.php:192 +#: mod/group.php:190 msgid "Members" msgstr "Участники" -#: ../../mod/apps.php:7 ../../index.php:212 +#: mod/group.php:193 mod/network.php:576 mod/content.php:130 +msgid "Group is empty" +msgstr "Группа пуста" + +#: mod/apps.php:7 index.php:226 msgid "You must be logged in to use addons. " msgstr "Вы должны войти в систему, чтобы использовать аддоны." -#: ../../mod/apps.php:11 +#: mod/apps.php:11 msgid "Applications" msgstr "Приложения" -#: ../../mod/apps.php:14 +#: mod/apps.php:14 msgid "No installed applications." msgstr "Нет установленных приложений." -#: ../../mod/dfrn_confirm.php:64 ../../mod/profiles.php:18 -#: ../../mod/profiles.php:133 ../../mod/profiles.php:179 -#: ../../mod/profiles.php:630 +#: mod/dfrn_confirm.php:64 mod/profiles.php:18 mod/profiles.php:133 +#: mod/profiles.php:179 mod/profiles.php:627 msgid "Profile not found." msgstr "Профиль не найден." -#: ../../mod/dfrn_confirm.php:120 ../../mod/fsuggest.php:20 -#: ../../mod/fsuggest.php:92 ../../mod/crepair.php:133 +#: mod/dfrn_confirm.php:120 mod/fsuggest.php:20 mod/fsuggest.php:92 +#: mod/crepair.php:131 msgid "Contact not found." msgstr "Контакт не найден." -#: ../../mod/dfrn_confirm.php:121 +#: mod/dfrn_confirm.php:121 msgid "" -"This may occasionally happen if contact was requested by both persons and it" -" has already been approved." -msgstr "Это может иногда происходить, если контакт запрашивали двое людей, и он был уже одобрен." +"This may occasionally happen if contact was requested by both persons and it " +"has already been approved." +msgstr "" +"Это может иногда происходить, если контакт запрашивали двое людей, и он был " +"уже одобрен." -#: ../../mod/dfrn_confirm.php:240 +#: mod/dfrn_confirm.php:240 msgid "Response from remote site was not understood." msgstr "Ответ от удаленного сайта не был понят." -#: ../../mod/dfrn_confirm.php:249 ../../mod/dfrn_confirm.php:254 +#: mod/dfrn_confirm.php:249 mod/dfrn_confirm.php:254 msgid "Unexpected response from remote site: " msgstr "Неожиданный ответ от удаленного сайта: " -#: ../../mod/dfrn_confirm.php:263 +#: mod/dfrn_confirm.php:263 msgid "Confirmation completed successfully." msgstr "Подтверждение успешно завершено." -#: ../../mod/dfrn_confirm.php:265 ../../mod/dfrn_confirm.php:279 -#: ../../mod/dfrn_confirm.php:286 +#: mod/dfrn_confirm.php:265 mod/dfrn_confirm.php:279 mod/dfrn_confirm.php:286 msgid "Remote site reported: " msgstr "Удаленный сайт сообщил: " -#: ../../mod/dfrn_confirm.php:277 +#: mod/dfrn_confirm.php:277 msgid "Temporary failure. Please wait and try again." msgstr "Временные неудачи. Подождите и попробуйте еще раз." -#: ../../mod/dfrn_confirm.php:284 +#: mod/dfrn_confirm.php:284 msgid "Introduction failed or was revoked." msgstr "Запрос ошибочен или был отозван." -#: ../../mod/dfrn_confirm.php:429 +#: mod/dfrn_confirm.php:430 msgid "Unable to set contact photo." msgstr "Не удается установить фото контакта." -#: ../../mod/dfrn_confirm.php:486 ../../include/conversation.php:172 -#: ../../include/diaspora.php:620 +#: mod/dfrn_confirm.php:487 include/conversation.php:185 +#: include/diaspora.php:637 #, php-format msgid "%1$s is now friends with %2$s" msgstr "%1$s и %2$s теперь друзья" -#: ../../mod/dfrn_confirm.php:571 +#: mod/dfrn_confirm.php:572 #, php-format msgid "No user record found for '%s' " msgstr "Не найдено записи пользователя для '%s' " -#: ../../mod/dfrn_confirm.php:581 +#: mod/dfrn_confirm.php:582 msgid "Our site encryption key is apparently messed up." msgstr "Наш ключ шифрования сайта, по-видимому, перепутался." -#: ../../mod/dfrn_confirm.php:592 +#: mod/dfrn_confirm.php:593 msgid "Empty site URL was provided or URL could not be decrypted by us." -msgstr "Был предоставлен пустой URL сайта ​​или URL не может быть расшифрован нами." +msgstr "" +"Был предоставлен пустой URL сайта ​​или URL не может быть расшифрован нами." -#: ../../mod/dfrn_confirm.php:613 +#: mod/dfrn_confirm.php:614 msgid "Contact record was not found for you on our site." msgstr "Запись контакта не найдена для вас на нашем сайте." -#: ../../mod/dfrn_confirm.php:627 +#: mod/dfrn_confirm.php:628 #, php-format msgid "Site public key not available in contact record for URL %s." msgstr "Публичный ключ недоступен в записи о контакте по ссылке %s" -#: ../../mod/dfrn_confirm.php:647 +#: mod/dfrn_confirm.php:648 msgid "" "The ID provided by your system is a duplicate on our system. It should work " "if you try again." -msgstr "ID, предложенный вашей системой, является дубликатом в нашей системе. Он должен работать, если вы повторите попытку." +msgstr "" +"ID, предложенный вашей системой, является дубликатом в нашей системе. Он " +"должен работать, если вы повторите попытку." -#: ../../mod/dfrn_confirm.php:658 +#: mod/dfrn_confirm.php:659 msgid "Unable to set your contact credentials on our system." msgstr "Не удалось установить ваши учетные данные контакта в нашей системе." -#: ../../mod/dfrn_confirm.php:725 +#: mod/dfrn_confirm.php:726 msgid "Unable to update your contact profile details on our system" msgstr "Не удается обновить ваши контактные детали профиля в нашей системе" -#: ../../mod/dfrn_confirm.php:752 ../../mod/dfrn_request.php:717 -#: ../../include/items.php:4008 +#: mod/dfrn_confirm.php:753 mod/dfrn_request.php:741 include/items.php:4299 msgid "[Name Withheld]" msgstr "[Имя не разглашается]" -#: ../../mod/dfrn_confirm.php:797 +#: mod/dfrn_confirm.php:798 #, php-format msgid "%1$s has joined %2$s" msgstr "%1$s присоединился %2$s" -#: ../../mod/profile.php:21 ../../boot.php:1458 +#: mod/profile.php:21 include/identity.php:51 msgid "Requested profile is not available." msgstr "Запрашиваемый профиль недоступен." -#: ../../mod/profile.php:180 +#: mod/profile.php:179 msgid "Tips for New Members" msgstr "Советы для новых участников" -#: ../../mod/videos.php:125 +#: mod/videos.php:123 +msgid "Do you really want to delete this video?" +msgstr "" + +#: mod/videos.php:128 +msgid "Delete Video" +msgstr "Удалить видео" + +#: mod/videos.php:207 msgid "No videos selected" msgstr "Видео не выбрано" -#: ../../mod/videos.php:226 ../../mod/photos.php:1031 +#: mod/videos.php:308 mod/photos.php:1087 msgid "Access to this item is restricted." msgstr "Доступ к этому пункту ограничен." -#: ../../mod/videos.php:301 ../../include/text.php:1405 +#: mod/videos.php:383 include/text.php:1472 msgid "View Video" msgstr "Просмотреть видео" -#: ../../mod/videos.php:308 ../../mod/photos.php:1808 +#: mod/videos.php:390 mod/photos.php:1890 msgid "View Album" msgstr "Просмотреть альбом" -#: ../../mod/videos.php:317 +#: mod/videos.php:399 msgid "Recent Videos" msgstr "Последние видео" -#: ../../mod/videos.php:319 +#: mod/videos.php:401 msgid "Upload New Videos" msgstr "Загрузить новые видео" -#: ../../mod/tagger.php:95 ../../include/conversation.php:266 +#: mod/tagger.php:95 include/conversation.php:278 #, php-format msgid "%1$s tagged %2$s's %3$s with %4$s" msgstr "%1$s tagged %2$s's %3$s в %4$s" -#: ../../mod/fsuggest.php:63 +#: mod/fsuggest.php:63 msgid "Friend suggestion sent." msgstr "Приглашение в друзья отправлено." -#: ../../mod/fsuggest.php:97 +#: mod/fsuggest.php:97 msgid "Suggest Friends" msgstr "Предложить друзей" -#: ../../mod/fsuggest.php:99 +#: mod/fsuggest.php:99 #, php-format msgid "Suggest a friend for %s" msgstr "Предложить друга для %s." -#: ../../mod/lostpass.php:19 +#: mod/wall_upload.php:20 mod/wall_upload.php:33 mod/wall_upload.php:86 +#: mod/wall_upload.php:122 mod/wall_upload.php:125 mod/wall_attach.php:17 +#: mod/wall_attach.php:25 mod/wall_attach.php:76 include/api.php:1781 +msgid "Invalid request." +msgstr "Неверный запрос." + +#: mod/lostpass.php:19 msgid "No valid account found." msgstr "Не найдено действительного аккаунта." -#: ../../mod/lostpass.php:35 +#: mod/lostpass.php:35 msgid "Password reset request issued. Check your email." msgstr "Запрос на сброс пароля принят. Проверьте вашу электронную почту." -#: ../../mod/lostpass.php:42 +#: mod/lostpass.php:42 #, php-format msgid "" "\n" "\t\tDear %1$s,\n" "\t\t\tA request was recently received at \"%2$s\" to reset your account\n" -"\t\tpassword. In order to confirm this request, please select the verification link\n" +"\t\tpassword. In order to confirm this request, please select the " +"verification link\n" "\t\tbelow or paste it into your web browser address bar.\n" "\n" "\t\tIf you did NOT request this change, please DO NOT follow the link\n" @@ -1169,7 +1336,7 @@ msgid "" "\t\tissued this request." msgstr "" -#: ../../mod/lostpass.php:53 +#: mod/lostpass.php:53 #, php-format msgid "" "\n" @@ -1178,7 +1345,8 @@ msgid "" "\t\t%1$s\n" "\n" "\t\tYou will then receive a follow-up message containing the new password.\n" -"\t\tYou may change that password from your account settings page after logging in.\n" +"\t\tYou may change that password from your account settings page after " +"logging in.\n" "\n" "\t\tThe login details are as follows:\n" "\n" @@ -1186,55 +1354,60 @@ msgid "" "\t\tLogin Name:\t%3$s" msgstr "" -#: ../../mod/lostpass.php:72 +#: mod/lostpass.php:72 #, php-format msgid "Password reset requested at %s" msgstr "Запрос на сброс пароля получен %s" -#: ../../mod/lostpass.php:92 +#: mod/lostpass.php:92 msgid "" "Request could not be verified. (You may have previously submitted it.) " "Password reset failed." -msgstr "Запрос не может быть проверен. (Вы, возможно, ранее представляли его.) Попытка сброса пароля неудачная." +msgstr "" +"Запрос не может быть проверен. (Вы, возможно, ранее представляли его.) " +"Попытка сброса пароля неудачная." -#: ../../mod/lostpass.php:109 ../../boot.php:1280 +#: mod/lostpass.php:109 boot.php:1444 msgid "Password Reset" msgstr "Сброс пароля" -#: ../../mod/lostpass.php:110 +#: mod/lostpass.php:110 msgid "Your password has been reset as requested." msgstr "Ваш пароль был сброшен по требованию." -#: ../../mod/lostpass.php:111 +#: mod/lostpass.php:111 msgid "Your new password is" msgstr "Ваш новый пароль" -#: ../../mod/lostpass.php:112 +#: mod/lostpass.php:112 msgid "Save or copy your new password - and then" msgstr "Сохраните или скопируйте новый пароль - и затем" -#: ../../mod/lostpass.php:113 +#: mod/lostpass.php:113 msgid "click here to login" msgstr "нажмите здесь для входа" -#: ../../mod/lostpass.php:114 +#: mod/lostpass.php:114 msgid "" "Your password may be changed from the Settings page after " "successful login." -msgstr "Ваш пароль может быть изменен на странице Настройки после успешного входа." +msgstr "" +"Ваш пароль может быть изменен на странице Настройки после успешного " +"входа." -#: ../../mod/lostpass.php:125 +#: mod/lostpass.php:125 #, php-format msgid "" "\n" "\t\t\t\tDear %1$s,\n" "\t\t\t\t\tYour password has been changed as requested. Please retain this\n" -"\t\t\t\tinformation for your records (or change your password immediately to\n" +"\t\t\t\tinformation for your records (or change your password immediately " +"to\n" "\t\t\t\tsomething that you will remember).\n" "\t\t\t" msgstr "" -#: ../../mod/lostpass.php:131 +#: mod/lostpass.php:131 #, php-format msgid "" "\n" @@ -1244,1379 +1417,1693 @@ msgid "" "\t\t\t\tLogin Name:\t%2$s\n" "\t\t\t\tPassword:\t%3$s\n" "\n" -"\t\t\t\tYou may change that password from your account settings page after logging in.\n" +"\t\t\t\tYou may change that password from your account settings page after " +"logging in.\n" "\t\t\t" msgstr "" -#: ../../mod/lostpass.php:147 +#: mod/lostpass.php:147 #, php-format msgid "Your password has been changed at %s" msgstr "Ваш пароль был изменен %s" -#: ../../mod/lostpass.php:159 +#: mod/lostpass.php:159 msgid "Forgot your Password?" msgstr "Забыли пароль?" -#: ../../mod/lostpass.php:160 +#: mod/lostpass.php:160 msgid "" "Enter your email address and submit to have your password reset. Then check " "your email for further instructions." -msgstr "Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш пароль. Затем проверьте свою электронную почту для получения дальнейших инструкций." +msgstr "" +"Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш " +"пароль. Затем проверьте свою электронную почту для получения дальнейших " +"инструкций." -#: ../../mod/lostpass.php:161 +#: mod/lostpass.php:161 msgid "Nickname or Email: " msgstr "Ник или E-mail: " -#: ../../mod/lostpass.php:162 +#: mod/lostpass.php:162 msgid "Reset" msgstr "Сброс" -#: ../../mod/like.php:166 ../../include/conversation.php:137 -#: ../../include/diaspora.php:2103 ../../view/theme/diabook/theme.php:480 -#, php-format -msgid "%1$s likes %2$s's %3$s" -msgstr "%1$s нравится %3$s от %2$s " - -#: ../../mod/like.php:168 ../../include/conversation.php:140 -#, php-format -msgid "%1$s doesn't like %2$s's %3$s" -msgstr "%1$s не нравится %3$s от %2$s " - -#: ../../mod/ping.php:240 +#: mod/ping.php:265 msgid "{0} wants to be your friend" msgstr "{0} хочет стать Вашим другом" -#: ../../mod/ping.php:245 +#: mod/ping.php:280 msgid "{0} sent you a message" msgstr "{0} отправил Вам сообщение" -#: ../../mod/ping.php:250 +#: mod/ping.php:295 msgid "{0} requested registration" msgstr "{0} требуемая регистрация" -#: ../../mod/ping.php:256 -#, php-format -msgid "{0} commented %s's post" -msgstr "{0} прокомментировал сообщение от %s" - -#: ../../mod/ping.php:261 -#, php-format -msgid "{0} liked %s's post" -msgstr "{0} нравится сообщение от %s" - -#: ../../mod/ping.php:266 -#, php-format -msgid "{0} disliked %s's post" -msgstr "{0} не нравится сообщение от %s" - -#: ../../mod/ping.php:271 -#, php-format -msgid "{0} is now friends with %s" -msgstr "{0} теперь друзья с %s" - -#: ../../mod/ping.php:276 -msgid "{0} posted" -msgstr "{0} опубликовано" - -#: ../../mod/ping.php:281 -#, php-format -msgid "{0} tagged %s's post with #%s" -msgstr "{0} пометил сообщение %s с #%s" - -#: ../../mod/ping.php:287 -msgid "{0} mentioned you in a post" -msgstr "{0} упоменул Вас в сообщение" - -#: ../../mod/viewcontacts.php:41 +#: mod/viewcontacts.php:72 msgid "No contacts." msgstr "Нет контактов." -#: ../../mod/viewcontacts.php:78 ../../include/text.php:876 -msgid "View Contacts" -msgstr "Просмотр контактов" - -#: ../../mod/notifications.php:26 +#: mod/notifications.php:29 msgid "Invalid request identifier." msgstr "Неверный идентификатор запроса." -#: ../../mod/notifications.php:35 ../../mod/notifications.php:165 -#: ../../mod/notifications.php:211 +#: mod/notifications.php:38 mod/notifications.php:180 +#: mod/notifications.php:260 msgid "Discard" msgstr "Отказаться" -#: ../../mod/notifications.php:78 +#: mod/notifications.php:81 msgid "System" msgstr "Система" -#: ../../mod/notifications.php:83 ../../include/nav.php:145 +#: mod/notifications.php:87 mod/admin.php:390 include/nav.php:154 msgid "Network" -msgstr "Сеть" +msgstr "Новости" -#: ../../mod/notifications.php:88 ../../mod/network.php:371 +#: mod/notifications.php:93 mod/network.php:384 msgid "Personal" msgstr "Персонал" -#: ../../mod/notifications.php:93 ../../include/nav.php:105 -#: ../../include/nav.php:148 ../../view/theme/diabook/theme.php:123 +#: mod/notifications.php:99 include/nav.php:104 include/nav.php:157 +#: view/theme/diabook/theme.php:123 msgid "Home" -msgstr "Главная" +msgstr "Мой профиль" -#: ../../mod/notifications.php:98 ../../include/nav.php:154 +#: mod/notifications.php:105 include/nav.php:162 msgid "Introductions" msgstr "Запросы" -#: ../../mod/notifications.php:122 +#: mod/notifications.php:130 msgid "Show Ignored Requests" msgstr "Показать проигнорированные запросы" -#: ../../mod/notifications.php:122 +#: mod/notifications.php:130 msgid "Hide Ignored Requests" msgstr "Скрыть проигнорированные запросы" -#: ../../mod/notifications.php:149 ../../mod/notifications.php:195 +#: mod/notifications.php:164 mod/notifications.php:234 msgid "Notification type: " msgstr "Тип уведомления: " -#: ../../mod/notifications.php:150 +#: mod/notifications.php:165 msgid "Friend Suggestion" msgstr "Предложение в друзья" -#: ../../mod/notifications.php:152 +#: mod/notifications.php:167 #, php-format msgid "suggested by %s" msgstr "предложено юзером %s" -#: ../../mod/notifications.php:158 ../../mod/notifications.php:205 +#: mod/notifications.php:173 mod/notifications.php:252 msgid "Post a new friend activity" msgstr "Настроение" -#: ../../mod/notifications.php:158 ../../mod/notifications.php:205 +#: mod/notifications.php:173 mod/notifications.php:252 msgid "if applicable" msgstr "если требуется" -#: ../../mod/notifications.php:161 ../../mod/notifications.php:208 -#: ../../mod/admin.php:1005 +#: mod/notifications.php:176 mod/notifications.php:257 mod/admin.php:1308 msgid "Approve" msgstr "Одобрить" -#: ../../mod/notifications.php:181 +#: mod/notifications.php:196 msgid "Claims to be known to you: " msgstr "Утверждения, о которых должно быть вам известно: " -#: ../../mod/notifications.php:181 +#: mod/notifications.php:196 msgid "yes" msgstr "да" -#: ../../mod/notifications.php:181 +#: mod/notifications.php:196 msgid "no" msgstr "нет" -#: ../../mod/notifications.php:188 -msgid "Approve as: " -msgstr "Утвердить как: " +#: mod/notifications.php:197 +msgid "" +"Shall your connection be bidirectional or not? \"Friend\" implies that you " +"allow to read and you subscribe to their posts. \"Fan/Admirer\" means that " +"you allow to read but you do not want to read theirs. Approve as: " +msgstr "" -#: ../../mod/notifications.php:189 +#: mod/notifications.php:200 +msgid "" +"Shall your connection be bidirectional or not? \"Friend\" implies that you " +"allow to read and you subscribe to their posts. \"Sharer\" means that you " +"allow to read but you do not want to read theirs. Approve as: " +msgstr "" + +#: mod/notifications.php:208 msgid "Friend" msgstr "Друг" -#: ../../mod/notifications.php:190 +#: mod/notifications.php:209 msgid "Sharer" msgstr "Участник" -#: ../../mod/notifications.php:190 +#: mod/notifications.php:209 msgid "Fan/Admirer" msgstr "Фанат / Поклонник" -#: ../../mod/notifications.php:196 +#: mod/notifications.php:235 msgid "Friend/Connect Request" msgstr "Запрос в друзья / на подключение" -#: ../../mod/notifications.php:196 +#: mod/notifications.php:235 msgid "New Follower" msgstr "Новый фолловер" -#: ../../mod/notifications.php:217 +#: mod/notifications.php:250 mod/directory.php:147 include/identity.php:310 +#: include/identity.php:590 +msgid "Gender:" +msgstr "Пол:" + +#: mod/notifications.php:266 msgid "No introductions." msgstr "Запросов нет." -#: ../../mod/notifications.php:220 ../../include/nav.php:155 +#: mod/notifications.php:269 include/nav.php:165 msgid "Notifications" msgstr "Уведомления" -#: ../../mod/notifications.php:258 ../../mod/notifications.php:387 -#: ../../mod/notifications.php:478 +#: mod/notifications.php:307 mod/notifications.php:436 +#: mod/notifications.php:527 #, php-format msgid "%s liked %s's post" msgstr "%s нравится %s сообшение" -#: ../../mod/notifications.php:268 ../../mod/notifications.php:397 -#: ../../mod/notifications.php:488 +#: mod/notifications.php:317 mod/notifications.php:446 +#: mod/notifications.php:537 #, php-format msgid "%s disliked %s's post" msgstr "%s не нравится %s сообшение" -#: ../../mod/notifications.php:283 ../../mod/notifications.php:412 -#: ../../mod/notifications.php:503 +#: mod/notifications.php:332 mod/notifications.php:461 +#: mod/notifications.php:552 #, php-format msgid "%s is now friends with %s" msgstr "%s теперь друзья с %s" -#: ../../mod/notifications.php:290 ../../mod/notifications.php:419 +#: mod/notifications.php:339 mod/notifications.php:468 #, php-format msgid "%s created a new post" msgstr "%s написал новое сообщение" -#: ../../mod/notifications.php:291 ../../mod/notifications.php:420 -#: ../../mod/notifications.php:513 +#: mod/notifications.php:340 mod/notifications.php:469 +#: mod/notifications.php:562 #, php-format msgid "%s commented on %s's post" msgstr "%s прокомментировал %s сообщение" -#: ../../mod/notifications.php:306 +#: mod/notifications.php:355 msgid "No more network notifications." msgstr "Уведомлений из сети больше нет." -#: ../../mod/notifications.php:310 +#: mod/notifications.php:359 msgid "Network Notifications" msgstr "Уведомления сети" -#: ../../mod/notifications.php:336 ../../mod/notify.php:75 +#: mod/notifications.php:385 mod/notify.php:72 msgid "No more system notifications." msgstr "Системных уведомлений больше нет." -#: ../../mod/notifications.php:340 ../../mod/notify.php:79 +#: mod/notifications.php:389 mod/notify.php:76 msgid "System Notifications" msgstr "Уведомления системы" -#: ../../mod/notifications.php:435 +#: mod/notifications.php:484 msgid "No more personal notifications." msgstr "Персональных уведомлений больше нет." -#: ../../mod/notifications.php:439 +#: mod/notifications.php:488 msgid "Personal Notifications" msgstr "Личные уведомления" -#: ../../mod/notifications.php:520 +#: mod/notifications.php:569 msgid "No more home notifications." msgstr "Уведомлений больше нет." -#: ../../mod/notifications.php:524 +#: mod/notifications.php:573 msgid "Home Notifications" msgstr "Уведомления" -#: ../../mod/babel.php:17 +#: mod/babel.php:17 msgid "Source (bbcode) text:" msgstr "Код (bbcode):" -#: ../../mod/babel.php:23 +#: mod/babel.php:23 msgid "Source (Diaspora) text to convert to BBcode:" msgstr "Код (Diaspora) для конвертации в BBcode:" -#: ../../mod/babel.php:31 +#: mod/babel.php:31 msgid "Source input: " msgstr "Ввести код:" -#: ../../mod/babel.php:35 +#: mod/babel.php:35 msgid "bb2html (raw HTML): " msgstr "bb2html (raw HTML): " -#: ../../mod/babel.php:39 +#: mod/babel.php:39 msgid "bb2html: " msgstr "bb2html: " -#: ../../mod/babel.php:43 +#: mod/babel.php:43 msgid "bb2html2bb: " msgstr "bb2html2bb: " -#: ../../mod/babel.php:47 +#: mod/babel.php:47 msgid "bb2md: " msgstr "bb2md: " -#: ../../mod/babel.php:51 +#: mod/babel.php:51 msgid "bb2md2html: " msgstr "bb2md2html: " -#: ../../mod/babel.php:55 +#: mod/babel.php:55 msgid "bb2dia2bb: " msgstr "bb2dia2bb: " -#: ../../mod/babel.php:59 +#: mod/babel.php:59 msgid "bb2md2html2bb: " msgstr "bb2md2html2bb: " -#: ../../mod/babel.php:69 +#: mod/babel.php:69 msgid "Source input (Diaspora format): " msgstr "Ввод кода (формат Diaspora):" -#: ../../mod/babel.php:74 +#: mod/babel.php:74 msgid "diaspora2bb: " msgstr "diaspora2bb: " -#: ../../mod/navigation.php:20 ../../include/nav.php:34 +#: mod/navigation.php:19 include/nav.php:33 msgid "Nothing new here" msgstr "Ничего нового здесь" -#: ../../mod/navigation.php:24 ../../include/nav.php:38 +#: mod/navigation.php:23 include/nav.php:37 msgid "Clear notifications" msgstr "Стереть уведомления" -#: ../../mod/message.php:9 ../../include/nav.php:164 +#: mod/message.php:15 include/nav.php:174 msgid "New Message" msgstr "Новое сообщение" -#: ../../mod/message.php:63 ../../mod/wallmessage.php:56 +#: mod/message.php:70 mod/wallmessage.php:56 msgid "No recipient selected." msgstr "Не выбран получатель." -#: ../../mod/message.php:67 +#: mod/message.php:74 msgid "Unable to locate contact information." msgstr "Не удалось найти контактную информацию." -#: ../../mod/message.php:70 ../../mod/wallmessage.php:62 +#: mod/message.php:77 mod/wallmessage.php:62 msgid "Message could not be sent." msgstr "Сообщение не может быть отправлено." -#: ../../mod/message.php:73 ../../mod/wallmessage.php:65 +#: mod/message.php:80 mod/wallmessage.php:65 msgid "Message collection failure." msgstr "Неудача коллекции сообщения." -#: ../../mod/message.php:76 ../../mod/wallmessage.php:68 +#: mod/message.php:83 mod/wallmessage.php:68 msgid "Message sent." msgstr "Сообщение отправлено." -#: ../../mod/message.php:182 ../../include/nav.php:161 +#: mod/message.php:189 include/nav.php:171 msgid "Messages" msgstr "Сообщения" -#: ../../mod/message.php:207 +#: mod/message.php:214 msgid "Do you really want to delete this message?" msgstr "Вы действительно хотите удалить это сообщение?" -#: ../../mod/message.php:227 +#: mod/message.php:234 msgid "Message deleted." msgstr "Сообщение удалено." -#: ../../mod/message.php:258 +#: mod/message.php:265 msgid "Conversation removed." msgstr "Беседа удалена." -#: ../../mod/message.php:283 ../../mod/message.php:291 -#: ../../mod/message.php:466 ../../mod/message.php:474 -#: ../../mod/wallmessage.php:127 ../../mod/wallmessage.php:135 -#: ../../include/conversation.php:1002 ../../include/conversation.php:1020 +#: mod/message.php:290 mod/message.php:298 mod/message.php:427 +#: mod/message.php:435 mod/wallmessage.php:127 mod/wallmessage.php:135 +#: include/conversation.php:1128 include/conversation.php:1146 msgid "Please enter a link URL:" msgstr "Пожалуйста, введите URL ссылки:" -#: ../../mod/message.php:319 ../../mod/wallmessage.php:142 +#: mod/message.php:326 mod/wallmessage.php:142 msgid "Send Private Message" msgstr "Отправить личное сообщение" -#: ../../mod/message.php:320 ../../mod/message.php:553 -#: ../../mod/wallmessage.php:144 +#: mod/message.php:327 mod/message.php:514 mod/wallmessage.php:144 msgid "To:" msgstr "Кому:" -#: ../../mod/message.php:325 ../../mod/message.php:555 -#: ../../mod/wallmessage.php:145 +#: mod/message.php:332 mod/message.php:516 mod/wallmessage.php:145 msgid "Subject:" msgstr "Тема:" -#: ../../mod/message.php:329 ../../mod/message.php:558 -#: ../../mod/wallmessage.php:151 ../../mod/invite.php:134 +#: mod/message.php:336 mod/message.php:519 mod/wallmessage.php:151 +#: mod/invite.php:134 msgid "Your message:" msgstr "Ваше сообщение:" -#: ../../mod/message.php:332 ../../mod/message.php:562 -#: ../../mod/wallmessage.php:154 ../../mod/editpost.php:110 -#: ../../include/conversation.php:1091 +#: mod/message.php:339 mod/message.php:523 mod/wallmessage.php:154 +#: mod/editpost.php:110 include/conversation.php:1183 msgid "Upload photo" msgstr "Загрузить фото" -#: ../../mod/message.php:333 ../../mod/message.php:563 -#: ../../mod/wallmessage.php:155 ../../mod/editpost.php:114 -#: ../../include/conversation.php:1095 +#: mod/message.php:340 mod/message.php:524 mod/wallmessage.php:155 +#: mod/editpost.php:114 include/conversation.php:1187 msgid "Insert web link" msgstr "Вставить веб-ссылку" -#: ../../mod/message.php:334 ../../mod/message.php:565 -#: ../../mod/content.php:499 ../../mod/content.php:883 -#: ../../mod/wallmessage.php:156 ../../mod/editpost.php:124 -#: ../../mod/photos.php:1545 ../../object/Item.php:364 -#: ../../include/conversation.php:692 ../../include/conversation.php:1109 +#: mod/message.php:341 mod/message.php:526 mod/content.php:501 +#: mod/content.php:885 mod/wallmessage.php:156 mod/editpost.php:124 +#: mod/photos.php:1610 object/Item.php:396 include/conversation.php:713 +#: include/conversation.php:1201 msgid "Please wait" msgstr "Пожалуйста, подождите" -#: ../../mod/message.php:371 +#: mod/message.php:368 msgid "No messages." msgstr "Нет сообщений." -#: ../../mod/message.php:378 +#: mod/message.php:411 +msgid "Message not available." +msgstr "Сообщение не доступно." + +#: mod/message.php:481 +msgid "Delete message" +msgstr "Удалить сообщение" + +#: mod/message.php:507 mod/message.php:584 +msgid "Delete conversation" +msgstr "Удалить историю общения" + +#: mod/message.php:509 +msgid "" +"No secure communications available. You may be able to " +"respond from the sender's profile page." +msgstr "" +"Невозможно защищённое соединение. Вы имеете возможность " +"ответить со страницы профиля отправителя." + +#: mod/message.php:513 +msgid "Send Reply" +msgstr "Отправить ответ" + +#: mod/message.php:557 #, php-format msgid "Unknown sender - %s" msgstr "Неизвестный отправитель - %s" -#: ../../mod/message.php:381 +#: mod/message.php:560 #, php-format msgid "You and %s" msgstr "Вы и %s" -#: ../../mod/message.php:384 +#: mod/message.php:563 #, php-format msgid "%s and You" msgstr "%s и Вы" -#: ../../mod/message.php:405 ../../mod/message.php:546 -msgid "Delete conversation" -msgstr "Удалить историю общения" - -#: ../../mod/message.php:408 +#: mod/message.php:587 msgid "D, d M Y - g:i A" msgstr "D, d M Y - g:i A" -#: ../../mod/message.php:411 +#: mod/message.php:590 #, php-format msgid "%d message" msgid_plural "%d messages" msgstr[0] "%d сообщение" msgstr[1] "%d сообщений" msgstr[2] "%d сообщений" +msgstr[3] "%d сообщений" -#: ../../mod/message.php:450 -msgid "Message not available." -msgstr "Сообщение не доступно." - -#: ../../mod/message.php:520 -msgid "Delete message" -msgstr "Удалить сообщение" - -#: ../../mod/message.php:548 -msgid "" -"No secure communications available. You may be able to " -"respond from the sender's profile page." -msgstr "Невозможно защищённое соединение. Вы имеете возможность ответить со страницы профиля отправителя." - -#: ../../mod/message.php:552 -msgid "Send Reply" -msgstr "Отправить ответ" - -#: ../../mod/update_display.php:22 ../../mod/update_community.php:18 -#: ../../mod/update_notes.php:37 ../../mod/update_profile.php:41 -#: ../../mod/update_network.php:25 +#: mod/update_display.php:22 mod/update_community.php:18 +#: mod/update_notes.php:37 mod/update_profile.php:41 mod/update_network.php:25 msgid "[Embedded content - reload page to view]" msgstr "[Встроенное содержание - перезагрузите страницу для просмотра]" -#: ../../mod/crepair.php:106 +#: mod/crepair.php:104 msgid "Contact settings applied." msgstr "Установки контакта приняты." -#: ../../mod/crepair.php:108 +#: mod/crepair.php:106 msgid "Contact update failed." msgstr "Обновление контакта неудачное." -#: ../../mod/crepair.php:139 -msgid "Repair Contact Settings" -msgstr "Восстановить установки контакта" - -#: ../../mod/crepair.php:141 +#: mod/crepair.php:137 msgid "" -"WARNING: This is highly advanced and if you enter incorrect" -" information your communications with this contact may stop working." -msgstr "ВНИМАНИЕ: Это крайне важно! Если вы введете неверную информацию, ваша связь с этим контактом перестанет работать." +"WARNING: This is highly advanced and if you enter incorrect " +"information your communications with this contact may stop working." +msgstr "" +"ВНИМАНИЕ: Это крайне важно! Если вы введете неверную " +"информацию, ваша связь с этим контактом перестанет работать." -#: ../../mod/crepair.php:142 +#: mod/crepair.php:138 msgid "" "Please use your browser 'Back' button now if you are " "uncertain what to do on this page." -msgstr "Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' сейчас, если вы не уверены, что делаете на этой странице." +msgstr "" +"Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' " +"сейчас, если вы не уверены, что делаете на этой странице." -#: ../../mod/crepair.php:148 -msgid "Return to contact editor" -msgstr "Возврат к редактору контакта" - -#: ../../mod/crepair.php:159 ../../mod/crepair.php:161 +#: mod/crepair.php:151 mod/crepair.php:153 msgid "No mirroring" msgstr "" -#: ../../mod/crepair.php:159 +#: mod/crepair.php:151 msgid "Mirror as forwarded posting" msgstr "" -#: ../../mod/crepair.php:159 ../../mod/crepair.php:161 +#: mod/crepair.php:151 mod/crepair.php:153 msgid "Mirror as my own posting" msgstr "" -#: ../../mod/crepair.php:165 ../../mod/admin.php:1003 ../../mod/admin.php:1015 -#: ../../mod/admin.php:1016 ../../mod/admin.php:1029 -#: ../../mod/settings.php:616 ../../mod/settings.php:642 +#: mod/crepair.php:167 +msgid "Return to contact editor" +msgstr "Возврат к редактору контакта" + +#: mod/crepair.php:169 +msgid "Refetch contact data" +msgstr "" + +#: mod/crepair.php:170 mod/admin.php:1306 mod/admin.php:1318 +#: mod/admin.php:1319 mod/admin.php:1332 mod/settings.php:661 +#: mod/settings.php:687 msgid "Name" msgstr "Имя" -#: ../../mod/crepair.php:166 +#: mod/crepair.php:171 msgid "Account Nickname" msgstr "Ник аккаунта" -#: ../../mod/crepair.php:167 +#: mod/crepair.php:172 msgid "@Tagname - overrides Name/Nickname" msgstr "" -#: ../../mod/crepair.php:168 +#: mod/crepair.php:173 msgid "Account URL" msgstr "URL аккаунта" -#: ../../mod/crepair.php:169 +#: mod/crepair.php:174 msgid "Friend Request URL" msgstr "URL запроса в друзья" -#: ../../mod/crepair.php:170 +#: mod/crepair.php:175 msgid "Friend Confirm URL" msgstr "URL подтверждения друга" -#: ../../mod/crepair.php:171 +#: mod/crepair.php:176 msgid "Notification Endpoint URL" msgstr "URL эндпоинта уведомления" -#: ../../mod/crepair.php:172 +#: mod/crepair.php:177 msgid "Poll/Feed URL" msgstr "URL опроса/ленты" -#: ../../mod/crepair.php:173 +#: mod/crepair.php:178 msgid "New photo from this URL" msgstr "Новое фото из этой URL" -#: ../../mod/crepair.php:174 +#: mod/crepair.php:179 msgid "Remote Self" msgstr "" -#: ../../mod/crepair.php:176 +#: mod/crepair.php:182 msgid "Mirror postings from this contact" msgstr "" -#: ../../mod/crepair.php:176 +#: mod/crepair.php:184 msgid "" "Mark this contact as remote_self, this will cause friendica to repost new " "entries from this contact." msgstr "" -#: ../../mod/bookmarklet.php:12 ../../boot.php:1266 ../../include/nav.php:92 +#: mod/bookmarklet.php:12 boot.php:1430 include/nav.php:91 msgid "Login" msgstr "Вход" -#: ../../mod/bookmarklet.php:41 +#: mod/bookmarklet.php:41 msgid "The post was created" msgstr "" -#: ../../mod/viewsrc.php:7 +#: mod/viewsrc.php:7 msgid "Access denied." msgstr "Доступ запрещен." -#: ../../mod/dirfind.php:26 -msgid "People Search" -msgstr "Поиск людей" +#: mod/dirfind.php:194 mod/allfriends.php:80 mod/match.php:85 +#: mod/suggest.php:98 include/contact_widgets.php:10 include/identity.php:212 +msgid "Connect" +msgstr "Подключить" -#: ../../mod/dirfind.php:60 ../../mod/match.php:65 +#: mod/dirfind.php:195 mod/allfriends.php:64 mod/match.php:70 +#: mod/directory.php:162 mod/suggest.php:81 include/Contact.php:283 +#: include/Contact.php:296 include/Contact.php:338 +#: include/conversation.php:912 include/conversation.php:926 +msgid "View Profile" +msgstr "Просмотреть профиль" + +#: mod/dirfind.php:224 +#, php-format +msgid "People Search - %s" +msgstr "" + +#: mod/dirfind.php:231 mod/match.php:105 msgid "No matches" msgstr "Нет соответствий" -#: ../../mod/fbrowser.php:25 ../../boot.php:2126 ../../include/nav.php:78 -#: ../../view/theme/diabook/theme.php:126 +#: mod/fbrowser.php:32 include/identity.php:702 include/nav.php:77 +#: view/theme/diabook/theme.php:126 msgid "Photos" msgstr "Фото" -#: ../../mod/fbrowser.php:113 +#: mod/fbrowser.php:41 mod/fbrowser.php:62 mod/photos.php:62 +#: mod/photos.php:192 mod/photos.php:1119 mod/photos.php:1245 +#: mod/photos.php:1268 mod/photos.php:1838 mod/photos.php:1850 +#: view/theme/diabook/theme.php:499 +msgid "Contact Photos" +msgstr "Фотографии контакта" + +#: mod/fbrowser.php:125 msgid "Files" msgstr "Файлы" -#: ../../mod/nogroup.php:59 +#: mod/nogroup.php:63 msgid "Contacts who are not members of a group" msgstr "Контакты, которые не являются членами группы" -#: ../../mod/admin.php:57 +#: mod/admin.php:92 msgid "Theme settings updated." msgstr "Настройки темы обновлены." -#: ../../mod/admin.php:104 ../../mod/admin.php:619 +#: mod/admin.php:156 mod/admin.php:888 msgid "Site" msgstr "Сайт" -#: ../../mod/admin.php:105 ../../mod/admin.php:998 ../../mod/admin.php:1013 +#: mod/admin.php:157 mod/admin.php:832 mod/admin.php:1301 mod/admin.php:1316 msgid "Users" msgstr "Пользователи" -#: ../../mod/admin.php:106 ../../mod/admin.php:1102 ../../mod/admin.php:1155 -#: ../../mod/settings.php:57 +#: mod/admin.php:158 mod/admin.php:1416 mod/admin.php:1476 mod/settings.php:72 msgid "Plugins" msgstr "Плагины" -#: ../../mod/admin.php:107 ../../mod/admin.php:1323 ../../mod/admin.php:1357 +#: mod/admin.php:159 mod/admin.php:1674 mod/admin.php:1724 msgid "Themes" msgstr "Темы" -#: ../../mod/admin.php:108 +#: mod/admin.php:160 mod/settings.php:50 +msgid "Additional features" +msgstr "Дополнительные возможности" + +#: mod/admin.php:161 msgid "DB updates" msgstr "Обновление БД" -#: ../../mod/admin.php:123 ../../mod/admin.php:132 ../../mod/admin.php:1444 +#: mod/admin.php:162 mod/admin.php:385 +msgid "Inspect Queue" +msgstr "" + +#: mod/admin.php:163 mod/admin.php:354 +msgid "Federation Statistics" +msgstr "" + +#: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792 msgid "Logs" msgstr "Журналы" -#: ../../mod/admin.php:124 +#: mod/admin.php:178 mod/admin.php:1859 +msgid "View Logs" +msgstr "Просмотр логов" + +#: mod/admin.php:179 msgid "probe address" msgstr "" -#: ../../mod/admin.php:125 +#: mod/admin.php:180 msgid "check webfinger" msgstr "" -#: ../../mod/admin.php:130 ../../include/nav.php:184 +#: mod/admin.php:186 include/nav.php:194 msgid "Admin" msgstr "Администратор" -#: ../../mod/admin.php:131 +#: mod/admin.php:187 msgid "Plugin Features" msgstr "Возможности плагина" -#: ../../mod/admin.php:133 +#: mod/admin.php:189 msgid "diagnostics" -msgstr "" +msgstr "Диагностика" -#: ../../mod/admin.php:134 +#: mod/admin.php:190 msgid "User registrations waiting for confirmation" msgstr "Регистрации пользователей, ожидающие подтверждения" -#: ../../mod/admin.php:193 ../../mod/admin.php:952 -msgid "Normal Account" -msgstr "Обычный аккаунт" +#: mod/admin.php:347 +msgid "" +"This page offers you some numbers to the known part of the federated social " +"network your Friendica node is part of. These numbers are not complete but " +"only reflect the part of the network your node is aware of." +msgstr "" -#: ../../mod/admin.php:194 ../../mod/admin.php:953 -msgid "Soapbox Account" -msgstr "Аккаунт Витрина" +#: mod/admin.php:348 +msgid "" +"The Auto Discovered Contact Directory feature is not enabled, it " +"will improve the data displayed here." +msgstr "" -#: ../../mod/admin.php:195 ../../mod/admin.php:954 -msgid "Community/Celebrity Account" -msgstr "Аккаунт Сообщество / Знаменитость" - -#: ../../mod/admin.php:196 ../../mod/admin.php:955 -msgid "Automatic Friend Account" -msgstr "\"Автоматический друг\" Аккаунт" - -#: ../../mod/admin.php:197 -msgid "Blog Account" -msgstr "Аккаунт блога" - -#: ../../mod/admin.php:198 -msgid "Private Forum" -msgstr "Личный форум" - -#: ../../mod/admin.php:217 -msgid "Message queues" -msgstr "Очереди сообщений" - -#: ../../mod/admin.php:222 ../../mod/admin.php:618 ../../mod/admin.php:997 -#: ../../mod/admin.php:1101 ../../mod/admin.php:1154 ../../mod/admin.php:1322 -#: ../../mod/admin.php:1356 ../../mod/admin.php:1443 +#: mod/admin.php:353 mod/admin.php:384 mod/admin.php:441 mod/admin.php:887 +#: mod/admin.php:1300 mod/admin.php:1415 mod/admin.php:1475 mod/admin.php:1673 +#: mod/admin.php:1723 mod/admin.php:1791 mod/admin.php:1858 msgid "Administration" msgstr "Администрация" -#: ../../mod/admin.php:223 +#: mod/admin.php:360 +#, php-format +msgid "Currently this node is aware of %d nodes from the following platforms:" +msgstr "" + +#: mod/admin.php:387 +msgid "ID" +msgstr "" + +#: mod/admin.php:388 +msgid "Recipient Name" +msgstr "" + +#: mod/admin.php:389 +msgid "Recipient Profile" +msgstr "" + +#: mod/admin.php:391 +msgid "Created" +msgstr "" + +#: mod/admin.php:392 +msgid "Last Tried" +msgstr "" + +#: mod/admin.php:393 +msgid "" +"This page lists the content of the queue for outgoing postings. These are " +"postings the initial delivery failed for. They will be resend later and " +"eventually deleted if the delivery fails permanently." +msgstr "" + +#: mod/admin.php:412 mod/admin.php:1254 +msgid "Normal Account" +msgstr "Обычный аккаунт" + +#: mod/admin.php:413 mod/admin.php:1255 +msgid "Soapbox Account" +msgstr "Аккаунт Витрина" + +#: mod/admin.php:414 mod/admin.php:1256 +msgid "Community/Celebrity Account" +msgstr "Аккаунт Сообщество / Знаменитость" + +#: mod/admin.php:415 mod/admin.php:1257 +msgid "Automatic Friend Account" +msgstr "\"Автоматический друг\" Аккаунт" + +#: mod/admin.php:416 +msgid "Blog Account" +msgstr "Аккаунт блога" + +#: mod/admin.php:417 +msgid "Private Forum" +msgstr "Личный форум" + +#: mod/admin.php:436 +msgid "Message queues" +msgstr "Очереди сообщений" + +#: mod/admin.php:442 msgid "Summary" msgstr "Резюме" -#: ../../mod/admin.php:225 +#: mod/admin.php:444 msgid "Registered users" msgstr "Зарегистрированные пользователи" -#: ../../mod/admin.php:227 +#: mod/admin.php:446 msgid "Pending registrations" msgstr "Ожидающие регистрации" -#: ../../mod/admin.php:228 +#: mod/admin.php:447 msgid "Version" msgstr "Версия" -#: ../../mod/admin.php:232 +#: mod/admin.php:452 msgid "Active plugins" msgstr "Активные плагины" -#: ../../mod/admin.php:255 +#: mod/admin.php:475 msgid "Can not parse base url. Must have at least ://" -msgstr "Невозможно определить базовый URL. Он должен иметь следующий вид - ://" +msgstr "" +"Невозможно определить базовый URL. Он должен иметь следующий вид - " +"://" -#: ../../mod/admin.php:516 +#: mod/admin.php:760 +msgid "RINO2 needs mcrypt php extension to work." +msgstr "Для функционирования RINO2 необходим пакет php5-mcrypt" + +#: mod/admin.php:768 msgid "Site settings updated." msgstr "Установки сайта обновлены." -#: ../../mod/admin.php:545 ../../mod/settings.php:828 +#: mod/admin.php:796 mod/settings.php:912 msgid "No special theme for mobile devices" msgstr "Нет специальной темы для мобильных устройств" -#: ../../mod/admin.php:562 +#: mod/admin.php:815 msgid "No community page" msgstr "" -#: ../../mod/admin.php:563 +#: mod/admin.php:816 msgid "Public postings from users of this site" msgstr "" -#: ../../mod/admin.php:564 +#: mod/admin.php:817 msgid "Global community page" msgstr "" -#: ../../mod/admin.php:570 +#: mod/admin.php:823 msgid "At post arrival" msgstr "" -#: ../../mod/admin.php:571 ../../include/contact_selectors.php:56 +#: mod/admin.php:824 include/contact_selectors.php:56 msgid "Frequently" msgstr "Часто" -#: ../../mod/admin.php:572 ../../include/contact_selectors.php:57 +#: mod/admin.php:825 include/contact_selectors.php:57 msgid "Hourly" msgstr "Раз в час" -#: ../../mod/admin.php:573 ../../include/contact_selectors.php:58 +#: mod/admin.php:826 include/contact_selectors.php:58 msgid "Twice daily" msgstr "Два раза в день" -#: ../../mod/admin.php:574 ../../include/contact_selectors.php:59 +#: mod/admin.php:827 include/contact_selectors.php:59 msgid "Daily" msgstr "Ежедневно" -#: ../../mod/admin.php:579 +#: mod/admin.php:833 +msgid "Users, Global Contacts" +msgstr "" + +#: mod/admin.php:834 +msgid "Users, Global Contacts/fallback" +msgstr "" + +#: mod/admin.php:838 +msgid "One month" +msgstr "Один месяц" + +#: mod/admin.php:839 +msgid "Three months" +msgstr "Три месяца" + +#: mod/admin.php:840 +msgid "Half a year" +msgstr "Пол года" + +#: mod/admin.php:841 +msgid "One year" +msgstr "Один год" + +#: mod/admin.php:846 msgid "Multi user instance" msgstr "Многопользовательский вид" -#: ../../mod/admin.php:602 +#: mod/admin.php:869 msgid "Closed" msgstr "Закрыто" -#: ../../mod/admin.php:603 +#: mod/admin.php:870 msgid "Requires approval" msgstr "Требуется подтверждение" -#: ../../mod/admin.php:604 +#: mod/admin.php:871 msgid "Open" msgstr "Открыто" -#: ../../mod/admin.php:608 +#: mod/admin.php:875 msgid "No SSL policy, links will track page SSL state" msgstr "Нет режима SSL, состояние SSL не будет отслеживаться" -#: ../../mod/admin.php:609 +#: mod/admin.php:876 msgid "Force all links to use SSL" msgstr "Заставить все ссылки использовать SSL" -#: ../../mod/admin.php:610 +#: mod/admin.php:877 msgid "Self-signed certificate, use SSL for local links only (discouraged)" -msgstr "Само-подписанный сертификат, использовать SSL только локально (не рекомендуется)" +msgstr "" +"Само-подписанный сертификат, использовать SSL только локально (не " +"рекомендуется)" -#: ../../mod/admin.php:620 ../../mod/admin.php:1156 ../../mod/admin.php:1358 -#: ../../mod/admin.php:1445 ../../mod/settings.php:614 -#: ../../mod/settings.php:724 ../../mod/settings.php:798 -#: ../../mod/settings.php:880 ../../mod/settings.php:1113 +#: mod/admin.php:889 mod/admin.php:1477 mod/admin.php:1725 mod/admin.php:1793 +#: mod/admin.php:1942 mod/settings.php:659 mod/settings.php:769 +#: mod/settings.php:813 mod/settings.php:882 mod/settings.php:969 +#: mod/settings.php:1204 msgid "Save Settings" msgstr "Сохранить настройки" -#: ../../mod/admin.php:621 ../../mod/register.php:255 +#: mod/admin.php:890 mod/register.php:263 msgid "Registration" msgstr "Регистрация" -#: ../../mod/admin.php:622 +#: mod/admin.php:891 msgid "File upload" msgstr "Загрузка файлов" -#: ../../mod/admin.php:623 +#: mod/admin.php:892 msgid "Policies" msgstr "Политики" -#: ../../mod/admin.php:624 +#: mod/admin.php:893 msgid "Advanced" msgstr "Расширенный" -#: ../../mod/admin.php:625 +#: mod/admin.php:894 +msgid "Auto Discovered Contact Directory" +msgstr "" + +#: mod/admin.php:895 msgid "Performance" msgstr "Производительность" -#: ../../mod/admin.php:626 +#: mod/admin.php:896 msgid "" "Relocate - WARNING: advanced function. Could make this server unreachable." -msgstr "Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер недоступным." +msgstr "" +"Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер " +"недоступным." -#: ../../mod/admin.php:629 +#: mod/admin.php:899 msgid "Site name" msgstr "Название сайта" -#: ../../mod/admin.php:630 +#: mod/admin.php:900 msgid "Host name" -msgstr "" +msgstr "Имя хоста" -#: ../../mod/admin.php:631 +#: mod/admin.php:901 msgid "Sender Email" -msgstr "" +msgstr "Системный Email" -#: ../../mod/admin.php:632 +#: mod/admin.php:901 +msgid "" +"The email address your server shall use to send notification emails from." +msgstr "Адрес с которого будут приходить письма пользователям." + +#: mod/admin.php:902 msgid "Banner/Logo" msgstr "Баннер/Логотип" -#: ../../mod/admin.php:633 +#: mod/admin.php:903 msgid "Shortcut icon" msgstr "" -#: ../../mod/admin.php:634 +#: mod/admin.php:903 +msgid "Link to an icon that will be used for browsers." +msgstr "" + +#: mod/admin.php:904 msgid "Touch icon" msgstr "" -#: ../../mod/admin.php:635 +#: mod/admin.php:904 +msgid "Link to an icon that will be used for tablets and mobiles." +msgstr "" + +#: mod/admin.php:905 msgid "Additional Info" msgstr "Дополнительная информация" -#: ../../mod/admin.php:635 +#: mod/admin.php:905 +#, php-format msgid "" "For public servers: you can add additional information here that will be " -"listed at dir.friendica.com/siteinfo." -msgstr "Для публичных серверов: вы можете добавить дополнительную информацию, которая будет перечислена в dir.friendica.com/siteinfo." +"listed at %s/siteinfo." +msgstr "" -#: ../../mod/admin.php:636 +#: mod/admin.php:906 msgid "System language" msgstr "Системный язык" -#: ../../mod/admin.php:637 +#: mod/admin.php:907 msgid "System theme" msgstr "Системная тема" -#: ../../mod/admin.php:637 +#: mod/admin.php:907 msgid "" "Default system theme - may be over-ridden by user profiles - change theme settings" -msgstr "Тема системы по умолчанию - может быть переопределена пользователем - изменить настройки темы" +msgstr "" +"Тема системы по умолчанию - может быть переопределена пользователем - изменить настройки темы" -#: ../../mod/admin.php:638 +#: mod/admin.php:908 msgid "Mobile system theme" msgstr "Мобильная тема системы" -#: ../../mod/admin.php:638 +#: mod/admin.php:908 msgid "Theme for mobile devices" msgstr "Тема для мобильных устройств" -#: ../../mod/admin.php:639 +#: mod/admin.php:909 msgid "SSL link policy" msgstr "Политика SSL" -#: ../../mod/admin.php:639 +#: mod/admin.php:909 msgid "Determines whether generated links should be forced to use SSL" msgstr "Ссылки должны быть вынуждены использовать SSL" -#: ../../mod/admin.php:640 +#: mod/admin.php:910 msgid "Force SSL" -msgstr "" +msgstr "SSL принудительно" -#: ../../mod/admin.php:640 +#: mod/admin.php:910 msgid "" -"Force all Non-SSL requests to SSL - Attention: on some systems it could lead" -" to endless loops." +"Force all Non-SSL requests to SSL - Attention: on some systems it could lead " +"to endless loops." msgstr "" -#: ../../mod/admin.php:641 +#: mod/admin.php:911 msgid "Old style 'Share'" msgstr "Старый стиль 'Share'" -#: ../../mod/admin.php:641 +#: mod/admin.php:911 msgid "Deactivates the bbcode element 'share' for repeating items." msgstr "Отключение BBCode элемента 'share' для повторяющихся элементов." -#: ../../mod/admin.php:642 +#: mod/admin.php:912 msgid "Hide help entry from navigation menu" msgstr "Скрыть пункт \"помощь\" в меню навигации" -#: ../../mod/admin.php:642 +#: mod/admin.php:912 msgid "" "Hides the menu entry for the Help pages from the navigation menu. You can " "still access it calling /help directly." -msgstr "Скрывает элемент меню для страницы справки из меню навигации. Вы все еще можете получить доступ к нему через вызов/помощь напрямую." +msgstr "" +"Скрывает элемент меню для страницы справки из меню навигации. Вы все еще " +"можете получить доступ к нему через вызов/помощь напрямую." -#: ../../mod/admin.php:643 +#: mod/admin.php:913 msgid "Single user instance" msgstr "Однопользовательский режим" -#: ../../mod/admin.php:643 +#: mod/admin.php:913 msgid "Make this instance multi-user or single-user for the named user" -msgstr "Сделать этот экземпляр многопользовательским, или однопользовательским для названного пользователя" +msgstr "" +"Сделать этот экземпляр многопользовательским, или однопользовательским для " +"названного пользователя" -#: ../../mod/admin.php:644 +#: mod/admin.php:914 msgid "Maximum image size" msgstr "Максимальный размер изображения" -#: ../../mod/admin.php:644 +#: mod/admin.php:914 msgid "" "Maximum size in bytes of uploaded images. Default is 0, which means no " "limits." -msgstr "Максимальный размер в байтах для загружаемых изображений. По умолчанию 0, что означает отсутствие ограничений." +msgstr "" +"Максимальный размер в байтах для загружаемых изображений. По умолчанию 0, " +"что означает отсутствие ограничений." -#: ../../mod/admin.php:645 +#: mod/admin.php:915 msgid "Maximum image length" msgstr "Максимальная длина картинки" -#: ../../mod/admin.php:645 +#: mod/admin.php:915 msgid "" "Maximum length in pixels of the longest side of uploaded images. Default is " "-1, which means no limits." -msgstr "Максимальная длина в пикселях для длинной стороны загруженных изображений. По умолчанию равно -1, что означает отсутствие ограничений." +msgstr "" +"Максимальная длина в пикселях для длинной стороны загруженных изображений. " +"По умолчанию равно -1, что означает отсутствие ограничений." -#: ../../mod/admin.php:646 +#: mod/admin.php:916 msgid "JPEG image quality" msgstr "Качество JPEG изображения" -#: ../../mod/admin.php:646 +#: mod/admin.php:916 msgid "" "Uploaded JPEGS will be saved at this quality setting [0-100]. Default is " "100, which is full quality." -msgstr "Загруженные изображения JPEG будут сохранены в этом качестве [0-100]. По умолчанию 100, что означает полное качество." +msgstr "" +"Загруженные изображения JPEG будут сохранены в этом качестве [0-100]. По " +"умолчанию 100, что означает полное качество." -#: ../../mod/admin.php:648 +#: mod/admin.php:918 msgid "Register policy" msgstr "Политика регистрация" -#: ../../mod/admin.php:649 +#: mod/admin.php:919 msgid "Maximum Daily Registrations" msgstr "Максимальное число регистраций в день" -#: ../../mod/admin.php:649 +#: mod/admin.php:919 msgid "" -"If registration is permitted above, this sets the maximum number of new user" -" registrations to accept per day. If register is set to closed, this " -"setting has no effect." -msgstr "Если регистрация разрешена, этот параметр устанавливает максимальное количество новых регистраций пользователей в день. Если регистрация закрыта, эта опция не имеет никакого эффекта." +"If registration is permitted above, this sets the maximum number of new user " +"registrations to accept per day. If register is set to closed, this setting " +"has no effect." +msgstr "" +"Если регистрация разрешена, этот параметр устанавливает максимальное " +"количество новых регистраций пользователей в день. Если регистрация закрыта, " +"эта опция не имеет никакого эффекта." -#: ../../mod/admin.php:650 +#: mod/admin.php:920 msgid "Register text" msgstr "Текст регистрации" -#: ../../mod/admin.php:650 +#: mod/admin.php:920 msgid "Will be displayed prominently on the registration page." msgstr "Будет находиться на видном месте на странице регистрации." -#: ../../mod/admin.php:651 +#: mod/admin.php:921 msgid "Accounts abandoned after x days" msgstr "Аккаунт считается после x дней не воспользованным" -#: ../../mod/admin.php:651 +#: mod/admin.php:921 msgid "" "Will not waste system resources polling external sites for abandonded " "accounts. Enter 0 for no time limit." -msgstr "Не будет тратить ресурсы для опроса сайтов для бесхозных контактов. Введите 0 для отключения лимита времени." +msgstr "" +"Не будет тратить ресурсы для опроса сайтов для бесхозных контактов. Введите " +"0 для отключения лимита времени." -#: ../../mod/admin.php:652 +#: mod/admin.php:922 msgid "Allowed friend domains" msgstr "Разрешенные домены друзей" -#: ../../mod/admin.php:652 +#: mod/admin.php:922 msgid "" "Comma separated list of domains which are allowed to establish friendships " "with this site. Wildcards are accepted. Empty to allow any domains" -msgstr "Разделенный запятыми список доменов, которые разрешены для установления связей. Групповые символы принимаются. Оставьте пустым для разрешения связи со всеми доменами." +msgstr "" +"Разделенный запятыми список доменов, которые разрешены для установления " +"связей. Групповые символы принимаются. Оставьте пустым для разрешения связи " +"со всеми доменами." -#: ../../mod/admin.php:653 +#: mod/admin.php:923 msgid "Allowed email domains" msgstr "Разрешенные почтовые домены" -#: ../../mod/admin.php:653 +#: mod/admin.php:923 msgid "" "Comma separated list of domains which are allowed in email addresses for " "registrations to this site. Wildcards are accepted. Empty to allow any " "domains" -msgstr "Разделенный запятыми список доменов, которые разрешены для установления связей. Групповые символы принимаются. Оставьте пустым для разрешения связи со всеми доменами." +msgstr "" +"Разделенный запятыми список доменов, которые разрешены для установления " +"связей. Групповые символы принимаются. Оставьте пустым для разрешения связи " +"со всеми доменами." -#: ../../mod/admin.php:654 +#: mod/admin.php:924 msgid "Block public" msgstr "Блокировать общественный доступ" -#: ../../mod/admin.php:654 +#: mod/admin.php:924 msgid "" "Check to block public access to all otherwise public personal pages on this " "site unless you are currently logged in." -msgstr "Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным персональным страницам на этом сайте, если вы не вошли на сайт." +msgstr "" +"Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным " +"персональным страницам на этом сайте, если вы не вошли на сайт." -#: ../../mod/admin.php:655 +#: mod/admin.php:925 msgid "Force publish" msgstr "Принудительная публикация" -#: ../../mod/admin.php:655 +#: mod/admin.php:925 msgid "" "Check to force all profiles on this site to be listed in the site directory." -msgstr "Отметьте, чтобы принудительно заставить все профили на этом сайте, быть перечислеными в каталоге сайта." +msgstr "" +"Отметьте, чтобы принудительно заставить все профили на этом сайте, быть " +"перечислеными в каталоге сайта." -#: ../../mod/admin.php:656 -msgid "Global directory update URL" -msgstr "URL обновления глобального каталога" +#: mod/admin.php:926 +msgid "Global directory URL" +msgstr "" -#: ../../mod/admin.php:656 +#: mod/admin.php:926 msgid "" -"URL to update the global directory. If this is not set, the global directory" -" is completely unavailable to the application." -msgstr "URL для обновления глобального каталога. Если он не установлен, глобальный каталог полностью недоступен для приложения." +"URL to the global directory. If this is not set, the global directory is " +"completely unavailable to the application." +msgstr "" -#: ../../mod/admin.php:657 +#: mod/admin.php:927 msgid "Allow threaded items" msgstr "Разрешить темы в обсуждении" -#: ../../mod/admin.php:657 +#: mod/admin.php:927 msgid "Allow infinite level threading for items on this site." msgstr "Разрешить бесконечный уровень для тем на этом сайте." -#: ../../mod/admin.php:658 +#: mod/admin.php:928 msgid "Private posts by default for new users" msgstr "Частные сообщения по умолчанию для новых пользователей" -#: ../../mod/admin.php:658 +#: mod/admin.php:928 msgid "" "Set default post permissions for all new members to the default privacy " "group rather than public." -msgstr "Установить права на создание постов по умолчанию для всех участников в дефолтной приватной группе, а не для публичных участников." +msgstr "" +"Установить права на создание постов по умолчанию для всех участников в " +"дефолтной приватной группе, а не для публичных участников." -#: ../../mod/admin.php:659 +#: mod/admin.php:929 msgid "Don't include post content in email notifications" msgstr "Не включать текст сообщения в email-оповещение." -#: ../../mod/admin.php:659 +#: mod/admin.php:929 msgid "" "Don't include the content of a post/comment/private message/etc. in the " "email notifications that are sent out from this site, as a privacy measure." -msgstr "Не включать содержание сообщения/комментария/личного сообщения и т.д.. в уведомления электронной почты, отправленных с сайта, в качестве меры конфиденциальности." +msgstr "" +"Не включать содержание сообщения/комментария/личного сообщения и т.д.. в " +"уведомления электронной почты, отправленных с сайта, в качестве меры " +"конфиденциальности." -#: ../../mod/admin.php:660 +#: mod/admin.php:930 msgid "Disallow public access to addons listed in the apps menu." msgstr "Запретить публичный доступ к аддонам, перечисленным в меню приложений." -#: ../../mod/admin.php:660 +#: mod/admin.php:930 msgid "" "Checking this box will restrict addons listed in the apps menu to members " "only." -msgstr "При установке этого флажка, будут ограничены аддоны, перечисленные в меню приложений, только для участников." +msgstr "" +"При установке этого флажка, будут ограничены аддоны, перечисленные в меню " +"приложений, только для участников." -#: ../../mod/admin.php:661 +#: mod/admin.php:931 msgid "Don't embed private images in posts" msgstr "Не вставлять личные картинки в постах" -#: ../../mod/admin.php:661 +#: mod/admin.php:931 msgid "" "Don't replace locally-hosted private photos in posts with an embedded copy " "of the image. This means that contacts who receive posts containing private " -"photos will have to authenticate and load each image, which may take a " -"while." -msgstr "Не заменяйте локально расположенные фотографии в постах на внедрённые копии изображений. Это означает, что контакты, которые получают сообщения, содержащие личные фотографии, будут вынуждены идентефицироваться и грузить каждое изображение, что может занять некоторое время." +"photos will have to authenticate and load each image, which may take a while." +msgstr "" +"Не заменяйте локально расположенные фотографии в постах на внедрённые копии " +"изображений. Это означает, что контакты, которые получают сообщения, " +"содержащие личные фотографии, будут вынуждены идентефицироваться и грузить " +"каждое изображение, что может занять некоторое время." -#: ../../mod/admin.php:662 +#: mod/admin.php:932 msgid "Allow Users to set remote_self" msgstr "Разрешить пользователям установить remote_self" -#: ../../mod/admin.php:662 +#: mod/admin.php:932 msgid "" "With checking this, every user is allowed to mark every contact as a " "remote_self in the repair contact dialog. Setting this flag on a contact " "causes mirroring every posting of that contact in the users stream." msgstr "" -#: ../../mod/admin.php:663 +#: mod/admin.php:933 msgid "Block multiple registrations" msgstr "Блокировать множественные регистрации" -#: ../../mod/admin.php:663 +#: mod/admin.php:933 msgid "Disallow users to register additional accounts for use as pages." -msgstr "Запретить пользователям регистрировать дополнительные аккаунты для использования в качестве страниц." +msgstr "" +"Запретить пользователям регистрировать дополнительные аккаунты для " +"использования в качестве страниц." -#: ../../mod/admin.php:664 +#: mod/admin.php:934 msgid "OpenID support" msgstr "Поддержка OpenID" -#: ../../mod/admin.php:664 +#: mod/admin.php:934 msgid "OpenID support for registration and logins." msgstr "OpenID поддержка для регистрации и входа в систему." -#: ../../mod/admin.php:665 +#: mod/admin.php:935 msgid "Fullname check" msgstr "Проверка полного имени" -#: ../../mod/admin.php:665 +#: mod/admin.php:935 msgid "" "Force users to register with a space between firstname and lastname in Full " "name, as an antispam measure" -msgstr "Принудить пользователей регистрироваться с пробелом между именем и фамилией в строке \"полное имя\". Антиспам мера." +msgstr "" +"Принудить пользователей регистрироваться с пробелом между именем и фамилией " +"в строке \"полное имя\". Антиспам мера." -#: ../../mod/admin.php:666 +#: mod/admin.php:936 msgid "UTF-8 Regular expressions" msgstr "UTF-8 регулярные выражения" -#: ../../mod/admin.php:666 +#: mod/admin.php:936 msgid "Use PHP UTF8 regular expressions" msgstr "Используйте PHP UTF-8 для регулярных выражений" -#: ../../mod/admin.php:667 +#: mod/admin.php:937 msgid "Community Page Style" msgstr "" -#: ../../mod/admin.php:667 +#: mod/admin.php:937 msgid "" "Type of community page to show. 'Global community' shows every public " "posting from an open distributed network that arrived on this server." msgstr "" -#: ../../mod/admin.php:668 +#: mod/admin.php:938 msgid "Posts per user on community page" msgstr "" -#: ../../mod/admin.php:668 +#: mod/admin.php:938 msgid "" "The maximum number of posts per user on the community page. (Not valid for " "'Global Community')" msgstr "" -#: ../../mod/admin.php:669 +#: mod/admin.php:939 msgid "Enable OStatus support" msgstr "Включить поддержку OStatus" -#: ../../mod/admin.php:669 +#: mod/admin.php:939 msgid "" "Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All " "communications in OStatus are public, so privacy warnings will be " "occasionally displayed." msgstr "" -#: ../../mod/admin.php:670 +#: mod/admin.php:940 msgid "OStatus conversation completion interval" msgstr "" -#: ../../mod/admin.php:670 +#: mod/admin.php:940 msgid "" "How often shall the poller check for new entries in OStatus conversations? " "This can be a very ressource task." -msgstr "Как часто процессы должны проверять наличие новых записей в OStatus разговорах? Это может быть очень ресурсоёмкой задачей." +msgstr "" +"Как часто процессы должны проверять наличие новых записей в OStatus " +"разговорах? Это может быть очень ресурсоёмкой задачей." -#: ../../mod/admin.php:671 +#: mod/admin.php:941 +msgid "OStatus support can only be enabled if threading is enabled." +msgstr "" + +#: mod/admin.php:943 +msgid "" +"Diaspora support can't be enabled because Friendica was installed into a sub " +"directory." +msgstr "" + +#: mod/admin.php:944 msgid "Enable Diaspora support" msgstr "Включить поддержку Diaspora" -#: ../../mod/admin.php:671 +#: mod/admin.php:944 msgid "Provide built-in Diaspora network compatibility." msgstr "Обеспечить встроенную поддержку сети Diaspora." -#: ../../mod/admin.php:672 +#: mod/admin.php:945 msgid "Only allow Friendica contacts" msgstr "Позвольть только Friendica контакты" -#: ../../mod/admin.php:672 +#: mod/admin.php:945 msgid "" "All contacts must use Friendica protocols. All other built-in communication " "protocols disabled." -msgstr "Все контакты должны использовать только Friendica протоколы. Все другие встроенные коммуникационные протоколы отключены." +msgstr "" +"Все контакты должны использовать только Friendica протоколы. Все другие " +"встроенные коммуникационные протоколы отключены." -#: ../../mod/admin.php:673 +#: mod/admin.php:946 msgid "Verify SSL" msgstr "Проверка SSL" -#: ../../mod/admin.php:673 +#: mod/admin.php:946 msgid "" -"If you wish, you can turn on strict certificate checking. This will mean you" -" cannot connect (at all) to self-signed SSL sites." -msgstr "Если хотите, вы можете включить строгую проверку сертификатов. Это будет означать, что вы не сможете соединиться (вообще) с сайтами, имеющими само-подписанный SSL сертификат." +"If you wish, you can turn on strict certificate checking. This will mean you " +"cannot connect (at all) to self-signed SSL sites." +msgstr "" +"Если хотите, вы можете включить строгую проверку сертификатов. Это будет " +"означать, что вы не сможете соединиться (вообще) с сайтами, имеющими само-" +"подписанный SSL сертификат." -#: ../../mod/admin.php:674 +#: mod/admin.php:947 msgid "Proxy user" msgstr "Прокси пользователь" -#: ../../mod/admin.php:675 +#: mod/admin.php:948 msgid "Proxy URL" msgstr "Прокси URL" -#: ../../mod/admin.php:676 +#: mod/admin.php:949 msgid "Network timeout" msgstr "Тайм-аут сети" -#: ../../mod/admin.php:676 +#: mod/admin.php:949 msgid "Value is in seconds. Set to 0 for unlimited (not recommended)." -msgstr "Значение указывается в секундах. Установите 0 для снятия ограничений (не рекомендуется)." +msgstr "" +"Значение указывается в секундах. Установите 0 для снятия ограничений (не " +"рекомендуется)." -#: ../../mod/admin.php:677 +#: mod/admin.php:950 msgid "Delivery interval" msgstr "Интервал поставки" -#: ../../mod/admin.php:677 +#: mod/admin.php:950 msgid "" "Delay background delivery processes by this many seconds to reduce system " "load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 " "for large dedicated servers." -msgstr "Установите задержку выполнения фоновых процессов доставки до указанного количества секунд, чтобы уменьшить нагрузку на систему. Рекомендация: 4-5 для обычного shared хостинга, 2-3 для виртуальных частных серверов. 0-1 для мощных выделенных серверов." +msgstr "" +"Установите задержку выполнения фоновых процессов доставки до указанного " +"количества секунд, чтобы уменьшить нагрузку на систему. Рекомендация: 4-5 " +"для обычного shared хостинга, 2-3 для виртуальных частных серверов. 0-1 для " +"мощных выделенных серверов." -#: ../../mod/admin.php:678 +#: mod/admin.php:951 msgid "Poll interval" msgstr "Интервал опроса" -#: ../../mod/admin.php:678 +#: mod/admin.php:951 msgid "" "Delay background polling processes by this many seconds to reduce system " "load. If 0, use delivery interval." -msgstr "Установить задержку фоновых процессов опросов путем ограничения количества секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал доставки." +msgstr "" +"Установить задержку фоновых процессов опросов путем ограничения количества " +"секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал " +"доставки." -#: ../../mod/admin.php:679 +#: mod/admin.php:952 msgid "Maximum Load Average" msgstr "Средняя максимальная нагрузка" -#: ../../mod/admin.php:679 +#: mod/admin.php:952 msgid "" "Maximum system load before delivery and poll processes are deferred - " "default 50." -msgstr "Максимальная нагрузка на систему перед приостановкой процессов доставки и опросов - по умолчанию 50." +msgstr "" +"Максимальная нагрузка на систему перед приостановкой процессов доставки и " +"опросов - по умолчанию 50." -#: ../../mod/admin.php:681 +#: mod/admin.php:953 +msgid "Maximum Load Average (Frontend)" +msgstr "" + +#: mod/admin.php:953 +msgid "Maximum system load before the frontend quits service - default 50." +msgstr "" + +#: mod/admin.php:954 +msgid "Maximum table size for optimization" +msgstr "" + +#: mod/admin.php:954 +msgid "" +"Maximum table size (in MB) for the automatic optimization - default 100 MB. " +"Enter -1 to disable it." +msgstr "" + +#: mod/admin.php:955 +msgid "Minimum level of fragmentation" +msgstr "" + +#: mod/admin.php:955 +msgid "" +"Minimum fragmenation level to start the automatic optimization - default " +"value is 30%." +msgstr "" + +#: mod/admin.php:957 +msgid "Periodical check of global contacts" +msgstr "" + +#: mod/admin.php:957 +msgid "" +"If enabled, the global contacts are checked periodically for missing or " +"outdated data and the vitality of the contacts and servers." +msgstr "" + +#: mod/admin.php:958 +msgid "Days between requery" +msgstr "" + +#: mod/admin.php:958 +msgid "Number of days after which a server is requeried for his contacts." +msgstr "" + +#: mod/admin.php:959 +msgid "Discover contacts from other servers" +msgstr "" + +#: mod/admin.php:959 +msgid "" +"Periodically query other servers for contacts. You can choose between " +"'users': the users on the remote system, 'Global Contacts': active contacts " +"that are known on the system. The fallback is meant for Redmatrix servers " +"and older friendica servers, where global contacts weren't available. The " +"fallback increases the server load, so the recommened setting is 'Users, " +"Global Contacts'." +msgstr "" + +#: mod/admin.php:960 +msgid "Timeframe for fetching global contacts" +msgstr "" + +#: mod/admin.php:960 +msgid "" +"When the discovery is activated, this value defines the timeframe for the " +"activity of the global contacts that are fetched from other servers." +msgstr "" + +#: mod/admin.php:961 +msgid "Search the local directory" +msgstr "" + +#: mod/admin.php:961 +msgid "" +"Search the local directory instead of the global directory. When searching " +"locally, every search will be executed on the global directory in the " +"background. This improves the search results when the search is repeated." +msgstr "" + +#: mod/admin.php:963 +msgid "Publish server information" +msgstr "" + +#: mod/admin.php:963 +msgid "" +"If enabled, general server and usage data will be published. The data " +"contains the name and version of the server, number of users with public " +"profiles, number of posts and the activated protocols and connectors. See the-federation.info for details." +msgstr "" + +#: mod/admin.php:965 msgid "Use MySQL full text engine" msgstr "Использовать систему полнотексного поиска MySQL" -#: ../../mod/admin.php:681 +#: mod/admin.php:965 msgid "" "Activates the full text engine. Speeds up search - but can only search for " "four and more characters." -msgstr "Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать только при указании четырех и более символов." +msgstr "" +"Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать " +"только при указании четырех и более символов." -#: ../../mod/admin.php:682 +#: mod/admin.php:966 msgid "Suppress Language" msgstr "" -#: ../../mod/admin.php:682 +#: mod/admin.php:966 msgid "Suppress language information in meta information about a posting." msgstr "" -#: ../../mod/admin.php:683 +#: mod/admin.php:967 msgid "Suppress Tags" msgstr "" -#: ../../mod/admin.php:683 +#: mod/admin.php:967 msgid "Suppress showing a list of hashtags at the end of the posting." msgstr "" -#: ../../mod/admin.php:684 +#: mod/admin.php:968 msgid "Path to item cache" msgstr "Путь к элементам кэша" -#: ../../mod/admin.php:685 +#: mod/admin.php:968 +msgid "The item caches buffers generated bbcode and external images." +msgstr "" + +#: mod/admin.php:969 msgid "Cache duration in seconds" msgstr "Время жизни кэша в секундах" -#: ../../mod/admin.php:685 +#: mod/admin.php:969 msgid "" -"How long should the cache files be hold? Default value is 86400 seconds (One" -" day). To disable the item cache, set the value to -1." +"How long should the cache files be hold? Default value is 86400 seconds (One " +"day). To disable the item cache, set the value to -1." msgstr "" -#: ../../mod/admin.php:686 +#: mod/admin.php:970 msgid "Maximum numbers of comments per post" msgstr "" -#: ../../mod/admin.php:686 +#: mod/admin.php:970 msgid "How much comments should be shown for each post? Default value is 100." msgstr "" -#: ../../mod/admin.php:687 +#: mod/admin.php:971 msgid "Path for lock file" msgstr "Путь к файлу блокировки" -#: ../../mod/admin.php:688 +#: mod/admin.php:971 +msgid "" +"The lock file is used to avoid multiple pollers at one time. Only define a " +"folder here." +msgstr "" + +#: mod/admin.php:972 msgid "Temp path" msgstr "Временная папка" -#: ../../mod/admin.php:689 +#: mod/admin.php:972 +msgid "" +"If you have a restricted system where the webserver can't access the system " +"temp path, enter another path here." +msgstr "" + +#: mod/admin.php:973 msgid "Base path to installation" msgstr "Путь для установки" -#: ../../mod/admin.php:690 +#: mod/admin.php:973 +msgid "" +"If the system cannot detect the correct path to your installation, enter the " +"correct path here. This setting should only be set if you are using a " +"restricted system and symbolic links to your webroot." +msgstr "" + +#: mod/admin.php:974 msgid "Disable picture proxy" msgstr "" -#: ../../mod/admin.php:690 +#: mod/admin.php:974 msgid "" -"The picture proxy increases performance and privacy. It shouldn't be used on" -" systems with very low bandwith." +"The picture proxy increases performance and privacy. It shouldn't be used on " +"systems with very low bandwith." msgstr "" -#: ../../mod/admin.php:691 +#: mod/admin.php:975 msgid "Enable old style pager" msgstr "" -#: ../../mod/admin.php:691 +#: mod/admin.php:975 msgid "" -"The old style pager has page numbers but slows down massively the page " -"speed." +"The old style pager has page numbers but slows down massively the page speed." msgstr "" -#: ../../mod/admin.php:692 +#: mod/admin.php:976 msgid "Only search in tags" msgstr "" -#: ../../mod/admin.php:692 +#: mod/admin.php:976 msgid "On large systems the text search can slow down the system extremely." msgstr "" -#: ../../mod/admin.php:694 +#: mod/admin.php:978 msgid "New base url" msgstr "Новый базовый url" -#: ../../mod/admin.php:711 +#: mod/admin.php:978 +msgid "" +"Change base url for this server. Sends relocate message to all DFRN contacts " +"of all users." +msgstr "" + +#: mod/admin.php:980 +msgid "RINO Encryption" +msgstr "RINO шифрование" + +#: mod/admin.php:980 +msgid "Encryption layer between nodes." +msgstr "Слой шифрования между узлами." + +#: mod/admin.php:981 +msgid "Embedly API key" +msgstr "" + +#: mod/admin.php:981 +msgid "" +"Embedly is used to fetch additional data for " +"web pages. This is an optional parameter." +msgstr "" + +#: mod/admin.php:1010 msgid "Update has been marked successful" msgstr "Обновление было успешно отмечено" -#: ../../mod/admin.php:719 +#: mod/admin.php:1018 #, php-format msgid "Database structure update %s was successfully applied." msgstr "" -#: ../../mod/admin.php:722 +#: mod/admin.php:1021 #, php-format msgid "Executing of database structure update %s failed with error: %s" msgstr "" -#: ../../mod/admin.php:734 +#: mod/admin.php:1033 #, php-format msgid "Executing %s failed with error: %s" msgstr "" -#: ../../mod/admin.php:737 +#: mod/admin.php:1036 #, php-format msgid "Update %s was successfully applied." msgstr "Обновление %s успешно применено." -#: ../../mod/admin.php:741 +#: mod/admin.php:1040 #, php-format msgid "Update %s did not return a status. Unknown if it succeeded." -msgstr "Процесс обновления %s не вернул статус. Не известно, выполнено, или нет." +msgstr "" +"Процесс обновления %s не вернул статус. Не известно, выполнено, или нет." -#: ../../mod/admin.php:743 +#: mod/admin.php:1042 #, php-format msgid "There was no additional update function %s that needed to be called." msgstr "" -#: ../../mod/admin.php:762 +#: mod/admin.php:1061 msgid "No failed updates." msgstr "Неудавшихся обновлений нет." -#: ../../mod/admin.php:763 +#: mod/admin.php:1062 msgid "Check database structure" msgstr "" -#: ../../mod/admin.php:768 +#: mod/admin.php:1067 msgid "Failed Updates" msgstr "Неудавшиеся обновления" -#: ../../mod/admin.php:769 +#: mod/admin.php:1068 msgid "" "This does not include updates prior to 1139, which did not return a status." -msgstr "Эта цифра не включает обновления до 1139, которое не возвращает статус." +msgstr "" +"Эта цифра не включает обновления до 1139, которое не возвращает статус." -#: ../../mod/admin.php:770 +#: mod/admin.php:1069 msgid "Mark success (if update was manually applied)" msgstr "Отмечено успешно (если обновление было применено вручную)" -#: ../../mod/admin.php:771 +#: mod/admin.php:1070 msgid "Attempt to execute this update step automatically" msgstr "Попытаться выполнить этот шаг обновления автоматически" -#: ../../mod/admin.php:803 +#: mod/admin.php:1102 #, php-format msgid "" "\n" @@ -2624,7 +3111,7 @@ msgid "" "\t\t\t\tthe administrator of %2$s has set up an account for you." msgstr "" -#: ../../mod/admin.php:806 +#: mod/admin.php:1105 #, php-format msgid "" "\n" @@ -2634,310 +3121,354 @@ msgid "" "\t\t\tLogin Name:\t\t%2$s\n" "\t\t\tPassword:\t\t%3$s\n" "\n" -"\t\t\tYou may change your password from your account \"Settings\" page after logging\n" +"\t\t\tYou may change your password from your account \"Settings\" page after " +"logging\n" "\t\t\tin.\n" "\n" -"\t\t\tPlease take a few moments to review the other account settings on that page.\n" +"\t\t\tPlease take a few moments to review the other account settings on that " +"page.\n" "\n" -"\t\t\tYou may also wish to add some basic information to your default profile\n" +"\t\t\tYou may also wish to add some basic information to your default " +"profile\n" "\t\t\t(on the \"Profiles\" page) so that other people can easily find you.\n" "\n" "\t\t\tWe recommend setting your full name, adding a profile photo,\n" -"\t\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n" -"\t\t\tperhaps what country you live in; if you do not wish to be more specific\n" +"\t\t\tadding some profile \"keywords\" (very useful in making new friends) - " +"and\n" +"\t\t\tperhaps what country you live in; if you do not wish to be more " +"specific\n" "\t\t\tthan that.\n" "\n" -"\t\t\tWe fully respect your right to privacy, and none of these items are necessary.\n" +"\t\t\tWe fully respect your right to privacy, and none of these items are " +"necessary.\n" "\t\t\tIf you are new and do not know anybody here, they may help\n" "\t\t\tyou to make some new and interesting friends.\n" "\n" "\t\t\tThank you and welcome to %4$s." msgstr "" -#: ../../mod/admin.php:838 ../../include/user.php:413 +#: mod/admin.php:1137 include/user.php:423 #, php-format msgid "Registration details for %s" msgstr "Подробности регистрации для %s" -#: ../../mod/admin.php:850 +#: mod/admin.php:1149 #, php-format msgid "%s user blocked/unblocked" msgid_plural "%s users blocked/unblocked" msgstr[0] "%s пользователь заблокирован/разблокирован" msgstr[1] "%s пользователей заблокировано/разблокировано" msgstr[2] "%s пользователей заблокировано/разблокировано" +msgstr[3] "%s пользователей заблокировано/разблокировано" -#: ../../mod/admin.php:857 +#: mod/admin.php:1156 #, php-format msgid "%s user deleted" msgid_plural "%s users deleted" msgstr[0] "%s человек удален" msgstr[1] "%s чел. удалено" msgstr[2] "%s чел. удалено" +msgstr[3] "%s чел. удалено" -#: ../../mod/admin.php:896 +#: mod/admin.php:1203 #, php-format msgid "User '%s' deleted" msgstr "Пользователь '%s' удален" -#: ../../mod/admin.php:904 +#: mod/admin.php:1211 #, php-format msgid "User '%s' unblocked" msgstr "Пользователь '%s' разблокирован" -#: ../../mod/admin.php:904 +#: mod/admin.php:1211 #, php-format msgid "User '%s' blocked" msgstr "Пользователь '%s' блокирован" -#: ../../mod/admin.php:999 +#: mod/admin.php:1302 msgid "Add User" msgstr "Добавить пользователя" -#: ../../mod/admin.php:1000 +#: mod/admin.php:1303 msgid "select all" msgstr "выбрать все" -#: ../../mod/admin.php:1001 +#: mod/admin.php:1304 msgid "User registrations waiting for confirm" msgstr "Регистрации пользователей, ожидающие подтверждения" -#: ../../mod/admin.php:1002 +#: mod/admin.php:1305 msgid "User waiting for permanent deletion" msgstr "Пользователь ожидает окончательного удаления" -#: ../../mod/admin.php:1003 +#: mod/admin.php:1306 msgid "Request date" msgstr "Запрос даты" -#: ../../mod/admin.php:1003 ../../mod/admin.php:1015 ../../mod/admin.php:1016 -#: ../../mod/admin.php:1031 ../../include/contact_selectors.php:79 -#: ../../include/contact_selectors.php:86 +#: mod/admin.php:1306 mod/admin.php:1318 mod/admin.php:1319 mod/admin.php:1334 +#: include/contact_selectors.php:79 include/contact_selectors.php:86 msgid "Email" msgstr "Эл. почта" -#: ../../mod/admin.php:1004 +#: mod/admin.php:1307 msgid "No registrations." msgstr "Нет регистраций." -#: ../../mod/admin.php:1006 +#: mod/admin.php:1309 msgid "Deny" msgstr "Отклонить" -#: ../../mod/admin.php:1010 +#: mod/admin.php:1313 msgid "Site admin" msgstr "Админ сайта" -#: ../../mod/admin.php:1011 +#: mod/admin.php:1314 msgid "Account expired" msgstr "Аккаунт просрочен" -#: ../../mod/admin.php:1014 +#: mod/admin.php:1317 msgid "New User" msgstr "Новый пользователь" -#: ../../mod/admin.php:1015 ../../mod/admin.php:1016 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Register date" msgstr "Дата регистрации" -#: ../../mod/admin.php:1015 ../../mod/admin.php:1016 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Last login" msgstr "Последний вход" -#: ../../mod/admin.php:1015 ../../mod/admin.php:1016 +#: mod/admin.php:1318 mod/admin.php:1319 msgid "Last item" msgstr "Последний пункт" -#: ../../mod/admin.php:1015 +#: mod/admin.php:1318 msgid "Deleted since" msgstr "Удалён с" -#: ../../mod/admin.php:1016 ../../mod/settings.php:36 +#: mod/admin.php:1319 mod/settings.php:41 msgid "Account" msgstr "Аккаунт" -#: ../../mod/admin.php:1018 +#: mod/admin.php:1321 msgid "" "Selected users will be deleted!\\n\\nEverything these users had posted on " "this site will be permanently deleted!\\n\\nAre you sure?" -msgstr "Выбранные пользователи будут удалены!\\n\\nВсе, что эти пользователи написали на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?" +msgstr "" +"Выбранные пользователи будут удалены!\\n\\nВсе, что эти пользователи " +"написали на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?" -#: ../../mod/admin.php:1019 +#: mod/admin.php:1322 msgid "" "The user {0} will be deleted!\\n\\nEverything this user has posted on this " "site will be permanently deleted!\\n\\nAre you sure?" -msgstr "Пользователь {0} будет удален!\\n\\nВсе, что этот пользователь написал на этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?" +msgstr "" +"Пользователь {0} будет удален!\\n\\nВсе, что этот пользователь написал на " +"этом сайте, будет удалено!\\n\\nВы уверены в вашем действии?" -#: ../../mod/admin.php:1029 +#: mod/admin.php:1332 msgid "Name of the new user." msgstr "Имя нового пользователя." -#: ../../mod/admin.php:1030 +#: mod/admin.php:1333 msgid "Nickname" msgstr "Ник" -#: ../../mod/admin.php:1030 +#: mod/admin.php:1333 msgid "Nickname of the new user." msgstr "Ник нового пользователя." -#: ../../mod/admin.php:1031 +#: mod/admin.php:1334 msgid "Email address of the new user." msgstr "Email адрес нового пользователя." -#: ../../mod/admin.php:1064 +#: mod/admin.php:1377 #, php-format msgid "Plugin %s disabled." msgstr "Плагин %s отключен." -#: ../../mod/admin.php:1068 +#: mod/admin.php:1381 #, php-format msgid "Plugin %s enabled." msgstr "Плагин %s включен." -#: ../../mod/admin.php:1078 ../../mod/admin.php:1294 +#: mod/admin.php:1392 mod/admin.php:1628 msgid "Disable" msgstr "Отключить" -#: ../../mod/admin.php:1080 ../../mod/admin.php:1296 +#: mod/admin.php:1394 mod/admin.php:1630 msgid "Enable" msgstr "Включить" -#: ../../mod/admin.php:1103 ../../mod/admin.php:1324 +#: mod/admin.php:1417 mod/admin.php:1675 msgid "Toggle" msgstr "Переключить" -#: ../../mod/admin.php:1111 ../../mod/admin.php:1334 +#: mod/admin.php:1425 mod/admin.php:1684 msgid "Author: " msgstr "Автор:" -#: ../../mod/admin.php:1112 ../../mod/admin.php:1335 +#: mod/admin.php:1426 mod/admin.php:1685 msgid "Maintainer: " msgstr "Программа обслуживания: " -#: ../../mod/admin.php:1254 +#: mod/admin.php:1478 +msgid "Reload active plugins" +msgstr "" + +#: mod/admin.php:1483 +#, php-format +msgid "" +"There are currently no plugins available on your node. You can find the " +"official plugin repository at %1$s and might find other interesting plugins " +"in the open plugin registry at %2$s" +msgstr "" + +#: mod/admin.php:1588 msgid "No themes found." msgstr "Темы не найдены." -#: ../../mod/admin.php:1316 +#: mod/admin.php:1666 msgid "Screenshot" msgstr "Скриншот" -#: ../../mod/admin.php:1362 +#: mod/admin.php:1726 +msgid "Reload active themes" +msgstr "" + +#: mod/admin.php:1731 +#, php-format +msgid "No themes found on the system. They should be paced in %1$s" +msgstr "" + +#: mod/admin.php:1732 msgid "[Experimental]" msgstr "[экспериментально]" -#: ../../mod/admin.php:1363 +#: mod/admin.php:1733 msgid "[Unsupported]" msgstr "[Неподдерживаемое]" -#: ../../mod/admin.php:1390 +#: mod/admin.php:1757 msgid "Log settings updated." msgstr "Настройки журнала обновлены." -#: ../../mod/admin.php:1446 +#: mod/admin.php:1794 msgid "Clear" msgstr "Очистить" -#: ../../mod/admin.php:1452 +#: mod/admin.php:1799 msgid "Enable Debugging" msgstr "Включить отладку" -#: ../../mod/admin.php:1453 +#: mod/admin.php:1800 msgid "Log file" msgstr "Лог-файл" -#: ../../mod/admin.php:1453 +#: mod/admin.php:1800 msgid "" "Must be writable by web server. Relative to your Friendica top-level " "directory." -msgstr "Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica каталога верхнего уровня." +msgstr "" +"Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica " +"каталога верхнего уровня." -#: ../../mod/admin.php:1454 +#: mod/admin.php:1801 msgid "Log level" msgstr "Уровень лога" -#: ../../mod/admin.php:1504 -msgid "Close" -msgstr "Закрыть" +#: mod/admin.php:1804 +msgid "PHP logging" +msgstr "PHP логирование" -#: ../../mod/admin.php:1510 -msgid "FTP Host" -msgstr "FTP хост" +#: mod/admin.php:1805 +msgid "" +"To enable logging of PHP errors and warnings you can add the following to " +"the .htconfig.php file of your installation. The filename set in the " +"'error_log' line is relative to the friendica top-level directory and must " +"be writeable by the web server. The option '1' for 'log_errors' and " +"'display_errors' is to enable these options, set to '0' to disable them." +msgstr "" -#: ../../mod/admin.php:1511 -msgid "FTP Path" -msgstr "Путь FTP" +#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +msgid "Off" +msgstr "Выкл." -#: ../../mod/admin.php:1512 -msgid "FTP User" -msgstr "FTP пользователь" +#: mod/admin.php:1931 mod/admin.php:1932 mod/settings.php:759 +msgid "On" +msgstr "Вкл." -#: ../../mod/admin.php:1513 -msgid "FTP Password" -msgstr "FTP пароль" +#: mod/admin.php:1932 +#, php-format +msgid "Lock feature %s" +msgstr "" -#: ../../mod/network.php:142 -msgid "Search Results For:" -msgstr "Результаты поиска для:" +#: mod/admin.php:1940 +msgid "Manage Additional Features" +msgstr "" -#: ../../mod/network.php:185 ../../mod/search.php:21 +#: mod/network.php:146 +#, php-format +msgid "Search Results For: %s" +msgstr "" + +#: mod/network.php:191 mod/search.php:25 msgid "Remove term" msgstr "Удалить элемент" -#: ../../mod/network.php:194 ../../mod/search.php:30 -#: ../../include/features.php:42 +#: mod/network.php:200 mod/search.php:34 include/features.php:84 msgid "Saved Searches" msgstr "запомненные поиски" -#: ../../mod/network.php:195 ../../include/group.php:275 +#: mod/network.php:201 include/group.php:293 msgid "add" msgstr "добавить" -#: ../../mod/network.php:356 +#: mod/network.php:365 msgid "Commented Order" -msgstr "Прокомментированный запрос" +msgstr "Последние комментарии" -#: ../../mod/network.php:359 +#: mod/network.php:368 msgid "Sort by Comment Date" msgstr "Сортировать по дате комментария" -#: ../../mod/network.php:362 +#: mod/network.php:373 msgid "Posted Order" -msgstr "Отправленный запрос" +msgstr "Лента записей" -#: ../../mod/network.php:365 +#: mod/network.php:376 msgid "Sort by Post Date" msgstr "Сортировать по дате отправки" -#: ../../mod/network.php:374 +#: mod/network.php:387 msgid "Posts that mention or involve you" msgstr "" -#: ../../mod/network.php:380 +#: mod/network.php:395 msgid "New" msgstr "Новый" -#: ../../mod/network.php:383 +#: mod/network.php:398 msgid "Activity Stream - by date" msgstr "Лента активности - по дате" -#: ../../mod/network.php:389 +#: mod/network.php:406 msgid "Shared Links" msgstr "Ссылки, которыми поделились" -#: ../../mod/network.php:392 +#: mod/network.php:409 msgid "Interesting Links" msgstr "Интересные ссылки" -#: ../../mod/network.php:398 +#: mod/network.php:417 msgid "Starred" msgstr "Помеченный" -#: ../../mod/network.php:401 +#: mod/network.php:420 msgid "Favourite Posts" msgstr "Избранные посты" -#: ../../mod/network.php:463 +#: mod/network.php:479 #, php-format msgid "Warning: This group contains %s member from an insecure network." msgid_plural "" @@ -2945,4152 +3476,4632 @@ msgid_plural "" msgstr[0] "Внимание: Эта группа содержит %s участника с незащищенной сети." msgstr[1] "Внимание: Эта группа содержит %s участников с незащищенной сети." msgstr[2] "Внимание: Эта группа содержит %s участников с незащищенной сети." +msgstr[3] "Внимание: Эта группа содержит %s участников с незащищенной сети." -#: ../../mod/network.php:466 +#: mod/network.php:482 msgid "Private messages to this group are at risk of public disclosure." msgstr "Личные сообщения к этой группе находятся под угрозой обнародования." -#: ../../mod/network.php:520 ../../mod/content.php:119 +#: mod/network.php:549 mod/content.php:119 msgid "No such group" msgstr "Нет такой группы" -#: ../../mod/network.php:537 ../../mod/content.php:130 -msgid "Group is empty" -msgstr "Группа пуста" +#: mod/network.php:580 mod/content.php:135 +#, php-format +msgid "Group: %s" +msgstr "Группа: %s" -#: ../../mod/network.php:544 ../../mod/content.php:134 -msgid "Group: " -msgstr "Группа: " - -#: ../../mod/network.php:554 -msgid "Contact: " -msgstr "Контакт: " - -#: ../../mod/network.php:556 +#: mod/network.php:608 msgid "Private messages to this person are at risk of public disclosure." msgstr "Личные сообщения этому человеку находятся под угрозой обнародования." -#: ../../mod/network.php:561 +#: mod/network.php:613 msgid "Invalid contact." msgstr "Недопустимый контакт." -#: ../../mod/allfriends.php:34 -#, php-format -msgid "Friends of %s" -msgstr "%s Друзья" - -#: ../../mod/allfriends.php:40 +#: mod/allfriends.php:43 msgid "No friends to display." msgstr "Нет друзей." -#: ../../mod/events.php:66 +#: mod/events.php:71 mod/events.php:73 +msgid "Event can not end before it has started." +msgstr "" + +#: mod/events.php:80 mod/events.php:82 msgid "Event title and start time are required." msgstr "Название мероприятия и время начала обязательны для заполнения." -#: ../../mod/events.php:291 +#: mod/events.php:201 +msgid "Sun" +msgstr "Вс" + +#: mod/events.php:202 +msgid "Mon" +msgstr "Пн" + +#: mod/events.php:203 +msgid "Tue" +msgstr "Вт" + +#: mod/events.php:204 +msgid "Wed" +msgstr "Ср" + +#: mod/events.php:205 +msgid "Thu" +msgstr "Чт" + +#: mod/events.php:206 +msgid "Fri" +msgstr "Пт" + +#: mod/events.php:207 +msgid "Sat" +msgstr "Сб" + +#: mod/events.php:208 mod/settings.php:948 include/text.php:1274 +msgid "Sunday" +msgstr "Воскресенье" + +#: mod/events.php:209 mod/settings.php:948 include/text.php:1274 +msgid "Monday" +msgstr "Понедельник" + +#: mod/events.php:210 include/text.php:1274 +msgid "Tuesday" +msgstr "Вторник" + +#: mod/events.php:211 include/text.php:1274 +msgid "Wednesday" +msgstr "Среда" + +#: mod/events.php:212 include/text.php:1274 +msgid "Thursday" +msgstr "Четверг" + +#: mod/events.php:213 include/text.php:1274 +msgid "Friday" +msgstr "Пятница" + +#: mod/events.php:214 include/text.php:1274 +msgid "Saturday" +msgstr "Суббота" + +#: mod/events.php:215 +msgid "Jan" +msgstr "Янв" + +#: mod/events.php:216 +msgid "Feb" +msgstr "Фев" + +#: mod/events.php:217 +msgid "Mar" +msgstr "Мрт" + +#: mod/events.php:218 +msgid "Apr" +msgstr "Апр" + +#: mod/events.php:219 mod/events.php:231 include/text.php:1278 +msgid "May" +msgstr "Май" + +#: mod/events.php:220 +msgid "Jun" +msgstr "Июн" + +#: mod/events.php:221 +msgid "Jul" +msgstr "Июл" + +#: mod/events.php:222 +msgid "Aug" +msgstr "Авг" + +#: mod/events.php:223 +msgid "Sept" +msgstr "Сен" + +#: mod/events.php:224 +msgid "Oct" +msgstr "Окт" + +#: mod/events.php:225 +msgid "Nov" +msgstr "Нбр" + +#: mod/events.php:226 +msgid "Dec" +msgstr "Дек" + +#: mod/events.php:227 include/text.php:1278 +msgid "January" +msgstr "Январь" + +#: mod/events.php:228 include/text.php:1278 +msgid "February" +msgstr "Февраль" + +#: mod/events.php:229 include/text.php:1278 +msgid "March" +msgstr "Март" + +#: mod/events.php:230 include/text.php:1278 +msgid "April" +msgstr "Апрель" + +#: mod/events.php:232 include/text.php:1278 +msgid "June" +msgstr "Июнь" + +#: mod/events.php:233 include/text.php:1278 +msgid "July" +msgstr "Июль" + +#: mod/events.php:234 include/text.php:1278 +msgid "August" +msgstr "Август" + +#: mod/events.php:235 include/text.php:1278 +msgid "September" +msgstr "Сентябрь" + +#: mod/events.php:236 include/text.php:1278 +msgid "October" +msgstr "Октябрь" + +#: mod/events.php:237 include/text.php:1278 +msgid "November" +msgstr "Ноябрь" + +#: mod/events.php:238 include/text.php:1278 +msgid "December" +msgstr "Декабрь" + +#: mod/events.php:239 +msgid "today" +msgstr "сегодня" + +#: mod/events.php:240 include/datetime.php:288 +msgid "month" +msgstr "мес." + +#: mod/events.php:241 include/datetime.php:289 +msgid "week" +msgstr "неделя" + +#: mod/events.php:242 include/datetime.php:290 +msgid "day" +msgstr "день" + +#: mod/events.php:377 msgid "l, F j" msgstr "l, j F" -#: ../../mod/events.php:313 +#: mod/events.php:399 msgid "Edit event" msgstr "Редактировать мероприятие" -#: ../../mod/events.php:335 ../../include/text.php:1647 -#: ../../include/text.php:1657 +#: mod/events.php:421 include/text.php:1728 include/text.php:1735 msgid "link to source" -msgstr "ссылка на источник" +msgstr "ссылка на сообщение" -#: ../../mod/events.php:370 ../../boot.php:2143 ../../include/nav.php:80 -#: ../../view/theme/diabook/theme.php:127 +#: mod/events.php:456 include/identity.php:722 include/nav.php:79 +#: include/nav.php:140 view/theme/diabook/theme.php:127 msgid "Events" msgstr "Мероприятия" -#: ../../mod/events.php:371 +#: mod/events.php:457 msgid "Create New Event" msgstr "Создать новое мероприятие" -#: ../../mod/events.php:372 +#: mod/events.php:458 msgid "Previous" msgstr "Назад" -#: ../../mod/events.php:373 ../../mod/install.php:207 +#: mod/events.php:459 mod/install.php:220 msgid "Next" msgstr "Далее" -#: ../../mod/events.php:446 -msgid "hour:minute" -msgstr "час:минута" - -#: ../../mod/events.php:456 +#: mod/events.php:554 msgid "Event details" msgstr "Сведения о мероприятии" -#: ../../mod/events.php:457 -#, php-format -msgid "Format is %s %s. Starting date and Title are required." -msgstr "Формат %s %s. Необхлдима дата старта и заголовок." +#: mod/events.php:555 +msgid "Starting date and Title are required." +msgstr "" -#: ../../mod/events.php:459 +#: mod/events.php:556 msgid "Event Starts:" msgstr "Начало мероприятия:" -#: ../../mod/events.php:459 ../../mod/events.php:473 +#: mod/events.php:556 mod/events.php:568 msgid "Required" msgstr "Требуется" -#: ../../mod/events.php:462 +#: mod/events.php:558 msgid "Finish date/time is not known or not relevant" msgstr "Дата/время окончания не известны, или не указаны" -#: ../../mod/events.php:464 +#: mod/events.php:560 msgid "Event Finishes:" msgstr "Окончание мероприятия:" -#: ../../mod/events.php:467 +#: mod/events.php:562 msgid "Adjust for viewer timezone" msgstr "Настройка часового пояса" -#: ../../mod/events.php:469 +#: mod/events.php:564 msgid "Description:" msgstr "Описание:" -#: ../../mod/events.php:471 ../../mod/directory.php:136 ../../boot.php:1648 -#: ../../include/bb2diaspora.php:170 ../../include/event.php:40 -msgid "Location:" -msgstr "Откуда:" - -#: ../../mod/events.php:473 +#: mod/events.php:568 msgid "Title:" msgstr "Титул:" -#: ../../mod/events.php:475 +#: mod/events.php:570 msgid "Share this event" msgstr "Поделитесь этим мероприятием" -#: ../../mod/content.php:437 ../../mod/content.php:740 -#: ../../mod/photos.php:1653 ../../object/Item.php:129 -#: ../../include/conversation.php:613 +#: mod/events.php:572 mod/content.php:721 mod/editpost.php:145 +#: mod/photos.php:1631 mod/photos.php:1679 mod/photos.php:1767 +#: object/Item.php:719 include/conversation.php:1216 +msgid "Preview" +msgstr "Предварительный просмотр" + +#: mod/credits.php:16 +msgid "Credits" +msgstr "" + +#: mod/credits.php:17 +msgid "" +"Friendica is a community project, that would not be possible without the " +"help of many people. Here is a list of those who have contributed to the " +"code or the translation of Friendica. Thank you all!" +msgstr "" + +#: mod/content.php:439 mod/content.php:742 mod/photos.php:1722 +#: object/Item.php:133 include/conversation.php:634 msgid "Select" msgstr "Выберите" -#: ../../mod/content.php:471 ../../mod/content.php:852 -#: ../../mod/content.php:853 ../../object/Item.php:326 -#: ../../object/Item.php:327 ../../include/conversation.php:654 +#: mod/content.php:473 mod/content.php:854 mod/content.php:855 +#: object/Item.php:357 object/Item.php:358 include/conversation.php:675 #, php-format msgid "View %s's profile @ %s" msgstr "Просмотреть профиль %s [@ %s]" -#: ../../mod/content.php:481 ../../mod/content.php:864 -#: ../../object/Item.php:340 ../../include/conversation.php:674 +#: mod/content.php:483 mod/content.php:866 object/Item.php:371 +#: include/conversation.php:695 #, php-format msgid "%s from %s" msgstr "%s с %s" -#: ../../mod/content.php:497 ../../include/conversation.php:690 +#: mod/content.php:499 include/conversation.php:711 msgid "View in context" msgstr "Смотреть в контексте" -#: ../../mod/content.php:603 ../../object/Item.php:387 +#: mod/content.php:605 object/Item.php:419 #, php-format msgid "%d comment" msgid_plural "%d comments" msgstr[0] "%d комментарий" msgstr[1] "%d комментариев" msgstr[2] "%d комментариев" +msgstr[3] "%d комментариев" -#: ../../mod/content.php:605 ../../object/Item.php:389 -#: ../../object/Item.php:402 ../../include/text.php:1972 +#: mod/content.php:607 object/Item.php:421 object/Item.php:434 +#: include/text.php:2004 msgid "comment" msgid_plural "comments" msgstr[0] "" msgstr[1] "" msgstr[2] "комментарий" +msgstr[3] "комментарий" -#: ../../mod/content.php:606 ../../boot.php:751 ../../object/Item.php:390 -#: ../../include/contact_widgets.php:205 +#: mod/content.php:608 boot.php:870 object/Item.php:422 +#: include/contact_widgets.php:242 include/forums.php:110 +#: include/items.php:5207 view/theme/vier/theme.php:264 msgid "show more" msgstr "показать больше" -#: ../../mod/content.php:620 ../../mod/photos.php:1359 -#: ../../object/Item.php:116 +#: mod/content.php:622 mod/photos.php:1418 object/Item.php:117 msgid "Private Message" msgstr "Личное сообщение" -#: ../../mod/content.php:684 ../../mod/photos.php:1542 -#: ../../object/Item.php:231 +#: mod/content.php:686 mod/photos.php:1607 object/Item.php:253 msgid "I like this (toggle)" msgstr "Нравится" -#: ../../mod/content.php:684 ../../object/Item.php:231 +#: mod/content.php:686 object/Item.php:253 msgid "like" msgstr "нравится" -#: ../../mod/content.php:685 ../../mod/photos.php:1543 -#: ../../object/Item.php:232 +#: mod/content.php:687 mod/photos.php:1608 object/Item.php:254 msgid "I don't like this (toggle)" msgstr "Не нравится" -#: ../../mod/content.php:685 ../../object/Item.php:232 +#: mod/content.php:687 object/Item.php:254 msgid "dislike" msgstr "не нравитса" -#: ../../mod/content.php:687 ../../object/Item.php:234 +#: mod/content.php:689 object/Item.php:256 msgid "Share this" msgstr "Поделитесь этим" -#: ../../mod/content.php:687 ../../object/Item.php:234 +#: mod/content.php:689 object/Item.php:256 msgid "share" msgstr "делиться" -#: ../../mod/content.php:707 ../../mod/photos.php:1562 -#: ../../mod/photos.php:1606 ../../mod/photos.php:1694 -#: ../../object/Item.php:675 +#: mod/content.php:709 mod/photos.php:1627 mod/photos.php:1675 +#: mod/photos.php:1763 object/Item.php:707 msgid "This is you" msgstr "Это вы" -#: ../../mod/content.php:709 ../../mod/photos.php:1564 -#: ../../mod/photos.php:1608 ../../mod/photos.php:1696 ../../boot.php:750 -#: ../../object/Item.php:361 ../../object/Item.php:677 +#: mod/content.php:711 mod/photos.php:1629 mod/photos.php:1677 +#: mod/photos.php:1765 boot.php:869 object/Item.php:393 object/Item.php:709 msgid "Comment" -msgstr "Комментарий" +msgstr "Оставить комментарий" -#: ../../mod/content.php:711 ../../object/Item.php:679 +#: mod/content.php:713 object/Item.php:711 msgid "Bold" msgstr "Жирный" -#: ../../mod/content.php:712 ../../object/Item.php:680 +#: mod/content.php:714 object/Item.php:712 msgid "Italic" msgstr "Kурсивный" -#: ../../mod/content.php:713 ../../object/Item.php:681 +#: mod/content.php:715 object/Item.php:713 msgid "Underline" msgstr "Подчеркнутый" -#: ../../mod/content.php:714 ../../object/Item.php:682 +#: mod/content.php:716 object/Item.php:714 msgid "Quote" msgstr "Цитата" -#: ../../mod/content.php:715 ../../object/Item.php:683 +#: mod/content.php:717 object/Item.php:715 msgid "Code" msgstr "Код" -#: ../../mod/content.php:716 ../../object/Item.php:684 +#: mod/content.php:718 object/Item.php:716 msgid "Image" msgstr "Изображение / Фото" -#: ../../mod/content.php:717 ../../object/Item.php:685 +#: mod/content.php:719 object/Item.php:717 msgid "Link" msgstr "Ссылка" -#: ../../mod/content.php:718 ../../object/Item.php:686 +#: mod/content.php:720 object/Item.php:718 msgid "Video" msgstr "Видео" -#: ../../mod/content.php:719 ../../mod/editpost.php:145 -#: ../../mod/photos.php:1566 ../../mod/photos.php:1610 -#: ../../mod/photos.php:1698 ../../object/Item.php:687 -#: ../../include/conversation.php:1126 -msgid "Preview" -msgstr "предварительный просмотр" - -#: ../../mod/content.php:728 ../../mod/settings.php:676 -#: ../../object/Item.php:120 +#: mod/content.php:730 mod/settings.php:721 object/Item.php:122 +#: object/Item.php:124 msgid "Edit" msgstr "Редактировать" -#: ../../mod/content.php:753 ../../object/Item.php:195 +#: mod/content.php:755 object/Item.php:217 msgid "add star" msgstr "пометить" -#: ../../mod/content.php:754 ../../object/Item.php:196 +#: mod/content.php:756 object/Item.php:218 msgid "remove star" msgstr "убрать метку" -#: ../../mod/content.php:755 ../../object/Item.php:197 +#: mod/content.php:757 object/Item.php:219 msgid "toggle star status" msgstr "переключить статус" -#: ../../mod/content.php:758 ../../object/Item.php:200 +#: mod/content.php:760 object/Item.php:222 msgid "starred" msgstr "помечено" -#: ../../mod/content.php:759 ../../object/Item.php:220 +#: mod/content.php:761 object/Item.php:242 msgid "add tag" msgstr "добавить ключевое слово (таг)" -#: ../../mod/content.php:763 ../../object/Item.php:133 +#: mod/content.php:765 object/Item.php:137 msgid "save to folder" msgstr "сохранить в папке" -#: ../../mod/content.php:854 ../../object/Item.php:328 +#: mod/content.php:856 object/Item.php:359 msgid "to" msgstr "к" -#: ../../mod/content.php:855 ../../object/Item.php:330 +#: mod/content.php:857 object/Item.php:361 msgid "Wall-to-Wall" msgstr "Стена-на-Стену" -#: ../../mod/content.php:856 ../../object/Item.php:331 +#: mod/content.php:858 object/Item.php:362 msgid "via Wall-To-Wall:" msgstr "через Стена-на-Стену:" -#: ../../mod/removeme.php:46 ../../mod/removeme.php:49 +#: mod/removeme.php:46 mod/removeme.php:49 msgid "Remove My Account" msgstr "Удалить мой аккаунт" -#: ../../mod/removeme.php:47 +#: mod/removeme.php:47 msgid "" "This will completely remove your account. Once this has been done it is not " "recoverable." -msgstr "Это позволит полностью удалить ваш аккаунт. Как только это будет сделано, аккаунт восстановлению не подлежит." +msgstr "" +"Это позволит полностью удалить ваш аккаунт. Как только это будет сделано, " +"аккаунт восстановлению не подлежит." -#: ../../mod/removeme.php:48 +#: mod/removeme.php:48 msgid "Please enter your password for verification:" msgstr "Пожалуйста, введите свой пароль для проверки:" -#: ../../mod/install.php:117 +#: mod/install.php:128 msgid "Friendica Communications Server - Setup" msgstr "Коммуникационный сервер Friendica - Доступ" -#: ../../mod/install.php:123 +#: mod/install.php:134 msgid "Could not connect to database." msgstr "Не удалось подключиться к базе данных." -#: ../../mod/install.php:127 +#: mod/install.php:138 msgid "Could not create table." msgstr "Не удалось создать таблицу." -#: ../../mod/install.php:133 +#: mod/install.php:144 msgid "Your Friendica site database has been installed." msgstr "База данных сайта установлена." -#: ../../mod/install.php:138 +#: mod/install.php:149 msgid "" "You may need to import the file \"database.sql\" manually using phpmyadmin " "or mysql." -msgstr "Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью PhpMyAdmin или MySQL." +msgstr "" +"Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью " +"PhpMyAdmin или MySQL." -#: ../../mod/install.php:139 ../../mod/install.php:206 -#: ../../mod/install.php:525 +#: mod/install.php:150 mod/install.php:219 mod/install.php:577 msgid "Please see the file \"INSTALL.txt\"." msgstr "Пожалуйста, смотрите файл \"INSTALL.txt\"." -#: ../../mod/install.php:203 +#: mod/install.php:162 +msgid "Database already in use." +msgstr "" + +#: mod/install.php:216 msgid "System check" msgstr "Проверить систему" -#: ../../mod/install.php:208 +#: mod/install.php:221 msgid "Check again" msgstr "Проверить еще раз" -#: ../../mod/install.php:227 +#: mod/install.php:240 msgid "Database connection" msgstr "Подключение к базе данных" -#: ../../mod/install.php:228 +#: mod/install.php:241 msgid "" "In order to install Friendica we need to know how to connect to your " "database." -msgstr "Для того, чтобы установить Friendica, мы должны знать, как подключиться к базе данных." +msgstr "" +"Для того, чтобы установить Friendica, мы должны знать, как подключиться к " +"базе данных." -#: ../../mod/install.php:229 +#: mod/install.php:242 msgid "" "Please contact your hosting provider or site administrator if you have " "questions about these settings." -msgstr "Пожалуйста, свяжитесь с вашим хостинг-провайдером или администратором сайта, если у вас есть вопросы об этих параметрах." +msgstr "" +"Пожалуйста, свяжитесь с вашим хостинг-провайдером или администратором сайта, " +"если у вас есть вопросы об этих параметрах." -#: ../../mod/install.php:230 +#: mod/install.php:243 msgid "" "The database you specify below should already exist. If it does not, please " "create it before continuing." -msgstr "Базы данных, указанная ниже, должна уже существовать. Если этого нет, пожалуйста, создайте ее перед продолжением." +msgstr "" +"Базы данных, указанная ниже, должна уже существовать. Если этого нет, " +"пожалуйста, создайте ее перед продолжением." -#: ../../mod/install.php:234 +#: mod/install.php:247 msgid "Database Server Name" msgstr "Имя сервера базы данных" -#: ../../mod/install.php:235 +#: mod/install.php:248 msgid "Database Login Name" msgstr "Логин базы данных" -#: ../../mod/install.php:236 +#: mod/install.php:249 msgid "Database Login Password" msgstr "Пароль базы данных" -#: ../../mod/install.php:237 +#: mod/install.php:250 msgid "Database Name" msgstr "Имя базы данных" -#: ../../mod/install.php:238 ../../mod/install.php:277 +#: mod/install.php:251 mod/install.php:290 msgid "Site administrator email address" msgstr "Адрес электронной почты администратора сайта" -#: ../../mod/install.php:238 ../../mod/install.php:277 +#: mod/install.php:251 mod/install.php:290 msgid "" "Your account email address must match this in order to use the web admin " "panel." -msgstr "Ваш адрес электронной почты аккаунта должен соответствовать этому, чтобы использовать веб-панель администратора." +msgstr "" +"Ваш адрес электронной почты аккаунта должен соответствовать этому, чтобы " +"использовать веб-панель администратора." -#: ../../mod/install.php:242 ../../mod/install.php:280 +#: mod/install.php:255 mod/install.php:293 msgid "Please select a default timezone for your website" msgstr "Пожалуйста, выберите часовой пояс по умолчанию для вашего сайта" -#: ../../mod/install.php:267 +#: mod/install.php:280 msgid "Site settings" msgstr "Настройки сайта" -#: ../../mod/install.php:321 +#: mod/install.php:334 msgid "Could not find a command line version of PHP in the web server PATH." msgstr "Не удалось найти PATH веб-сервера в установках PHP." -#: ../../mod/install.php:322 +#: mod/install.php:335 msgid "" "If you don't have a command line version of PHP installed on server, you " -"will not be able to run background polling via cron. See 'Activating scheduled tasks'" -msgstr "Если на вашем сервере не установлена версия командной строки PHP, вы не будете иметь возможность запускать фоновые опросы через крон. См. 'Активация запланированных задачах' " +"will not be able to run background polling via cron. See 'Setup the poller'" +msgstr "" -#: ../../mod/install.php:326 +#: mod/install.php:339 msgid "PHP executable path" msgstr "PHP executable path" -#: ../../mod/install.php:326 +#: mod/install.php:339 msgid "" "Enter full path to php executable. You can leave this blank to continue the " "installation." -msgstr "Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле пустым, чтобы продолжить установку." +msgstr "" +"Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле " +"пустым, чтобы продолжить установку." -#: ../../mod/install.php:331 +#: mod/install.php:344 msgid "Command line PHP" msgstr "Command line PHP" -#: ../../mod/install.php:340 +#: mod/install.php:353 msgid "PHP executable is not the php cli binary (could be cgi-fgci version)" msgstr "" -#: ../../mod/install.php:341 +#: mod/install.php:354 msgid "Found PHP version: " msgstr "Найденная PHP версия: " -#: ../../mod/install.php:343 +#: mod/install.php:356 msgid "PHP cli binary" msgstr "PHP cli binary" -#: ../../mod/install.php:354 +#: mod/install.php:367 msgid "" "The command line version of PHP on your system does not have " "\"register_argc_argv\" enabled." msgstr "Не включено \"register_argc_argv\" в установках PHP." -#: ../../mod/install.php:355 +#: mod/install.php:368 msgid "This is required for message delivery to work." msgstr "Это необходимо для работы доставки сообщений." -#: ../../mod/install.php:357 +#: mod/install.php:370 msgid "PHP register_argc_argv" msgstr "PHP register_argc_argv" -#: ../../mod/install.php:378 +#: mod/install.php:391 msgid "" "Error: the \"openssl_pkey_new\" function on this system is not able to " "generate encryption keys" -msgstr "Ошибка: функция \"openssl_pkey_new\" в этой системе не в состоянии генерировать ключи шифрования" +msgstr "" +"Ошибка: функция \"openssl_pkey_new\" в этой системе не в состоянии " +"генерировать ключи шифрования" -#: ../../mod/install.php:379 +#: mod/install.php:392 msgid "" -"If running under Windows, please see " -"\"http://www.php.net/manual/en/openssl.installation.php\"." -msgstr "Если вы работаете под Windows, см. \"http://www.php.net/manual/en/openssl.installation.php\"." +"If running under Windows, please see \"http://www.php.net/manual/en/openssl." +"installation.php\"." +msgstr "" +"Если вы работаете под Windows, см. \"http://www.php.net/manual/en/openssl." +"installation.php\"." -#: ../../mod/install.php:381 +#: mod/install.php:394 msgid "Generate encryption keys" msgstr "Генерация шифрованых ключей" -#: ../../mod/install.php:388 +#: mod/install.php:401 msgid "libCurl PHP module" msgstr "libCurl PHP модуль" -#: ../../mod/install.php:389 +#: mod/install.php:402 msgid "GD graphics PHP module" msgstr "GD graphics PHP модуль" -#: ../../mod/install.php:390 +#: mod/install.php:403 msgid "OpenSSL PHP module" msgstr "OpenSSL PHP модуль" -#: ../../mod/install.php:391 +#: mod/install.php:404 msgid "mysqli PHP module" msgstr "mysqli PHP модуль" -#: ../../mod/install.php:392 +#: mod/install.php:405 msgid "mb_string PHP module" msgstr "mb_string PHP модуль" -#: ../../mod/install.php:397 ../../mod/install.php:399 +#: mod/install.php:406 +msgid "mcrypt PHP module" +msgstr "" + +#: mod/install.php:411 mod/install.php:413 msgid "Apache mod_rewrite module" msgstr "Apache mod_rewrite module" -#: ../../mod/install.php:397 +#: mod/install.php:411 msgid "" "Error: Apache webserver mod-rewrite module is required but not installed." -msgstr "Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен." +msgstr "" +"Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен." -#: ../../mod/install.php:405 +#: mod/install.php:419 msgid "Error: libCURL PHP module required but not installed." msgstr "Ошибка: необходим libCURL PHP модуль, но он не установлен." -#: ../../mod/install.php:409 +#: mod/install.php:423 msgid "" "Error: GD graphics PHP module with JPEG support required but not installed." -msgstr "Ошибка: необходим PHP модуль GD графики с поддержкой JPEG, но он не установлен." +msgstr "" +"Ошибка: необходим PHP модуль GD графики с поддержкой JPEG, но он не " +"установлен." -#: ../../mod/install.php:413 +#: mod/install.php:427 msgid "Error: openssl PHP module required but not installed." msgstr "Ошибка: необходим PHP модуль OpenSSL, но он не установлен." -#: ../../mod/install.php:417 +#: mod/install.php:431 msgid "Error: mysqli PHP module required but not installed." msgstr "Ошибка: необходим PHP модуль MySQLi, но он не установлен." -#: ../../mod/install.php:421 +#: mod/install.php:435 msgid "Error: mb_string PHP module required but not installed." msgstr "Ошибка: необходим PHP модуль mb_string, но он не установлен." -#: ../../mod/install.php:438 -msgid "" -"The web installer needs to be able to create a file called \".htconfig.php\"" -" in the top folder of your web server and it is unable to do so." -msgstr "Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в верхней папке веб-сервера, но он не в состоянии это сделать." +#: mod/install.php:439 +msgid "Error: mcrypt PHP module required but not installed." +msgstr "" -#: ../../mod/install.php:439 +#: mod/install.php:451 +msgid "" +"Function mcrypt_create_iv() is not defined. This is needed to enable RINO2 " +"encryption layer." +msgstr "" + +#: mod/install.php:453 +msgid "mcrypt_create_iv() function" +msgstr "" + +#: mod/install.php:469 +msgid "" +"The web installer needs to be able to create a file called \".htconfig.php\" " +"in the top folder of your web server and it is unable to do so." +msgstr "" +"Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в " +"верхней папке веб-сервера, но он не в состоянии это сделать." + +#: mod/install.php:470 msgid "" "This is most often a permission setting, as the web server may not be able " "to write files in your folder - even if you can." -msgstr "Это наиболее частые параметры разрешений, когда веб-сервер не может записать файлы в папке - даже если вы можете." +msgstr "" +"Это наиболее частые параметры разрешений, когда веб-сервер не может записать " +"файлы в папке - даже если вы можете." -#: ../../mod/install.php:440 +#: mod/install.php:471 msgid "" "At the end of this procedure, we will give you a text to save in a file " "named .htconfig.php in your Friendica top folder." -msgstr "В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем .htconfig.php в корневой папке, где установлена Friendica." +msgstr "" +"В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем ." +"htconfig.php в корневой папке, где установлена Friendica." -#: ../../mod/install.php:441 +#: mod/install.php:472 msgid "" -"You can alternatively skip this procedure and perform a manual installation." -" Please see the file \"INSTALL.txt\" for instructions." -msgstr "В качестве альтернативы вы можете пропустить эту процедуру и выполнить установку вручную. Пожалуйста, обратитесь к файлу \"INSTALL.txt\" для получения инструкций." +"You can alternatively skip this procedure and perform a manual installation. " +"Please see the file \"INSTALL.txt\" for instructions." +msgstr "" +"В качестве альтернативы вы можете пропустить эту процедуру и выполнить " +"установку вручную. Пожалуйста, обратитесь к файлу \"INSTALL.txt\" для " +"получения инструкций." -#: ../../mod/install.php:444 +#: mod/install.php:475 msgid ".htconfig.php is writable" msgstr ".htconfig.php is writable" -#: ../../mod/install.php:454 +#: mod/install.php:485 msgid "" "Friendica uses the Smarty3 template engine to render its web views. Smarty3 " "compiles templates to PHP to speed up rendering." -msgstr "Friendica использует механизм шаблонов Smarty3 для генерации веб-страниц. Smarty3 компилирует шаблоны в PHP для увеличения скорости загрузки." +msgstr "" +"Friendica использует механизм шаблонов Smarty3 для генерации веб-страниц. " +"Smarty3 компилирует шаблоны в PHP для увеличения скорости загрузки." -#: ../../mod/install.php:455 +#: mod/install.php:486 msgid "" "In order to store these compiled templates, the web server needs to have " "write access to the directory view/smarty3/ under the Friendica top level " "folder." -msgstr "Для того чтобы хранить эти скомпилированные шаблоны, веб-сервер должен иметь доступ на запись для папки view/smarty3 в директории, где установлена Friendica." +msgstr "" +"Для того чтобы хранить эти скомпилированные шаблоны, веб-сервер должен иметь " +"доступ на запись для папки view/smarty3 в директории, где установлена " +"Friendica." -#: ../../mod/install.php:456 +#: mod/install.php:487 msgid "" -"Please ensure that the user that your web server runs as (e.g. www-data) has" -" write access to this folder." -msgstr "Пожалуйста, убедитесь, что пользователь, под которым работает ваш веб-сервер (например www-data), имеет доступ на запись в этой папке." +"Please ensure that the user that your web server runs as (e.g. www-data) has " +"write access to this folder." +msgstr "" +"Пожалуйста, убедитесь, что пользователь, под которым работает ваш веб-сервер " +"(например www-data), имеет доступ на запись в этой папке." -#: ../../mod/install.php:457 +#: mod/install.php:488 msgid "" "Note: as a security measure, you should give the web server write access to " "view/smarty3/ only--not the template files (.tpl) that it contains." -msgstr "Примечание: в качестве меры безопасности, вы должны дать вебсерверу доступ на запись только в view/smarty3 - но не на сами файлы шаблонов (.tpl)., Которые содержатся в этой папке." +msgstr "" +"Примечание: в качестве меры безопасности, вы должны дать вебсерверу доступ " +"на запись только в view/smarty3 - но не на сами файлы шаблонов (.tpl)., " +"Которые содержатся в этой папке." -#: ../../mod/install.php:460 +#: mod/install.php:491 msgid "view/smarty3 is writable" msgstr "view/smarty3 доступен для записи" -#: ../../mod/install.php:472 +#: mod/install.php:507 msgid "" "Url rewrite in .htaccess is not working. Check your server configuration." -msgstr "Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера.." +msgstr "" +"Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера.." -#: ../../mod/install.php:474 +#: mod/install.php:509 msgid "Url rewrite is working" msgstr "Url rewrite работает" -#: ../../mod/install.php:484 +#: mod/install.php:526 +msgid "ImageMagick PHP extension is installed" +msgstr "" + +#: mod/install.php:528 +msgid "ImageMagick supports GIF" +msgstr "" + +#: mod/install.php:536 msgid "" "The database configuration file \".htconfig.php\" could not be written. " "Please use the enclosed text to create a configuration file in your web " "server root." -msgstr "Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный файл в корневом каталоге веб-сервера." +msgstr "" +"Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. " +"Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный " +"файл в корневом каталоге веб-сервера." -#: ../../mod/install.php:523 +#: mod/install.php:575 msgid "

            What next

            " msgstr "

            Что далее

            " -#: ../../mod/install.php:524 +#: mod/install.php:576 msgid "" -"IMPORTANT: You will need to [manually] setup a scheduled task for the " -"poller." -msgstr "ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для регистратора." +"IMPORTANT: You will need to [manually] setup a scheduled task for the poller." +msgstr "" +"ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для " +"регистратора." -#: ../../mod/wallmessage.php:42 ../../mod/wallmessage.php:112 +#: mod/wallmessage.php:42 mod/wallmessage.php:112 #, php-format msgid "Number of daily wall messages for %s exceeded. Message failed." -msgstr "Количество ежедневных сообщений на стене %s превышено. Сообщение отменено.." +msgstr "" +"Количество ежедневных сообщений на стене %s превышено. Сообщение отменено.." -#: ../../mod/wallmessage.php:59 +#: mod/wallmessage.php:59 msgid "Unable to check your home location." msgstr "Невозможно проверить местоположение." -#: ../../mod/wallmessage.php:86 ../../mod/wallmessage.php:95 +#: mod/wallmessage.php:86 mod/wallmessage.php:95 msgid "No recipient." msgstr "Без адресата." -#: ../../mod/wallmessage.php:143 +#: mod/wallmessage.php:143 #, php-format msgid "" "If you wish for %s to respond, please check that the privacy settings on " "your site allow private mail from unknown senders." -msgstr "Если Вы хотите ответить %s, пожалуйста, проверьте, позволяют ли настройки конфиденциальности на Вашем сайте принимать персональную почту от неизвестных отправителей." +msgstr "" +"Если Вы хотите ответить %s, пожалуйста, проверьте, позволяют ли настройки " +"конфиденциальности на Вашем сайте принимать персональную почту от " +"неизвестных отправителей." -#: ../../mod/help.php:79 +#: mod/help.php:41 msgid "Help:" msgstr "Помощь:" -#: ../../mod/help.php:84 ../../include/nav.php:114 +#: mod/help.php:47 include/nav.php:113 view/theme/vier/theme.php:302 msgid "Help" msgstr "Помощь" -#: ../../mod/help.php:90 ../../index.php:256 +#: mod/help.php:53 mod/p.php:16 mod/p.php:25 index.php:270 msgid "Not Found" msgstr "Не найдено" -#: ../../mod/help.php:93 ../../index.php:259 +#: mod/help.php:56 index.php:273 msgid "Page not found." msgstr "Страница не найдена." -#: ../../mod/dfrn_poll.php:103 ../../mod/dfrn_poll.php:536 +#: mod/dfrn_poll.php:103 mod/dfrn_poll.php:536 #, php-format msgid "%1$s welcomes %2$s" msgstr "%1$s добро пожаловать %2$s" -#: ../../mod/home.php:35 +#: mod/home.php:35 #, php-format msgid "Welcome to %s" msgstr "Добро пожаловать на %s!" -#: ../../mod/wall_attach.php:75 +#: mod/wall_attach.php:94 msgid "Sorry, maybe your upload is bigger than the PHP configuration allows" msgstr "" -#: ../../mod/wall_attach.php:75 +#: mod/wall_attach.php:94 msgid "Or - did you try to upload an empty file?" msgstr "" -#: ../../mod/wall_attach.php:81 +#: mod/wall_attach.php:105 #, php-format -msgid "File exceeds size limit of %d" -msgstr "Файл превышает предельный размер %d" +msgid "File exceeds size limit of %s" +msgstr "" -#: ../../mod/wall_attach.php:122 ../../mod/wall_attach.php:133 +#: mod/wall_attach.php:156 mod/wall_attach.php:172 msgid "File upload failed." msgstr "Загрузка файла не удалась." -#: ../../mod/match.php:12 -msgid "Profile Match" -msgstr "Похожие профили" - -#: ../../mod/match.php:20 +#: mod/match.php:33 msgid "No keywords to match. Please add keywords to your default profile." -msgstr "Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для вашего профиля по умолчанию." +msgstr "" +"Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для " +"вашего профиля по умолчанию." -#: ../../mod/match.php:57 +#: mod/match.php:84 msgid "is interested in:" msgstr "интересуется:" -#: ../../mod/match.php:58 ../../mod/suggest.php:90 ../../boot.php:1568 -#: ../../include/contact_widgets.php:10 -msgid "Connect" -msgstr "Подключить" +#: mod/match.php:98 +msgid "Profile Match" +msgstr "Похожие профили" -#: ../../mod/share.php:44 +#: mod/share.php:38 msgid "link" msgstr "ссылка" -#: ../../mod/community.php:23 +#: mod/community.php:27 msgid "Not available." msgstr "Недоступно." -#: ../../mod/community.php:32 ../../include/nav.php:129 -#: ../../include/nav.php:131 ../../view/theme/diabook/theme.php:129 +#: mod/community.php:36 include/nav.php:136 include/nav.php:138 +#: view/theme/diabook/theme.php:129 msgid "Community" msgstr "Сообщество" -#: ../../mod/community.php:62 ../../mod/community.php:71 -#: ../../mod/search.php:168 ../../mod/search.php:192 +#: mod/community.php:66 mod/community.php:75 mod/search.php:228 msgid "No results." msgstr "Нет результатов." -#: ../../mod/settings.php:29 ../../mod/photos.php:80 +#: mod/settings.php:34 mod/photos.php:117 msgid "everybody" msgstr "каждый" -#: ../../mod/settings.php:41 -msgid "Additional features" -msgstr "Дополнительные возможности" - -#: ../../mod/settings.php:46 +#: mod/settings.php:58 msgid "Display" -msgstr "" +msgstr "Внешний вид" -#: ../../mod/settings.php:52 ../../mod/settings.php:780 +#: mod/settings.php:65 mod/settings.php:864 msgid "Social Networks" -msgstr "" +msgstr "Социальные сети" -#: ../../mod/settings.php:62 ../../include/nav.php:170 +#: mod/settings.php:79 include/nav.php:180 msgid "Delegations" -msgstr "" +msgstr "Делегирование" -#: ../../mod/settings.php:67 +#: mod/settings.php:86 msgid "Connected apps" msgstr "Подключенные приложения" -#: ../../mod/settings.php:72 ../../mod/uexport.php:85 +#: mod/settings.php:93 mod/uexport.php:85 msgid "Export personal data" msgstr "Экспорт личных данных" -#: ../../mod/settings.php:77 +#: mod/settings.php:100 msgid "Remove account" msgstr "Удалить аккаунт" -#: ../../mod/settings.php:129 +#: mod/settings.php:153 msgid "Missing some important data!" msgstr "Не хватает важных данных!" -#: ../../mod/settings.php:238 +#: mod/settings.php:266 msgid "Failed to connect with email account using the settings provided." -msgstr "Не удалось подключиться к аккаунту e-mail, используя указанные настройки." +msgstr "" +"Не удалось подключиться к аккаунту e-mail, используя указанные настройки." -#: ../../mod/settings.php:243 +#: mod/settings.php:271 msgid "Email settings updated." msgstr "Настройки эл. почты обновлены." -#: ../../mod/settings.php:258 +#: mod/settings.php:286 msgid "Features updated" msgstr "Настройки обновлены" -#: ../../mod/settings.php:321 +#: mod/settings.php:353 msgid "Relocate message has been send to your contacts" msgstr "Перемещённое сообщение было отправлено списку контактов" -#: ../../mod/settings.php:335 +#: mod/settings.php:367 include/user.php:39 msgid "Passwords do not match. Password unchanged." msgstr "Пароли не совпадают. Пароль не изменен." -#: ../../mod/settings.php:340 +#: mod/settings.php:372 msgid "Empty passwords are not allowed. Password unchanged." msgstr "Пустые пароли не допускаются. Пароль не изменен." -#: ../../mod/settings.php:348 +#: mod/settings.php:380 msgid "Wrong password." msgstr "Неверный пароль." -#: ../../mod/settings.php:359 +#: mod/settings.php:391 msgid "Password changed." msgstr "Пароль изменен." -#: ../../mod/settings.php:361 +#: mod/settings.php:393 msgid "Password update failed. Please try again." msgstr "Обновление пароля не удалось. Пожалуйста, попробуйте еще раз." -#: ../../mod/settings.php:428 +#: mod/settings.php:462 msgid " Please use a shorter name." msgstr " Пожалуйста, используйте более короткое имя." -#: ../../mod/settings.php:430 +#: mod/settings.php:464 msgid " Name too short." msgstr " Имя слишком короткое." -#: ../../mod/settings.php:439 +#: mod/settings.php:473 msgid "Wrong Password" msgstr "Неверный пароль." -#: ../../mod/settings.php:444 +#: mod/settings.php:478 msgid " Not valid email." msgstr " Неверный e-mail." -#: ../../mod/settings.php:450 +#: mod/settings.php:484 msgid " Cannot change to that email." msgstr " Невозможно изменить на этот e-mail." -#: ../../mod/settings.php:506 +#: mod/settings.php:540 msgid "Private forum has no privacy permissions. Using default privacy group." -msgstr "Частный форум не имеет настроек приватности. Используется группа конфиденциальности по умолчанию." +msgstr "" +"Частный форум не имеет настроек приватности. Используется группа " +"конфиденциальности по умолчанию." -#: ../../mod/settings.php:510 +#: mod/settings.php:544 msgid "Private forum has no privacy permissions and no default privacy group." -msgstr "Частный форум не имеет настроек приватности и не имеет групп приватности по умолчанию." +msgstr "" +"Частный форум не имеет настроек приватности и не имеет групп приватности по " +"умолчанию." -#: ../../mod/settings.php:540 +#: mod/settings.php:583 msgid "Settings updated." msgstr "Настройки обновлены." -#: ../../mod/settings.php:613 ../../mod/settings.php:639 -#: ../../mod/settings.php:675 +#: mod/settings.php:658 mod/settings.php:684 mod/settings.php:720 msgid "Add application" msgstr "Добавить приложения" -#: ../../mod/settings.php:617 ../../mod/settings.php:643 +#: mod/settings.php:662 mod/settings.php:688 msgid "Consumer Key" msgstr "Consumer Key" -#: ../../mod/settings.php:618 ../../mod/settings.php:644 +#: mod/settings.php:663 mod/settings.php:689 msgid "Consumer Secret" msgstr "Consumer Secret" -#: ../../mod/settings.php:619 ../../mod/settings.php:645 +#: mod/settings.php:664 mod/settings.php:690 msgid "Redirect" msgstr "Перенаправление" -#: ../../mod/settings.php:620 ../../mod/settings.php:646 +#: mod/settings.php:665 mod/settings.php:691 msgid "Icon url" msgstr "URL символа" -#: ../../mod/settings.php:631 +#: mod/settings.php:676 msgid "You can't edit this application." msgstr "Вы не можете изменить это приложение." -#: ../../mod/settings.php:674 +#: mod/settings.php:719 msgid "Connected Apps" msgstr "Подключенные приложения" -#: ../../mod/settings.php:678 +#: mod/settings.php:723 msgid "Client key starts with" msgstr "Ключ клиента начинается с" -#: ../../mod/settings.php:679 +#: mod/settings.php:724 msgid "No name" msgstr "Нет имени" -#: ../../mod/settings.php:680 +#: mod/settings.php:725 msgid "Remove authorization" msgstr "Удалить авторизацию" -#: ../../mod/settings.php:692 +#: mod/settings.php:737 msgid "No Plugin settings configured" msgstr "Нет сконфигурированных настроек плагина" -#: ../../mod/settings.php:700 +#: mod/settings.php:745 msgid "Plugin Settings" msgstr "Настройки плагина" -#: ../../mod/settings.php:714 -msgid "Off" -msgstr "" - -#: ../../mod/settings.php:714 -msgid "On" -msgstr "" - -#: ../../mod/settings.php:722 +#: mod/settings.php:767 msgid "Additional Features" msgstr "Дополнительные возможности" -#: ../../mod/settings.php:736 ../../mod/settings.php:737 +#: mod/settings.php:777 mod/settings.php:781 +msgid "General Social Media Settings" +msgstr "" + +#: mod/settings.php:787 +msgid "Disable intelligent shortening" +msgstr "" + +#: mod/settings.php:789 +msgid "" +"Normally the system tries to find the best link to add to shortened posts. " +"If this option is enabled then every shortened post will always point to the " +"original friendica post." +msgstr "" + +#: mod/settings.php:795 +msgid "Automatically follow any GNU Social (OStatus) followers/mentioners" +msgstr "" + +#: mod/settings.php:797 +msgid "" +"If you receive a message from an unknown OStatus user, this option decides " +"what to do. If it is checked, a new contact will be created for every " +"unknown user." +msgstr "" + +#: mod/settings.php:806 +msgid "Your legacy GNU Social account" +msgstr "" + +#: mod/settings.php:808 +msgid "" +"If you enter your old GNU Social/Statusnet account name here (in the format " +"user@domain.tld), your contacts will be added automatically. The field will " +"be emptied when done." +msgstr "" + +#: mod/settings.php:811 +msgid "Repair OStatus subscriptions" +msgstr "" + +#: mod/settings.php:820 mod/settings.php:821 #, php-format msgid "Built-in support for %s connectivity is %s" msgstr "Встроенная поддержка для %s подключение %s" -#: ../../mod/settings.php:736 ../../mod/dfrn_request.php:838 -#: ../../include/contact_selectors.php:80 +#: mod/settings.php:820 mod/dfrn_request.php:865 +#: include/contact_selectors.php:80 msgid "Diaspora" msgstr "Diaspora" -#: ../../mod/settings.php:736 ../../mod/settings.php:737 +#: mod/settings.php:820 mod/settings.php:821 msgid "enabled" msgstr "подключено" -#: ../../mod/settings.php:736 ../../mod/settings.php:737 +#: mod/settings.php:820 mod/settings.php:821 msgid "disabled" msgstr "отключено" -#: ../../mod/settings.php:737 -msgid "StatusNet" -msgstr "StatusNet" +#: mod/settings.php:821 +msgid "GNU Social (OStatus)" +msgstr "" -#: ../../mod/settings.php:773 +#: mod/settings.php:857 msgid "Email access is disabled on this site." msgstr "Доступ эл. почты отключен на этом сайте." -#: ../../mod/settings.php:785 +#: mod/settings.php:869 msgid "Email/Mailbox Setup" msgstr "Настройка эл. почты / почтового ящика" -#: ../../mod/settings.php:786 +#: mod/settings.php:870 msgid "" "If you wish to communicate with email contacts using this service " "(optional), please specify how to connect to your mailbox." -msgstr "Если вы хотите общаться с Email контактами, используя этот сервис (по желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику." +msgstr "" +"Если вы хотите общаться с Email контактами, используя этот сервис (по " +"желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику." -#: ../../mod/settings.php:787 +#: mod/settings.php:871 msgid "Last successful email check:" msgstr "Последняя успешная проверка электронной почты:" -#: ../../mod/settings.php:789 +#: mod/settings.php:873 msgid "IMAP server name:" msgstr "Имя IMAP сервера:" -#: ../../mod/settings.php:790 +#: mod/settings.php:874 msgid "IMAP port:" msgstr "Порт IMAP:" -#: ../../mod/settings.php:791 +#: mod/settings.php:875 msgid "Security:" msgstr "Безопасность:" -#: ../../mod/settings.php:791 ../../mod/settings.php:796 +#: mod/settings.php:875 mod/settings.php:880 msgid "None" msgstr "Ничего" -#: ../../mod/settings.php:792 +#: mod/settings.php:876 msgid "Email login name:" msgstr "Логин эл. почты:" -#: ../../mod/settings.php:793 +#: mod/settings.php:877 msgid "Email password:" msgstr "Пароль эл. почты:" -#: ../../mod/settings.php:794 +#: mod/settings.php:878 msgid "Reply-to address:" msgstr "Адрес для ответа:" -#: ../../mod/settings.php:795 +#: mod/settings.php:879 msgid "Send public posts to all email contacts:" msgstr "Отправлять открытые сообщения на все контакты электронной почты:" -#: ../../mod/settings.php:796 +#: mod/settings.php:880 msgid "Action after import:" msgstr "Действие после импорта:" -#: ../../mod/settings.php:796 +#: mod/settings.php:880 msgid "Mark as seen" msgstr "Отметить, как прочитанное" -#: ../../mod/settings.php:796 +#: mod/settings.php:880 msgid "Move to folder" msgstr "Переместить в папку" -#: ../../mod/settings.php:797 +#: mod/settings.php:881 msgid "Move to folder:" msgstr "Переместить в папку:" -#: ../../mod/settings.php:878 +#: mod/settings.php:967 msgid "Display Settings" msgstr "Параметры дисплея" -#: ../../mod/settings.php:884 ../../mod/settings.php:899 +#: mod/settings.php:973 mod/settings.php:991 msgid "Display Theme:" msgstr "Показать тему:" -#: ../../mod/settings.php:885 +#: mod/settings.php:974 msgid "Mobile Theme:" msgstr "Мобильная тема:" -#: ../../mod/settings.php:886 +#: mod/settings.php:975 msgid "Update browser every xx seconds" msgstr "Обновление браузера каждые хх секунд" -#: ../../mod/settings.php:886 -msgid "Minimum of 10 seconds, no maximum" -msgstr "Минимум 10 секунд, максимума нет" +#: mod/settings.php:975 +msgid "Minimum of 10 seconds. Enter -1 to disable it." +msgstr "" -#: ../../mod/settings.php:887 +#: mod/settings.php:976 msgid "Number of items to display per page:" msgstr "Количество элементов, отображаемых на одной странице:" -#: ../../mod/settings.php:887 ../../mod/settings.php:888 +#: mod/settings.php:976 mod/settings.php:977 msgid "Maximum of 100 items" msgstr "Максимум 100 элементов" -#: ../../mod/settings.php:888 +#: mod/settings.php:977 msgid "Number of items to display per page when viewed from mobile device:" -msgstr "Количество элементов на странице, когда просмотр осуществляется с мобильных устройств:" +msgstr "" +"Количество элементов на странице, когда просмотр осуществляется с мобильных " +"устройств:" -#: ../../mod/settings.php:889 +#: mod/settings.php:978 msgid "Don't show emoticons" msgstr "не показывать emoticons" -#: ../../mod/settings.php:890 +#: mod/settings.php:979 +msgid "Calendar" +msgstr "" + +#: mod/settings.php:980 +msgid "Beginning of week:" +msgstr "" + +#: mod/settings.php:981 msgid "Don't show notices" msgstr "" -#: ../../mod/settings.php:891 +#: mod/settings.php:982 msgid "Infinite scroll" msgstr "Бесконечная прокрутка" -#: ../../mod/settings.php:892 +#: mod/settings.php:983 msgid "Automatic updates only at the top of the network page" msgstr "" -#: ../../mod/settings.php:969 +#: mod/settings.php:985 view/theme/cleanzero/config.php:82 +#: view/theme/dispy/config.php:72 view/theme/quattro/config.php:66 +#: view/theme/diabook/config.php:150 view/theme/vier/config.php:109 +#: view/theme/duepuntozero/config.php:61 +msgid "Theme settings" +msgstr "Настройки темы" + +#: mod/settings.php:1062 msgid "User Types" msgstr "" -#: ../../mod/settings.php:970 +#: mod/settings.php:1063 msgid "Community Types" msgstr "" -#: ../../mod/settings.php:971 +#: mod/settings.php:1064 msgid "Normal Account Page" msgstr "Стандартная страница аккаунта" -#: ../../mod/settings.php:972 +#: mod/settings.php:1065 msgid "This account is a normal personal profile" msgstr "Этот аккаунт является обычным персональным профилем" -#: ../../mod/settings.php:975 +#: mod/settings.php:1068 msgid "Soapbox Page" msgstr "" -#: ../../mod/settings.php:976 +#: mod/settings.php:1069 msgid "Automatically approve all connection/friend requests as read-only fans" -msgstr "Автоматически одобряются все подключения / запросы в друзья, \"только для чтения\" поклонниками" +msgstr "" +"Автоматически одобряются все подключения / запросы в друзья, \"только для " +"чтения\" поклонниками" -#: ../../mod/settings.php:979 +#: mod/settings.php:1072 msgid "Community Forum/Celebrity Account" msgstr "Аккаунт сообщества Форум/Знаменитость" -#: ../../mod/settings.php:980 -msgid "" -"Automatically approve all connection/friend requests as read-write fans" -msgstr "Автоматически одобряются все подключения / запросы в друзья, \"для чтения и записей\" поклонников" +#: mod/settings.php:1073 +msgid "Automatically approve all connection/friend requests as read-write fans" +msgstr "" +"Автоматически одобряются все подключения / запросы в друзья, \"для чтения и " +"записей\" поклонников" -#: ../../mod/settings.php:983 +#: mod/settings.php:1076 msgid "Automatic Friend Page" msgstr "\"Автоматический друг\" страница" -#: ../../mod/settings.php:984 +#: mod/settings.php:1077 msgid "Automatically approve all connection/friend requests as friends" -msgstr "Автоматически одобряются все подключения / запросы в друзья, расширяется список друзей" +msgstr "" +"Автоматически одобряются все подключения / запросы в друзья, расширяется " +"список друзей" -#: ../../mod/settings.php:987 +#: mod/settings.php:1080 msgid "Private Forum [Experimental]" msgstr "Личный форум [экспериментально]" -#: ../../mod/settings.php:988 +#: mod/settings.php:1081 msgid "Private forum - approved members only" msgstr "Приватный форум - разрешено только участникам" -#: ../../mod/settings.php:1000 +#: mod/settings.php:1093 msgid "OpenID:" msgstr "OpenID:" -#: ../../mod/settings.php:1000 +#: mod/settings.php:1093 msgid "(Optional) Allow this OpenID to login to this account." msgstr "(Необязательно) Разрешить этому OpenID входить в этот аккаунт" -#: ../../mod/settings.php:1010 +#: mod/settings.php:1103 msgid "Publish your default profile in your local site directory?" -msgstr "Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?" +msgstr "" +"Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?" -#: ../../mod/settings.php:1010 ../../mod/settings.php:1016 -#: ../../mod/settings.php:1024 ../../mod/settings.php:1028 -#: ../../mod/settings.php:1033 ../../mod/settings.php:1039 -#: ../../mod/settings.php:1045 ../../mod/settings.php:1051 -#: ../../mod/settings.php:1081 ../../mod/settings.php:1082 -#: ../../mod/settings.php:1083 ../../mod/settings.php:1084 -#: ../../mod/settings.php:1085 ../../mod/dfrn_request.php:830 -#: ../../mod/register.php:234 ../../mod/profiles.php:661 -#: ../../mod/profiles.php:665 ../../mod/api.php:106 -msgid "No" -msgstr "Нет" - -#: ../../mod/settings.php:1016 +#: mod/settings.php:1109 msgid "Publish your default profile in the global social directory?" msgstr "Публиковать ваш профиль по умолчанию в глобальном социальном каталоге?" -#: ../../mod/settings.php:1024 +#: mod/settings.php:1117 msgid "Hide your contact/friend list from viewers of your default profile?" -msgstr "Скрывать ваш список контактов/друзей от посетителей вашего профиля по умолчанию?" +msgstr "" +"Скрывать ваш список контактов/друзей от посетителей вашего профиля по " +"умолчанию?" -#: ../../mod/settings.php:1028 ../../include/conversation.php:1057 +#: mod/settings.php:1121 include/acl_selectors.php:331 msgid "Hide your profile details from unknown viewers?" msgstr "Скрыть данные профиля из неизвестных зрителей?" -#: ../../mod/settings.php:1028 +#: mod/settings.php:1121 msgid "" "If enabled, posting public messages to Diaspora and other networks isn't " "possible." msgstr "" -#: ../../mod/settings.php:1033 +#: mod/settings.php:1126 msgid "Allow friends to post to your profile page?" msgstr "Разрешить друзьям оставлять сообщения на страницу вашего профиля?" -#: ../../mod/settings.php:1039 +#: mod/settings.php:1132 msgid "Allow friends to tag your posts?" msgstr "Разрешить друзьям отмечять ваши сообщения?" -#: ../../mod/settings.php:1045 +#: mod/settings.php:1138 msgid "Allow us to suggest you as a potential friend to new members?" msgstr "Позвольть предлогать Вам потенциальных друзей?" -#: ../../mod/settings.php:1051 +#: mod/settings.php:1144 msgid "Permit unknown people to send you private mail?" msgstr "Разрешить незнакомым людям отправлять вам личные сообщения?" -#: ../../mod/settings.php:1059 +#: mod/settings.php:1152 msgid "Profile is not published." msgstr "Профиль не публикуется." -#: ../../mod/settings.php:1067 -msgid "Your Identity Address is" -msgstr "Ваш идентификационный адрес" +#: mod/settings.php:1160 +#, php-format +msgid "Your Identity Address is '%s' or '%s'." +msgstr "" -#: ../../mod/settings.php:1078 +#: mod/settings.php:1167 msgid "Automatically expire posts after this many days:" msgstr "Автоматическое истекание срока действия сообщения после стольких дней:" -#: ../../mod/settings.php:1078 +#: mod/settings.php:1167 msgid "If empty, posts will not expire. Expired posts will be deleted" -msgstr "Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим сроком действия будут удалены" +msgstr "" +"Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим " +"сроком действия будут удалены" -#: ../../mod/settings.php:1079 +#: mod/settings.php:1168 msgid "Advanced expiration settings" msgstr "Настройки расширенного окончания срока действия" -#: ../../mod/settings.php:1080 +#: mod/settings.php:1169 msgid "Advanced Expiration" msgstr "Расширенное окончание срока действия" -#: ../../mod/settings.php:1081 +#: mod/settings.php:1170 msgid "Expire posts:" msgstr "Срок хранения сообщений:" -#: ../../mod/settings.php:1082 +#: mod/settings.php:1171 msgid "Expire personal notes:" msgstr "Срок хранения личных заметок:" -#: ../../mod/settings.php:1083 +#: mod/settings.php:1172 msgid "Expire starred posts:" msgstr "Срок хранения усеянных сообщений:" -#: ../../mod/settings.php:1084 +#: mod/settings.php:1173 msgid "Expire photos:" msgstr "Срок хранения фотографий:" -#: ../../mod/settings.php:1085 +#: mod/settings.php:1174 msgid "Only expire posts by others:" msgstr "Только устаревшие посты других:" -#: ../../mod/settings.php:1111 +#: mod/settings.php:1202 msgid "Account Settings" msgstr "Настройки аккаунта" -#: ../../mod/settings.php:1119 +#: mod/settings.php:1210 msgid "Password Settings" -msgstr "Настройка пароля" +msgstr "Смена пароля" -#: ../../mod/settings.php:1120 +#: mod/settings.php:1211 mod/register.php:274 msgid "New Password:" msgstr "Новый пароль:" -#: ../../mod/settings.php:1121 +#: mod/settings.php:1212 mod/register.php:275 msgid "Confirm:" msgstr "Подтвердите:" -#: ../../mod/settings.php:1121 +#: mod/settings.php:1212 msgid "Leave password fields blank unless changing" msgstr "Оставьте поля пароля пустыми, если он не изменяется" -#: ../../mod/settings.php:1122 +#: mod/settings.php:1213 msgid "Current Password:" msgstr "Текущий пароль:" -#: ../../mod/settings.php:1122 ../../mod/settings.php:1123 +#: mod/settings.php:1213 mod/settings.php:1214 msgid "Your current password to confirm the changes" msgstr "Ваш текущий пароль, для подтверждения изменений" -#: ../../mod/settings.php:1123 +#: mod/settings.php:1214 msgid "Password:" msgstr "Пароль:" -#: ../../mod/settings.php:1127 +#: mod/settings.php:1218 msgid "Basic Settings" msgstr "Основные параметры" -#: ../../mod/settings.php:1128 ../../include/profile_advanced.php:15 +#: mod/settings.php:1219 include/identity.php:588 msgid "Full Name:" msgstr "Полное имя:" -#: ../../mod/settings.php:1129 +#: mod/settings.php:1220 msgid "Email Address:" msgstr "Адрес электронной почты:" -#: ../../mod/settings.php:1130 +#: mod/settings.php:1221 msgid "Your Timezone:" msgstr "Ваш часовой пояс:" -#: ../../mod/settings.php:1131 +#: mod/settings.php:1222 +msgid "Your Language:" +msgstr "" + +#: mod/settings.php:1222 +msgid "" +"Set the language we use to show you friendica interface and to send you " +"emails" +msgstr "" + +#: mod/settings.php:1223 msgid "Default Post Location:" msgstr "Местонахождение по умолчанию:" -#: ../../mod/settings.php:1132 +#: mod/settings.php:1224 msgid "Use Browser Location:" msgstr "Использовать определение местоположения браузером:" -#: ../../mod/settings.php:1135 +#: mod/settings.php:1227 msgid "Security and Privacy Settings" msgstr "Параметры безопасности и конфиденциальности" -#: ../../mod/settings.php:1137 +#: mod/settings.php:1229 msgid "Maximum Friend Requests/Day:" msgstr "Максимум запросов в друзья в день:" -#: ../../mod/settings.php:1137 ../../mod/settings.php:1167 +#: mod/settings.php:1229 mod/settings.php:1259 msgid "(to prevent spam abuse)" msgstr "(для предотвращения спама)" -#: ../../mod/settings.php:1138 +#: mod/settings.php:1230 msgid "Default Post Permissions" msgstr "Разрешение на сообщения по умолчанию" -#: ../../mod/settings.php:1139 +#: mod/settings.php:1231 msgid "(click to open/close)" msgstr "(нажмите, чтобы открыть / закрыть)" -#: ../../mod/settings.php:1148 ../../mod/photos.php:1146 -#: ../../mod/photos.php:1519 +#: mod/settings.php:1240 mod/photos.php:1199 mod/photos.php:1584 msgid "Show to Groups" msgstr "Показать в группах" -#: ../../mod/settings.php:1149 ../../mod/photos.php:1147 -#: ../../mod/photos.php:1520 +#: mod/settings.php:1241 mod/photos.php:1200 mod/photos.php:1585 msgid "Show to Contacts" msgstr "Показывать контактам" -#: ../../mod/settings.php:1150 +#: mod/settings.php:1242 msgid "Default Private Post" msgstr "Личное сообщение по умолчанию" -#: ../../mod/settings.php:1151 +#: mod/settings.php:1243 msgid "Default Public Post" msgstr "Публичное сообщение по умолчанию" -#: ../../mod/settings.php:1155 +#: mod/settings.php:1247 msgid "Default Permissions for New Posts" msgstr "Права для новых записей по умолчанию" -#: ../../mod/settings.php:1167 +#: mod/settings.php:1259 msgid "Maximum private messages per day from unknown people:" msgstr "Максимальное количество личных сообщений от незнакомых людей в день:" -#: ../../mod/settings.php:1170 +#: mod/settings.php:1262 msgid "Notification Settings" msgstr "Настройка уведомлений" -#: ../../mod/settings.php:1171 +#: mod/settings.php:1263 msgid "By default post a status message when:" msgstr "Отправить состояние о статусе по умолчанию, если:" -#: ../../mod/settings.php:1172 +#: mod/settings.php:1264 msgid "accepting a friend request" msgstr "принятие запроса на добавление в друзья" -#: ../../mod/settings.php:1173 +#: mod/settings.php:1265 msgid "joining a forum/community" msgstr "вступление в сообщество/форум" -#: ../../mod/settings.php:1174 +#: mod/settings.php:1266 msgid "making an interesting profile change" msgstr "сделать изменения в настройках интересов профиля" -#: ../../mod/settings.php:1175 +#: mod/settings.php:1267 msgid "Send a notification email when:" msgstr "Отправлять уведомление по электронной почте, когда:" -#: ../../mod/settings.php:1176 +#: mod/settings.php:1268 msgid "You receive an introduction" msgstr "Вы получили запрос" -#: ../../mod/settings.php:1177 +#: mod/settings.php:1269 msgid "Your introductions are confirmed" msgstr "Ваши запросы подтверждены" -#: ../../mod/settings.php:1178 +#: mod/settings.php:1270 msgid "Someone writes on your profile wall" msgstr "Кто-то пишет на стене вашего профиля" -#: ../../mod/settings.php:1179 +#: mod/settings.php:1271 msgid "Someone writes a followup comment" msgstr "Кто-то пишет последующий комментарий" -#: ../../mod/settings.php:1180 +#: mod/settings.php:1272 msgid "You receive a private message" msgstr "Вы получаете личное сообщение" -#: ../../mod/settings.php:1181 +#: mod/settings.php:1273 msgid "You receive a friend suggestion" msgstr "Вы полулили предложение о добавлении в друзья" -#: ../../mod/settings.php:1182 +#: mod/settings.php:1274 msgid "You are tagged in a post" msgstr "Вы отмечены в посте" -#: ../../mod/settings.php:1183 +#: mod/settings.php:1275 msgid "You are poked/prodded/etc. in a post" msgstr "" -#: ../../mod/settings.php:1185 +#: mod/settings.php:1277 +msgid "Activate desktop notifications" +msgstr "" + +#: mod/settings.php:1277 +msgid "Show desktop popup on new notifications" +msgstr "" + +#: mod/settings.php:1279 msgid "Text-only notification emails" msgstr "" -#: ../../mod/settings.php:1187 +#: mod/settings.php:1281 msgid "Send text only notification emails, without the html part" msgstr "" -#: ../../mod/settings.php:1189 +#: mod/settings.php:1283 msgid "Advanced Account/Page Type Settings" -msgstr "Расширенные настройки типа аккаунта/страницы" +msgstr "Расширенные настройки учётной записи" -#: ../../mod/settings.php:1190 +#: mod/settings.php:1284 msgid "Change the behaviour of this account for special situations" msgstr "Измените поведение этого аккаунта в специальных ситуациях" -#: ../../mod/settings.php:1193 +#: mod/settings.php:1287 msgid "Relocate" -msgstr "Переместить" +msgstr "Перемещение" -#: ../../mod/settings.php:1194 +#: mod/settings.php:1288 msgid "" "If you have moved this profile from another server, and some of your " "contacts don't receive your updates, try pushing this button." -msgstr "Если вы переместили эту анкету с другого сервера, и некоторые из ваших контактов не получили ваши обновления, попробуйте нажать эту кнопку." +msgstr "" +"Если вы переместили эту анкету с другого сервера, и некоторые из ваших " +"контактов не получили ваши обновления, попробуйте нажать эту кнопку." -#: ../../mod/settings.php:1195 +#: mod/settings.php:1289 msgid "Resend relocate message to contacts" msgstr "Отправить перемещённые сообщения контактам" -#: ../../mod/dfrn_request.php:95 +#: mod/dfrn_request.php:96 msgid "This introduction has already been accepted." msgstr "Этот запрос был уже принят." -#: ../../mod/dfrn_request.php:120 ../../mod/dfrn_request.php:518 +#: mod/dfrn_request.php:119 mod/dfrn_request.php:516 msgid "Profile location is not valid or does not contain profile information." -msgstr "Местоположение профиля является недопустимым или не содержит информацию о профиле." +msgstr "" +"Местоположение профиля является недопустимым или не содержит информацию о " +"профиле." -#: ../../mod/dfrn_request.php:125 ../../mod/dfrn_request.php:523 +#: mod/dfrn_request.php:124 mod/dfrn_request.php:521 msgid "Warning: profile location has no identifiable owner name." -msgstr "Внимание: местоположение профиля не имеет идентифицируемого имени владельца." +msgstr "" +"Внимание: местоположение профиля не имеет идентифицируемого имени владельца." -#: ../../mod/dfrn_request.php:127 ../../mod/dfrn_request.php:525 +#: mod/dfrn_request.php:126 mod/dfrn_request.php:523 msgid "Warning: profile location has no profile photo." msgstr "Внимание: местоположение профиля не имеет еще фотографии профиля." -#: ../../mod/dfrn_request.php:130 ../../mod/dfrn_request.php:528 +#: mod/dfrn_request.php:129 mod/dfrn_request.php:526 #, php-format msgid "%d required parameter was not found at the given location" msgid_plural "%d required parameters were not found at the given location" msgstr[0] "%d требуемый параметр не был найден в заданном месте" msgstr[1] "%d требуемых параметров не были найдены в заданном месте" msgstr[2] "%d требуемых параметров не были найдены в заданном месте" +msgstr[3] "%d требуемых параметров не были найдены в заданном месте" -#: ../../mod/dfrn_request.php:172 +#: mod/dfrn_request.php:172 msgid "Introduction complete." msgstr "Запрос создан." -#: ../../mod/dfrn_request.php:214 +#: mod/dfrn_request.php:214 msgid "Unrecoverable protocol error." msgstr "Неисправимая ошибка протокола." -#: ../../mod/dfrn_request.php:242 +#: mod/dfrn_request.php:242 msgid "Profile unavailable." msgstr "Профиль недоступен." -#: ../../mod/dfrn_request.php:267 +#: mod/dfrn_request.php:267 #, php-format msgid "%s has received too many connection requests today." msgstr "К %s пришло сегодня слишком много запросов на подключение." -#: ../../mod/dfrn_request.php:268 +#: mod/dfrn_request.php:268 msgid "Spam protection measures have been invoked." msgstr "Были применены меры защиты от спама." -#: ../../mod/dfrn_request.php:269 +#: mod/dfrn_request.php:269 msgid "Friends are advised to please try again in 24 hours." msgstr "Друзья советуют попробовать еще раз в ближайшие 24 часа." -#: ../../mod/dfrn_request.php:331 +#: mod/dfrn_request.php:331 msgid "Invalid locator" msgstr "Недопустимый локатор" -#: ../../mod/dfrn_request.php:340 +#: mod/dfrn_request.php:340 msgid "Invalid email address." msgstr "Неверный адрес электронной почты." -#: ../../mod/dfrn_request.php:367 +#: mod/dfrn_request.php:367 msgid "This account has not been configured for email. Request failed." msgstr "Этот аккаунт не настроен для электронной почты. Запрос не удался." -#: ../../mod/dfrn_request.php:463 -msgid "Unable to resolve your name at the provided location." -msgstr "Не удается установить ваше имя на предложенном местоположении." - -#: ../../mod/dfrn_request.php:476 +#: mod/dfrn_request.php:474 msgid "You have already introduced yourself here." msgstr "Вы уже ввели информацию о себе здесь." -#: ../../mod/dfrn_request.php:480 +#: mod/dfrn_request.php:478 #, php-format msgid "Apparently you are already friends with %s." msgstr "Похоже, что вы уже друзья с %s." -#: ../../mod/dfrn_request.php:501 +#: mod/dfrn_request.php:499 msgid "Invalid profile URL." msgstr "Неверный URL профиля." -#: ../../mod/dfrn_request.php:507 ../../include/follow.php:27 +#: mod/dfrn_request.php:505 include/follow.php:72 msgid "Disallowed profile URL." msgstr "Запрещенный URL профиля." -#: ../../mod/dfrn_request.php:597 +#: mod/dfrn_request.php:596 msgid "Your introduction has been sent." msgstr "Ваш запрос отправлен." -#: ../../mod/dfrn_request.php:650 +#: mod/dfrn_request.php:636 +msgid "" +"Remote subscription can't be done for your network. Please subscribe " +"directly on your system." +msgstr "" + +#: mod/dfrn_request.php:659 msgid "Please login to confirm introduction." msgstr "Для подтверждения запроса войдите пожалуйста с паролем." -#: ../../mod/dfrn_request.php:660 +#: mod/dfrn_request.php:669 msgid "" -"Incorrect identity currently logged in. Please login to " -"this profile." -msgstr "Неверно идентифицирован вход. Пожалуйста, войдите в этот профиль." +"Incorrect identity currently logged in. Please login to this profile." +msgstr "" +"Неверно идентифицирован вход. Пожалуйста, войдите в этот " +"профиль." -#: ../../mod/dfrn_request.php:671 +#: mod/dfrn_request.php:683 mod/dfrn_request.php:700 +msgid "Confirm" +msgstr "Подтвердить" + +#: mod/dfrn_request.php:695 msgid "Hide this contact" msgstr "Скрыть этот контакт" -#: ../../mod/dfrn_request.php:674 +#: mod/dfrn_request.php:698 #, php-format msgid "Welcome home %s." msgstr "Добро пожаловать домой, %s!" -#: ../../mod/dfrn_request.php:675 +#: mod/dfrn_request.php:699 #, php-format msgid "Please confirm your introduction/connection request to %s." -msgstr "Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s." +msgstr "" +"Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s." -#: ../../mod/dfrn_request.php:676 -msgid "Confirm" -msgstr "Подтвердить" - -#: ../../mod/dfrn_request.php:804 +#: mod/dfrn_request.php:828 msgid "" "Please enter your 'Identity Address' from one of the following supported " "communications networks:" -msgstr "Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих поддерживаемых социальных сетей:" +msgstr "" +"Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих " +"поддерживаемых социальных сетей:" -#: ../../mod/dfrn_request.php:824 +#: mod/dfrn_request.php:849 +#, php-format msgid "" -"If you are not yet a member of the free social web, follow this link to find a public" -" Friendica site and join us today." -msgstr "Если вы еще не являетесь членом свободной социальной сети, перейдите по этой ссылке, чтобы найти публичный сервер Friendica и присоединиться к нам сейчас ." +"If you are not yet a member of the free social web, follow this link to find a public Friendica site and join us today." +msgstr "" -#: ../../mod/dfrn_request.php:827 +#: mod/dfrn_request.php:854 msgid "Friend/Connection Request" msgstr "Запрос в друзья / на подключение" -#: ../../mod/dfrn_request.php:828 +#: mod/dfrn_request.php:855 msgid "" "Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, " "testuser@identi.ca" -msgstr "Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca" +msgstr "" +"Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, " +"testuser@identi.ca" -#: ../../mod/dfrn_request.php:829 -msgid "Please answer the following:" -msgstr "Пожалуйста, ответьте следующее:" - -#: ../../mod/dfrn_request.php:830 -#, php-format -msgid "Does %s know you?" -msgstr "%s знает вас?" - -#: ../../mod/dfrn_request.php:834 -msgid "Add a personal note:" -msgstr "Добавить личную заметку:" - -#: ../../mod/dfrn_request.php:836 ../../include/contact_selectors.php:76 +#: mod/dfrn_request.php:863 include/contact_selectors.php:76 msgid "Friendica" msgstr "Friendica" -#: ../../mod/dfrn_request.php:837 +#: mod/dfrn_request.php:864 msgid "StatusNet/Federated Social Web" msgstr "StatusNet / Federated Social Web" -#: ../../mod/dfrn_request.php:839 +#: mod/dfrn_request.php:866 #, php-format msgid "" -" - please do not use this form. Instead, enter %s into your Diaspora search" -" bar." -msgstr "Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо этого введите %s в строке поиска Diaspora" +" - please do not use this form. Instead, enter %s into your Diaspora search " +"bar." +msgstr "" +"Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо " +"этого введите %s в строке поиска Diaspora" -#: ../../mod/dfrn_request.php:840 -msgid "Your Identity Address:" -msgstr "Ваш идентификационный адрес:" - -#: ../../mod/dfrn_request.php:843 -msgid "Submit Request" -msgstr "Отправить запрос" - -#: ../../mod/register.php:90 +#: mod/register.php:92 msgid "" "Registration successful. Please check your email for further instructions." -msgstr "Регистрация успешна. Пожалуйста, проверьте свою электронную почту для получения дальнейших инструкций." +msgstr "" +"Регистрация успешна. Пожалуйста, проверьте свою электронную почту для " +"получения дальнейших инструкций." -#: ../../mod/register.php:96 +#: mod/register.php:97 #, php-format msgid "" "Failed to send email message. Here your accout details:
            login: %s
            " "password: %s

            You can change your password after login." msgstr "" -#: ../../mod/register.php:105 +#: mod/register.php:104 +msgid "Registration successful." +msgstr "" + +#: mod/register.php:110 msgid "Your registration can not be processed." msgstr "Ваша регистрация не может быть обработана." -#: ../../mod/register.php:148 +#: mod/register.php:153 msgid "Your registration is pending approval by the site owner." msgstr "Ваша регистрация в ожидании одобрения владельцем сайта." -#: ../../mod/register.php:186 ../../mod/uimport.php:50 +#: mod/register.php:191 mod/uimport.php:50 msgid "" "This site has exceeded the number of allowed daily account registrations. " "Please try again tomorrow." -msgstr "Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, повторите попытку завтра." +msgstr "" +"Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, " +"повторите попытку завтра." -#: ../../mod/register.php:214 +#: mod/register.php:219 msgid "" "You may (optionally) fill in this form via OpenID by supplying your OpenID " "and clicking 'Register'." -msgstr "Вы можете (по желанию), заполнить эту форму с помощью OpenID, поддерживая ваш OpenID и нажав клавишу \"Регистрация\"." +msgstr "" +"Вы можете (по желанию), заполнить эту форму с помощью OpenID, поддерживая " +"ваш OpenID и нажав клавишу \"Регистрация\"." -#: ../../mod/register.php:215 +#: mod/register.php:220 msgid "" "If you are not familiar with OpenID, please leave that field blank and fill " "in the rest of the items." -msgstr "Если вы не знакомы с OpenID, пожалуйста, оставьте это поле пустым и заполните остальные элементы." +msgstr "" +"Если вы не знакомы с OpenID, пожалуйста, оставьте это поле пустым и " +"заполните остальные элементы." -#: ../../mod/register.php:216 +#: mod/register.php:221 msgid "Your OpenID (optional): " msgstr "Ваш OpenID (необязательно):" -#: ../../mod/register.php:230 +#: mod/register.php:235 msgid "Include your profile in member directory?" msgstr "Включить ваш профиль в каталог участников?" -#: ../../mod/register.php:251 +#: mod/register.php:259 msgid "Membership on this site is by invitation only." msgstr "Членство на сайте только по приглашению." -#: ../../mod/register.php:252 +#: mod/register.php:260 msgid "Your invitation ID: " msgstr "ID вашего приглашения:" -#: ../../mod/register.php:263 -msgid "Your Full Name (e.g. Joe Smith): " -msgstr "Ваше полное имя (например, Joe Smith): " +#: mod/register.php:271 +msgid "Your Full Name (e.g. Joe Smith, real or real-looking): " +msgstr "" -#: ../../mod/register.php:264 +#: mod/register.php:272 msgid "Your Email Address: " msgstr "Ваш адрес электронной почты: " -#: ../../mod/register.php:265 +#: mod/register.php:274 +msgid "Leave empty for an auto generated password." +msgstr "" + +#: mod/register.php:276 msgid "" "Choose a profile nickname. This must begin with a text character. Your " -"profile address on this site will then be " -"'nickname@$sitename'." -msgstr "Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля на данном сайте будет в этом случае 'nickname@$sitename'." +"profile address on this site will then be 'nickname@$sitename'." +msgstr "" +"Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля " +"на данном сайте будет в этом случае 'nickname@$sitename'." -#: ../../mod/register.php:266 +#: mod/register.php:277 msgid "Choose a nickname: " msgstr "Выберите псевдоним: " -#: ../../mod/register.php:269 ../../boot.php:1241 ../../include/nav.php:109 +#: mod/register.php:280 boot.php:1405 include/nav.php:108 msgid "Register" msgstr "Регистрация" -#: ../../mod/register.php:275 ../../mod/uimport.php:64 +#: mod/register.php:286 mod/uimport.php:64 msgid "Import" msgstr "Импорт" -#: ../../mod/register.php:276 +#: mod/register.php:287 msgid "Import your profile to this friendica instance" msgstr "Импорт своего профиля в этот экземпляр friendica" -#: ../../mod/maintenance.php:5 +#: mod/maintenance.php:5 msgid "System down for maintenance" msgstr "Система закрыта на техническое обслуживание" -#: ../../mod/search.php:99 ../../include/text.php:953 -#: ../../include/text.php:954 ../../include/nav.php:119 +#: mod/search.php:100 +msgid "Only logged in users are permitted to perform a search." +msgstr "" + +#: mod/search.php:124 +msgid "Too Many Requests" +msgstr "" + +#: mod/search.php:125 +msgid "Only one search per minute is permitted for not logged in users." +msgstr "" + +#: mod/search.php:136 include/text.php:1003 include/nav.php:118 msgid "Search" msgstr "Поиск" -#: ../../mod/directory.php:51 ../../view/theme/diabook/theme.php:525 -msgid "Global Directory" -msgstr "Глобальный каталог" +#: mod/search.php:234 +#, php-format +msgid "Items tagged with: %s" +msgstr "" -#: ../../mod/directory.php:59 -msgid "Find on this site" -msgstr "Найти на этом сайте" +#: mod/search.php:236 +#, php-format +msgid "Search results for: %s" +msgstr "" -#: ../../mod/directory.php:62 -msgid "Site Directory" -msgstr "Каталог сайта" - -#: ../../mod/directory.php:113 ../../mod/profiles.php:750 -msgid "Age: " -msgstr "Возраст: " - -#: ../../mod/directory.php:116 -msgid "Gender: " -msgstr "Пол: " - -#: ../../mod/directory.php:138 ../../boot.php:1650 -#: ../../include/profile_advanced.php:17 -msgid "Gender:" -msgstr "Пол:" - -#: ../../mod/directory.php:140 ../../boot.php:1653 -#: ../../include/profile_advanced.php:37 +#: mod/directory.php:149 include/identity.php:313 include/identity.php:610 msgid "Status:" msgstr "Статус:" -#: ../../mod/directory.php:142 ../../boot.php:1655 -#: ../../include/profile_advanced.php:48 +#: mod/directory.php:151 include/identity.php:315 include/identity.php:621 msgid "Homepage:" msgstr "Домашняя страничка:" -#: ../../mod/directory.php:144 ../../boot.php:1657 -#: ../../include/profile_advanced.php:58 -msgid "About:" -msgstr "О себе:" +#: mod/directory.php:203 view/theme/diabook/theme.php:525 +#: view/theme/vier/theme.php:205 +msgid "Global Directory" +msgstr "Глобальный каталог" -#: ../../mod/directory.php:189 +#: mod/directory.php:205 +msgid "Find on this site" +msgstr "Найти на этом сайте" + +#: mod/directory.php:207 +msgid "Finding:" +msgstr "" + +#: mod/directory.php:209 +msgid "Site Directory" +msgstr "Каталог сайта" + +#: mod/directory.php:216 msgid "No entries (some entries may be hidden)." msgstr "Нет записей (некоторые записи могут быть скрыты)." -#: ../../mod/delegate.php:101 +#: mod/delegate.php:101 msgid "No potential page delegates located." msgstr "" -#: ../../mod/delegate.php:130 ../../include/nav.php:170 +#: mod/delegate.php:130 include/nav.php:180 msgid "Delegate Page Management" msgstr "Делегировать управление страницей" -#: ../../mod/delegate.php:132 +#: mod/delegate.php:132 msgid "" "Delegates are able to manage all aspects of this account/page except for " "basic account settings. Please do not delegate your personal account to " "anybody that you do not trust completely." -msgstr "Доверенные лица могут управлять всеми аспектами этого аккаунта/страницы, за исключением основных настроек аккаунта. Пожалуйста, не предоставляйте доступ в личный кабинет тому, кому вы не полностью доверяете." +msgstr "" +"Доверенные лица могут управлять всеми аспектами этого аккаунта/страницы, за " +"исключением основных настроек аккаунта. Пожалуйста, не предоставляйте доступ " +"в личный кабинет тому, кому вы не полностью доверяете." -#: ../../mod/delegate.php:133 +#: mod/delegate.php:133 msgid "Existing Page Managers" msgstr "Существующие менеджеры страницы" -#: ../../mod/delegate.php:135 +#: mod/delegate.php:135 msgid "Existing Page Delegates" msgstr "Существующие уполномоченные страницы" -#: ../../mod/delegate.php:137 +#: mod/delegate.php:137 msgid "Potential Delegates" msgstr "Возможные доверенные лица" -#: ../../mod/delegate.php:140 +#: mod/delegate.php:140 msgid "Add" msgstr "Добавить" -#: ../../mod/delegate.php:141 +#: mod/delegate.php:141 msgid "No entries." msgstr "Нет записей." -#: ../../mod/common.php:42 -msgid "Common Friends" -msgstr "Общие друзья" - -#: ../../mod/common.php:78 +#: mod/common.php:86 msgid "No contacts in common." msgstr "Нет общих контактов." -#: ../../mod/uexport.php:77 +#: mod/uexport.php:77 msgid "Export account" msgstr "Экспорт аккаунта" -#: ../../mod/uexport.php:77 +#: mod/uexport.php:77 msgid "" "Export your account info and contacts. Use this to make a backup of your " "account and/or to move it to another server." -msgstr "Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать резервную копию вашего аккаунта и/или переместить его на другой сервер." +msgstr "" +"Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать " +"резервную копию вашего аккаунта и/или переместить его на другой сервер." -#: ../../mod/uexport.php:78 +#: mod/uexport.php:78 msgid "Export all" msgstr "Экспорт всего" -#: ../../mod/uexport.php:78 +#: mod/uexport.php:78 msgid "" "Export your accout info, contacts and all your items as json. Could be a " "very big file, and could take a lot of time. Use this to make a full backup " "of your account (photos are not exported)" -msgstr "Экспорт информации вашего аккаунта, контактов и всех ваших пунктов, как JSON. Может получиться очень большой файл и это может занять много времени. Используйте, чтобы создать полную резервную копию вашего аккаунта (фото не экспортируются)" +msgstr "" +"Экспорт информации вашего аккаунта, контактов и всех ваших пунктов, как " +"JSON. Может получиться очень большой файл и это может занять много времени. " +"Используйте, чтобы создать полную резервную копию вашего аккаунта (фото не " +"экспортируются)" -#: ../../mod/mood.php:62 ../../include/conversation.php:227 +#: mod/mood.php:62 include/conversation.php:239 #, php-format msgid "%1$s is currently %2$s" msgstr "" -#: ../../mod/mood.php:133 +#: mod/mood.php:133 msgid "Mood" msgstr "Настроение" -#: ../../mod/mood.php:134 +#: mod/mood.php:134 msgid "Set your current mood and tell your friends" msgstr "Напишите о вашем настроении и расскажите своим друзьям" -#: ../../mod/suggest.php:27 +#: mod/suggest.php:27 msgid "Do you really want to delete this suggestion?" msgstr "Вы действительно хотите удалить это предложение?" -#: ../../mod/suggest.php:68 ../../include/contact_widgets.php:35 -#: ../../view/theme/diabook/theme.php:527 -msgid "Friend Suggestions" -msgstr "Предложения друзей" - -#: ../../mod/suggest.php:74 +#: mod/suggest.php:71 msgid "" "No suggestions available. If this is a new site, please try again in 24 " "hours." -msgstr "Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 часа." +msgstr "" +"Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 " +"часа." -#: ../../mod/suggest.php:92 +#: mod/suggest.php:83 mod/suggest.php:101 msgid "Ignore/Hide" msgstr "Проигнорировать/Скрыть" -#: ../../mod/profiles.php:37 +#: mod/suggest.php:111 include/contact_widgets.php:35 +#: view/theme/diabook/theme.php:527 view/theme/vier/theme.php:207 +msgid "Friend Suggestions" +msgstr "Предложения друзей" + +#: mod/profiles.php:37 msgid "Profile deleted." msgstr "Профиль удален." -#: ../../mod/profiles.php:55 ../../mod/profiles.php:89 +#: mod/profiles.php:55 mod/profiles.php:89 msgid "Profile-" msgstr "Профиль-" -#: ../../mod/profiles.php:74 ../../mod/profiles.php:117 +#: mod/profiles.php:74 mod/profiles.php:117 msgid "New profile created." msgstr "Новый профиль создан." -#: ../../mod/profiles.php:95 +#: mod/profiles.php:95 msgid "Profile unavailable to clone." msgstr "Профиль недоступен для клонирования." -#: ../../mod/profiles.php:189 +#: mod/profiles.php:189 msgid "Profile Name is required." msgstr "Необходимо имя профиля." -#: ../../mod/profiles.php:340 +#: mod/profiles.php:336 msgid "Marital Status" msgstr "Семейное положение" -#: ../../mod/profiles.php:344 +#: mod/profiles.php:340 msgid "Romantic Partner" msgstr "Любимый человек" -#: ../../mod/profiles.php:348 +#: mod/profiles.php:344 mod/photos.php:1647 include/conversation.php:508 msgid "Likes" msgstr "Лайки" -#: ../../mod/profiles.php:352 +#: mod/profiles.php:348 mod/photos.php:1647 include/conversation.php:508 msgid "Dislikes" msgstr "Дизлайк" -#: ../../mod/profiles.php:356 +#: mod/profiles.php:352 msgid "Work/Employment" msgstr "Работа/Занятость" -#: ../../mod/profiles.php:359 +#: mod/profiles.php:355 msgid "Religion" msgstr "Религия" -#: ../../mod/profiles.php:363 +#: mod/profiles.php:359 msgid "Political Views" msgstr "Политические взгляды" -#: ../../mod/profiles.php:367 +#: mod/profiles.php:363 msgid "Gender" msgstr "Пол" -#: ../../mod/profiles.php:371 +#: mod/profiles.php:367 msgid "Sexual Preference" msgstr "Сексуальные предпочтения" -#: ../../mod/profiles.php:375 +#: mod/profiles.php:371 msgid "Homepage" msgstr "Домашняя страница" -#: ../../mod/profiles.php:379 ../../mod/profiles.php:698 +#: mod/profiles.php:375 mod/profiles.php:708 msgid "Interests" msgstr "Хобби / Интересы" -#: ../../mod/profiles.php:383 +#: mod/profiles.php:379 msgid "Address" msgstr "Адрес" -#: ../../mod/profiles.php:390 ../../mod/profiles.php:694 +#: mod/profiles.php:386 mod/profiles.php:704 msgid "Location" msgstr "Местонахождение" -#: ../../mod/profiles.php:473 +#: mod/profiles.php:469 msgid "Profile updated." msgstr "Профиль обновлен." -#: ../../mod/profiles.php:568 +#: mod/profiles.php:565 msgid " and " msgstr "и" -#: ../../mod/profiles.php:576 +#: mod/profiles.php:573 msgid "public profile" msgstr "публичный профиль" -#: ../../mod/profiles.php:579 +#: mod/profiles.php:576 #, php-format msgid "%1$s changed %2$s to “%3$s”" msgstr "%1$s изменились с %2$s на “%3$s”" -#: ../../mod/profiles.php:580 +#: mod/profiles.php:577 #, php-format msgid " - Visit %1$s's %2$s" msgstr " - Посетить профиль %1$s [%2$s]" -#: ../../mod/profiles.php:583 +#: mod/profiles.php:580 #, php-format msgid "%1$s has an updated %2$s, changing %3$s." msgstr "" -#: ../../mod/profiles.php:658 +#: mod/profiles.php:655 msgid "Hide contacts and friends:" msgstr "" -#: ../../mod/profiles.php:663 +#: mod/profiles.php:660 msgid "Hide your contact/friend list from viewers of this profile?" msgstr "Скрывать ваш список контактов / друзей от посетителей этого профиля?" -#: ../../mod/profiles.php:685 +#: mod/profiles.php:684 +msgid "Show more profile fields:" +msgstr "" + +#: mod/profiles.php:695 msgid "Edit Profile Details" msgstr "Редактировать детали профиля" -#: ../../mod/profiles.php:687 +#: mod/profiles.php:697 msgid "Change Profile Photo" msgstr "Изменить фото профиля" -#: ../../mod/profiles.php:688 +#: mod/profiles.php:698 msgid "View this profile" msgstr "Просмотреть этот профиль" -#: ../../mod/profiles.php:689 +#: mod/profiles.php:699 msgid "Create a new profile using these settings" msgstr "Создать новый профиль, используя эти настройки" -#: ../../mod/profiles.php:690 +#: mod/profiles.php:700 msgid "Clone this profile" msgstr "Клонировать этот профиль" -#: ../../mod/profiles.php:691 +#: mod/profiles.php:701 msgid "Delete this profile" msgstr "Удалить этот профиль" -#: ../../mod/profiles.php:692 +#: mod/profiles.php:702 msgid "Basic information" msgstr "" -#: ../../mod/profiles.php:693 +#: mod/profiles.php:703 msgid "Profile picture" msgstr "" -#: ../../mod/profiles.php:695 +#: mod/profiles.php:705 msgid "Preferences" msgstr "" -#: ../../mod/profiles.php:696 +#: mod/profiles.php:706 msgid "Status information" msgstr "" -#: ../../mod/profiles.php:697 +#: mod/profiles.php:707 msgid "Additional information" msgstr "" -#: ../../mod/profiles.php:700 +#: mod/profiles.php:710 msgid "Profile Name:" msgstr "Имя профиля:" -#: ../../mod/profiles.php:701 +#: mod/profiles.php:711 msgid "Your Full Name:" msgstr "Ваше полное имя:" -#: ../../mod/profiles.php:702 +#: mod/profiles.php:712 msgid "Title/Description:" msgstr "Заголовок / Описание:" -#: ../../mod/profiles.php:703 +#: mod/profiles.php:713 msgid "Your Gender:" msgstr "Ваш пол:" -#: ../../mod/profiles.php:704 -#, php-format -msgid "Birthday (%s):" -msgstr "День рождения (%s):" +#: mod/profiles.php:714 +msgid "Birthday :" +msgstr "" -#: ../../mod/profiles.php:705 +#: mod/profiles.php:715 msgid "Street Address:" msgstr "Адрес:" -#: ../../mod/profiles.php:706 +#: mod/profiles.php:716 msgid "Locality/City:" msgstr "Город / Населенный пункт:" -#: ../../mod/profiles.php:707 +#: mod/profiles.php:717 msgid "Postal/Zip Code:" msgstr "Почтовый индекс:" -#: ../../mod/profiles.php:708 +#: mod/profiles.php:718 msgid "Country:" msgstr "Страна:" -#: ../../mod/profiles.php:709 +#: mod/profiles.php:719 msgid "Region/State:" msgstr "Район / Область:" -#: ../../mod/profiles.php:710 +#: mod/profiles.php:720 msgid " Marital Status:" msgstr " Семейное положение:" -#: ../../mod/profiles.php:711 +#: mod/profiles.php:721 msgid "Who: (if applicable)" msgstr "Кто: (если требуется)" -#: ../../mod/profiles.php:712 +#: mod/profiles.php:722 msgid "Examples: cathy123, Cathy Williams, cathy@example.com" msgstr "Примеры: cathy123, Кэти Уильямс, cathy@example.com" -#: ../../mod/profiles.php:713 +#: mod/profiles.php:723 msgid "Since [date]:" msgstr "С какого времени [дата]:" -#: ../../mod/profiles.php:714 ../../include/profile_advanced.php:46 +#: mod/profiles.php:724 include/identity.php:619 msgid "Sexual Preference:" msgstr "Сексуальные предпочтения:" -#: ../../mod/profiles.php:715 +#: mod/profiles.php:725 msgid "Homepage URL:" msgstr "Адрес домашней странички:" -#: ../../mod/profiles.php:716 ../../include/profile_advanced.php:50 +#: mod/profiles.php:726 include/identity.php:623 msgid "Hometown:" msgstr "Родной город:" -#: ../../mod/profiles.php:717 ../../include/profile_advanced.php:54 +#: mod/profiles.php:727 include/identity.php:627 msgid "Political Views:" msgstr "Политические взгляды:" -#: ../../mod/profiles.php:718 +#: mod/profiles.php:728 msgid "Religious Views:" msgstr "Религиозные взгляды:" -#: ../../mod/profiles.php:719 +#: mod/profiles.php:729 msgid "Public Keywords:" msgstr "Общественные ключевые слова:" -#: ../../mod/profiles.php:720 +#: mod/profiles.php:730 msgid "Private Keywords:" msgstr "Личные ключевые слова:" -#: ../../mod/profiles.php:721 ../../include/profile_advanced.php:62 +#: mod/profiles.php:731 include/identity.php:635 msgid "Likes:" msgstr "Нравится:" -#: ../../mod/profiles.php:722 ../../include/profile_advanced.php:64 +#: mod/profiles.php:732 include/identity.php:637 msgid "Dislikes:" msgstr "Не нравится:" -#: ../../mod/profiles.php:723 +#: mod/profiles.php:733 msgid "Example: fishing photography software" msgstr "Пример: рыбалка фотографии программное обеспечение" -#: ../../mod/profiles.php:724 +#: mod/profiles.php:734 msgid "(Used for suggesting potential friends, can be seen by others)" -msgstr "(Используется для предложения потенциальным друзьям, могут увидеть другие)" +msgstr "" +"(Используется для предложения потенциальным друзьям, могут увидеть другие)" -#: ../../mod/profiles.php:725 +#: mod/profiles.php:735 msgid "(Used for searching profiles, never shown to others)" msgstr "(Используется для поиска профилей, никогда не показывается другим)" -#: ../../mod/profiles.php:726 +#: mod/profiles.php:736 msgid "Tell us about yourself..." msgstr "Расскажите нам о себе ..." -#: ../../mod/profiles.php:727 +#: mod/profiles.php:737 msgid "Hobbies/Interests" msgstr "Хобби / Интересы" -#: ../../mod/profiles.php:728 +#: mod/profiles.php:738 msgid "Contact information and Social Networks" msgstr "Контактная информация и социальные сети" -#: ../../mod/profiles.php:729 +#: mod/profiles.php:739 msgid "Musical interests" msgstr "Музыкальные интересы" -#: ../../mod/profiles.php:730 +#: mod/profiles.php:740 msgid "Books, literature" msgstr "Книги, литература" -#: ../../mod/profiles.php:731 +#: mod/profiles.php:741 msgid "Television" msgstr "Телевидение" -#: ../../mod/profiles.php:732 +#: mod/profiles.php:742 msgid "Film/dance/culture/entertainment" msgstr "Кино / танцы / культура / развлечения" -#: ../../mod/profiles.php:733 +#: mod/profiles.php:743 msgid "Love/romance" msgstr "Любовь / романтика" -#: ../../mod/profiles.php:734 +#: mod/profiles.php:744 msgid "Work/employment" msgstr "Работа / занятость" -#: ../../mod/profiles.php:735 +#: mod/profiles.php:745 msgid "School/education" msgstr "Школа / образование" -#: ../../mod/profiles.php:740 +#: mod/profiles.php:750 msgid "" "This is your public profile.
            It may " "be visible to anybody using the internet." -msgstr "Это ваш публичный профиль.
            Он может быть виден каждому через Интернет." +msgstr "" +"Это ваш публичный профиль.
            Он может " +"быть виден каждому через Интернет." -#: ../../mod/profiles.php:803 +#: mod/profiles.php:760 +msgid "Age: " +msgstr "Возраст: " + +#: mod/profiles.php:813 msgid "Edit/Manage Profiles" msgstr "Редактировать профиль" -#: ../../mod/profiles.php:804 ../../boot.php:1611 ../../boot.php:1637 +#: mod/profiles.php:814 include/identity.php:260 include/identity.php:286 msgid "Change profile photo" msgstr "Изменить фото профиля" -#: ../../mod/profiles.php:805 ../../boot.php:1612 +#: mod/profiles.php:815 include/identity.php:261 msgid "Create New Profile" msgstr "Создать новый профиль" -#: ../../mod/profiles.php:816 ../../boot.php:1622 +#: mod/profiles.php:826 include/identity.php:271 msgid "Profile Image" msgstr "Фото профиля" -#: ../../mod/profiles.php:818 ../../boot.php:1625 +#: mod/profiles.php:828 include/identity.php:274 msgid "visible to everybody" msgstr "видимый всем" -#: ../../mod/profiles.php:819 ../../boot.php:1626 +#: mod/profiles.php:829 include/identity.php:275 msgid "Edit visibility" msgstr "Редактировать видимость" -#: ../../mod/editpost.php:17 ../../mod/editpost.php:27 +#: mod/editpost.php:17 mod/editpost.php:27 msgid "Item not found" msgstr "Элемент не найден" -#: ../../mod/editpost.php:39 +#: mod/editpost.php:40 msgid "Edit post" msgstr "Редактировать сообщение" -#: ../../mod/editpost.php:111 ../../include/conversation.php:1092 +#: mod/editpost.php:111 include/conversation.php:1184 msgid "upload photo" msgstr "загрузить фото" -#: ../../mod/editpost.php:112 ../../include/conversation.php:1093 +#: mod/editpost.php:112 include/conversation.php:1185 msgid "Attach file" -msgstr "Приложить файл" +msgstr "Прикрепить файл" -#: ../../mod/editpost.php:113 ../../include/conversation.php:1094 +#: mod/editpost.php:113 include/conversation.php:1186 msgid "attach file" msgstr "приложить файл" -#: ../../mod/editpost.php:115 ../../include/conversation.php:1096 +#: mod/editpost.php:115 include/conversation.php:1188 msgid "web link" msgstr "веб-ссылка" -#: ../../mod/editpost.php:116 ../../include/conversation.php:1097 +#: mod/editpost.php:116 include/conversation.php:1189 msgid "Insert video link" msgstr "Вставить ссылку видео" -#: ../../mod/editpost.php:117 ../../include/conversation.php:1098 +#: mod/editpost.php:117 include/conversation.php:1190 msgid "video link" msgstr "видео-ссылка" -#: ../../mod/editpost.php:118 ../../include/conversation.php:1099 +#: mod/editpost.php:118 include/conversation.php:1191 msgid "Insert audio link" msgstr "Вставить ссылку аудио" -#: ../../mod/editpost.php:119 ../../include/conversation.php:1100 +#: mod/editpost.php:119 include/conversation.php:1192 msgid "audio link" msgstr "аудио-ссылка" -#: ../../mod/editpost.php:120 ../../include/conversation.php:1101 +#: mod/editpost.php:120 include/conversation.php:1193 msgid "Set your location" msgstr "Задать ваше местоположение" -#: ../../mod/editpost.php:121 ../../include/conversation.php:1102 +#: mod/editpost.php:121 include/conversation.php:1194 msgid "set location" msgstr "установить местонахождение" -#: ../../mod/editpost.php:122 ../../include/conversation.php:1103 +#: mod/editpost.php:122 include/conversation.php:1195 msgid "Clear browser location" msgstr "Очистить местонахождение браузера" -#: ../../mod/editpost.php:123 ../../include/conversation.php:1104 +#: mod/editpost.php:123 include/conversation.php:1196 msgid "clear location" msgstr "убрать местонахождение" -#: ../../mod/editpost.php:125 ../../include/conversation.php:1110 +#: mod/editpost.php:125 include/conversation.php:1202 msgid "Permission settings" msgstr "Настройки разрешений" -#: ../../mod/editpost.php:133 ../../include/conversation.php:1119 +#: mod/editpost.php:133 include/acl_selectors.php:344 msgid "CC: email addresses" msgstr "Копии на email адреса" -#: ../../mod/editpost.php:134 ../../include/conversation.php:1120 +#: mod/editpost.php:134 include/conversation.php:1211 msgid "Public post" msgstr "Публичное сообщение" -#: ../../mod/editpost.php:137 ../../include/conversation.php:1106 +#: mod/editpost.php:137 include/conversation.php:1198 msgid "Set title" msgstr "Установить заголовок" -#: ../../mod/editpost.php:139 ../../include/conversation.php:1108 +#: mod/editpost.php:139 include/conversation.php:1200 msgid "Categories (comma-separated list)" msgstr "Категории (список через запятую)" -#: ../../mod/editpost.php:140 ../../include/conversation.php:1122 +#: mod/editpost.php:140 include/acl_selectors.php:345 msgid "Example: bob@example.com, mary@example.com" msgstr "Пример: bob@example.com, mary@example.com" -#: ../../mod/friendica.php:59 +#: mod/friendica.php:70 msgid "This is Friendica, version" msgstr "Это Friendica, версия" -#: ../../mod/friendica.php:60 +#: mod/friendica.php:71 msgid "running at web location" msgstr "работает на веб-узле" -#: ../../mod/friendica.php:62 +#: mod/friendica.php:73 msgid "" "Please visit Friendica.com to learn " "more about the Friendica project." -msgstr "Пожалуйста, посетите сайт Friendica.com, чтобы узнать больше о проекте Friendica." +msgstr "" +"Пожалуйста, посетите сайт Friendica.com, чтобы узнать больше о проекте Friendica." -#: ../../mod/friendica.php:64 +#: mod/friendica.php:75 msgid "Bug reports and issues: please visit" msgstr "Отчет об ошибках и проблемах: пожалуйста, посетите" -#: ../../mod/friendica.php:65 +#: mod/friendica.php:75 +msgid "the bugtracker at github" +msgstr "" + +#: mod/friendica.php:76 msgid "" "Suggestions, praise, donations, etc. - please email \"Info\" at Friendica - " "dot com" -msgstr "Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка com" +msgstr "" +"Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка " +"com" -#: ../../mod/friendica.php:79 +#: mod/friendica.php:90 msgid "Installed plugins/addons/apps:" msgstr "Установленные плагины / добавки / приложения:" -#: ../../mod/friendica.php:92 +#: mod/friendica.php:103 msgid "No installed plugins/addons/apps" msgstr "Нет установленных плагинов / добавок / приложений" -#: ../../mod/api.php:76 ../../mod/api.php:102 +#: mod/api.php:76 mod/api.php:102 msgid "Authorize application connection" msgstr "Разрешить связь с приложением" -#: ../../mod/api.php:77 +#: mod/api.php:77 msgid "Return to your app and insert this Securty Code:" msgstr "Вернитесь в ваше приложение и задайте этот код:" -#: ../../mod/api.php:89 +#: mod/api.php:89 msgid "Please login to continue." msgstr "Пожалуйста, войдите для продолжения." -#: ../../mod/api.php:104 +#: mod/api.php:104 msgid "" -"Do you want to authorize this application to access your posts and contacts," -" and/or create new posts for you?" -msgstr "Вы действительно хотите разрешить этому приложению доступ к своим постам и контактам, а также создавать новые записи от вашего имени?" +"Do you want to authorize this application to access your posts and contacts, " +"and/or create new posts for you?" +msgstr "" +"Вы действительно хотите разрешить этому приложению доступ к своим постам и " +"контактам, а также создавать новые записи от вашего имени?" -#: ../../mod/lockview.php:31 ../../mod/lockview.php:39 +#: mod/lockview.php:31 mod/lockview.php:39 msgid "Remote privacy information not available." msgstr "Личная информация удаленно недоступна." -#: ../../mod/lockview.php:48 +#: mod/lockview.php:48 msgid "Visible to:" msgstr "Кто может видеть:" -#: ../../mod/notes.php:44 ../../boot.php:2150 +#: mod/notes.php:46 include/identity.php:730 msgid "Personal Notes" msgstr "Личные заметки" -#: ../../mod/localtime.php:12 ../../include/bb2diaspora.php:148 -#: ../../include/event.php:11 +#: mod/localtime.php:12 include/bb2diaspora.php:148 include/event.php:13 msgid "l F d, Y \\@ g:i A" msgstr "l F d, Y \\@ g:i A" -#: ../../mod/localtime.php:24 +#: mod/localtime.php:24 msgid "Time Conversion" msgstr "История общения" -#: ../../mod/localtime.php:26 +#: mod/localtime.php:26 msgid "" "Friendica provides this service for sharing events with other networks and " "friends in unknown timezones." -msgstr "Friendica предоставляет этот сервис для обмена событиями с другими сетями и друзьями, находящимися в неопределённых часовых поясах." +msgstr "" +"Friendica предоставляет этот сервис для обмена событиями с другими сетями и " +"друзьями, находящимися в неопределённых часовых поясах." -#: ../../mod/localtime.php:30 +#: mod/localtime.php:30 #, php-format msgid "UTC time: %s" msgstr "UTC время: %s" -#: ../../mod/localtime.php:33 +#: mod/localtime.php:33 #, php-format msgid "Current timezone: %s" msgstr "Ваш часовой пояс: %s" -#: ../../mod/localtime.php:36 +#: mod/localtime.php:36 #, php-format msgid "Converted localtime: %s" msgstr "Ваше изменённое время: %s" -#: ../../mod/localtime.php:41 +#: mod/localtime.php:41 msgid "Please select your timezone:" msgstr "Выберите пожалуйста ваш часовой пояс:" -#: ../../mod/poke.php:192 +#: mod/poke.php:191 msgid "Poke/Prod" msgstr "" -#: ../../mod/poke.php:193 +#: mod/poke.php:192 msgid "poke, prod or do other things to somebody" msgstr "" -#: ../../mod/poke.php:194 +#: mod/poke.php:193 msgid "Recipient" msgstr "Получатель" -#: ../../mod/poke.php:195 +#: mod/poke.php:194 msgid "Choose what you wish to do to recipient" msgstr "Выберите действия для получателя" -#: ../../mod/poke.php:198 +#: mod/poke.php:197 msgid "Make this post private" msgstr "Сделать эту запись личной" -#: ../../mod/invite.php:27 +#: mod/repair_ostatus.php:14 +msgid "Resubscribing to OStatus contacts" +msgstr "" + +#: mod/repair_ostatus.php:30 +msgid "Error" +msgstr "Ошибка" + +#: mod/invite.php:27 msgid "Total invitation limit exceeded." msgstr "Превышен общий лимит приглашений." -#: ../../mod/invite.php:49 +#: mod/invite.php:49 #, php-format msgid "%s : Not a valid email address." msgstr "%s: Неверный адрес электронной почты." -#: ../../mod/invite.php:73 +#: mod/invite.php:73 msgid "Please join us on Friendica" msgstr "Пожалуйста, присоединяйтесь к нам на Friendica" -#: ../../mod/invite.php:84 +#: mod/invite.php:84 msgid "Invitation limit exceeded. Please contact your site administrator." -msgstr "Лимит приглашений превышен. Пожалуйста, свяжитесь с администратором сайта." +msgstr "" +"Лимит приглашений превышен. Пожалуйста, свяжитесь с администратором сайта." -#: ../../mod/invite.php:89 +#: mod/invite.php:89 #, php-format msgid "%s : Message delivery failed." msgstr "%s: Доставка сообщения не удалась." -#: ../../mod/invite.php:93 +#: mod/invite.php:93 #, php-format msgid "%d message sent." msgid_plural "%d messages sent." msgstr[0] "%d сообщение отправлено." msgstr[1] "%d сообщений отправлено." msgstr[2] "%d сообщений отправлено." +msgstr[3] "%d сообщений отправлено." -#: ../../mod/invite.php:112 +#: mod/invite.php:112 msgid "You have no more invitations available" msgstr "У вас нет больше приглашений" -#: ../../mod/invite.php:120 +#: mod/invite.php:120 #, php-format msgid "" "Visit %s for a list of public sites that you can join. Friendica members on " -"other sites can all connect with each other, as well as with members of many" -" other social networks." -msgstr "Посетите %s со списком общедоступных сайтов, к которым вы можете присоединиться. Все участники Friendica на других сайтах могут соединиться друг с другом, а также с участниками многих других социальных сетей." +"other sites can all connect with each other, as well as with members of many " +"other social networks." +msgstr "" +"Посетите %s со списком общедоступных сайтов, к которым вы можете " +"присоединиться. Все участники Friendica на других сайтах могут соединиться " +"друг с другом, а также с участниками многих других социальных сетей." -#: ../../mod/invite.php:122 +#: mod/invite.php:122 #, php-format msgid "" "To accept this invitation, please visit and register at %s or any other " "public Friendica website." -msgstr "Для одобрения этого приглашения, пожалуйста, посетите и зарегистрируйтесь на %s ,или любом другом публичном сервере Friendica" +msgstr "" +"Для одобрения этого приглашения, пожалуйста, посетите и зарегистрируйтесь на " +"%s ,или любом другом публичном сервере Friendica" -#: ../../mod/invite.php:123 +#: mod/invite.php:123 #, php-format msgid "" "Friendica sites all inter-connect to create a huge privacy-enhanced social " "web that is owned and controlled by its members. They can also connect with " "many traditional social networks. See %s for a list of alternate Friendica " "sites you can join." -msgstr "Сайты Friendica, подключившись между собой, могут создать сеть с повышенной безопасностью, которая принадлежит и управляется её членами. Они также могут подключаться ко многим традиционным социальным сетям. См. %s со списком альтернативных сайтов Friendica, к которым вы можете присоединиться." +msgstr "" +"Сайты Friendica, подключившись между собой, могут создать сеть с повышенной " +"безопасностью, которая принадлежит и управляется её членами. Они также могут " +"подключаться ко многим традиционным социальным сетям. См. %s со списком " +"альтернативных сайтов Friendica, к которым вы можете присоединиться." -#: ../../mod/invite.php:126 +#: mod/invite.php:126 msgid "" -"Our apologies. This system is not currently configured to connect with other" -" public sites or invite members." -msgstr "Извините. Эта система в настоящее время не сконфигурирована для соединения с другими общественными сайтами и для приглашения участников." +"Our apologies. This system is not currently configured to connect with other " +"public sites or invite members." +msgstr "" +"Извините. Эта система в настоящее время не сконфигурирована для соединения с " +"другими общественными сайтами и для приглашения участников." -#: ../../mod/invite.php:132 +#: mod/invite.php:132 msgid "Send invitations" msgstr "Отправить приглашения" -#: ../../mod/invite.php:133 +#: mod/invite.php:133 msgid "Enter email addresses, one per line:" msgstr "Введите адреса электронной почты, по одному в строке:" -#: ../../mod/invite.php:135 +#: mod/invite.php:135 msgid "" "You are cordially invited to join me and other close friends on Friendica - " "and help us to create a better social web." -msgstr "Приглашаем Вас присоединиться ко мне и другим близким друзьям на Friendica - помочь нам создать лучшую социальную сеть." +msgstr "" +"Приглашаем Вас присоединиться ко мне и другим близким друзьям на Friendica - " +"помочь нам создать лучшую социальную сеть." -#: ../../mod/invite.php:137 +#: mod/invite.php:137 msgid "You will need to supply this invitation code: $invite_code" msgstr "Вам нужно будет предоставить этот код приглашения: $invite_code" -#: ../../mod/invite.php:137 +#: mod/invite.php:137 msgid "" "Once you have registered, please connect with me via my profile page at:" -msgstr "После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через мою страницу профиля по адресу:" +msgstr "" +"После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через " +"мою страницу профиля по адресу:" -#: ../../mod/invite.php:139 +#: mod/invite.php:139 msgid "" "For more information about the Friendica project and why we feel it is " "important, please visit http://friendica.com" -msgstr "Для получения более подробной информации о проекте Friendica, пожалуйста, посетите http://friendica.com" +msgstr "" +"Для получения более подробной информации о проекте Friendica, пожалуйста, " +"посетите http://friendica.com" -#: ../../mod/photos.php:52 ../../boot.php:2129 +#: mod/photos.php:99 include/identity.php:705 msgid "Photo Albums" msgstr "Фотоальбомы" -#: ../../mod/photos.php:60 ../../mod/photos.php:155 ../../mod/photos.php:1064 -#: ../../mod/photos.php:1187 ../../mod/photos.php:1210 -#: ../../mod/photos.php:1760 ../../mod/photos.php:1772 -#: ../../view/theme/diabook/theme.php:499 -msgid "Contact Photos" -msgstr "Фотографии контакта" +#: mod/photos.php:100 mod/photos.php:1899 +msgid "Recent Photos" +msgstr "Последние фото" -#: ../../mod/photos.php:67 ../../mod/photos.php:1262 ../../mod/photos.php:1819 +#: mod/photos.php:103 mod/photos.php:1320 mod/photos.php:1901 msgid "Upload New Photos" msgstr "Загрузить новые фото" -#: ../../mod/photos.php:144 +#: mod/photos.php:181 msgid "Contact information unavailable" msgstr "Информация о контакте недоступна" -#: ../../mod/photos.php:165 +#: mod/photos.php:202 msgid "Album not found." msgstr "Альбом не найден." -#: ../../mod/photos.php:188 ../../mod/photos.php:200 ../../mod/photos.php:1204 +#: mod/photos.php:232 mod/photos.php:244 mod/photos.php:1262 msgid "Delete Album" msgstr "Удалить альбом" -#: ../../mod/photos.php:198 +#: mod/photos.php:242 msgid "Do you really want to delete this photo album and all its photos?" msgstr "Вы действительно хотите удалить этот альбом и все его фотографии?" -#: ../../mod/photos.php:278 ../../mod/photos.php:289 ../../mod/photos.php:1515 +#: mod/photos.php:322 mod/photos.php:333 mod/photos.php:1580 msgid "Delete Photo" msgstr "Удалить фото" -#: ../../mod/photos.php:287 +#: mod/photos.php:331 msgid "Do you really want to delete this photo?" msgstr "Вы действительно хотите удалить эту фотографию?" -#: ../../mod/photos.php:662 +#: mod/photos.php:706 #, php-format msgid "%1$s was tagged in %2$s by %3$s" msgstr "%1$s отмечен/а/ в %2$s by %3$s" -#: ../../mod/photos.php:662 +#: mod/photos.php:706 msgid "a photo" msgstr "фото" -#: ../../mod/photos.php:767 -msgid "Image exceeds size limit of " -msgstr "Размер фото превышает лимит " - -#: ../../mod/photos.php:775 +#: mod/photos.php:819 msgid "Image file is empty." msgstr "Файл изображения пуст." -#: ../../mod/photos.php:930 +#: mod/photos.php:986 msgid "No photos selected" msgstr "Не выбрано фото." -#: ../../mod/photos.php:1094 +#: mod/photos.php:1147 #, php-format msgid "You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage." -msgstr "Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий." +msgstr "" +"Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий." -#: ../../mod/photos.php:1129 +#: mod/photos.php:1182 msgid "Upload Photos" msgstr "Загрузить фото" -#: ../../mod/photos.php:1133 ../../mod/photos.php:1199 +#: mod/photos.php:1186 mod/photos.php:1257 msgid "New album name: " msgstr "Название нового альбома: " -#: ../../mod/photos.php:1134 +#: mod/photos.php:1187 msgid "or existing album name: " msgstr "или название существующего альбома: " -#: ../../mod/photos.php:1135 +#: mod/photos.php:1188 msgid "Do not show a status post for this upload" msgstr "Не показывать статус-сообщение для этой закачки" -#: ../../mod/photos.php:1137 ../../mod/photos.php:1510 +#: mod/photos.php:1190 mod/photos.php:1575 include/acl_selectors.php:347 msgid "Permissions" msgstr "Разрешения" -#: ../../mod/photos.php:1148 +#: mod/photos.php:1201 msgid "Private Photo" msgstr "Личное фото" -#: ../../mod/photos.php:1149 +#: mod/photos.php:1202 msgid "Public Photo" msgstr "Публичное фото" -#: ../../mod/photos.php:1212 +#: mod/photos.php:1270 msgid "Edit Album" msgstr "Редактировать альбом" -#: ../../mod/photos.php:1218 +#: mod/photos.php:1276 msgid "Show Newest First" msgstr "Показать новые первыми" -#: ../../mod/photos.php:1220 +#: mod/photos.php:1278 msgid "Show Oldest First" msgstr "Показать старые первыми" -#: ../../mod/photos.php:1248 ../../mod/photos.php:1802 +#: mod/photos.php:1306 mod/photos.php:1884 msgid "View Photo" msgstr "Просмотр фото" -#: ../../mod/photos.php:1294 +#: mod/photos.php:1353 msgid "Permission denied. Access to this item may be restricted." msgstr "Нет разрешения. Доступ к этому элементу ограничен." -#: ../../mod/photos.php:1296 +#: mod/photos.php:1355 msgid "Photo not available" msgstr "Фото недоступно" -#: ../../mod/photos.php:1352 +#: mod/photos.php:1411 msgid "View photo" msgstr "Просмотр фото" -#: ../../mod/photos.php:1352 +#: mod/photos.php:1411 msgid "Edit photo" msgstr "Редактировать фото" -#: ../../mod/photos.php:1353 +#: mod/photos.php:1412 msgid "Use as profile photo" msgstr "Использовать как фото профиля" -#: ../../mod/photos.php:1378 +#: mod/photos.php:1437 msgid "View Full Size" msgstr "Просмотреть полный размер" -#: ../../mod/photos.php:1457 +#: mod/photos.php:1523 msgid "Tags: " msgstr "Ключевые слова: " -#: ../../mod/photos.php:1460 +#: mod/photos.php:1526 msgid "[Remove any tag]" msgstr "[Удалить любое ключевое слово]" -#: ../../mod/photos.php:1500 -msgid "Rotate CW (right)" -msgstr "Поворот по часовой стрелке (направо)" - -#: ../../mod/photos.php:1501 -msgid "Rotate CCW (left)" -msgstr "Поворот против часовой стрелки (налево)" - -#: ../../mod/photos.php:1503 +#: mod/photos.php:1566 msgid "New album name" msgstr "Название нового альбома" -#: ../../mod/photos.php:1506 +#: mod/photos.php:1567 msgid "Caption" msgstr "Подпись" -#: ../../mod/photos.php:1508 +#: mod/photos.php:1568 msgid "Add a Tag" msgstr "Добавить ключевое слово (таг)" -#: ../../mod/photos.php:1512 -msgid "" -"Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" +#: mod/photos.php:1568 +msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" msgstr "Пример: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping" -#: ../../mod/photos.php:1521 +#: mod/photos.php:1569 +msgid "Do not rotate" +msgstr "" + +#: mod/photos.php:1570 +msgid "Rotate CW (right)" +msgstr "Поворот по часовой стрелке (направо)" + +#: mod/photos.php:1571 +msgid "Rotate CCW (left)" +msgstr "Поворот против часовой стрелки (налево)" + +#: mod/photos.php:1586 msgid "Private photo" msgstr "Личное фото" -#: ../../mod/photos.php:1522 +#: mod/photos.php:1587 msgid "Public photo" msgstr "Публичное фото" -#: ../../mod/photos.php:1544 ../../include/conversation.php:1090 +#: mod/photos.php:1609 include/conversation.php:1182 msgid "Share" msgstr "Поделиться" -#: ../../mod/photos.php:1817 -msgid "Recent Photos" -msgstr "Последние фото" +#: mod/photos.php:1648 include/conversation.php:509 +#: include/conversation.php:1413 +msgid "Attending" +msgid_plural "Attending" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" -#: ../../mod/regmod.php:55 +#: mod/photos.php:1648 include/conversation.php:509 +msgid "Not attending" +msgstr "" + +#: mod/photos.php:1648 include/conversation.php:509 +msgid "Might attend" +msgstr "" + +#: mod/photos.php:1813 +msgid "Map" +msgstr "Карта" + +#: mod/p.php:9 +msgid "Not Extended" +msgstr "" + +#: mod/regmod.php:55 msgid "Account approved." msgstr "Аккаунт утвержден." -#: ../../mod/regmod.php:92 +#: mod/regmod.php:92 #, php-format msgid "Registration revoked for %s" msgstr "Регистрация отменена для %s" -#: ../../mod/regmod.php:104 +#: mod/regmod.php:104 msgid "Please login." msgstr "Пожалуйста, войдите с паролем." -#: ../../mod/uimport.php:66 +#: mod/uimport.php:66 msgid "Move account" msgstr "Удалить аккаунт" -#: ../../mod/uimport.php:67 +#: mod/uimport.php:67 msgid "You can import an account from another Friendica server." msgstr "Вы можете импортировать учетную запись с другого сервера Friendica." -#: ../../mod/uimport.php:68 +#: mod/uimport.php:68 msgid "" "You need to export your account from the old server and upload it here. We " -"will recreate your old account here with all your contacts. We will try also" -" to inform your friends that you moved here." -msgstr "Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы постараемся также сообщить друзьям, что вы переехали сюда." +"will recreate your old account here with all your contacts. We will try also " +"to inform your friends that you moved here." +msgstr "" +"Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его " +"сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы " +"постараемся также сообщить друзьям, что вы переехали сюда." -#: ../../mod/uimport.php:69 +#: mod/uimport.php:69 msgid "" "This feature is experimental. We can't import contacts from the OStatus " -"network (statusnet/identi.ca) or from Diaspora" -msgstr "Это экспериментальная функция. Мы не можем импортировать контакты из сети OStatus (StatusNet / identi.ca) или из Diaspora" +"network (GNU Social/Statusnet) or from Diaspora" +msgstr "" -#: ../../mod/uimport.php:70 +#: mod/uimport.php:70 msgid "Account file" msgstr "Файл аккаунта" -#: ../../mod/uimport.php:70 +#: mod/uimport.php:70 msgid "" "To export your account, go to \"Settings->Export your personal data\" and " "select \"Export account\"" -msgstr "Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" и выберите \"Экспорт аккаунта\"" +msgstr "" +"Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" " +"и выберите \"Экспорт аккаунта\"" -#: ../../mod/attach.php:8 +#: mod/attach.php:8 msgid "Item not available." msgstr "Пункт не доступен." -#: ../../mod/attach.php:20 +#: mod/attach.php:20 msgid "Item was not found." msgstr "Пункт не был найден." -#: ../../boot.php:749 +#: boot.php:868 msgid "Delete this item?" msgstr "Удалить этот элемент?" -#: ../../boot.php:752 +#: boot.php:871 msgid "show fewer" msgstr "показать меньше" -#: ../../boot.php:1122 +#: boot.php:1292 #, php-format msgid "Update %s failed. See error logs." msgstr "Обновление %s не удалось. Смотрите журнал ошибок." -#: ../../boot.php:1240 +#: boot.php:1404 msgid "Create a New Account" msgstr "Создать новый аккаунт" -#: ../../boot.php:1265 ../../include/nav.php:73 +#: boot.php:1429 include/nav.php:72 msgid "Logout" msgstr "Выход" -#: ../../boot.php:1268 +#: boot.php:1432 msgid "Nickname or Email address: " msgstr "Ник или адрес электронной почты: " -#: ../../boot.php:1269 +#: boot.php:1433 msgid "Password: " msgstr "Пароль: " -#: ../../boot.php:1270 +#: boot.php:1434 msgid "Remember me" msgstr "Запомнить" -#: ../../boot.php:1273 +#: boot.php:1437 msgid "Or login using OpenID: " msgstr "Или зайти с OpenID: " -#: ../../boot.php:1279 +#: boot.php:1443 msgid "Forgot your password?" msgstr "Забыли пароль?" -#: ../../boot.php:1282 +#: boot.php:1446 msgid "Website Terms of Service" msgstr "Правила сайта" -#: ../../boot.php:1283 +#: boot.php:1447 msgid "terms of service" msgstr "правила" -#: ../../boot.php:1285 +#: boot.php:1449 msgid "Website Privacy Policy" msgstr "Политика конфиденциальности сервера" -#: ../../boot.php:1286 +#: boot.php:1450 msgid "privacy policy" msgstr "политика конфиденциальности" -#: ../../boot.php:1419 -msgid "Requested account is not available." -msgstr "Запрашиваемый профиль недоступен." - -#: ../../boot.php:1501 ../../boot.php:1635 -#: ../../include/profile_advanced.php:84 -msgid "Edit profile" -msgstr "Редактировать профиль" - -#: ../../boot.php:1600 -msgid "Message" -msgstr "Сообщение" - -#: ../../boot.php:1606 ../../include/nav.php:175 -msgid "Profiles" -msgstr "Профили" - -#: ../../boot.php:1606 -msgid "Manage/edit profiles" -msgstr "Управление / редактирование профилей" - -#: ../../boot.php:1706 -msgid "Network:" -msgstr "" - -#: ../../boot.php:1736 ../../boot.php:1822 -msgid "g A l F d" -msgstr "g A l F d" - -#: ../../boot.php:1737 ../../boot.php:1823 -msgid "F d" -msgstr "F d" - -#: ../../boot.php:1782 ../../boot.php:1863 -msgid "[today]" -msgstr "[сегодня]" - -#: ../../boot.php:1794 -msgid "Birthday Reminders" -msgstr "Напоминания о днях рождения" - -#: ../../boot.php:1795 -msgid "Birthdays this week:" -msgstr "Дни рождения на этой неделе:" - -#: ../../boot.php:1856 -msgid "[No description]" -msgstr "[без описания]" - -#: ../../boot.php:1874 -msgid "Event Reminders" -msgstr "Напоминания о мероприятиях" - -#: ../../boot.php:1875 -msgid "Events this week:" -msgstr "Мероприятия на этой неделе:" - -#: ../../boot.php:2112 ../../include/nav.php:76 -msgid "Status" -msgstr "Статус" - -#: ../../boot.php:2115 -msgid "Status Messages and Posts" -msgstr "Сообщение статуса и посты" - -#: ../../boot.php:2122 -msgid "Profile Details" -msgstr "Детали профиля" - -#: ../../boot.php:2133 ../../boot.php:2136 ../../include/nav.php:79 -msgid "Videos" -msgstr "Видео" - -#: ../../boot.php:2146 -msgid "Events and Calendar" -msgstr "Календарь и события" - -#: ../../boot.php:2153 -msgid "Only You Can See This" -msgstr "Только вы можете это видеть" - -#: ../../object/Item.php:94 +#: object/Item.php:95 msgid "This entry was edited" msgstr "Эта запись была отредактирована" -#: ../../object/Item.php:208 +#: object/Item.php:191 +msgid "I will attend" +msgstr "" + +#: object/Item.php:191 +msgid "I will not attend" +msgstr "" + +#: object/Item.php:191 +msgid "I might attend" +msgstr "" + +#: object/Item.php:230 msgid "ignore thread" msgstr "" -#: ../../object/Item.php:209 +#: object/Item.php:231 msgid "unignore thread" msgstr "" -#: ../../object/Item.php:210 +#: object/Item.php:232 msgid "toggle ignore status" msgstr "" -#: ../../object/Item.php:213 -msgid "ignored" -msgstr "" - -#: ../../object/Item.php:316 ../../include/conversation.php:666 +#: object/Item.php:345 include/conversation.php:687 msgid "Categories:" msgstr "Категории:" -#: ../../object/Item.php:317 ../../include/conversation.php:667 +#: object/Item.php:346 include/conversation.php:688 msgid "Filed under:" msgstr "В рубрике:" -#: ../../object/Item.php:329 +#: object/Item.php:360 msgid "via" msgstr "через" -#: ../../include/dbstructure.php:26 +#: include/dbstructure.php:26 #, php-format msgid "" "\n" "\t\t\tThe friendica developers released update %s recently,\n" "\t\t\tbut when I tried to install it, something went terribly wrong.\n" "\t\t\tThis needs to be fixed soon and I can't do it alone. Please contact a\n" -"\t\t\tfriendica developer if you can not help me on your own. My database might be invalid." +"\t\t\tfriendica developer if you can not help me on your own. My database " +"might be invalid." msgstr "" -#: ../../include/dbstructure.php:31 +#: include/dbstructure.php:31 #, php-format msgid "" "The error message is\n" "[pre]%s[/pre]" msgstr "" -#: ../../include/dbstructure.php:162 +#: include/dbstructure.php:153 msgid "Errors encountered creating database tables." msgstr "Обнаружены ошибки при создании таблиц базы данных." -#: ../../include/dbstructure.php:220 +#: include/dbstructure.php:230 msgid "Errors encountered performing database changes." msgstr "" -#: ../../include/auth.php:38 +#: include/auth.php:44 msgid "Logged out." msgstr "Выход из системы." -#: ../../include/auth.php:128 ../../include/user.php:67 +#: include/auth.php:134 include/user.php:75 msgid "" "We encountered a problem while logging in with the OpenID you provided. " "Please check the correct spelling of the ID." -msgstr "Мы столкнулись с проблемой при входе с OpenID, который вы указали. Пожалуйста, проверьте правильность написания ID." +msgstr "" +"Мы столкнулись с проблемой при входе с OpenID, который вы указали. " +"Пожалуйста, проверьте правильность написания ID." -#: ../../include/auth.php:128 ../../include/user.php:67 +#: include/auth.php:134 include/user.php:75 msgid "The error message was:" msgstr "Сообщение об ошибке было:" -#: ../../include/contact_widgets.php:6 +#: include/contact_widgets.php:6 msgid "Add New Contact" msgstr "Добавить контакт" -#: ../../include/contact_widgets.php:7 +#: include/contact_widgets.php:7 msgid "Enter address or web location" msgstr "Введите адрес или веб-местонахождение" -#: ../../include/contact_widgets.php:8 +#: include/contact_widgets.php:8 msgid "Example: bob@example.com, http://example.com/barbara" msgstr "Пример: bob@example.com, http://example.com/barbara" -#: ../../include/contact_widgets.php:24 +#: include/contact_widgets.php:24 #, php-format msgid "%d invitation available" msgid_plural "%d invitations available" msgstr[0] "%d приглашение доступно" msgstr[1] "%d приглашений доступно" msgstr[2] "%d приглашений доступно" +msgstr[3] "%d приглашений доступно" -#: ../../include/contact_widgets.php:30 +#: include/contact_widgets.php:30 msgid "Find People" msgstr "Поиск людей" -#: ../../include/contact_widgets.php:31 +#: include/contact_widgets.php:31 msgid "Enter name or interest" msgstr "Введите имя или интерес" -#: ../../include/contact_widgets.php:32 -msgid "Connect/Follow" -msgstr "Подключиться/Следовать" - -#: ../../include/contact_widgets.php:33 +#: include/contact_widgets.php:33 msgid "Examples: Robert Morgenstein, Fishing" msgstr "Примеры: Роберт Morgenstein, Рыбалка" -#: ../../include/contact_widgets.php:36 ../../view/theme/diabook/theme.php:526 +#: include/contact_widgets.php:36 view/theme/diabook/theme.php:526 +#: view/theme/vier/theme.php:206 msgid "Similar Interests" msgstr "Похожие интересы" -#: ../../include/contact_widgets.php:37 +#: include/contact_widgets.php:37 msgid "Random Profile" msgstr "Случайный профиль" -#: ../../include/contact_widgets.php:38 ../../view/theme/diabook/theme.php:528 +#: include/contact_widgets.php:38 view/theme/diabook/theme.php:528 +#: view/theme/vier/theme.php:208 msgid "Invite Friends" msgstr "Пригласить друзей" -#: ../../include/contact_widgets.php:71 +#: include/contact_widgets.php:108 msgid "Networks" msgstr "Сети" -#: ../../include/contact_widgets.php:74 +#: include/contact_widgets.php:111 msgid "All Networks" msgstr "Все сети" -#: ../../include/contact_widgets.php:104 ../../include/features.php:60 +#: include/contact_widgets.php:141 include/features.php:102 msgid "Saved Folders" msgstr "Сохранённые папки" -#: ../../include/contact_widgets.php:107 ../../include/contact_widgets.php:139 +#: include/contact_widgets.php:144 include/contact_widgets.php:176 msgid "Everything" msgstr "Всё" -#: ../../include/contact_widgets.php:136 +#: include/contact_widgets.php:173 msgid "Categories" msgstr "Категории" -#: ../../include/features.php:23 +#: include/contact_widgets.php:237 +#, php-format +msgid "%d contact in common" +msgid_plural "%d contacts in common" +msgstr[0] "%d Контакт" +msgstr[1] "%d Контактов" +msgstr[2] "%d Контактов" +msgstr[3] "%d Контактов" + +#: include/features.php:63 msgid "General Features" msgstr "Основные возможности" -#: ../../include/features.php:25 +#: include/features.php:65 msgid "Multiple Profiles" msgstr "Несколько профилей" -#: ../../include/features.php:25 +#: include/features.php:65 msgid "Ability to create multiple profiles" msgstr "Возможность создания нескольких профилей" -#: ../../include/features.php:30 -msgid "Post Composition Features" +#: include/features.php:66 +msgid "Photo Location" msgstr "" -#: ../../include/features.php:31 +#: include/features.php:66 +msgid "" +"Photo metadata is normally stripped. This extracts the location (if present) " +"prior to stripping metadata and links it to a map." +msgstr "" + +#: include/features.php:71 +msgid "Post Composition Features" +msgstr "Составление сообщений" + +#: include/features.php:72 msgid "Richtext Editor" msgstr "Редактор RTF" -#: ../../include/features.php:31 +#: include/features.php:72 msgid "Enable richtext editor" msgstr "Включить редактор RTF" -#: ../../include/features.php:32 +#: include/features.php:73 msgid "Post Preview" -msgstr "предварительный просмотр" +msgstr "Предварительный просмотр" -#: ../../include/features.php:32 +#: include/features.php:73 msgid "Allow previewing posts and comments before publishing them" msgstr "Разрешить предпросмотр сообщения и комментария перед их публикацией" -#: ../../include/features.php:33 +#: include/features.php:74 msgid "Auto-mention Forums" msgstr "" -#: ../../include/features.php:33 +#: include/features.php:74 msgid "" "Add/remove mention when a fourm page is selected/deselected in ACL window." msgstr "" -#: ../../include/features.php:38 +#: include/features.php:79 msgid "Network Sidebar Widgets" msgstr "Виджет боковой панели \"Сеть\"" -#: ../../include/features.php:39 +#: include/features.php:80 msgid "Search by Date" msgstr "Поиск по датам" -#: ../../include/features.php:39 +#: include/features.php:80 msgid "Ability to select posts by date ranges" msgstr "Возможность выбора постов по диапазону дат" -#: ../../include/features.php:40 +#: include/features.php:81 include/features.php:111 +msgid "List Forums" +msgstr "" + +#: include/features.php:81 +msgid "Enable widget to display the forums your are connected with" +msgstr "" + +#: include/features.php:82 msgid "Group Filter" msgstr "Фильтр групп" -#: ../../include/features.php:40 +#: include/features.php:82 msgid "Enable widget to display Network posts only from selected group" -msgstr "Включить виджет для отображения сообщений сети только от выбранной группы" +msgstr "" +"Включить виджет для отображения сообщений сети только от выбранной группы" -#: ../../include/features.php:41 +#: include/features.php:83 msgid "Network Filter" msgstr "Фильтр сети" -#: ../../include/features.php:41 +#: include/features.php:83 msgid "Enable widget to display Network posts only from selected network" -msgstr "Включить виджет для отображения сообщений сети только от выбранной сети" +msgstr "" +"Включить виджет для отображения сообщений сети только от выбранной сети" -#: ../../include/features.php:42 +#: include/features.php:84 msgid "Save search terms for re-use" msgstr "Сохранить условия поиска для повторного использования" -#: ../../include/features.php:47 +#: include/features.php:89 msgid "Network Tabs" msgstr "Сетевые вкладки" -#: ../../include/features.php:48 +#: include/features.php:90 msgid "Network Personal Tab" msgstr "Персональные сетевые вкладки" -#: ../../include/features.php:48 +#: include/features.php:90 msgid "Enable tab to display only Network posts that you've interacted on" -msgstr "Включить вкладку для отображения только сообщений сети, с которой вы взаимодействовали" +msgstr "" +"Включить вкладку для отображения только сообщений сети, с которой вы " +"взаимодействовали" -#: ../../include/features.php:49 +#: include/features.php:91 msgid "Network New Tab" msgstr "Новая вкладка сеть" -#: ../../include/features.php:49 +#: include/features.php:91 msgid "Enable tab to display only new Network posts (from the last 12 hours)" -msgstr "Включить вкладку для отображения только новых сообщений сети (за последние 12 часов)" +msgstr "" +"Включить вкладку для отображения только новых сообщений сети (за последние " +"12 часов)" -#: ../../include/features.php:50 +#: include/features.php:92 msgid "Network Shared Links Tab" msgstr "Вкладка shared ссылок сети" -#: ../../include/features.php:50 +#: include/features.php:92 msgid "Enable tab to display only Network posts with links in them" -msgstr "Включить вкладку для отображения только сообщений сети со ссылками на них" +msgstr "" +"Включить вкладку для отображения только сообщений сети со ссылками на них" -#: ../../include/features.php:55 +#: include/features.php:97 msgid "Post/Comment Tools" msgstr "Инструменты пост/комментарий" -#: ../../include/features.php:56 +#: include/features.php:98 msgid "Multiple Deletion" msgstr "Множественное удаление" -#: ../../include/features.php:56 +#: include/features.php:98 msgid "Select and delete multiple posts/comments at once" msgstr "Выбрать и удалить несколько постов/комментариев одновременно." -#: ../../include/features.php:57 +#: include/features.php:99 msgid "Edit Sent Posts" msgstr "Редактировать отправленные посты" -#: ../../include/features.php:57 +#: include/features.php:99 msgid "Edit and correct posts and comments after sending" msgstr "Редактировать и править посты и комментарии после отправления" -#: ../../include/features.php:58 +#: include/features.php:100 msgid "Tagging" msgstr "Отмеченное" -#: ../../include/features.php:58 +#: include/features.php:100 msgid "Ability to tag existing posts" msgstr "Возможность отмечать существующие посты" -#: ../../include/features.php:59 +#: include/features.php:101 msgid "Post Categories" msgstr "Категории постов" -#: ../../include/features.php:59 +#: include/features.php:101 msgid "Add categories to your posts" msgstr "Добавить категории вашего поста" -#: ../../include/features.php:60 +#: include/features.php:102 msgid "Ability to file posts under folders" msgstr "" -#: ../../include/features.php:61 +#: include/features.php:103 msgid "Dislike Posts" msgstr "Посты дизлайк" -#: ../../include/features.php:61 +#: include/features.php:103 msgid "Ability to dislike posts/comments" msgstr "Возможность дизлайка постов/комментариев" -#: ../../include/features.php:62 +#: include/features.php:104 msgid "Star Posts" msgstr "Популярные посты" -#: ../../include/features.php:62 +#: include/features.php:104 msgid "Ability to mark special posts with a star indicator" msgstr "Возможность отметить специальные сообщения индикатором популярности" -#: ../../include/features.php:63 +#: include/features.php:105 msgid "Mute Post Notifications" msgstr "" -#: ../../include/features.php:63 +#: include/features.php:105 msgid "Ability to mute notifications for a thread" msgstr "" -#: ../../include/follow.php:32 +#: include/features.php:110 +msgid "Advanced Profile Settings" +msgstr "Расширенные настройки профиля" + +#: include/features.php:111 +msgid "Show visitors public community forums at the Advanced Profile Page" +msgstr "" + +#: include/follow.php:77 msgid "Connect URL missing." msgstr "Connect-URL отсутствует." -#: ../../include/follow.php:59 +#: include/follow.php:104 msgid "" "This site is not configured to allow communications with other networks." msgstr "Данный сайт не настроен так, чтобы держать связь с другими сетями." -#: ../../include/follow.php:60 ../../include/follow.php:80 +#: include/follow.php:105 include/follow.php:125 msgid "No compatible communication protocols or feeds were discovered." msgstr "Обнаружены несовместимые протоколы связи или каналы." -#: ../../include/follow.php:78 +#: include/follow.php:123 msgid "The profile address specified does not provide adequate information." msgstr "Указанный адрес профиля не дает адекватной информации." -#: ../../include/follow.php:82 +#: include/follow.php:127 msgid "An author or name was not found." msgstr "Автор или имя не найдены." -#: ../../include/follow.php:84 +#: include/follow.php:129 msgid "No browser URL could be matched to this address." msgstr "Нет URL браузера, который соответствует этому адресу." -#: ../../include/follow.php:86 +#: include/follow.php:131 msgid "" "Unable to match @-style Identity Address with a known protocol or email " "contact." msgstr "" -#: ../../include/follow.php:87 +#: include/follow.php:132 msgid "Use mailto: in front of address to force email check." msgstr "Bcgjkmpeqnt mailto: перед адресом для быстрого доступа к email." -#: ../../include/follow.php:93 +#: include/follow.php:138 msgid "" "The profile address specified belongs to a network which has been disabled " "on this site." msgstr "Указанный адрес профиля принадлежит сети, недоступной на этом сайта." -#: ../../include/follow.php:103 +#: include/follow.php:148 msgid "" "Limited profile. This person will be unable to receive direct/personal " "notifications from you." -msgstr "Ограниченный профиль. Этот человек не сможет получить прямые / личные уведомления от вас." +msgstr "" +"Ограниченный профиль. Этот человек не сможет получить прямые / личные " +"уведомления от вас." -#: ../../include/follow.php:205 +#: include/follow.php:249 msgid "Unable to retrieve contact information." msgstr "Невозможно получить контактную информацию." -#: ../../include/follow.php:258 +#: include/follow.php:302 msgid "following" msgstr "следует" -#: ../../include/group.php:25 +#: include/group.php:25 msgid "" "A deleted group with this name was revived. Existing item permissions " "may apply to this group and any future members. If this is " "not what you intended, please create another group with a different name." -msgstr "Удаленная группа с таким названием была восстановлена. Существующие права доступа могут применяться к этой группе и любым будущим участникам. Если это не то, что вы хотели, пожалуйста, создайте еще ​​одну группу с другим названием." +msgstr "" +"Удаленная группа с таким названием была восстановлена. Существующие права " +"доступа могут применяться к этой группе и любым будущим " +"участникам. Если это не то, что вы хотели, пожалуйста, создайте еще ​​одну " +"группу с другим названием." -#: ../../include/group.php:207 +#: include/group.php:209 msgid "Default privacy group for new contacts" msgstr "Группа доступа по умолчанию для новых контактов" -#: ../../include/group.php:226 +#: include/group.php:239 msgid "Everybody" msgstr "Каждый" -#: ../../include/group.php:249 +#: include/group.php:262 msgid "edit" msgstr "редактировать" -#: ../../include/group.php:271 +#: include/group.php:285 +msgid "Edit groups" +msgstr "" + +#: include/group.php:287 msgid "Edit group" msgstr "Редактировать группу" -#: ../../include/group.php:272 +#: include/group.php:288 msgid "Create a new group" msgstr "Создать новую группу" -#: ../../include/group.php:273 +#: include/group.php:291 msgid "Contacts not in any group" msgstr "Контакты не состоят в группе" -#: ../../include/datetime.php:43 ../../include/datetime.php:45 +#: include/datetime.php:43 include/datetime.php:45 msgid "Miscellaneous" msgstr "Разное" -#: ../../include/datetime.php:153 ../../include/datetime.php:290 -msgid "year" -msgstr "год" +#: include/datetime.php:141 +msgid "YYYY-MM-DD or MM-DD" +msgstr "" -#: ../../include/datetime.php:158 ../../include/datetime.php:291 -msgid "month" -msgstr "мес." - -#: ../../include/datetime.php:163 ../../include/datetime.php:293 -msgid "day" -msgstr "день" - -#: ../../include/datetime.php:276 +#: include/datetime.php:271 msgid "never" msgstr "никогда" -#: ../../include/datetime.php:282 +#: include/datetime.php:277 msgid "less than a second ago" msgstr "менее сек. назад" -#: ../../include/datetime.php:290 +#: include/datetime.php:287 +msgid "year" +msgstr "год" + +#: include/datetime.php:287 msgid "years" msgstr "лет" -#: ../../include/datetime.php:291 +#: include/datetime.php:288 msgid "months" msgstr "мес." -#: ../../include/datetime.php:292 -msgid "week" -msgstr "неделя" - -#: ../../include/datetime.php:292 +#: include/datetime.php:289 msgid "weeks" msgstr "недель" -#: ../../include/datetime.php:293 +#: include/datetime.php:290 msgid "days" msgstr "дней" -#: ../../include/datetime.php:294 +#: include/datetime.php:291 msgid "hour" msgstr "час" -#: ../../include/datetime.php:294 +#: include/datetime.php:291 msgid "hours" msgstr "час." -#: ../../include/datetime.php:295 +#: include/datetime.php:292 msgid "minute" msgstr "минута" -#: ../../include/datetime.php:295 +#: include/datetime.php:292 msgid "minutes" msgstr "мин." -#: ../../include/datetime.php:296 +#: include/datetime.php:293 msgid "second" msgstr "секунда" -#: ../../include/datetime.php:296 +#: include/datetime.php:293 msgid "seconds" msgstr "сек." -#: ../../include/datetime.php:305 +#: include/datetime.php:302 #, php-format msgid "%1$d %2$s ago" msgstr "%1$d %2$s назад" -#: ../../include/datetime.php:477 ../../include/items.php:2211 +#: include/datetime.php:474 include/items.php:2500 #, php-format msgid "%s's birthday" msgstr "день рождения %s" -#: ../../include/datetime.php:478 ../../include/items.php:2212 +#: include/datetime.php:475 include/items.php:2501 #, php-format msgid "Happy Birthday %s" msgstr "С днём рождения %s" -#: ../../include/acl_selectors.php:333 -msgid "Visible to everybody" -msgstr "Видимо всем" +#: include/identity.php:42 +msgid "Requested account is not available." +msgstr "Запрашиваемый профиль недоступен." -#: ../../include/acl_selectors.php:334 ../../view/theme/diabook/config.php:142 -#: ../../view/theme/diabook/theme.php:621 -msgid "show" -msgstr "показывать" +#: include/identity.php:95 include/identity.php:284 include/identity.php:662 +msgid "Edit profile" +msgstr "Редактировать профиль" -#: ../../include/acl_selectors.php:335 ../../view/theme/diabook/config.php:142 -#: ../../view/theme/diabook/theme.php:621 -msgid "don't show" -msgstr "не показывать" - -#: ../../include/message.php:15 ../../include/message.php:172 -msgid "[no subject]" -msgstr "[без темы]" - -#: ../../include/Contact.php:115 -msgid "stopped following" -msgstr "остановлено следование" - -#: ../../include/Contact.php:228 ../../include/conversation.php:882 -msgid "Poke" +#: include/identity.php:244 +msgid "Atom feed" msgstr "" -#: ../../include/Contact.php:229 ../../include/conversation.php:876 -msgid "View Status" -msgstr "Просмотреть статус" +#: include/identity.php:249 +msgid "Message" +msgstr "Сообщение" -#: ../../include/Contact.php:230 ../../include/conversation.php:877 -msgid "View Profile" -msgstr "Просмотреть профиль" +#: include/identity.php:255 include/nav.php:185 +msgid "Profiles" +msgstr "Профили" -#: ../../include/Contact.php:231 ../../include/conversation.php:878 -msgid "View Photos" -msgstr "Просмотреть фото" +#: include/identity.php:255 +msgid "Manage/edit profiles" +msgstr "Управление / редактирование профилей" -#: ../../include/Contact.php:232 ../../include/Contact.php:255 -#: ../../include/conversation.php:879 -msgid "Network Posts" -msgstr "Посты сети" +#: include/identity.php:425 include/identity.php:509 +msgid "g A l F d" +msgstr "g A l F d" -#: ../../include/Contact.php:233 ../../include/Contact.php:255 -#: ../../include/conversation.php:880 -msgid "Edit Contact" -msgstr "Редактировать контакт" +#: include/identity.php:426 include/identity.php:510 +msgid "F d" +msgstr "F d" -#: ../../include/Contact.php:234 -msgid "Drop Contact" -msgstr "Удалить контакт" +#: include/identity.php:471 include/identity.php:556 +msgid "[today]" +msgstr "[сегодня]" -#: ../../include/Contact.php:235 ../../include/Contact.php:255 -#: ../../include/conversation.php:881 -msgid "Send PM" -msgstr "Отправить ЛС" +#: include/identity.php:483 +msgid "Birthday Reminders" +msgstr "Напоминания о днях рождения" -#: ../../include/security.php:22 -msgid "Welcome " -msgstr "Добро пожаловать, " +#: include/identity.php:484 +msgid "Birthdays this week:" +msgstr "Дни рождения на этой неделе:" -#: ../../include/security.php:23 -msgid "Please upload a profile photo." -msgstr "Пожалуйста, загрузите фотографию профиля." +#: include/identity.php:543 +msgid "[No description]" +msgstr "[без описания]" -#: ../../include/security.php:26 -msgid "Welcome back " -msgstr "Добро пожаловать обратно, " +#: include/identity.php:567 +msgid "Event Reminders" +msgstr "Напоминания о мероприятиях" -#: ../../include/security.php:366 -msgid "" -"The form security token was not correct. This probably happened because the " -"form has been opened for too long (>3 hours) before submitting it." -msgstr "Ключ формы безопасности неправильный. Вероятно, это произошло потому, что форма была открыта слишком долго (более 3 часов) до её отправки." +#: include/identity.php:568 +msgid "Events this week:" +msgstr "Мероприятия на этой неделе:" -#: ../../include/conversation.php:118 ../../include/conversation.php:246 -#: ../../include/text.php:1966 ../../view/theme/diabook/theme.php:463 +#: include/identity.php:595 +msgid "j F, Y" +msgstr "j F, Y" + +#: include/identity.php:596 +msgid "j F" +msgstr "j F" + +#: include/identity.php:603 +msgid "Birthday:" +msgstr "День рождения:" + +#: include/identity.php:607 +msgid "Age:" +msgstr "Возраст:" + +#: include/identity.php:616 +#, php-format +msgid "for %1$d %2$s" +msgstr "" + +#: include/identity.php:629 +msgid "Religion:" +msgstr "Религия:" + +#: include/identity.php:633 +msgid "Hobbies/Interests:" +msgstr "Хобби / Интересы:" + +#: include/identity.php:640 +msgid "Contact information and Social Networks:" +msgstr "Информация о контакте и социальных сетях:" + +#: include/identity.php:642 +msgid "Musical interests:" +msgstr "Музыкальные интересы:" + +#: include/identity.php:644 +msgid "Books, literature:" +msgstr "Книги, литература:" + +#: include/identity.php:646 +msgid "Television:" +msgstr "Телевидение:" + +#: include/identity.php:648 +msgid "Film/dance/culture/entertainment:" +msgstr "Кино / Танцы / Культура / Развлечения:" + +#: include/identity.php:650 +msgid "Love/Romance:" +msgstr "Любовь / Романтика:" + +#: include/identity.php:652 +msgid "Work/employment:" +msgstr "Работа / Занятость:" + +#: include/identity.php:654 +msgid "School/education:" +msgstr "Школа / Образование:" + +#: include/identity.php:658 +msgid "Forums:" +msgstr "" + +#: include/identity.php:710 include/identity.php:713 include/nav.php:78 +msgid "Videos" +msgstr "Видео" + +#: include/identity.php:725 include/nav.php:140 +msgid "Events and Calendar" +msgstr "Календарь и события" + +#: include/identity.php:733 +msgid "Only You Can See This" +msgstr "Только вы можете это видеть" + +#: include/like.php:167 include/conversation.php:122 +#: include/conversation.php:258 include/text.php:1998 +#: view/theme/diabook/theme.php:463 msgid "event" msgstr "мероприятие" -#: ../../include/conversation.php:207 +#: include/like.php:184 include/conversation.php:141 include/diaspora.php:2185 +#: view/theme/diabook/theme.php:480 #, php-format -msgid "%1$s poked %2$s" +msgid "%1$s likes %2$s's %3$s" +msgstr "%1$s нравится %3$s от %2$s " + +#: include/like.php:186 include/conversation.php:144 +#, php-format +msgid "%1$s doesn't like %2$s's %3$s" +msgstr "%1$s не нравится %3$s от %2$s " + +#: include/like.php:188 +#, php-format +msgid "%1$s is attending %2$s's %3$s" msgstr "" -#: ../../include/conversation.php:211 ../../include/text.php:1005 -msgid "poked" +#: include/like.php:190 +#, php-format +msgid "%1$s is not attending %2$s's %3$s" msgstr "" -#: ../../include/conversation.php:291 -msgid "post/item" -msgstr "пост/элемент" - -#: ../../include/conversation.php:292 +#: include/like.php:192 #, php-format -msgid "%1$s marked %2$s's %3$s as favorite" -msgstr "%1$s пометил %2$s %3$s как Фаворит" - -#: ../../include/conversation.php:772 -msgid "remove" -msgstr "удалить" - -#: ../../include/conversation.php:776 -msgid "Delete Selected Items" -msgstr "Удалить выбранные позиции" - -#: ../../include/conversation.php:875 -msgid "Follow Thread" +msgid "%1$s may attend %2$s's %3$s" msgstr "" -#: ../../include/conversation.php:944 -#, php-format -msgid "%s likes this." -msgstr "%s нравится это." - -#: ../../include/conversation.php:944 -#, php-format -msgid "%s doesn't like this." -msgstr "%s не нравится это." - -#: ../../include/conversation.php:949 -#, php-format -msgid "%2$d people like this" -msgstr "%2$d людям нравится это" - -#: ../../include/conversation.php:952 -#, php-format -msgid "%2$d people don't like this" -msgstr "%2$d людям не нравится это" - -#: ../../include/conversation.php:966 -msgid "and" -msgstr "и" - -#: ../../include/conversation.php:972 -#, php-format -msgid ", and %d other people" -msgstr ", и %d других чел." - -#: ../../include/conversation.php:974 -#, php-format -msgid "%s like this." -msgstr "%s нравится это." - -#: ../../include/conversation.php:974 -#, php-format -msgid "%s don't like this." -msgstr "%s не нравится это." - -#: ../../include/conversation.php:1001 ../../include/conversation.php:1019 -msgid "Visible to everybody" -msgstr "Видимое всем" - -#: ../../include/conversation.php:1003 ../../include/conversation.php:1021 -msgid "Please enter a video link/URL:" -msgstr "Введите ссылку на видео link/URL:" - -#: ../../include/conversation.php:1004 ../../include/conversation.php:1022 -msgid "Please enter an audio link/URL:" -msgstr "Введите ссылку на аудио link/URL:" - -#: ../../include/conversation.php:1005 ../../include/conversation.php:1023 -msgid "Tag term:" -msgstr "" - -#: ../../include/conversation.php:1007 ../../include/conversation.php:1025 -msgid "Where are you right now?" -msgstr "И где вы сейчас?" - -#: ../../include/conversation.php:1008 -msgid "Delete item(s)?" -msgstr "Удалить елемент(ты)?" - -#: ../../include/conversation.php:1051 +#: include/acl_selectors.php:325 msgid "Post to Email" msgstr "Отправить на Email" -#: ../../include/conversation.php:1056 +#: include/acl_selectors.php:330 #, php-format msgid "Connectors disabled, since \"%s\" is enabled." msgstr "" -#: ../../include/conversation.php:1111 +#: include/acl_selectors.php:336 +msgid "Visible to everybody" +msgstr "Видимо всем" + +#: include/acl_selectors.php:337 view/theme/diabook/config.php:142 +#: view/theme/diabook/theme.php:621 view/theme/vier/config.php:103 +msgid "show" +msgstr "показывать" + +#: include/acl_selectors.php:338 view/theme/diabook/config.php:142 +#: view/theme/diabook/theme.php:621 view/theme/vier/config.php:103 +msgid "don't show" +msgstr "не показывать" + +#: include/acl_selectors.php:348 +msgid "Close" +msgstr "Закрыть" + +#: include/message.php:15 include/message.php:173 +msgid "[no subject]" +msgstr "[без темы]" + +#: include/Contact.php:119 +msgid "stopped following" +msgstr "остановлено следование" + +#: include/Contact.php:337 include/conversation.php:911 +msgid "View Status" +msgstr "Просмотреть статус" + +#: include/Contact.php:339 include/conversation.php:913 +msgid "View Photos" +msgstr "Просмотреть фото" + +#: include/Contact.php:340 include/conversation.php:914 +msgid "Network Posts" +msgstr "Посты сети" + +#: include/Contact.php:341 include/conversation.php:915 +msgid "Edit Contact" +msgstr "Редактировать контакт" + +#: include/Contact.php:342 +msgid "Drop Contact" +msgstr "Удалить контакт" + +#: include/Contact.php:343 include/conversation.php:916 +msgid "Send PM" +msgstr "Отправить ЛС" + +#: include/Contact.php:344 include/conversation.php:920 +msgid "Poke" +msgstr "" + +#: include/security.php:22 +msgid "Welcome " +msgstr "Добро пожаловать, " + +#: include/security.php:23 +msgid "Please upload a profile photo." +msgstr "Пожалуйста, загрузите фотографию профиля." + +#: include/security.php:26 +msgid "Welcome back " +msgstr "Добро пожаловать обратно, " + +#: include/security.php:375 +msgid "" +"The form security token was not correct. This probably happened because the " +"form has been opened for too long (>3 hours) before submitting it." +msgstr "" +"Ключ формы безопасности неправильный. Вероятно, это произошло потому, что " +"форма была открыта слишком долго (более 3 часов) до её отправки." + +#: include/conversation.php:147 +#, php-format +msgid "%1$s attends %2$s's %3$s" +msgstr "" + +#: include/conversation.php:150 +#, php-format +msgid "%1$s doesn't attend %2$s's %3$s" +msgstr "" + +#: include/conversation.php:153 +#, php-format +msgid "%1$s attends maybe %2$s's %3$s" +msgstr "" + +#: include/conversation.php:219 +#, php-format +msgid "%1$s poked %2$s" +msgstr "" + +#: include/conversation.php:303 +msgid "post/item" +msgstr "пост/элемент" + +#: include/conversation.php:304 +#, php-format +msgid "%1$s marked %2$s's %3$s as favorite" +msgstr "%1$s пометил %2$s %3$s как Фаворит" + +#: include/conversation.php:792 +msgid "remove" +msgstr "удалить" + +#: include/conversation.php:796 +msgid "Delete Selected Items" +msgstr "Удалить выбранные позиции" + +#: include/conversation.php:910 +msgid "Follow Thread" +msgstr "" + +#: include/conversation.php:1034 +#, php-format +msgid "%s likes this." +msgstr "%s нравится это." + +#: include/conversation.php:1037 +#, php-format +msgid "%s doesn't like this." +msgstr "%s не нравится это." + +#: include/conversation.php:1040 +#, php-format +msgid "%s attends." +msgstr "" + +#: include/conversation.php:1043 +#, php-format +msgid "%s doesn't attend." +msgstr "" + +#: include/conversation.php:1046 +#, php-format +msgid "%s attends maybe." +msgstr "" + +#: include/conversation.php:1056 +msgid "and" +msgstr "и" + +#: include/conversation.php:1062 +#, php-format +msgid ", and %d other people" +msgstr ", и %d других чел." + +#: include/conversation.php:1071 +#, php-format +msgid "%2$d people like this" +msgstr "%2$d людям нравится это" + +#: include/conversation.php:1072 +#, php-format +msgid "%s like this." +msgstr "" + +#: include/conversation.php:1075 +#, php-format +msgid "%2$d people don't like this" +msgstr "%2$d людям не нравится это" + +#: include/conversation.php:1076 +#, php-format +msgid "%s don't like this." +msgstr "" + +#: include/conversation.php:1079 +#, php-format +msgid "%2$d people attend" +msgstr "" + +#: include/conversation.php:1080 +#, php-format +msgid "%s attend." +msgstr "" + +#: include/conversation.php:1083 +#, php-format +msgid "%2$d people don't attend" +msgstr "" + +#: include/conversation.php:1084 +#, php-format +msgid "%s don't attend." +msgstr "" + +#: include/conversation.php:1087 +#, php-format +msgid "%2$d people anttend maybe" +msgstr "" + +#: include/conversation.php:1088 +#, php-format +msgid "%s anttend maybe." +msgstr "" + +#: include/conversation.php:1127 include/conversation.php:1145 +msgid "Visible to everybody" +msgstr "Видимое всем" + +#: include/conversation.php:1129 include/conversation.php:1147 +msgid "Please enter a video link/URL:" +msgstr "Введите ссылку на видео link/URL:" + +#: include/conversation.php:1130 include/conversation.php:1148 +msgid "Please enter an audio link/URL:" +msgstr "Введите ссылку на аудио link/URL:" + +#: include/conversation.php:1131 include/conversation.php:1149 +msgid "Tag term:" +msgstr "" + +#: include/conversation.php:1133 include/conversation.php:1151 +msgid "Where are you right now?" +msgstr "И где вы сейчас?" + +#: include/conversation.php:1134 +msgid "Delete item(s)?" +msgstr "Удалить елемент(ты)?" + +#: include/conversation.php:1203 msgid "permissions" msgstr "разрешения" -#: ../../include/conversation.php:1135 +#: include/conversation.php:1226 msgid "Post to Groups" msgstr "Пост для групп" -#: ../../include/conversation.php:1136 +#: include/conversation.php:1227 msgid "Post to Contacts" msgstr "Пост для контактов" -#: ../../include/conversation.php:1137 +#: include/conversation.php:1228 msgid "Private post" msgstr "Личное сообщение" -#: ../../include/network.php:895 +#: include/conversation.php:1385 +msgid "View all" +msgstr "" + +#: include/conversation.php:1407 +msgid "Like" +msgid_plural "Likes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: include/conversation.php:1410 +msgid "Dislike" +msgid_plural "Dislikes" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: include/conversation.php:1416 +msgid "Not Attending" +msgid_plural "Not Attending" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: include/conversation.php:1419 include/profile_selectors.php:6 +msgid "Undecided" +msgid_plural "Undecided" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: include/forums.php:105 include/text.php:1015 include/nav.php:126 +#: view/theme/vier/theme.php:259 +msgid "Forums" +msgstr "" + +#: include/forums.php:107 view/theme/vier/theme.php:261 +msgid "External link to forum" +msgstr "" + +#: include/network.php:967 msgid "view full size" msgstr "посмотреть в полный размер" -#: ../../include/text.php:297 +#: include/text.php:303 msgid "newer" msgstr "новее" -#: ../../include/text.php:299 +#: include/text.php:305 msgid "older" msgstr "старее" -#: ../../include/text.php:304 +#: include/text.php:310 msgid "prev" msgstr "пред." -#: ../../include/text.php:306 +#: include/text.php:312 msgid "first" msgstr "первый" -#: ../../include/text.php:338 +#: include/text.php:344 msgid "last" msgstr "последний" -#: ../../include/text.php:341 +#: include/text.php:347 msgid "next" msgstr "след." -#: ../../include/text.php:855 +#: include/text.php:402 +msgid "Loading more entries..." +msgstr "" + +#: include/text.php:403 +msgid "The end" +msgstr "" + +#: include/text.php:894 msgid "No contacts" msgstr "Нет контактов" -#: ../../include/text.php:864 +#: include/text.php:909 #, php-format msgid "%d Contact" msgid_plural "%d Contacts" msgstr[0] "%d контакт" msgstr[1] "%d контактов" msgstr[2] "%d контактов" +msgstr[3] "%d контактов" -#: ../../include/text.php:1005 +#: include/text.php:921 +msgid "View Contacts" +msgstr "Просмотр контактов" + +#: include/text.php:1010 include/nav.php:121 +msgid "Full Text" +msgstr "Контент" + +#: include/text.php:1011 include/nav.php:122 +msgid "Tags" +msgstr "Тэги" + +#: include/text.php:1066 msgid "poke" msgstr "poke" -#: ../../include/text.php:1006 +#: include/text.php:1066 +msgid "poked" +msgstr "" + +#: include/text.php:1067 msgid "ping" msgstr "пинг" -#: ../../include/text.php:1006 +#: include/text.php:1067 msgid "pinged" msgstr "пингуется" -#: ../../include/text.php:1007 +#: include/text.php:1068 msgid "prod" msgstr "" -#: ../../include/text.php:1007 +#: include/text.php:1068 msgid "prodded" msgstr "" -#: ../../include/text.php:1008 +#: include/text.php:1069 msgid "slap" msgstr "" -#: ../../include/text.php:1008 +#: include/text.php:1069 msgid "slapped" msgstr "" -#: ../../include/text.php:1009 +#: include/text.php:1070 msgid "finger" msgstr "" -#: ../../include/text.php:1009 +#: include/text.php:1070 msgid "fingered" msgstr "" -#: ../../include/text.php:1010 +#: include/text.php:1071 msgid "rebuff" msgstr "" -#: ../../include/text.php:1010 +#: include/text.php:1071 msgid "rebuffed" msgstr "" -#: ../../include/text.php:1024 +#: include/text.php:1085 msgid "happy" msgstr "" -#: ../../include/text.php:1025 +#: include/text.php:1086 msgid "sad" msgstr "" -#: ../../include/text.php:1026 +#: include/text.php:1087 msgid "mellow" msgstr "" -#: ../../include/text.php:1027 +#: include/text.php:1088 msgid "tired" msgstr "" -#: ../../include/text.php:1028 +#: include/text.php:1089 msgid "perky" msgstr "" -#: ../../include/text.php:1029 +#: include/text.php:1090 msgid "angry" msgstr "" -#: ../../include/text.php:1030 +#: include/text.php:1091 msgid "stupified" msgstr "" -#: ../../include/text.php:1031 +#: include/text.php:1092 msgid "puzzled" msgstr "" -#: ../../include/text.php:1032 +#: include/text.php:1093 msgid "interested" msgstr "" -#: ../../include/text.php:1033 +#: include/text.php:1094 msgid "bitter" msgstr "" -#: ../../include/text.php:1034 +#: include/text.php:1095 msgid "cheerful" msgstr "" -#: ../../include/text.php:1035 +#: include/text.php:1096 msgid "alive" msgstr "" -#: ../../include/text.php:1036 +#: include/text.php:1097 msgid "annoyed" msgstr "" -#: ../../include/text.php:1037 +#: include/text.php:1098 msgid "anxious" msgstr "" -#: ../../include/text.php:1038 +#: include/text.php:1099 msgid "cranky" msgstr "" -#: ../../include/text.php:1039 +#: include/text.php:1100 msgid "disturbed" msgstr "" -#: ../../include/text.php:1040 +#: include/text.php:1101 msgid "frustrated" msgstr "" -#: ../../include/text.php:1041 +#: include/text.php:1102 msgid "motivated" msgstr "" -#: ../../include/text.php:1042 +#: include/text.php:1103 msgid "relaxed" msgstr "" -#: ../../include/text.php:1043 +#: include/text.php:1104 msgid "surprised" msgstr "" -#: ../../include/text.php:1213 -msgid "Monday" -msgstr "Понедельник" - -#: ../../include/text.php:1213 -msgid "Tuesday" -msgstr "Вторник" - -#: ../../include/text.php:1213 -msgid "Wednesday" -msgstr "Среда" - -#: ../../include/text.php:1213 -msgid "Thursday" -msgstr "Четверг" - -#: ../../include/text.php:1213 -msgid "Friday" -msgstr "Пятница" - -#: ../../include/text.php:1213 -msgid "Saturday" -msgstr "Суббота" - -#: ../../include/text.php:1213 -msgid "Sunday" -msgstr "Воскресенье" - -#: ../../include/text.php:1217 -msgid "January" -msgstr "Январь" - -#: ../../include/text.php:1217 -msgid "February" -msgstr "Февраль" - -#: ../../include/text.php:1217 -msgid "March" -msgstr "Март" - -#: ../../include/text.php:1217 -msgid "April" -msgstr "Апрель" - -#: ../../include/text.php:1217 -msgid "May" -msgstr "Май" - -#: ../../include/text.php:1217 -msgid "June" -msgstr "Июнь" - -#: ../../include/text.php:1217 -msgid "July" -msgstr "Июль" - -#: ../../include/text.php:1217 -msgid "August" -msgstr "Август" - -#: ../../include/text.php:1217 -msgid "September" -msgstr "Сентябрь" - -#: ../../include/text.php:1217 -msgid "October" -msgstr "Октябрь" - -#: ../../include/text.php:1217 -msgid "November" -msgstr "Ноябрь" - -#: ../../include/text.php:1217 -msgid "December" -msgstr "Декабрь" - -#: ../../include/text.php:1437 +#: include/text.php:1504 msgid "bytes" msgstr "байт" -#: ../../include/text.php:1461 ../../include/text.php:1473 +#: include/text.php:1536 include/text.php:1548 msgid "Click to open/close" msgstr "Нажмите, чтобы открыть / закрыть" -#: ../../include/text.php:1702 ../../include/user.php:247 -#: ../../view/theme/duepuntozero/config.php:44 -msgid "default" -msgstr "значение по умолчанию" +#: include/text.php:1722 +msgid "View on separate page" +msgstr "" -#: ../../include/text.php:1714 -msgid "Select an alternate language" -msgstr "Выбор альтернативного языка" +#: include/text.php:1723 +msgid "view on separate page" +msgstr "" -#: ../../include/text.php:1970 +#: include/text.php:2002 msgid "activity" msgstr "активность" -#: ../../include/text.php:1973 +#: include/text.php:2005 msgid "post" msgstr "сообщение" -#: ../../include/text.php:2141 +#: include/text.php:2173 msgid "Item filed" msgstr "" -#: ../../include/bbcode.php:428 ../../include/bbcode.php:1047 -#: ../../include/bbcode.php:1048 +#: include/bbcode.php:482 include/bbcode.php:1157 include/bbcode.php:1158 msgid "Image/photo" msgstr "Изображение / Фото" -#: ../../include/bbcode.php:528 +#: include/bbcode.php:595 #, php-format msgid "%2$s %3$s" msgstr "" -#: ../../include/bbcode.php:562 +#: include/bbcode.php:629 #, php-format msgid "" -"%s wrote the following post" +"%s wrote the following post" msgstr "" -#: ../../include/bbcode.php:1011 ../../include/bbcode.php:1031 +#: include/bbcode.php:1117 include/bbcode.php:1137 msgid "$1 wrote:" msgstr "$1 написал:" -#: ../../include/bbcode.php:1056 ../../include/bbcode.php:1057 +#: include/bbcode.php:1166 include/bbcode.php:1167 msgid "Encrypted content" msgstr "Зашифрованный контент" -#: ../../include/notifier.php:786 ../../include/delivery.php:456 -msgid "(no subject)" -msgstr "(без темы)" - -#: ../../include/notifier.php:796 ../../include/delivery.php:467 -#: ../../include/enotify.php:33 -msgid "noreply" -msgstr "без ответа" - -#: ../../include/dba_pdo.php:72 ../../include/dba.php:56 +#: include/dba_pdo.php:72 include/dba.php:55 #, php-format msgid "Cannot locate DNS info for database server '%s'" msgstr "Не могу найти информацию для DNS-сервера базы данных '%s'" -#: ../../include/contact_selectors.php:32 +#: include/contact_selectors.php:32 msgid "Unknown | Not categorised" msgstr "Неизвестно | Не определено" -#: ../../include/contact_selectors.php:33 +#: include/contact_selectors.php:33 msgid "Block immediately" msgstr "Блокировать немедленно" -#: ../../include/contact_selectors.php:34 +#: include/contact_selectors.php:34 msgid "Shady, spammer, self-marketer" msgstr "Тролль, спаммер, рассылает рекламу" -#: ../../include/contact_selectors.php:35 +#: include/contact_selectors.php:35 msgid "Known to me, but no opinion" msgstr "Известные мне, но нет определенного мнения" -#: ../../include/contact_selectors.php:36 +#: include/contact_selectors.php:36 msgid "OK, probably harmless" msgstr "Хорошо, наверное, безвредные" -#: ../../include/contact_selectors.php:37 +#: include/contact_selectors.php:37 msgid "Reputable, has my trust" msgstr "Уважаемые, есть мое доверие" -#: ../../include/contact_selectors.php:60 +#: include/contact_selectors.php:60 msgid "Weekly" msgstr "Еженедельно" -#: ../../include/contact_selectors.php:61 +#: include/contact_selectors.php:61 msgid "Monthly" msgstr "Ежемесячно" -#: ../../include/contact_selectors.php:77 +#: include/contact_selectors.php:77 msgid "OStatus" msgstr "OStatus" -#: ../../include/contact_selectors.php:78 +#: include/contact_selectors.php:78 msgid "RSS/Atom" msgstr "RSS/Atom" -#: ../../include/contact_selectors.php:82 +#: include/contact_selectors.php:81 +msgid "Facebook" +msgstr "Facebook" + +#: include/contact_selectors.php:82 msgid "Zot!" msgstr "Zot!" -#: ../../include/contact_selectors.php:83 +#: include/contact_selectors.php:83 msgid "LinkedIn" msgstr "LinkedIn" -#: ../../include/contact_selectors.php:84 +#: include/contact_selectors.php:84 msgid "XMPP/IM" msgstr "XMPP/IM" -#: ../../include/contact_selectors.php:85 +#: include/contact_selectors.php:85 msgid "MySpace" msgstr "MySpace" -#: ../../include/contact_selectors.php:87 +#: include/contact_selectors.php:87 msgid "Google+" msgstr "Google+" -#: ../../include/contact_selectors.php:88 +#: include/contact_selectors.php:88 msgid "pump.io" msgstr "pump.io" -#: ../../include/contact_selectors.php:89 +#: include/contact_selectors.php:89 msgid "Twitter" msgstr "Twitter" -#: ../../include/contact_selectors.php:90 +#: include/contact_selectors.php:90 msgid "Diaspora Connector" msgstr "" -#: ../../include/contact_selectors.php:91 -msgid "Statusnet" +#: include/contact_selectors.php:91 +msgid "GNU Social" msgstr "" -#: ../../include/contact_selectors.php:92 +#: include/contact_selectors.php:92 msgid "App.net" msgstr "" -#: ../../include/Scrape.php:614 +#: include/contact_selectors.php:103 +msgid "Redmatrix" +msgstr "" + +#: include/Scrape.php:624 msgid " on Last.fm" msgstr "на Last.fm" -#: ../../include/bb2diaspora.php:154 ../../include/event.php:20 +#: include/bb2diaspora.php:154 include/event.php:30 include/event.php:48 msgid "Starts:" msgstr "Начало:" -#: ../../include/bb2diaspora.php:162 ../../include/event.php:30 +#: include/bb2diaspora.php:162 include/event.php:33 include/event.php:54 msgid "Finishes:" msgstr "Окончание:" -#: ../../include/profile_advanced.php:22 -msgid "j F, Y" -msgstr "j F, Y" - -#: ../../include/profile_advanced.php:23 -msgid "j F" -msgstr "j F" - -#: ../../include/profile_advanced.php:30 -msgid "Birthday:" -msgstr "День рождения:" - -#: ../../include/profile_advanced.php:34 -msgid "Age:" -msgstr "Возраст:" - -#: ../../include/profile_advanced.php:43 -#, php-format -msgid "for %1$d %2$s" -msgstr "" - -#: ../../include/profile_advanced.php:52 -msgid "Tags:" -msgstr "Ключевые слова: " - -#: ../../include/profile_advanced.php:56 -msgid "Religion:" -msgstr "Религия:" - -#: ../../include/profile_advanced.php:60 -msgid "Hobbies/Interests:" -msgstr "Хобби / Интересы:" - -#: ../../include/profile_advanced.php:67 -msgid "Contact information and Social Networks:" -msgstr "Информация о контакте и социальных сетях:" - -#: ../../include/profile_advanced.php:69 -msgid "Musical interests:" -msgstr "Музыкальные интересы:" - -#: ../../include/profile_advanced.php:71 -msgid "Books, literature:" -msgstr "Книги, литература:" - -#: ../../include/profile_advanced.php:73 -msgid "Television:" -msgstr "Телевидение:" - -#: ../../include/profile_advanced.php:75 -msgid "Film/dance/culture/entertainment:" -msgstr "Кино / Танцы / Культура / Развлечения:" - -#: ../../include/profile_advanced.php:77 -msgid "Love/Romance:" -msgstr "Любовь / Романтика:" - -#: ../../include/profile_advanced.php:79 -msgid "Work/employment:" -msgstr "Работа / Занятость:" - -#: ../../include/profile_advanced.php:81 -msgid "School/education:" -msgstr "Школа / Образование:" - -#: ../../include/plugin.php:455 ../../include/plugin.php:457 +#: include/plugin.php:522 include/plugin.php:524 msgid "Click here to upgrade." msgstr "Нажмите для обновления." -#: ../../include/plugin.php:463 +#: include/plugin.php:530 msgid "This action exceeds the limits set by your subscription plan." msgstr "Это действие превышает лимиты, установленные вашим тарифным планом." -#: ../../include/plugin.php:468 +#: include/plugin.php:535 msgid "This action is not available under your subscription plan." msgstr "Это действие не доступно в соответствии с вашим планом подписки." -#: ../../include/nav.php:73 +#: include/nav.php:72 msgid "End this session" -msgstr "Конец этой сессии" +msgstr "Завершить эту сессию" -#: ../../include/nav.php:76 ../../include/nav.php:148 -#: ../../view/theme/diabook/theme.php:123 +#: include/nav.php:75 include/nav.php:157 view/theme/diabook/theme.php:123 msgid "Your posts and conversations" -msgstr "Ваши сообщения и беседы" +msgstr "Данные вашей учётной записи" -#: ../../include/nav.php:77 ../../view/theme/diabook/theme.php:124 +#: include/nav.php:76 view/theme/diabook/theme.php:124 msgid "Your profile page" -msgstr "Страница Вашего профиля" +msgstr "Информация о вас" -#: ../../include/nav.php:78 ../../view/theme/diabook/theme.php:126 +#: include/nav.php:77 view/theme/diabook/theme.php:126 msgid "Your photos" msgstr "Ваши фотографии" -#: ../../include/nav.php:79 +#: include/nav.php:78 msgid "Your videos" msgstr "" -#: ../../include/nav.php:80 ../../view/theme/diabook/theme.php:127 +#: include/nav.php:79 view/theme/diabook/theme.php:127 msgid "Your events" msgstr "Ваши события" -#: ../../include/nav.php:81 ../../view/theme/diabook/theme.php:128 +#: include/nav.php:80 view/theme/diabook/theme.php:128 msgid "Personal notes" msgstr "Личные заметки" -#: ../../include/nav.php:81 +#: include/nav.php:80 msgid "Your personal notes" msgstr "" -#: ../../include/nav.php:92 +#: include/nav.php:91 msgid "Sign in" msgstr "Вход" -#: ../../include/nav.php:105 +#: include/nav.php:104 msgid "Home Page" msgstr "Главная страница" -#: ../../include/nav.php:109 +#: include/nav.php:108 msgid "Create an account" msgstr "Создать аккаунт" -#: ../../include/nav.php:114 +#: include/nav.php:113 msgid "Help and documentation" msgstr "Помощь и документация" -#: ../../include/nav.php:117 +#: include/nav.php:116 msgid "Apps" msgstr "Приложения" -#: ../../include/nav.php:117 +#: include/nav.php:116 msgid "Addon applications, utilities, games" msgstr "Дополнительные приложения, утилиты, игры" -#: ../../include/nav.php:119 +#: include/nav.php:118 msgid "Search site content" msgstr "Поиск по сайту" -#: ../../include/nav.php:129 +#: include/nav.php:136 msgid "Conversations on this site" msgstr "Беседы на этом сайте" -#: ../../include/nav.php:131 +#: include/nav.php:138 msgid "Conversations on the network" msgstr "" -#: ../../include/nav.php:133 +#: include/nav.php:142 msgid "Directory" msgstr "Каталог" -#: ../../include/nav.php:133 +#: include/nav.php:142 msgid "People directory" msgstr "Каталог участников" -#: ../../include/nav.php:135 +#: include/nav.php:144 msgid "Information" -msgstr "" +msgstr "Информация" -#: ../../include/nav.php:135 +#: include/nav.php:144 msgid "Information about this friendica instance" msgstr "" -#: ../../include/nav.php:145 +#: include/nav.php:154 msgid "Conversations from your friends" -msgstr "Беседы с друзьями" +msgstr "Посты ваших друзей" -#: ../../include/nav.php:146 +#: include/nav.php:155 msgid "Network Reset" msgstr "Перезагрузка сети" -#: ../../include/nav.php:146 +#: include/nav.php:155 msgid "Load Network page with no filters" msgstr "Загрузить страницу сети без фильтров" -#: ../../include/nav.php:154 +#: include/nav.php:162 msgid "Friend Requests" msgstr "Запросы на добавление в список друзей" -#: ../../include/nav.php:156 +#: include/nav.php:166 msgid "See all notifications" msgstr "Посмотреть все уведомления" -#: ../../include/nav.php:157 +#: include/nav.php:167 msgid "Mark all system notifications seen" msgstr "Отметить все системные уведомления, как прочитанные" -#: ../../include/nav.php:161 +#: include/nav.php:171 msgid "Private mail" msgstr "Личная почта" -#: ../../include/nav.php:162 +#: include/nav.php:172 msgid "Inbox" msgstr "Входящие" -#: ../../include/nav.php:163 +#: include/nav.php:173 msgid "Outbox" msgstr "Исходящие" -#: ../../include/nav.php:167 +#: include/nav.php:177 msgid "Manage" msgstr "Управлять" -#: ../../include/nav.php:167 +#: include/nav.php:177 msgid "Manage other pages" msgstr "Управление другими страницами" -#: ../../include/nav.php:172 +#: include/nav.php:182 msgid "Account settings" msgstr "Настройки аккаунта" -#: ../../include/nav.php:175 +#: include/nav.php:185 msgid "Manage/Edit Profiles" msgstr "Управление/редактирование профилей" -#: ../../include/nav.php:177 +#: include/nav.php:187 msgid "Manage/edit friends and contacts" msgstr "Управление / редактирование друзей и контактов" -#: ../../include/nav.php:184 +#: include/nav.php:194 msgid "Site setup and configuration" -msgstr "Установка и конфигурация сайта" +msgstr "Конфигурация сайта" -#: ../../include/nav.php:188 +#: include/nav.php:198 msgid "Navigation" msgstr "Навигация" -#: ../../include/nav.php:188 +#: include/nav.php:198 msgid "Site map" msgstr "Карта сайта" -#: ../../include/api.php:304 ../../include/api.php:315 -#: ../../include/api.php:416 ../../include/api.php:1063 -#: ../../include/api.php:1065 -msgid "User not found." -msgstr "Пользователь не найден." - -#: ../../include/api.php:771 +#: include/api.php:878 #, php-format msgid "Daily posting limit of %d posts reached. The post was rejected." msgstr "" -#: ../../include/api.php:790 +#: include/api.php:897 #, php-format msgid "Weekly posting limit of %d posts reached. The post was rejected." msgstr "" -#: ../../include/api.php:809 +#: include/api.php:916 #, php-format msgid "Monthly posting limit of %d posts reached. The post was rejected." msgstr "" -#: ../../include/api.php:1272 -msgid "There is no status with this id." -msgstr "Нет статуса с таким id." - -#: ../../include/api.php:1342 -msgid "There is no conversation with this id." -msgstr "" - -#: ../../include/api.php:1614 -msgid "Invalid request." -msgstr "" - -#: ../../include/api.php:1625 -msgid "Invalid item." -msgstr "" - -#: ../../include/api.php:1635 -msgid "Invalid action. " -msgstr "" - -#: ../../include/api.php:1643 -msgid "DB error" -msgstr "" - -#: ../../include/user.php:40 +#: include/user.php:48 msgid "An invitation is required." msgstr "Требуется приглашение." -#: ../../include/user.php:45 +#: include/user.php:53 msgid "Invitation could not be verified." msgstr "Приглашение не может быть проверено." -#: ../../include/user.php:53 +#: include/user.php:61 msgid "Invalid OpenID url" msgstr "Неверный URL OpenID" -#: ../../include/user.php:74 +#: include/user.php:82 msgid "Please enter the required information." msgstr "Пожалуйста, введите необходимую информацию." -#: ../../include/user.php:88 +#: include/user.php:96 msgid "Please use a shorter name." msgstr "Пожалуйста, используйте более короткое имя." -#: ../../include/user.php:90 +#: include/user.php:98 msgid "Name too short." msgstr "Имя слишком короткое." -#: ../../include/user.php:105 +#: include/user.php:113 msgid "That doesn't appear to be your full (First Last) name." msgstr "Кажется, что это ваше неполное (Имя Фамилия) имя." -#: ../../include/user.php:110 +#: include/user.php:118 msgid "Your email domain is not among those allowed on this site." -msgstr "Домен вашего адреса электронной почты не относится к числу разрешенных на этом сайте." +msgstr "" +"Домен вашего адреса электронной почты не относится к числу разрешенных на " +"этом сайте." -#: ../../include/user.php:113 +#: include/user.php:121 msgid "Not a valid email address." msgstr "Неверный адрес электронной почты." -#: ../../include/user.php:126 +#: include/user.php:134 msgid "Cannot use that email." msgstr "Нельзя использовать этот Email." -#: ../../include/user.php:132 -msgid "" -"Your \"nickname\" can only contain \"a-z\", \"0-9\", \"-\", and \"_\", and " -"must also begin with a letter." -msgstr "Ваш \"ник\" может содержать только \"a-z\", \"0-9\", \"-\", и \"_\", а также должен начинаться с буквы." +#: include/user.php:140 +msgid "Your \"nickname\" can only contain \"a-z\", \"0-9\" and \"_\"." +msgstr "" -#: ../../include/user.php:138 ../../include/user.php:236 +#: include/user.php:147 include/user.php:245 msgid "Nickname is already registered. Please choose another." msgstr "Такой ник уже зарегистрирован. Пожалуйста, выберите другой." -#: ../../include/user.php:148 +#: include/user.php:157 msgid "" "Nickname was once registered here and may not be re-used. Please choose " "another." -msgstr "Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, выберите другой ник." +msgstr "" +"Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, " +"выберите другой ник." -#: ../../include/user.php:164 +#: include/user.php:173 msgid "SERIOUS ERROR: Generation of security keys failed." msgstr "СЕРЬЕЗНАЯ ОШИБКА: генерация ключей безопасности не удалась." -#: ../../include/user.php:222 +#: include/user.php:231 msgid "An error occurred during registration. Please try again." msgstr "Ошибка при регистрации. Пожалуйста, попробуйте еще раз." -#: ../../include/user.php:257 +#: include/user.php:256 view/theme/duepuntozero/config.php:44 +msgid "default" +msgstr "значение по умолчанию" + +#: include/user.php:266 msgid "An error occurred creating your default profile. Please try again." msgstr "Ошибка создания вашего профиля. Пожалуйста, попробуйте еще раз." -#: ../../include/user.php:289 ../../include/user.php:293 -#: ../../include/profile_selectors.php:42 +#: include/user.php:299 include/user.php:303 include/profile_selectors.php:42 msgid "Friends" msgstr "Друзья" -#: ../../include/user.php:377 +#: include/user.php:387 #, php-format msgid "" "\n" @@ -7099,7 +8110,7 @@ msgid "" "\t" msgstr "" -#: ../../include/user.php:381 +#: include/user.php:391 #, php-format msgid "" "\n" @@ -7108,20 +8119,25 @@ msgid "" "\t\t\tLogin Name:\t%1$s\n" "\t\t\tPassword:\t%5$s\n" "\n" -"\t\tYou may change your password from your account \"Settings\" page after logging\n" +"\t\tYou may change your password from your account \"Settings\" page after " +"logging\n" "\t\tin.\n" "\n" -"\t\tPlease take a few moments to review the other account settings on that page.\n" +"\t\tPlease take a few moments to review the other account settings on that " +"page.\n" "\n" "\t\tYou may also wish to add some basic information to your default profile\n" "\t\t(on the \"Profiles\" page) so that other people can easily find you.\n" "\n" "\t\tWe recommend setting your full name, adding a profile photo,\n" -"\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n" -"\t\tperhaps what country you live in; if you do not wish to be more specific\n" +"\t\tadding some profile \"keywords\" (very useful in making new friends) - " +"and\n" +"\t\tperhaps what country you live in; if you do not wish to be more " +"specific\n" "\t\tthan that.\n" "\n" -"\t\tWe fully respect your right to privacy, and none of these items are necessary.\n" +"\t\tWe fully respect your right to privacy, and none of these items are " +"necessary.\n" "\t\tIf you are new and do not know anybody here, they may help\n" "\t\tyou to make some new and interesting friends.\n" "\n" @@ -7129,495 +8145,504 @@ msgid "" "\t\tThank you and welcome to %2$s." msgstr "" -#: ../../include/diaspora.php:703 +#: include/diaspora.php:720 msgid "Sharing notification from Diaspora network" msgstr "Делиться уведомлениями из сети Diaspora" -#: ../../include/diaspora.php:2520 +#: include/diaspora.php:2625 msgid "Attachments:" msgstr "Вложения:" -#: ../../include/items.php:4555 +#: include/delivery.php:533 +msgid "(no subject)" +msgstr "(без темы)" + +#: include/delivery.php:544 include/enotify.php:37 +msgid "noreply" +msgstr "без ответа" + +#: include/items.php:4926 msgid "Do you really want to delete this item?" msgstr "Вы действительно хотите удалить этот элемент?" -#: ../../include/items.php:4778 +#: include/items.php:5201 msgid "Archives" msgstr "Архивы" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Male" msgstr "Мужчина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Female" msgstr "Женщина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Currently Male" msgstr "В данный момент мужчина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Currently Female" msgstr "В настоящее время женщина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Mostly Male" msgstr "В основном мужчина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Mostly Female" msgstr "В основном женщина" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Transgender" msgstr "Транссексуал" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Intersex" msgstr "Интерсексуал" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Transsexual" msgstr "Транссексуал" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Hermaphrodite" msgstr "Гермафродит" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Neuter" msgstr "Средний род" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Non-specific" msgstr "Не определен" -#: ../../include/profile_selectors.php:6 +#: include/profile_selectors.php:6 msgid "Other" msgstr "Другой" -#: ../../include/profile_selectors.php:6 -msgid "Undecided" -msgstr "Не решено" - -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Males" msgstr "Мужчины" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Females" msgstr "Женщины" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Gay" msgstr "Гей" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Lesbian" msgstr "Лесбиянка" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "No Preference" msgstr "Без предпочтений" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Bisexual" msgstr "Бисексуал" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Autosexual" msgstr "Автосексуал" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Abstinent" msgstr "Воздержанный" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Virgin" msgstr "Девственница" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Deviant" msgstr "Deviant" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Fetish" msgstr "Фетиш" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Oodles" msgstr "Групповой" -#: ../../include/profile_selectors.php:23 +#: include/profile_selectors.php:23 msgid "Nonsexual" msgstr "Нет интереса к сексу" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Single" msgstr "Без пары" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Lonely" msgstr "Пока никого нет" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Available" msgstr "Доступный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Unavailable" msgstr "Не ищу никого" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Has crush" msgstr "Имеет ошибку" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Infatuated" msgstr "Влюблён" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Dating" msgstr "Свидания" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Unfaithful" msgstr "Изменяю супругу" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Sex Addict" msgstr "Люблю секс" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Friends/Benefits" msgstr "Друзья / Предпочтения" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Casual" msgstr "Обычный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Engaged" msgstr "Занят" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Married" msgstr "Женат / Замужем" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Imaginarily married" msgstr "" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Partners" msgstr "Партнеры" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Cohabiting" msgstr "Партнерство" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Common law" msgstr "" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Happy" msgstr "Счастлив/а/" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Not looking" msgstr "Не в поиске" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Swinger" msgstr "Свинг" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Betrayed" msgstr "Преданный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Separated" msgstr "Разделенный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Unstable" msgstr "Нестабильный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Divorced" msgstr "Разведен(а)" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Imaginarily divorced" msgstr "" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Widowed" msgstr "Овдовевший" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Uncertain" msgstr "Неопределенный" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "It's complicated" msgstr "влишком сложно" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Don't care" msgstr "Не беспокоить" -#: ../../include/profile_selectors.php:42 +#: include/profile_selectors.php:42 msgid "Ask me" msgstr "Спросите меня" -#: ../../include/enotify.php:18 +#: include/enotify.php:18 msgid "Friendica Notification" msgstr "Friendica уведомления" -#: ../../include/enotify.php:21 +#: include/enotify.php:21 msgid "Thank You," msgstr "Спасибо," -#: ../../include/enotify.php:23 +#: include/enotify.php:24 #, php-format msgid "%s Administrator" msgstr "%s администратор" -#: ../../include/enotify.php:64 +#: include/enotify.php:26 +#, php-format +msgid "%1$s, %2$s Administrator" +msgstr "" + +#: include/enotify.php:68 #, php-format msgid "%s " msgstr "%s " -#: ../../include/enotify.php:68 +#: include/enotify.php:82 #, php-format msgid "[Friendica:Notify] New mail received at %s" msgstr "[Friendica: Оповещение] Новое сообщение, пришедшее на %s" -#: ../../include/enotify.php:70 +#: include/enotify.php:84 #, php-format msgid "%1$s sent you a new private message at %2$s." msgstr "%1$s отправил вам новое личное сообщение на %2$s." -#: ../../include/enotify.php:71 +#: include/enotify.php:85 #, php-format msgid "%1$s sent you %2$s." msgstr "%1$s послал вам %2$s." -#: ../../include/enotify.php:71 +#: include/enotify.php:85 msgid "a private message" msgstr "личное сообщение" -#: ../../include/enotify.php:72 +#: include/enotify.php:86 #, php-format msgid "Please visit %s to view and/or reply to your private messages." -msgstr "Пожалуйста, посетите %s для просмотра и/или ответа на личные сообщения." +msgstr "" +"Пожалуйста, посетите %s для просмотра и/или ответа на личные сообщения." -#: ../../include/enotify.php:124 +#: include/enotify.php:138 #, php-format msgid "%1$s commented on [url=%2$s]a %3$s[/url]" msgstr "%1$s прокомментировал [url=%2$s]a %3$s[/url]" -#: ../../include/enotify.php:131 +#: include/enotify.php:145 #, php-format msgid "%1$s commented on [url=%2$s]%3$s's %4$s[/url]" msgstr "%1$s прокомментировал [url=%2$s]%3$s's %4$s[/url]" -#: ../../include/enotify.php:139 +#: include/enotify.php:153 #, php-format msgid "%1$s commented on [url=%2$s]your %3$s[/url]" msgstr "%1$s прокомментировал [url=%2$s]your %3$s[/url]" -#: ../../include/enotify.php:149 +#: include/enotify.php:163 #, php-format msgid "[Friendica:Notify] Comment to conversation #%1$d by %2$s" msgstr "" -#: ../../include/enotify.php:150 +#: include/enotify.php:164 #, php-format msgid "%s commented on an item/conversation you have been following." msgstr "" -#: ../../include/enotify.php:153 ../../include/enotify.php:168 -#: ../../include/enotify.php:181 ../../include/enotify.php:194 -#: ../../include/enotify.php:212 ../../include/enotify.php:225 +#: include/enotify.php:167 include/enotify.php:182 include/enotify.php:195 +#: include/enotify.php:208 include/enotify.php:226 include/enotify.php:239 #, php-format msgid "Please visit %s to view and/or reply to the conversation." msgstr "" -#: ../../include/enotify.php:160 +#: include/enotify.php:174 #, php-format msgid "[Friendica:Notify] %s posted to your profile wall" msgstr "[Friendica:Оповещение] %s написал на стене вашего профиля" -#: ../../include/enotify.php:162 +#: include/enotify.php:176 #, php-format msgid "%1$s posted to your profile wall at %2$s" msgstr "" -#: ../../include/enotify.php:164 +#: include/enotify.php:178 #, php-format msgid "%1$s posted to [url=%2$s]your wall[/url]" msgstr "" -#: ../../include/enotify.php:175 +#: include/enotify.php:189 #, php-format msgid "[Friendica:Notify] %s tagged you" msgstr "" -#: ../../include/enotify.php:176 +#: include/enotify.php:190 #, php-format msgid "%1$s tagged you at %2$s" msgstr "" -#: ../../include/enotify.php:177 +#: include/enotify.php:191 #, php-format msgid "%1$s [url=%2$s]tagged you[/url]." msgstr "" -#: ../../include/enotify.php:188 +#: include/enotify.php:202 #, php-format msgid "[Friendica:Notify] %s shared a new post" msgstr "" -#: ../../include/enotify.php:189 +#: include/enotify.php:203 #, php-format msgid "%1$s shared a new post at %2$s" msgstr "" -#: ../../include/enotify.php:190 +#: include/enotify.php:204 #, php-format msgid "%1$s [url=%2$s]shared a post[/url]." msgstr "" -#: ../../include/enotify.php:202 +#: include/enotify.php:216 #, php-format msgid "[Friendica:Notify] %1$s poked you" msgstr "" -#: ../../include/enotify.php:203 +#: include/enotify.php:217 #, php-format msgid "%1$s poked you at %2$s" msgstr "" -#: ../../include/enotify.php:204 +#: include/enotify.php:218 #, php-format msgid "%1$s [url=%2$s]poked you[/url]." msgstr "" -#: ../../include/enotify.php:219 +#: include/enotify.php:233 #, php-format msgid "[Friendica:Notify] %s tagged your post" msgstr "" -#: ../../include/enotify.php:220 +#: include/enotify.php:234 #, php-format msgid "%1$s tagged your post at %2$s" msgstr "" -#: ../../include/enotify.php:221 +#: include/enotify.php:235 #, php-format msgid "%1$s tagged [url=%2$s]your post[/url]" msgstr "" -#: ../../include/enotify.php:232 +#: include/enotify.php:246 msgid "[Friendica:Notify] Introduction received" msgstr "[Friendica:Сообщение] получен запрос" -#: ../../include/enotify.php:233 +#: include/enotify.php:247 #, php-format msgid "You've received an introduction from '%1$s' at %2$s" msgstr "" -#: ../../include/enotify.php:234 +#: include/enotify.php:248 #, php-format msgid "You've received [url=%1$s]an introduction[/url] from %2$s." msgstr "" -#: ../../include/enotify.php:237 ../../include/enotify.php:279 +#: include/enotify.php:251 include/enotify.php:293 #, php-format msgid "You may visit their profile at %s" msgstr "Вы можете посмотреть его профиль здесь %s" -#: ../../include/enotify.php:239 +#: include/enotify.php:253 #, php-format msgid "Please visit %s to approve or reject the introduction." msgstr "Посетите %s для подтверждения или отказа запроса." -#: ../../include/enotify.php:247 +#: include/enotify.php:261 msgid "[Friendica:Notify] A new person is sharing with you" msgstr "" -#: ../../include/enotify.php:248 ../../include/enotify.php:249 +#: include/enotify.php:262 include/enotify.php:263 #, php-format msgid "%1$s is sharing with you at %2$s" msgstr "" -#: ../../include/enotify.php:255 +#: include/enotify.php:269 msgid "[Friendica:Notify] You have a new follower" msgstr "" -#: ../../include/enotify.php:256 ../../include/enotify.php:257 +#: include/enotify.php:270 include/enotify.php:271 #, php-format msgid "You have a new follower at %2$s : %1$s" msgstr "" -#: ../../include/enotify.php:270 +#: include/enotify.php:284 msgid "[Friendica:Notify] Friend suggestion received" msgstr "[Friendica: Оповещение] получено предложение дружбы" -#: ../../include/enotify.php:271 +#: include/enotify.php:285 #, php-format msgid "You've received a friend suggestion from '%1$s' at %2$s" msgstr "Вы получили предложение дружбы от '%1$s' на %2$s" -#: ../../include/enotify.php:272 +#: include/enotify.php:286 #, php-format -msgid "" -"You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s." +msgid "You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s." msgstr "" -#: ../../include/enotify.php:277 +#: include/enotify.php:291 msgid "Name:" msgstr "Имя:" -#: ../../include/enotify.php:278 +#: include/enotify.php:292 msgid "Photo:" msgstr "Фото:" -#: ../../include/enotify.php:281 +#: include/enotify.php:295 #, php-format msgid "Please visit %s to approve or reject the suggestion." msgstr "Пожалуйста, посетите %s для подтверждения, или отказа запроса." -#: ../../include/enotify.php:289 ../../include/enotify.php:302 +#: include/enotify.php:303 include/enotify.php:316 msgid "[Friendica:Notify] Connection accepted" msgstr "" -#: ../../include/enotify.php:290 ../../include/enotify.php:303 +#: include/enotify.php:304 include/enotify.php:317 #, php-format -msgid "'%1$s' has acepted your connection request at %2$s" +msgid "'%1$s' has accepted your connection request at %2$s" msgstr "" -#: ../../include/enotify.php:291 ../../include/enotify.php:304 +#: include/enotify.php:305 include/enotify.php:318 #, php-format msgid "%2$s has accepted your [url=%1$s]connection request[/url]." msgstr "" -#: ../../include/enotify.php:294 +#: include/enotify.php:308 msgid "" -"You are now mutual friends and may exchange status updates, photos, and email\n" +"You are now mutual friends and may exchange status updates, photos, and " +"email\n" "\twithout restriction." msgstr "" -#: ../../include/enotify.php:297 ../../include/enotify.php:311 +#: include/enotify.php:311 include/enotify.php:325 #, php-format msgid "Please visit %s if you wish to make any changes to this relationship." msgstr "" -#: ../../include/enotify.php:307 +#: include/enotify.php:321 #, php-format msgid "" "'%1$s' has chosen to accept you a \"fan\", which restricts some forms of " @@ -7626,266 +8651,261 @@ msgid "" "automatically." msgstr "" -#: ../../include/enotify.php:309 +#: include/enotify.php:323 #, php-format msgid "" "'%1$s' may choose to extend this into a two-way or more permissive " "relationship in the future. " msgstr "" -#: ../../include/enotify.php:322 +#: include/enotify.php:336 msgid "[Friendica System:Notify] registration request" msgstr "" -#: ../../include/enotify.php:323 +#: include/enotify.php:337 #, php-format msgid "You've received a registration request from '%1$s' at %2$s" msgstr "" -#: ../../include/enotify.php:324 +#: include/enotify.php:338 #, php-format msgid "You've received a [url=%1$s]registration request[/url] from %2$s." msgstr "" -#: ../../include/enotify.php:327 +#: include/enotify.php:341 #, php-format msgid "Full Name:\t%1$s\\nSite Location:\t%2$s\\nLogin Name:\t%3$s (%4$s)" msgstr "" -#: ../../include/enotify.php:330 +#: include/enotify.php:344 #, php-format msgid "Please visit %s to approve or reject the request." msgstr "" -#: ../../include/oembed.php:212 +#: include/oembed.php:226 msgid "Embedded content" msgstr "Встроенное содержание" -#: ../../include/oembed.php:221 +#: include/oembed.php:235 msgid "Embedding disabled" msgstr "Встраивание отключено" -#: ../../include/uimport.php:94 +#: include/uimport.php:94 msgid "Error decoding account file" msgstr "Ошибка расшифровки файла аккаунта" -#: ../../include/uimport.php:100 +#: include/uimport.php:100 msgid "Error! No version data in file! This is not a Friendica account file?" -msgstr "Ошибка! Неправильная версия данных в файле! Это не файл аккаунта Friendica?" +msgstr "" +"Ошибка! Неправильная версия данных в файле! Это не файл аккаунта Friendica?" -#: ../../include/uimport.php:116 ../../include/uimport.php:127 +#: include/uimport.php:116 include/uimport.php:127 msgid "Error! Cannot check nickname" msgstr "Ошибка! Невозможно проверить никнейм" -#: ../../include/uimport.php:120 ../../include/uimport.php:131 +#: include/uimport.php:120 include/uimport.php:131 #, php-format msgid "User '%s' already exists on this server!" msgstr "Пользователь '%s' уже существует на этом сервере!" -#: ../../include/uimport.php:153 +#: include/uimport.php:153 msgid "User creation error" msgstr "Ошибка создания пользователя" -#: ../../include/uimport.php:171 +#: include/uimport.php:173 msgid "User profile creation error" msgstr "Ошибка создания профиля пользователя" -#: ../../include/uimport.php:220 +#: include/uimport.php:222 #, php-format msgid "%d contact not imported" msgid_plural "%d contacts not imported" msgstr[0] "%d контакт не импортирован" msgstr[1] "%d контакты не импортированы" msgstr[2] "%d контакты не импортированы" +msgstr[3] "%d контакты не импортированы" -#: ../../include/uimport.php:290 +#: include/uimport.php:292 msgid "Done. You can now login with your username and password" msgstr "Завершено. Теперь вы можете войти с вашим логином и паролем" -#: ../../index.php:428 +#: index.php:442 msgid "toggle mobile" msgstr "мобильная версия" -#: ../../view/theme/cleanzero/config.php:82 -#: ../../view/theme/dispy/config.php:72 ../../view/theme/quattro/config.php:66 -#: ../../view/theme/diabook/config.php:150 ../../view/theme/vier/config.php:55 -#: ../../view/theme/duepuntozero/config.php:61 -msgid "Theme settings" -msgstr "Настройки темы" - -#: ../../view/theme/cleanzero/config.php:83 +#: view/theme/cleanzero/config.php:83 msgid "Set resize level for images in posts and comments (width and height)" -msgstr "Установить уровень изменения размера изображений в постах и ​​комментариях (ширина и высота)" +msgstr "" +"Установить уровень изменения размера изображений в постах и ​​комментариях " +"(ширина и высота)" -#: ../../view/theme/cleanzero/config.php:84 -#: ../../view/theme/dispy/config.php:73 -#: ../../view/theme/diabook/config.php:151 +#: view/theme/cleanzero/config.php:84 view/theme/dispy/config.php:73 +#: view/theme/diabook/config.php:151 msgid "Set font-size for posts and comments" msgstr "Установить шрифт-размер для постов и комментариев" -#: ../../view/theme/cleanzero/config.php:85 +#: view/theme/cleanzero/config.php:85 msgid "Set theme width" msgstr "Установить ширину темы" -#: ../../view/theme/cleanzero/config.php:86 -#: ../../view/theme/quattro/config.php:68 +#: view/theme/cleanzero/config.php:86 view/theme/quattro/config.php:68 msgid "Color scheme" msgstr "Цветовая схема" -#: ../../view/theme/dispy/config.php:74 -#: ../../view/theme/diabook/config.php:152 +#: view/theme/dispy/config.php:74 view/theme/diabook/config.php:152 msgid "Set line-height for posts and comments" msgstr "Установить высоту строки для постов и комментариев" -#: ../../view/theme/dispy/config.php:75 +#: view/theme/dispy/config.php:75 msgid "Set colour scheme" msgstr "Установить цветовую схему" -#: ../../view/theme/quattro/config.php:67 +#: view/theme/quattro/config.php:67 msgid "Alignment" msgstr "Выравнивание" -#: ../../view/theme/quattro/config.php:67 +#: view/theme/quattro/config.php:67 msgid "Left" msgstr "" -#: ../../view/theme/quattro/config.php:67 +#: view/theme/quattro/config.php:67 msgid "Center" msgstr "Центр" -#: ../../view/theme/quattro/config.php:69 +#: view/theme/quattro/config.php:69 msgid "Posts font size" msgstr "Размер шрифта постов" -#: ../../view/theme/quattro/config.php:70 +#: view/theme/quattro/config.php:70 msgid "Textareas font size" msgstr "Размер шрифта текстовых полей" -#: ../../view/theme/diabook/config.php:153 +#: view/theme/diabook/config.php:153 msgid "Set resolution for middle column" msgstr "Установить разрешение для средней колонки" -#: ../../view/theme/diabook/config.php:154 +#: view/theme/diabook/config.php:154 msgid "Set color scheme" msgstr "Установить цветовую схему" -#: ../../view/theme/diabook/config.php:155 +#: view/theme/diabook/config.php:155 msgid "Set zoomfactor for Earth Layer" msgstr "Установить масштаб карты" -#: ../../view/theme/diabook/config.php:156 -#: ../../view/theme/diabook/theme.php:585 +#: view/theme/diabook/config.php:156 view/theme/diabook/theme.php:585 msgid "Set longitude (X) for Earth Layers" msgstr "Установить длину (X) карты" -#: ../../view/theme/diabook/config.php:157 -#: ../../view/theme/diabook/theme.php:586 +#: view/theme/diabook/config.php:157 view/theme/diabook/theme.php:586 msgid "Set latitude (Y) for Earth Layers" msgstr "Установить ширину (Y) карты" -#: ../../view/theme/diabook/config.php:158 -#: ../../view/theme/diabook/theme.php:130 -#: ../../view/theme/diabook/theme.php:544 -#: ../../view/theme/diabook/theme.php:624 +#: view/theme/diabook/config.php:158 view/theme/diabook/theme.php:130 +#: view/theme/diabook/theme.php:544 view/theme/diabook/theme.php:624 +#: view/theme/vier/config.php:111 msgid "Community Pages" msgstr "Страницы сообщества" -#: ../../view/theme/diabook/config.php:159 -#: ../../view/theme/diabook/theme.php:579 -#: ../../view/theme/diabook/theme.php:625 +#: view/theme/diabook/config.php:159 view/theme/diabook/theme.php:579 +#: view/theme/diabook/theme.php:625 msgid "Earth Layers" msgstr "Карта" -#: ../../view/theme/diabook/config.php:160 -#: ../../view/theme/diabook/theme.php:391 -#: ../../view/theme/diabook/theme.php:626 +#: view/theme/diabook/config.php:160 view/theme/diabook/theme.php:391 +#: view/theme/diabook/theme.php:626 view/theme/vier/config.php:112 +#: view/theme/vier/theme.php:156 msgid "Community Profiles" msgstr "Профили сообщества" -#: ../../view/theme/diabook/config.php:161 -#: ../../view/theme/diabook/theme.php:599 -#: ../../view/theme/diabook/theme.php:627 +#: view/theme/diabook/config.php:161 view/theme/diabook/theme.php:599 +#: view/theme/diabook/theme.php:627 view/theme/vier/config.php:113 msgid "Help or @NewHere ?" msgstr "Помощь" -#: ../../view/theme/diabook/config.php:162 -#: ../../view/theme/diabook/theme.php:606 -#: ../../view/theme/diabook/theme.php:628 +#: view/theme/diabook/config.php:162 view/theme/diabook/theme.php:606 +#: view/theme/diabook/theme.php:628 view/theme/vier/config.php:114 +#: view/theme/vier/theme.php:377 msgid "Connect Services" msgstr "Подключить службы" -#: ../../view/theme/diabook/config.php:163 -#: ../../view/theme/diabook/theme.php:523 -#: ../../view/theme/diabook/theme.php:629 +#: view/theme/diabook/config.php:163 view/theme/diabook/theme.php:523 +#: view/theme/diabook/theme.php:629 view/theme/vier/config.php:115 +#: view/theme/vier/theme.php:203 msgid "Find Friends" msgstr "Найти друзей" -#: ../../view/theme/diabook/config.php:164 -#: ../../view/theme/diabook/theme.php:412 -#: ../../view/theme/diabook/theme.php:630 +#: view/theme/diabook/config.php:164 view/theme/diabook/theme.php:412 +#: view/theme/diabook/theme.php:630 view/theme/vier/config.php:116 +#: view/theme/vier/theme.php:185 msgid "Last users" msgstr "Последние пользователи" -#: ../../view/theme/diabook/config.php:165 -#: ../../view/theme/diabook/theme.php:486 -#: ../../view/theme/diabook/theme.php:631 +#: view/theme/diabook/config.php:165 view/theme/diabook/theme.php:486 +#: view/theme/diabook/theme.php:631 msgid "Last photos" msgstr "Последние фото" -#: ../../view/theme/diabook/config.php:166 -#: ../../view/theme/diabook/theme.php:441 -#: ../../view/theme/diabook/theme.php:632 +#: view/theme/diabook/config.php:166 view/theme/diabook/theme.php:441 +#: view/theme/diabook/theme.php:632 msgid "Last likes" msgstr "Последние likes" -#: ../../view/theme/diabook/theme.php:125 +#: view/theme/diabook/theme.php:125 msgid "Your contacts" msgstr "Ваши контакты" -#: ../../view/theme/diabook/theme.php:128 +#: view/theme/diabook/theme.php:128 msgid "Your personal photos" msgstr "Ваши личные фотографии" -#: ../../view/theme/diabook/theme.php:524 +#: view/theme/diabook/theme.php:524 view/theme/vier/theme.php:204 msgid "Local Directory" msgstr "Локальный каталог" -#: ../../view/theme/diabook/theme.php:584 +#: view/theme/diabook/theme.php:584 msgid "Set zoomfactor for Earth Layers" msgstr "Установить масштаб карты" -#: ../../view/theme/diabook/theme.php:622 +#: view/theme/diabook/theme.php:622 msgid "Show/hide boxes at right-hand column:" msgstr "Показать/скрыть блоки в правой колонке:" -#: ../../view/theme/vier/config.php:56 +#: view/theme/vier/config.php:64 +msgid "Comma separated list of helper forums" +msgstr "" + +#: view/theme/vier/config.php:110 msgid "Set style" msgstr "" -#: ../../view/theme/duepuntozero/config.php:45 +#: view/theme/vier/theme.php:295 +msgid "Quick Start" +msgstr "Быстрый запуск" + +#: view/theme/duepuntozero/config.php:45 msgid "greenzero" msgstr "" -#: ../../view/theme/duepuntozero/config.php:46 +#: view/theme/duepuntozero/config.php:46 msgid "purplezero" msgstr "" -#: ../../view/theme/duepuntozero/config.php:47 +#: view/theme/duepuntozero/config.php:47 msgid "easterbunny" msgstr "" -#: ../../view/theme/duepuntozero/config.php:48 +#: view/theme/duepuntozero/config.php:48 msgid "darkzero" msgstr "" -#: ../../view/theme/duepuntozero/config.php:49 +#: view/theme/duepuntozero/config.php:49 msgid "comix" msgstr "" -#: ../../view/theme/duepuntozero/config.php:50 +#: view/theme/duepuntozero/config.php:50 msgid "slackr" msgstr "" -#: ../../view/theme/duepuntozero/config.php:62 +#: view/theme/duepuntozero/config.php:62 msgid "Variations" msgstr "" diff --git a/view/ru/strings.php b/view/ru/strings.php index 09755ca7bb..81600c48f3 100644 --- a/view/ru/strings.php +++ b/view/ru/strings.php @@ -2,13 +2,16 @@ if(! function_exists("string_plural_select_ru")) { function string_plural_select_ru($n){ - return ($n%10==1 && $n%100!=11 ? 0 : $n%10>=2 && $n%10<=4 && ($n%100<10 || $n%100>=20) ? 1 : 2);; + return ; }} ; +$a->strings["Network:"] = "Сеть:"; +$a->strings["Forum"] = "Форум"; $a->strings["%d contact edited."] = array( - 0 => "%d контакт изменён.", - 1 => "%d контакты изменены", - 2 => "%d контакты изменены", + 0 => "", + 1 => "", + 2 => "", + 3 => "", ); $a->strings["Could not access contact record."] = "Не удалось получить доступ к записи контакта."; $a->strings["Could not locate selected profile."] = "Не удалось найти выбранный профиль."; @@ -34,26 +37,12 @@ $a->strings["(Update was successful)"] = "(Обновление было усп $a->strings["(Update was not successful)"] = "(Обновление не удалось)"; $a->strings["Suggest friends"] = "Предложить друзей"; $a->strings["Network type: %s"] = "Сеть: %s"; -$a->strings["%d contact in common"] = array( - 0 => "%d Контакт", - 1 => "%d Контактов", - 2 => "%d Контактов", -); -$a->strings["View all contacts"] = "Показать все контакты"; -$a->strings["Unblock"] = "Разблокировать"; -$a->strings["Block"] = "Заблокировать"; -$a->strings["Toggle Blocked status"] = "Изменить статус блокированности (заблокировать/разблокировать)"; -$a->strings["Unignore"] = "Не игнорировать"; -$a->strings["Ignore"] = "Игнорировать"; -$a->strings["Toggle Ignored status"] = "Изменить статус игнорирования"; -$a->strings["Unarchive"] = "Разархивировать"; -$a->strings["Archive"] = "Архивировать"; -$a->strings["Toggle Archive status"] = "Сменить статус архивации (архивирова/не архивировать)"; -$a->strings["Repair"] = "Восстановить"; -$a->strings["Advanced Contact Settings"] = "Дополнительные Настройки Контакта"; $a->strings["Communications lost with this contact!"] = "Связь с контактом утеряна!"; -$a->strings["Contact Editor"] = "Редактор контакта"; -$a->strings["Submit"] = "Подтвердить"; +$a->strings["Fetch further information for feeds"] = ""; +$a->strings["Disabled"] = "Отключенный"; +$a->strings["Fetch information"] = ""; +$a->strings["Fetch information and keywords"] = ""; +$a->strings["Submit"] = "Добавить"; $a->strings["Profile Visibility"] = "Видимость профиля"; $a->strings["Please choose the profile you would like to display to %s when viewing your profile securely."] = "Пожалуйста, выберите профиль, который вы хотите отображать %s, когда просмотр вашего профиля безопасен."; $a->strings["Contact Information / Notes"] = "Информация о контакте / Заметки"; @@ -67,6 +56,11 @@ $a->strings["Delete contact"] = "Удалить контакт"; $a->strings["Last update:"] = "Последнее обновление: "; $a->strings["Update public posts"] = "Обновить публичные сообщения"; $a->strings["Update now"] = "Обновить сейчас"; +$a->strings["Connect/Follow"] = "Подключиться/Следовать"; +$a->strings["Unblock"] = "Разблокировать"; +$a->strings["Block"] = "Заблокировать"; +$a->strings["Unignore"] = "Не игнорировать"; +$a->strings["Ignore"] = "Игнорировать"; $a->strings["Currently blocked"] = "В настоящее время заблокирован"; $a->strings["Currently ignored"] = "В настоящее время игнорируется"; $a->strings["Currently archived"] = "В данный момент архивирован"; @@ -74,12 +68,12 @@ $a->strings["Hide this contact from others"] = "Скрыть этот конта $a->strings["Replies/likes to your public posts may still be visible"] = "Ответы/лайки ваших публичных сообщений будут видимы."; $a->strings["Notification for new posts"] = ""; $a->strings["Send a notification of every new post of this contact"] = ""; -$a->strings["Fetch further information for feeds"] = ""; -$a->strings["Disabled"] = ""; -$a->strings["Fetch information"] = ""; -$a->strings["Fetch information and keywords"] = ""; $a->strings["Blacklisted keywords"] = ""; $a->strings["Comma separated list of keywords that should not be converted to hashtags, when \"Fetch information and keywords\" is selected"] = ""; +$a->strings["Profile URL"] = "URL профиля"; +$a->strings["Location:"] = "Откуда:"; +$a->strings["About:"] = "О себе:"; +$a->strings["Tags:"] = "Ключевые слова: "; $a->strings["Suggestions"] = "Предложения"; $a->strings["Suggest potential friends"] = "Предложить потенциального знакомого"; $a->strings["All Contacts"] = "Все контакты"; @@ -94,16 +88,30 @@ $a->strings["Archived"] = "Архивированные"; $a->strings["Only show archived contacts"] = "Показывать только архивные контакты"; $a->strings["Hidden"] = "Скрытые"; $a->strings["Only show hidden contacts"] = "Показывать только скрытые контакты"; -$a->strings["Mutual Friendship"] = "Взаимная дружба"; -$a->strings["is a fan of yours"] = "является вашим поклонником"; -$a->strings["you are a fan of"] = "Вы - поклонник"; -$a->strings["Edit contact"] = "Редактировать контакт"; $a->strings["Contacts"] = "Контакты"; $a->strings["Search your contacts"] = "Поиск ваших контактов"; $a->strings["Finding: "] = "Результат поиска: "; $a->strings["Find"] = "Найти"; $a->strings["Update"] = "Обновление"; +$a->strings["Archive"] = "Архивировать"; +$a->strings["Unarchive"] = "Разархивировать"; $a->strings["Delete"] = "Удалить"; +$a->strings["Status"] = "Посты"; +$a->strings["Status Messages and Posts"] = "Ваши посты"; +$a->strings["Profile"] = "Информация"; +$a->strings["Profile Details"] = "Информация о вас"; +$a->strings["View all contacts"] = "Показать все контакты"; +$a->strings["Common Friends"] = "Общие друзья"; +$a->strings["View all common friends"] = ""; +$a->strings["Repair"] = "Восстановить"; +$a->strings["Advanced Contact Settings"] = "Дополнительные Настройки Контакта"; +$a->strings["Toggle Blocked status"] = "Изменить статус блокированности (заблокировать/разблокировать)"; +$a->strings["Toggle Ignored status"] = "Изменить статус игнорирования"; +$a->strings["Toggle Archive status"] = "Сменить статус архивации (архивирова/не архивировать)"; +$a->strings["Mutual Friendship"] = "Взаимная дружба"; +$a->strings["is a fan of yours"] = "является вашим поклонником"; +$a->strings["you are a fan of"] = "Вы - поклонник"; +$a->strings["Edit contact"] = "Редактировать контакт"; $a->strings["No profile"] = "Нет профиля"; $a->strings["Manage Identities and/or Pages"] = "Управление идентификацией и / или страницами"; $a->strings["Toggle between different identities or community/group pages which share your account details or which you have been granted \"manage\" permissions"] = ""; @@ -112,7 +120,6 @@ $a->strings["Post successful."] = "Успешно добавлено."; $a->strings["Permission denied"] = "Доступ запрещен"; $a->strings["Invalid profile identifier."] = "Недопустимый идентификатор профиля."; $a->strings["Profile Visibility Editor"] = "Редактор видимости профиля"; -$a->strings["Profile"] = "Профиль"; $a->strings["Click on a contact to add or remove."] = "Нажмите на контакт, чтобы добавить или удалить."; $a->strings["Visible To"] = "Видимый для"; $a->strings["All Contacts (with secure profile access)"] = "Все контакты (с безопасным доступом к профилю)"; @@ -137,9 +144,6 @@ $a->strings["Edit your default profile to your liking. Review t $a->strings["Profile Keywords"] = "Ключевые слова профиля"; $a->strings["Set some public keywords for your default profile which describe your interests. We may be able to find other people with similar interests and suggest friendships."] = "Установите некоторые публичные ключевые слова для вашего профиля по умолчанию, которые описывают ваши интересы. Мы можем быть в состоянии найти других людей со схожими интересами и предложить дружбу."; $a->strings["Connecting"] = "Подключение"; -$a->strings["Facebook"] = "Facebook"; -$a->strings["Authorise the Facebook Connector if you currently have a Facebook account and we will (optionally) import all your Facebook friends and conversations."] = "Авторизуйте Facebook Connector , если у вас уже есть аккаунт на Facebook, и мы (по желанию) импортируем всех ваших друзей и беседы с Facebook."; -$a->strings["If this is your own personal server, installing the Facebook addon may ease your transition to the free social web."] = "Если это ваш личный сервер, установите дополнение Facebook, это может облегчить ваш переход на свободную социальную сеть."; $a->strings["Importing Emails"] = "Импортирование Email-ов"; $a->strings["Enter your email access information on your Connector Settings page if you wish to import and interact with friends or mailing lists from your email INBOX"] = "Введите информацию о доступе к вашему email на странице настроек вашего коннектора, если вы хотите импортировать, и общаться с друзьями или получать рассылки на ваш ящик электронной почты"; $a->strings["Go to Your Contacts Page"] = "Перейти на страницу ваших контактов"; @@ -164,7 +168,7 @@ $a->strings["Profile Photos"] = "Фотографии профиля"; $a->strings["Image size reduction [%s] failed."] = "Уменьшение размера изображения [%s] не удалось."; $a->strings["Shift-reload the page or clear browser cache if the new photo does not display immediately."] = "Перезагрузите страницу с зажатой клавишей \"Shift\" для того, чтобы увидеть свое новое фото немедленно."; $a->strings["Unable to process image"] = "Не удается обработать изображение"; -$a->strings["Image exceeds size limit of %d"] = "Изображение превышает предельный размер %d"; +$a->strings["Image exceeds size limit of %s"] = ""; $a->strings["Unable to process image."] = "Невозможно обработать фото."; $a->strings["Upload File:"] = "Загрузить файл:"; $a->strings["Select a profile:"] = "Выбрать этот профиль:"; @@ -184,9 +188,28 @@ $a->strings["Tag removed"] = "Ключевое слово удалено"; $a->strings["Remove Item Tag"] = "Удалить ключевое слово"; $a->strings["Select a tag to remove: "] = "Выберите ключевое слово для удаления: "; $a->strings["Remove"] = "Удалить"; +$a->strings["Subscribing to OStatus contacts"] = ""; +$a->strings["No contact provided."] = ""; +$a->strings["Couldn't fetch information for contact."] = ""; +$a->strings["Couldn't fetch friends for contact."] = ""; +$a->strings["Done"] = "Готово"; +$a->strings["success"] = "удачно"; +$a->strings["failed"] = "неудача"; +$a->strings["ignored"] = ""; +$a->strings["Keep this window open until done."] = ""; $a->strings["Save to Folder:"] = "Сохранить в папку:"; $a->strings["- select -"] = "- выбрать -"; $a->strings["Save"] = "Сохранить"; +$a->strings["Submit Request"] = "Отправить запрос"; +$a->strings["You already added this contact."] = ""; +$a->strings["Diaspora support isn't enabled. Contact can't be added."] = ""; +$a->strings["OStatus support is disabled. Contact can't be added."] = ""; +$a->strings["The network type couldn't be detected. Contact can't be added."] = ""; +$a->strings["Please answer the following:"] = "Пожалуйста, ответьте следующее:"; +$a->strings["Does %s know you?"] = "%s знает вас?"; +$a->strings["No"] = "Нет"; +$a->strings["Add a personal note:"] = "Добавить личную заметку:"; +$a->strings["Your Identity Address:"] = "Ваш идентификационный адрес:"; $a->strings["Contact added"] = "Контакт добавлен"; $a->strings["Unable to locate original post."] = "Не удалось найти оригинальный пост."; $a->strings["Empty post discarded."] = "Пустое сообщение отбрасывается."; @@ -207,6 +230,7 @@ $a->strings["Group removed."] = "Группа удалена."; $a->strings["Unable to remove group."] = "Не удается удалить группу."; $a->strings["Group Editor"] = "Редактор групп"; $a->strings["Members"] = "Участники"; +$a->strings["Group is empty"] = "Группа пуста"; $a->strings["You must be logged in to use addons. "] = "Вы должны войти в систему, чтобы использовать аддоны."; $a->strings["Applications"] = "Приложения"; $a->strings["No installed applications."] = "Нет установленных приложений."; @@ -233,6 +257,8 @@ $a->strings["[Name Withheld]"] = "[Имя не разглашается]"; $a->strings["%1\$s has joined %2\$s"] = "%1\$s присоединился %2\$s"; $a->strings["Requested profile is not available."] = "Запрашиваемый профиль недоступен."; $a->strings["Tips for New Members"] = "Советы для новых участников"; +$a->strings["Do you really want to delete this video?"] = ""; +$a->strings["Delete Video"] = "Удалить видео"; $a->strings["No videos selected"] = "Видео не выбрано"; $a->strings["Access to this item is restricted."] = "Доступ к этому пункту ограничен."; $a->strings["View Video"] = "Просмотреть видео"; @@ -243,6 +269,7 @@ $a->strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s tagged %2\$s's %3\ $a->strings["Friend suggestion sent."] = "Приглашение в друзья отправлено."; $a->strings["Suggest Friends"] = "Предложить друзей"; $a->strings["Suggest a friend for %s"] = "Предложить друга для %s."; +$a->strings["Invalid request."] = "Неверный запрос."; $a->strings["No valid account found."] = "Не найдено действительного аккаунта."; $a->strings["Password reset request issued. Check your email."] = "Запрос на сброс пароля принят. Проверьте вашу электронную почту."; $a->strings["\n\t\tDear %1\$s,\n\t\t\tA request was recently received at \"%2\$s\" to reset your account\n\t\tpassword. In order to confirm this request, please select the verification link\n\t\tbelow or paste it into your web browser address bar.\n\n\t\tIf you did NOT request this change, please DO NOT follow the link\n\t\tprovided and ignore and/or delete this email.\n\n\t\tYour password will not be changed unless we can verify that you\n\t\tissued this request."] = ""; @@ -262,26 +289,16 @@ $a->strings["Forgot your Password?"] = "Забыли пароль?"; $a->strings["Enter your email address and submit to have your password reset. Then check your email for further instructions."] = "Введите адрес электронной почты и подтвердите, что вы хотите сбросить ваш пароль. Затем проверьте свою электронную почту для получения дальнейших инструкций."; $a->strings["Nickname or Email: "] = "Ник или E-mail: "; $a->strings["Reset"] = "Сброс"; -$a->strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s нравится %3\$s от %2\$s "; -$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s не нравится %3\$s от %2\$s "; $a->strings["{0} wants to be your friend"] = "{0} хочет стать Вашим другом"; $a->strings["{0} sent you a message"] = "{0} отправил Вам сообщение"; $a->strings["{0} requested registration"] = "{0} требуемая регистрация"; -$a->strings["{0} commented %s's post"] = "{0} прокомментировал сообщение от %s"; -$a->strings["{0} liked %s's post"] = "{0} нравится сообщение от %s"; -$a->strings["{0} disliked %s's post"] = "{0} не нравится сообщение от %s"; -$a->strings["{0} is now friends with %s"] = "{0} теперь друзья с %s"; -$a->strings["{0} posted"] = "{0} опубликовано"; -$a->strings["{0} tagged %s's post with #%s"] = "{0} пометил сообщение %s с #%s"; -$a->strings["{0} mentioned you in a post"] = "{0} упоменул Вас в сообщение"; $a->strings["No contacts."] = "Нет контактов."; -$a->strings["View Contacts"] = "Просмотр контактов"; $a->strings["Invalid request identifier."] = "Неверный идентификатор запроса."; $a->strings["Discard"] = "Отказаться"; $a->strings["System"] = "Система"; -$a->strings["Network"] = "Сеть"; +$a->strings["Network"] = "Новости"; $a->strings["Personal"] = "Персонал"; -$a->strings["Home"] = "Главная"; +$a->strings["Home"] = "Мой профиль"; $a->strings["Introductions"] = "Запросы"; $a->strings["Show Ignored Requests"] = "Показать проигнорированные запросы"; $a->strings["Hide Ignored Requests"] = "Скрыть проигнорированные запросы"; @@ -294,12 +311,14 @@ $a->strings["Approve"] = "Одобрить"; $a->strings["Claims to be known to you: "] = "Утверждения, о которых должно быть вам известно: "; $a->strings["yes"] = "да"; $a->strings["no"] = "нет"; -$a->strings["Approve as: "] = "Утвердить как: "; +$a->strings["Shall your connection be bidirectional or not? \"Friend\" implies that you allow to read and you subscribe to their posts. \"Fan/Admirer\" means that you allow to read but you do not want to read theirs. Approve as: "] = ""; +$a->strings["Shall your connection be bidirectional or not? \"Friend\" implies that you allow to read and you subscribe to their posts. \"Sharer\" means that you allow to read but you do not want to read theirs. Approve as: "] = ""; $a->strings["Friend"] = "Друг"; $a->strings["Sharer"] = "Участник"; $a->strings["Fan/Admirer"] = "Фанат / Поклонник"; $a->strings["Friend/Connect Request"] = "Запрос в друзья / на подключение"; $a->strings["New Follower"] = "Новый фолловер"; +$a->strings["Gender:"] = "Пол:"; $a->strings["No introductions."] = "Запросов нет."; $a->strings["Notifications"] = "Уведомления"; $a->strings["%s liked %s's post"] = "%s нравится %s сообшение"; @@ -348,30 +367,31 @@ $a->strings["Upload photo"] = "Загрузить фото"; $a->strings["Insert web link"] = "Вставить веб-ссылку"; $a->strings["Please wait"] = "Пожалуйста, подождите"; $a->strings["No messages."] = "Нет сообщений."; +$a->strings["Message not available."] = "Сообщение не доступно."; +$a->strings["Delete message"] = "Удалить сообщение"; +$a->strings["Delete conversation"] = "Удалить историю общения"; +$a->strings["No secure communications available. You may be able to respond from the sender's profile page."] = "Невозможно защищённое соединение. Вы имеете возможность ответить со страницы профиля отправителя."; +$a->strings["Send Reply"] = "Отправить ответ"; $a->strings["Unknown sender - %s"] = "Неизвестный отправитель - %s"; $a->strings["You and %s"] = "Вы и %s"; $a->strings["%s and You"] = "%s и Вы"; -$a->strings["Delete conversation"] = "Удалить историю общения"; $a->strings["D, d M Y - g:i A"] = "D, d M Y - g:i A"; $a->strings["%d message"] = array( 0 => "%d сообщение", 1 => "%d сообщений", 2 => "%d сообщений", + 3 => "%d сообщений", ); -$a->strings["Message not available."] = "Сообщение не доступно."; -$a->strings["Delete message"] = "Удалить сообщение"; -$a->strings["No secure communications available. You may be able to respond from the sender's profile page."] = "Невозможно защищённое соединение. Вы имеете возможность ответить со страницы профиля отправителя."; -$a->strings["Send Reply"] = "Отправить ответ"; $a->strings["[Embedded content - reload page to view]"] = "[Встроенное содержание - перезагрузите страницу для просмотра]"; $a->strings["Contact settings applied."] = "Установки контакта приняты."; $a->strings["Contact update failed."] = "Обновление контакта неудачное."; -$a->strings["Repair Contact Settings"] = "Восстановить установки контакта"; $a->strings["WARNING: This is highly advanced and if you enter incorrect information your communications with this contact may stop working."] = "ВНИМАНИЕ: Это крайне важно! Если вы введете неверную информацию, ваша связь с этим контактом перестанет работать."; $a->strings["Please use your browser 'Back' button now if you are uncertain what to do on this page."] = "Пожалуйста, нажмите клавишу вашего браузера 'Back' или 'Назад' сейчас, если вы не уверены, что делаете на этой странице."; -$a->strings["Return to contact editor"] = "Возврат к редактору контакта"; $a->strings["No mirroring"] = ""; $a->strings["Mirror as forwarded posting"] = ""; $a->strings["Mirror as my own posting"] = ""; +$a->strings["Return to contact editor"] = "Возврат к редактору контакта"; +$a->strings["Refetch contact data"] = ""; $a->strings["Name"] = "Имя"; $a->strings["Account Nickname"] = "Ник аккаунта"; $a->strings["@Tagname - overrides Name/Nickname"] = ""; @@ -387,9 +407,12 @@ $a->strings["Mark this contact as remote_self, this will cause friendica to repo $a->strings["Login"] = "Вход"; $a->strings["The post was created"] = ""; $a->strings["Access denied."] = "Доступ запрещен."; -$a->strings["People Search"] = "Поиск людей"; +$a->strings["Connect"] = "Подключить"; +$a->strings["View Profile"] = "Просмотреть профиль"; +$a->strings["People Search - %s"] = ""; $a->strings["No matches"] = "Нет соответствий"; $a->strings["Photos"] = "Фото"; +$a->strings["Contact Photos"] = "Фотографии контакта"; $a->strings["Files"] = "Файлы"; $a->strings["Contacts who are not members of a group"] = "Контакты, которые не являются членами группы"; $a->strings["Theme settings updated."] = "Настройки темы обновлены."; @@ -397,14 +420,28 @@ $a->strings["Site"] = "Сайт"; $a->strings["Users"] = "Пользователи"; $a->strings["Plugins"] = "Плагины"; $a->strings["Themes"] = "Темы"; +$a->strings["Additional features"] = "Дополнительные возможности"; $a->strings["DB updates"] = "Обновление БД"; +$a->strings["Inspect Queue"] = ""; +$a->strings["Federation Statistics"] = ""; $a->strings["Logs"] = "Журналы"; +$a->strings["View Logs"] = "Просмотр логов"; $a->strings["probe address"] = ""; $a->strings["check webfinger"] = ""; $a->strings["Admin"] = "Администратор"; $a->strings["Plugin Features"] = "Возможности плагина"; -$a->strings["diagnostics"] = ""; +$a->strings["diagnostics"] = "Диагностика"; $a->strings["User registrations waiting for confirmation"] = "Регистрации пользователей, ожидающие подтверждения"; +$a->strings["This page offers you some numbers to the known part of the federated social network your Friendica node is part of. These numbers are not complete but only reflect the part of the network your node is aware of."] = ""; +$a->strings["The Auto Discovered Contact Directory feature is not enabled, it will improve the data displayed here."] = ""; +$a->strings["Administration"] = "Администрация"; +$a->strings["Currently this node is aware of %d nodes from the following platforms:"] = ""; +$a->strings["ID"] = ""; +$a->strings["Recipient Name"] = ""; +$a->strings["Recipient Profile"] = ""; +$a->strings["Created"] = ""; +$a->strings["Last Tried"] = ""; +$a->strings["This page lists the content of the queue for outgoing postings. These are postings the initial delivery failed for. They will be resend later and eventually deleted if the delivery fails permanently."] = ""; $a->strings["Normal Account"] = "Обычный аккаунт"; $a->strings["Soapbox Account"] = "Аккаунт Витрина"; $a->strings["Community/Celebrity Account"] = "Аккаунт Сообщество / Знаменитость"; @@ -412,13 +449,13 @@ $a->strings["Automatic Friend Account"] = "\"Автоматический дру $a->strings["Blog Account"] = "Аккаунт блога"; $a->strings["Private Forum"] = "Личный форум"; $a->strings["Message queues"] = "Очереди сообщений"; -$a->strings["Administration"] = "Администрация"; $a->strings["Summary"] = "Резюме"; $a->strings["Registered users"] = "Зарегистрированные пользователи"; $a->strings["Pending registrations"] = "Ожидающие регистрации"; $a->strings["Version"] = "Версия"; $a->strings["Active plugins"] = "Активные плагины"; $a->strings["Can not parse base url. Must have at least ://"] = "Невозможно определить базовый URL. Он должен иметь следующий вид - ://"; +$a->strings["RINO2 needs mcrypt php extension to work."] = "Для функционирования RINO2 необходим пакет php5-mcrypt"; $a->strings["Site settings updated."] = "Установки сайта обновлены."; $a->strings["No special theme for mobile devices"] = "Нет специальной темы для мобильных устройств"; $a->strings["No community page"] = ""; @@ -429,6 +466,12 @@ $a->strings["Frequently"] = "Часто"; $a->strings["Hourly"] = "Раз в час"; $a->strings["Twice daily"] = "Два раза в день"; $a->strings["Daily"] = "Ежедневно"; +$a->strings["Users, Global Contacts"] = ""; +$a->strings["Users, Global Contacts/fallback"] = ""; +$a->strings["One month"] = "Один месяц"; +$a->strings["Three months"] = "Три месяца"; +$a->strings["Half a year"] = "Пол года"; +$a->strings["One year"] = "Один год"; $a->strings["Multi user instance"] = "Многопользовательский вид"; $a->strings["Closed"] = "Закрыто"; $a->strings["Requires approval"] = "Требуется подтверждение"; @@ -441,16 +484,20 @@ $a->strings["Registration"] = "Регистрация"; $a->strings["File upload"] = "Загрузка файлов"; $a->strings["Policies"] = "Политики"; $a->strings["Advanced"] = "Расширенный"; +$a->strings["Auto Discovered Contact Directory"] = ""; $a->strings["Performance"] = "Производительность"; $a->strings["Relocate - WARNING: advanced function. Could make this server unreachable."] = "Переместить - ПРЕДУПРЕЖДЕНИЕ: расширеная функция. Может сделать этот сервер недоступным."; $a->strings["Site name"] = "Название сайта"; -$a->strings["Host name"] = ""; -$a->strings["Sender Email"] = ""; +$a->strings["Host name"] = "Имя хоста"; +$a->strings["Sender Email"] = "Системный Email"; +$a->strings["The email address your server shall use to send notification emails from."] = "Адрес с которого будут приходить письма пользователям."; $a->strings["Banner/Logo"] = "Баннер/Логотип"; $a->strings["Shortcut icon"] = ""; +$a->strings["Link to an icon that will be used for browsers."] = ""; $a->strings["Touch icon"] = ""; +$a->strings["Link to an icon that will be used for tablets and mobiles."] = ""; $a->strings["Additional Info"] = "Дополнительная информация"; -$a->strings["For public servers: you can add additional information here that will be listed at dir.friendica.com/siteinfo."] = "Для публичных серверов: вы можете добавить дополнительную информацию, которая будет перечислена в dir.friendica.com/siteinfo."; +$a->strings["For public servers: you can add additional information here that will be listed at %s/siteinfo."] = ""; $a->strings["System language"] = "Системный язык"; $a->strings["System theme"] = "Системная тема"; $a->strings["Default system theme - may be over-ridden by user profiles - change theme settings"] = "Тема системы по умолчанию - может быть переопределена пользователем - изменить настройки темы"; @@ -458,7 +505,7 @@ $a->strings["Mobile system theme"] = "Мобильная тема системы $a->strings["Theme for mobile devices"] = "Тема для мобильных устройств"; $a->strings["SSL link policy"] = "Политика SSL"; $a->strings["Determines whether generated links should be forced to use SSL"] = "Ссылки должны быть вынуждены использовать SSL"; -$a->strings["Force SSL"] = ""; +$a->strings["Force SSL"] = "SSL принудительно"; $a->strings["Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops."] = ""; $a->strings["Old style 'Share'"] = "Старый стиль 'Share'"; $a->strings["Deactivates the bbcode element 'share' for repeating items."] = "Отключение BBCode элемента 'share' для повторяющихся элементов."; @@ -487,8 +534,8 @@ $a->strings["Block public"] = "Блокировать общественный $a->strings["Check to block public access to all otherwise public personal pages on this site unless you are currently logged in."] = "Отметьте, чтобы заблокировать публичный доступ ко всем иным публичным персональным страницам на этом сайте, если вы не вошли на сайт."; $a->strings["Force publish"] = "Принудительная публикация"; $a->strings["Check to force all profiles on this site to be listed in the site directory."] = "Отметьте, чтобы принудительно заставить все профили на этом сайте, быть перечислеными в каталоге сайта."; -$a->strings["Global directory update URL"] = "URL обновления глобального каталога"; -$a->strings["URL to update the global directory. If this is not set, the global directory is completely unavailable to the application."] = "URL для обновления глобального каталога. Если он не установлен, глобальный каталог полностью недоступен для приложения."; +$a->strings["Global directory URL"] = ""; +$a->strings["URL to the global directory. If this is not set, the global directory is completely unavailable to the application."] = ""; $a->strings["Allow threaded items"] = "Разрешить темы в обсуждении"; $a->strings["Allow infinite level threading for items on this site."] = "Разрешить бесконечный уровень для тем на этом сайте."; $a->strings["Private posts by default for new users"] = "Частные сообщения по умолчанию для новых пользователей"; @@ -517,6 +564,8 @@ $a->strings["Enable OStatus support"] = "Включить поддержку OSt $a->strings["Provide built-in OStatus (StatusNet, GNU Social etc.) compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed."] = ""; $a->strings["OStatus conversation completion interval"] = ""; $a->strings["How often shall the poller check for new entries in OStatus conversations? This can be a very ressource task."] = "Как часто процессы должны проверять наличие новых записей в OStatus разговорах? Это может быть очень ресурсоёмкой задачей."; +$a->strings["OStatus support can only be enabled if threading is enabled."] = ""; +$a->strings["Diaspora support can't be enabled because Friendica was installed into a sub directory."] = ""; $a->strings["Enable Diaspora support"] = "Включить поддержку Diaspora"; $a->strings["Provide built-in Diaspora network compatibility."] = "Обеспечить встроенную поддержку сети Diaspora."; $a->strings["Only allow Friendica contacts"] = "Позвольть только Friendica контакты"; @@ -533,6 +582,24 @@ $a->strings["Poll interval"] = "Интервал опроса"; $a->strings["Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval."] = "Установить задержку фоновых процессов опросов путем ограничения количества секунд, чтобы уменьшить нагрузку на систему. Если 0, используется интервал доставки."; $a->strings["Maximum Load Average"] = "Средняя максимальная нагрузка"; $a->strings["Maximum system load before delivery and poll processes are deferred - default 50."] = "Максимальная нагрузка на систему перед приостановкой процессов доставки и опросов - по умолчанию 50."; +$a->strings["Maximum Load Average (Frontend)"] = ""; +$a->strings["Maximum system load before the frontend quits service - default 50."] = ""; +$a->strings["Maximum table size for optimization"] = ""; +$a->strings["Maximum table size (in MB) for the automatic optimization - default 100 MB. Enter -1 to disable it."] = ""; +$a->strings["Minimum level of fragmentation"] = ""; +$a->strings["Minimum fragmenation level to start the automatic optimization - default value is 30%."] = ""; +$a->strings["Periodical check of global contacts"] = ""; +$a->strings["If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers."] = ""; +$a->strings["Days between requery"] = ""; +$a->strings["Number of days after which a server is requeried for his contacts."] = ""; +$a->strings["Discover contacts from other servers"] = ""; +$a->strings["Periodically query other servers for contacts. You can choose between 'users': the users on the remote system, 'Global Contacts': active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren't available. The fallback increases the server load, so the recommened setting is 'Users, Global Contacts'."] = ""; +$a->strings["Timeframe for fetching global contacts"] = ""; +$a->strings["When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers."] = ""; +$a->strings["Search the local directory"] = ""; +$a->strings["Search the local directory instead of the global directory. When searching locally, every search will be executed on the global directory in the background. This improves the search results when the search is repeated."] = ""; +$a->strings["Publish server information"] = ""; +$a->strings["If enabled, general server and usage data will be published. The data contains the name and version of the server, number of users with public profiles, number of posts and the activated protocols and connectors. See the-federation.info for details."] = ""; $a->strings["Use MySQL full text engine"] = "Использовать систему полнотексного поиска MySQL"; $a->strings["Activates the full text engine. Speeds up search - but can only search for four and more characters."] = "Активизирует систему полнотексного поиска. Ускоряет поиск - но может искать только при указании четырех и более символов."; $a->strings["Suppress Language"] = ""; @@ -540,13 +607,17 @@ $a->strings["Suppress language information in meta information about a posting." $a->strings["Suppress Tags"] = ""; $a->strings["Suppress showing a list of hashtags at the end of the posting."] = ""; $a->strings["Path to item cache"] = "Путь к элементам кэша"; +$a->strings["The item caches buffers generated bbcode and external images."] = ""; $a->strings["Cache duration in seconds"] = "Время жизни кэша в секундах"; $a->strings["How long should the cache files be hold? Default value is 86400 seconds (One day). To disable the item cache, set the value to -1."] = ""; $a->strings["Maximum numbers of comments per post"] = ""; $a->strings["How much comments should be shown for each post? Default value is 100."] = ""; $a->strings["Path for lock file"] = "Путь к файлу блокировки"; +$a->strings["The lock file is used to avoid multiple pollers at one time. Only define a folder here."] = ""; $a->strings["Temp path"] = "Временная папка"; +$a->strings["If you have a restricted system where the webserver can't access the system temp path, enter another path here."] = ""; $a->strings["Base path to installation"] = "Путь для установки"; +$a->strings["If the system cannot detect the correct path to your installation, enter the correct path here. This setting should only be set if you are using a restricted system and symbolic links to your webroot."] = ""; $a->strings["Disable picture proxy"] = ""; $a->strings["The picture proxy increases performance and privacy. It shouldn't be used on systems with very low bandwith."] = ""; $a->strings["Enable old style pager"] = ""; @@ -554,6 +625,11 @@ $a->strings["The old style pager has page numbers but slows down massively the p $a->strings["Only search in tags"] = ""; $a->strings["On large systems the text search can slow down the system extremely."] = ""; $a->strings["New base url"] = "Новый базовый url"; +$a->strings["Change base url for this server. Sends relocate message to all DFRN contacts of all users."] = ""; +$a->strings["RINO Encryption"] = "RINO шифрование"; +$a->strings["Encryption layer between nodes."] = "Слой шифрования между узлами."; +$a->strings["Embedly API key"] = ""; +$a->strings["Embedly is used to fetch additional data for web pages. This is an optional parameter."] = ""; $a->strings["Update has been marked successful"] = "Обновление было успешно отмечено"; $a->strings["Database structure update %s was successfully applied."] = ""; $a->strings["Executing of database structure update %s failed with error: %s"] = ""; @@ -574,11 +650,13 @@ $a->strings["%s user blocked/unblocked"] = array( 0 => "%s пользователь заблокирован/разблокирован", 1 => "%s пользователей заблокировано/разблокировано", 2 => "%s пользователей заблокировано/разблокировано", + 3 => "%s пользователей заблокировано/разблокировано", ); $a->strings["%s user deleted"] = array( 0 => "%s человек удален", 1 => "%s чел. удалено", 2 => "%s чел. удалено", + 3 => "%s чел. удалено", ); $a->strings["User '%s' deleted"] = "Пользователь '%s' удален"; $a->strings["User '%s' unblocked"] = "Пользователь '%s' разблокирован"; @@ -612,8 +690,12 @@ $a->strings["Enable"] = "Включить"; $a->strings["Toggle"] = "Переключить"; $a->strings["Author: "] = "Автор:"; $a->strings["Maintainer: "] = "Программа обслуживания: "; +$a->strings["Reload active plugins"] = ""; +$a->strings["There are currently no plugins available on your node. You can find the official plugin repository at %1\$s and might find other interesting plugins in the open plugin registry at %2\$s"] = ""; $a->strings["No themes found."] = "Темы не найдены."; $a->strings["Screenshot"] = "Скриншот"; +$a->strings["Reload active themes"] = ""; +$a->strings["No themes found on the system. They should be paced in %1\$s"] = ""; $a->strings["[Experimental]"] = "[экспериментально]"; $a->strings["[Unsupported]"] = "[Неподдерживаемое]"; $a->strings["Log settings updated."] = "Настройки журнала обновлены."; @@ -622,18 +704,19 @@ $a->strings["Enable Debugging"] = "Включить отладку"; $a->strings["Log file"] = "Лог-файл"; $a->strings["Must be writable by web server. Relative to your Friendica top-level directory."] = "Должно быть доступно для записи в веб-сервере. Относительно вашего Friendica каталога верхнего уровня."; $a->strings["Log level"] = "Уровень лога"; -$a->strings["Close"] = "Закрыть"; -$a->strings["FTP Host"] = "FTP хост"; -$a->strings["FTP Path"] = "Путь FTP"; -$a->strings["FTP User"] = "FTP пользователь"; -$a->strings["FTP Password"] = "FTP пароль"; -$a->strings["Search Results For:"] = "Результаты поиска для:"; +$a->strings["PHP logging"] = "PHP логирование"; +$a->strings["To enable logging of PHP errors and warnings you can add the following to the .htconfig.php file of your installation. The filename set in the 'error_log' line is relative to the friendica top-level directory and must be writeable by the web server. The option '1' for 'log_errors' and 'display_errors' is to enable these options, set to '0' to disable them."] = ""; +$a->strings["Off"] = "Выкл."; +$a->strings["On"] = "Вкл."; +$a->strings["Lock feature %s"] = ""; +$a->strings["Manage Additional Features"] = ""; +$a->strings["Search Results For: %s"] = ""; $a->strings["Remove term"] = "Удалить элемент"; $a->strings["Saved Searches"] = "запомненные поиски"; $a->strings["add"] = "добавить"; -$a->strings["Commented Order"] = "Прокомментированный запрос"; +$a->strings["Commented Order"] = "Последние комментарии"; $a->strings["Sort by Comment Date"] = "Сортировать по дате комментария"; -$a->strings["Posted Order"] = "Отправленный запрос"; +$a->strings["Posted Order"] = "Лента записей"; $a->strings["Sort by Post Date"] = "Сортировать по дате отправки"; $a->strings["Posts that mention or involve you"] = ""; $a->strings["New"] = "Новый"; @@ -646,36 +729,77 @@ $a->strings["Warning: This group contains %s member from an insecure network."] 0 => "Внимание: Эта группа содержит %s участника с незащищенной сети.", 1 => "Внимание: Эта группа содержит %s участников с незащищенной сети.", 2 => "Внимание: Эта группа содержит %s участников с незащищенной сети.", + 3 => "Внимание: Эта группа содержит %s участников с незащищенной сети.", ); $a->strings["Private messages to this group are at risk of public disclosure."] = "Личные сообщения к этой группе находятся под угрозой обнародования."; $a->strings["No such group"] = "Нет такой группы"; -$a->strings["Group is empty"] = "Группа пуста"; -$a->strings["Group: "] = "Группа: "; -$a->strings["Contact: "] = "Контакт: "; +$a->strings["Group: %s"] = "Группа: %s"; $a->strings["Private messages to this person are at risk of public disclosure."] = "Личные сообщения этому человеку находятся под угрозой обнародования."; $a->strings["Invalid contact."] = "Недопустимый контакт."; -$a->strings["Friends of %s"] = "%s Друзья"; $a->strings["No friends to display."] = "Нет друзей."; +$a->strings["Event can not end before it has started."] = ""; $a->strings["Event title and start time are required."] = "Название мероприятия и время начала обязательны для заполнения."; +$a->strings["Sun"] = "Вс"; +$a->strings["Mon"] = "Пн"; +$a->strings["Tue"] = "Вт"; +$a->strings["Wed"] = "Ср"; +$a->strings["Thu"] = "Чт"; +$a->strings["Fri"] = "Пт"; +$a->strings["Sat"] = "Сб"; +$a->strings["Sunday"] = "Воскресенье"; +$a->strings["Monday"] = "Понедельник"; +$a->strings["Tuesday"] = "Вторник"; +$a->strings["Wednesday"] = "Среда"; +$a->strings["Thursday"] = "Четверг"; +$a->strings["Friday"] = "Пятница"; +$a->strings["Saturday"] = "Суббота"; +$a->strings["Jan"] = "Янв"; +$a->strings["Feb"] = "Фев"; +$a->strings["Mar"] = "Мрт"; +$a->strings["Apr"] = "Апр"; +$a->strings["May"] = "Май"; +$a->strings["Jun"] = "Июн"; +$a->strings["Jul"] = "Июл"; +$a->strings["Aug"] = "Авг"; +$a->strings["Sept"] = "Сен"; +$a->strings["Oct"] = "Окт"; +$a->strings["Nov"] = "Нбр"; +$a->strings["Dec"] = "Дек"; +$a->strings["January"] = "Январь"; +$a->strings["February"] = "Февраль"; +$a->strings["March"] = "Март"; +$a->strings["April"] = "Апрель"; +$a->strings["June"] = "Июнь"; +$a->strings["July"] = "Июль"; +$a->strings["August"] = "Август"; +$a->strings["September"] = "Сентябрь"; +$a->strings["October"] = "Октябрь"; +$a->strings["November"] = "Ноябрь"; +$a->strings["December"] = "Декабрь"; +$a->strings["today"] = "сегодня"; +$a->strings["month"] = "мес."; +$a->strings["week"] = "неделя"; +$a->strings["day"] = "день"; $a->strings["l, F j"] = "l, j F"; $a->strings["Edit event"] = "Редактировать мероприятие"; -$a->strings["link to source"] = "ссылка на источник"; +$a->strings["link to source"] = "ссылка на сообщение"; $a->strings["Events"] = "Мероприятия"; $a->strings["Create New Event"] = "Создать новое мероприятие"; $a->strings["Previous"] = "Назад"; $a->strings["Next"] = "Далее"; -$a->strings["hour:minute"] = "час:минута"; $a->strings["Event details"] = "Сведения о мероприятии"; -$a->strings["Format is %s %s. Starting date and Title are required."] = "Формат %s %s. Необхлдима дата старта и заголовок."; +$a->strings["Starting date and Title are required."] = ""; $a->strings["Event Starts:"] = "Начало мероприятия:"; $a->strings["Required"] = "Требуется"; $a->strings["Finish date/time is not known or not relevant"] = "Дата/время окончания не известны, или не указаны"; $a->strings["Event Finishes:"] = "Окончание мероприятия:"; $a->strings["Adjust for viewer timezone"] = "Настройка часового пояса"; $a->strings["Description:"] = "Описание:"; -$a->strings["Location:"] = "Откуда:"; $a->strings["Title:"] = "Титул:"; $a->strings["Share this event"] = "Поделитесь этим мероприятием"; +$a->strings["Preview"] = "Предварительный просмотр"; +$a->strings["Credits"] = ""; +$a->strings["Friendica is a community project, that would not be possible without the help of many people. Here is a list of those who have contributed to the code or the translation of Friendica. Thank you all!"] = ""; $a->strings["Select"] = "Выберите"; $a->strings["View %s's profile @ %s"] = "Просмотреть профиль %s [@ %s]"; $a->strings["%s from %s"] = "%s с %s"; @@ -684,11 +808,13 @@ $a->strings["%d comment"] = array( 0 => "%d комментарий", 1 => "%d комментариев", 2 => "%d комментариев", + 3 => "%d комментариев", ); $a->strings["comment"] = array( 0 => "", 1 => "", 2 => "комментарий", + 3 => "комментарий", ); $a->strings["show more"] = "показать больше"; $a->strings["Private Message"] = "Личное сообщение"; @@ -699,7 +825,7 @@ $a->strings["dislike"] = "не нравитса"; $a->strings["Share this"] = "Поделитесь этим"; $a->strings["share"] = "делиться"; $a->strings["This is you"] = "Это вы"; -$a->strings["Comment"] = "Комментарий"; +$a->strings["Comment"] = "Оставить комментарий"; $a->strings["Bold"] = "Жирный"; $a->strings["Italic"] = "Kурсивный"; $a->strings["Underline"] = "Подчеркнутый"; @@ -708,7 +834,6 @@ $a->strings["Code"] = "Код"; $a->strings["Image"] = "Изображение / Фото"; $a->strings["Link"] = "Ссылка"; $a->strings["Video"] = "Видео"; -$a->strings["Preview"] = "предварительный просмотр"; $a->strings["Edit"] = "Редактировать"; $a->strings["add star"] = "пометить"; $a->strings["remove star"] = "убрать метку"; @@ -728,6 +853,7 @@ $a->strings["Could not create table."] = "Не удалось создать т $a->strings["Your Friendica site database has been installed."] = "База данных сайта установлена."; $a->strings["You may need to import the file \"database.sql\" manually using phpmyadmin or mysql."] = "Вам может понадобиться импортировать файл \"database.sql\" вручную с помощью PhpMyAdmin или MySQL."; $a->strings["Please see the file \"INSTALL.txt\"."] = "Пожалуйста, смотрите файл \"INSTALL.txt\"."; +$a->strings["Database already in use."] = ""; $a->strings["System check"] = "Проверить систему"; $a->strings["Check again"] = "Проверить еще раз"; $a->strings["Database connection"] = "Подключение к базе данных"; @@ -743,7 +869,7 @@ $a->strings["Your account email address must match this in order to use the web $a->strings["Please select a default timezone for your website"] = "Пожалуйста, выберите часовой пояс по умолчанию для вашего сайта"; $a->strings["Site settings"] = "Настройки сайта"; $a->strings["Could not find a command line version of PHP in the web server PATH."] = "Не удалось найти PATH веб-сервера в установках PHP."; -$a->strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron. See 'Activating scheduled tasks'"] = "Если на вашем сервере не установлена версия командной строки PHP, вы не будете иметь возможность запускать фоновые опросы через крон. См. 'Активация запланированных задачах' "; +$a->strings["If you don't have a command line version of PHP installed on server, you will not be able to run background polling via cron. See 'Setup the poller'"] = ""; $a->strings["PHP executable path"] = "PHP executable path"; $a->strings["Enter full path to php executable. You can leave this blank to continue the installation."] = "Введите полный путь к исполняемому файлу PHP. Вы можете оставить это поле пустым, чтобы продолжить установку."; $a->strings["Command line PHP"] = "Command line PHP"; @@ -761,6 +887,7 @@ $a->strings["GD graphics PHP module"] = "GD graphics PHP модуль"; $a->strings["OpenSSL PHP module"] = "OpenSSL PHP модуль"; $a->strings["mysqli PHP module"] = "mysqli PHP модуль"; $a->strings["mb_string PHP module"] = "mb_string PHP модуль"; +$a->strings["mcrypt PHP module"] = ""; $a->strings["Apache mod_rewrite module"] = "Apache mod_rewrite module"; $a->strings["Error: Apache webserver mod-rewrite module is required but not installed."] = "Ошибка: необходим модуль веб-сервера Apache mod-rewrite, но он не установлен."; $a->strings["Error: libCURL PHP module required but not installed."] = "Ошибка: необходим libCURL PHP модуль, но он не установлен."; @@ -768,6 +895,9 @@ $a->strings["Error: GD graphics PHP module with JPEG support required but not in $a->strings["Error: openssl PHP module required but not installed."] = "Ошибка: необходим PHP модуль OpenSSL, но он не установлен."; $a->strings["Error: mysqli PHP module required but not installed."] = "Ошибка: необходим PHP модуль MySQLi, но он не установлен."; $a->strings["Error: mb_string PHP module required but not installed."] = "Ошибка: необходим PHP модуль mb_string, но он не установлен."; +$a->strings["Error: mcrypt PHP module required but not installed."] = ""; +$a->strings["Function mcrypt_create_iv() is not defined. This is needed to enable RINO2 encryption layer."] = ""; +$a->strings["mcrypt_create_iv() function"] = ""; $a->strings["The web installer needs to be able to create a file called \".htconfig.php\" in the top folder of your web server and it is unable to do so."] = "Веб-инсталлятору требуется создать файл с именем \". htconfig.php\" в верхней папке веб-сервера, но он не в состоянии это сделать."; $a->strings["This is most often a permission setting, as the web server may not be able to write files in your folder - even if you can."] = "Это наиболее частые параметры разрешений, когда веб-сервер не может записать файлы в папке - даже если вы можете."; $a->strings["At the end of this procedure, we will give you a text to save in a file named .htconfig.php in your Friendica top folder."] = "В конце этой процедуры, мы дадим вам текст, для сохранения в файле с именем .htconfig.php в корневой папке, где установлена Friendica."; @@ -780,6 +910,8 @@ $a->strings["Note: as a security measure, you should give the web server write a $a->strings["view/smarty3 is writable"] = "view/smarty3 доступен для записи"; $a->strings["Url rewrite in .htaccess is not working. Check your server configuration."] = "Url rewrite в .htaccess не работает. Проверьте конфигурацию вашего сервера.."; $a->strings["Url rewrite is working"] = "Url rewrite работает"; +$a->strings["ImageMagick PHP extension is installed"] = ""; +$a->strings["ImageMagick supports GIF"] = ""; $a->strings["The database configuration file \".htconfig.php\" could not be written. Please use the enclosed text to create a configuration file in your web server root."] = "Файл конфигурации базы данных \".htconfig.php\" не могла быть записан. Пожалуйста, используйте приложенный текст, чтобы создать конфигурационный файл в корневом каталоге веб-сервера."; $a->strings["

            What next

            "] = "

            Что далее

            "; $a->strings["IMPORTANT: You will need to [manually] setup a scheduled task for the poller."] = "ВАЖНО: Вам нужно будет [вручную] установить запланированное задание для регистратора."; @@ -795,21 +927,19 @@ $a->strings["%1\$s welcomes %2\$s"] = "%1\$s добро пожаловать %2\ $a->strings["Welcome to %s"] = "Добро пожаловать на %s!"; $a->strings["Sorry, maybe your upload is bigger than the PHP configuration allows"] = ""; $a->strings["Or - did you try to upload an empty file?"] = ""; -$a->strings["File exceeds size limit of %d"] = "Файл превышает предельный размер %d"; +$a->strings["File exceeds size limit of %s"] = ""; $a->strings["File upload failed."] = "Загрузка файла не удалась."; -$a->strings["Profile Match"] = "Похожие профили"; $a->strings["No keywords to match. Please add keywords to your default profile."] = "Нет соответствующих ключевых слов. Пожалуйста, добавьте ключевые слова для вашего профиля по умолчанию."; $a->strings["is interested in:"] = "интересуется:"; -$a->strings["Connect"] = "Подключить"; +$a->strings["Profile Match"] = "Похожие профили"; $a->strings["link"] = "ссылка"; $a->strings["Not available."] = "Недоступно."; $a->strings["Community"] = "Сообщество"; $a->strings["No results."] = "Нет результатов."; $a->strings["everybody"] = "каждый"; -$a->strings["Additional features"] = "Дополнительные возможности"; -$a->strings["Display"] = ""; -$a->strings["Social Networks"] = ""; -$a->strings["Delegations"] = ""; +$a->strings["Display"] = "Внешний вид"; +$a->strings["Social Networks"] = "Социальные сети"; +$a->strings["Delegations"] = "Делегирование"; $a->strings["Connected apps"] = "Подключенные приложения"; $a->strings["Export personal data"] = "Экспорт личных данных"; $a->strings["Remove account"] = "Удалить аккаунт"; @@ -843,14 +973,20 @@ $a->strings["No name"] = "Нет имени"; $a->strings["Remove authorization"] = "Удалить авторизацию"; $a->strings["No Plugin settings configured"] = "Нет сконфигурированных настроек плагина"; $a->strings["Plugin Settings"] = "Настройки плагина"; -$a->strings["Off"] = ""; -$a->strings["On"] = ""; $a->strings["Additional Features"] = "Дополнительные возможности"; +$a->strings["General Social Media Settings"] = ""; +$a->strings["Disable intelligent shortening"] = ""; +$a->strings["Normally the system tries to find the best link to add to shortened posts. If this option is enabled then every shortened post will always point to the original friendica post."] = ""; +$a->strings["Automatically follow any GNU Social (OStatus) followers/mentioners"] = ""; +$a->strings["If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user."] = ""; +$a->strings["Your legacy GNU Social account"] = ""; +$a->strings["If you enter your old GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done."] = ""; +$a->strings["Repair OStatus subscriptions"] = ""; $a->strings["Built-in support for %s connectivity is %s"] = "Встроенная поддержка для %s подключение %s"; $a->strings["Diaspora"] = "Diaspora"; $a->strings["enabled"] = "подключено"; $a->strings["disabled"] = "отключено"; -$a->strings["StatusNet"] = "StatusNet"; +$a->strings["GNU Social (OStatus)"] = ""; $a->strings["Email access is disabled on this site."] = "Доступ эл. почты отключен на этом сайте."; $a->strings["Email/Mailbox Setup"] = "Настройка эл. почты / почтового ящика"; $a->strings["If you wish to communicate with email contacts using this service (optional), please specify how to connect to your mailbox."] = "Если вы хотите общаться с Email контактами, используя этот сервис (по желанию), пожалуйста, уточните, как подключиться к вашему почтовому ящику."; @@ -871,14 +1007,17 @@ $a->strings["Display Settings"] = "Параметры дисплея"; $a->strings["Display Theme:"] = "Показать тему:"; $a->strings["Mobile Theme:"] = "Мобильная тема:"; $a->strings["Update browser every xx seconds"] = "Обновление браузера каждые хх секунд"; -$a->strings["Minimum of 10 seconds, no maximum"] = "Минимум 10 секунд, максимума нет"; +$a->strings["Minimum of 10 seconds. Enter -1 to disable it."] = ""; $a->strings["Number of items to display per page:"] = "Количество элементов, отображаемых на одной странице:"; $a->strings["Maximum of 100 items"] = "Максимум 100 элементов"; $a->strings["Number of items to display per page when viewed from mobile device:"] = "Количество элементов на странице, когда просмотр осуществляется с мобильных устройств:"; $a->strings["Don't show emoticons"] = "не показывать emoticons"; +$a->strings["Calendar"] = ""; +$a->strings["Beginning of week:"] = ""; $a->strings["Don't show notices"] = ""; $a->strings["Infinite scroll"] = "Бесконечная прокрутка"; $a->strings["Automatic updates only at the top of the network page"] = ""; +$a->strings["Theme settings"] = "Настройки темы"; $a->strings["User Types"] = ""; $a->strings["Community Types"] = ""; $a->strings["Normal Account Page"] = "Стандартная страница аккаунта"; @@ -894,7 +1033,6 @@ $a->strings["Private forum - approved members only"] = "Приватный фо $a->strings["OpenID:"] = "OpenID:"; $a->strings["(Optional) Allow this OpenID to login to this account."] = "(Необязательно) Разрешить этому OpenID входить в этот аккаунт"; $a->strings["Publish your default profile in your local site directory?"] = "Публиковать ваш профиль по умолчанию в вашем локальном каталоге на сайте?"; -$a->strings["No"] = "Нет"; $a->strings["Publish your default profile in the global social directory?"] = "Публиковать ваш профиль по умолчанию в глобальном социальном каталоге?"; $a->strings["Hide your contact/friend list from viewers of your default profile?"] = "Скрывать ваш список контактов/друзей от посетителей вашего профиля по умолчанию?"; $a->strings["Hide your profile details from unknown viewers?"] = "Скрыть данные профиля из неизвестных зрителей?"; @@ -904,7 +1042,7 @@ $a->strings["Allow friends to tag your posts?"] = "Разрешить друзь $a->strings["Allow us to suggest you as a potential friend to new members?"] = "Позвольть предлогать Вам потенциальных друзей?"; $a->strings["Permit unknown people to send you private mail?"] = "Разрешить незнакомым людям отправлять вам личные сообщения?"; $a->strings["Profile is not published."] = "Профиль не публикуется."; -$a->strings["Your Identity Address is"] = "Ваш идентификационный адрес"; +$a->strings["Your Identity Address is '%s' or '%s'."] = ""; $a->strings["Automatically expire posts after this many days:"] = "Автоматическое истекание срока действия сообщения после стольких дней:"; $a->strings["If empty, posts will not expire. Expired posts will be deleted"] = "Если пусто, срок действия сообщений не будет ограничен. Сообщения с истекшим сроком действия будут удалены"; $a->strings["Advanced expiration settings"] = "Настройки расширенного окончания срока действия"; @@ -915,7 +1053,7 @@ $a->strings["Expire starred posts:"] = "Срок хранения усеянны $a->strings["Expire photos:"] = "Срок хранения фотографий:"; $a->strings["Only expire posts by others:"] = "Только устаревшие посты других:"; $a->strings["Account Settings"] = "Настройки аккаунта"; -$a->strings["Password Settings"] = "Настройка пароля"; +$a->strings["Password Settings"] = "Смена пароля"; $a->strings["New Password:"] = "Новый пароль:"; $a->strings["Confirm:"] = "Подтвердите:"; $a->strings["Leave password fields blank unless changing"] = "Оставьте поля пароля пустыми, если он не изменяется"; @@ -926,6 +1064,8 @@ $a->strings["Basic Settings"] = "Основные параметры"; $a->strings["Full Name:"] = "Полное имя:"; $a->strings["Email Address:"] = "Адрес электронной почты:"; $a->strings["Your Timezone:"] = "Ваш часовой пояс:"; +$a->strings["Your Language:"] = ""; +$a->strings["Set the language we use to show you friendica interface and to send you emails"] = ""; $a->strings["Default Post Location:"] = "Местонахождение по умолчанию:"; $a->strings["Use Browser Location:"] = "Использовать определение местоположения браузером:"; $a->strings["Security and Privacy Settings"] = "Параметры безопасности и конфиденциальности"; @@ -953,11 +1093,13 @@ $a->strings["You receive a private message"] = "Вы получаете личн $a->strings["You receive a friend suggestion"] = "Вы полулили предложение о добавлении в друзья"; $a->strings["You are tagged in a post"] = "Вы отмечены в посте"; $a->strings["You are poked/prodded/etc. in a post"] = ""; +$a->strings["Activate desktop notifications"] = ""; +$a->strings["Show desktop popup on new notifications"] = ""; $a->strings["Text-only notification emails"] = ""; $a->strings["Send text only notification emails, without the html part"] = ""; -$a->strings["Advanced Account/Page Type Settings"] = "Расширенные настройки типа аккаунта/страницы"; +$a->strings["Advanced Account/Page Type Settings"] = "Расширенные настройки учётной записи"; $a->strings["Change the behaviour of this account for special situations"] = "Измените поведение этого аккаунта в специальных ситуациях"; -$a->strings["Relocate"] = "Переместить"; +$a->strings["Relocate"] = "Перемещение"; $a->strings["If you have moved this profile from another server, and some of your contacts don't receive your updates, try pushing this button."] = "Если вы переместили эту анкету с другого сервера, и некоторые из ваших контактов не получили ваши обновления, попробуйте нажать эту кнопку."; $a->strings["Resend relocate message to contacts"] = "Отправить перемещённые сообщения контактам"; $a->strings["This introduction has already been accepted."] = "Этот запрос был уже принят."; @@ -968,6 +1110,7 @@ $a->strings["%d required parameter was not found at the given location"] = array 0 => "%d требуемый параметр не был найден в заданном месте", 1 => "%d требуемых параметров не были найдены в заданном месте", 2 => "%d требуемых параметров не были найдены в заданном месте", + 3 => "%d требуемых параметров не были найдены в заданном месте", ); $a->strings["Introduction complete."] = "Запрос создан."; $a->strings["Unrecoverable protocol error."] = "Неисправимая ошибка протокола."; @@ -978,32 +1121,28 @@ $a->strings["Friends are advised to please try again in 24 hours."] = "Друз $a->strings["Invalid locator"] = "Недопустимый локатор"; $a->strings["Invalid email address."] = "Неверный адрес электронной почты."; $a->strings["This account has not been configured for email. Request failed."] = "Этот аккаунт не настроен для электронной почты. Запрос не удался."; -$a->strings["Unable to resolve your name at the provided location."] = "Не удается установить ваше имя на предложенном местоположении."; $a->strings["You have already introduced yourself here."] = "Вы уже ввели информацию о себе здесь."; $a->strings["Apparently you are already friends with %s."] = "Похоже, что вы уже друзья с %s."; $a->strings["Invalid profile URL."] = "Неверный URL профиля."; $a->strings["Disallowed profile URL."] = "Запрещенный URL профиля."; $a->strings["Your introduction has been sent."] = "Ваш запрос отправлен."; +$a->strings["Remote subscription can't be done for your network. Please subscribe directly on your system."] = ""; $a->strings["Please login to confirm introduction."] = "Для подтверждения запроса войдите пожалуйста с паролем."; $a->strings["Incorrect identity currently logged in. Please login to this profile."] = "Неверно идентифицирован вход. Пожалуйста, войдите в этот профиль."; +$a->strings["Confirm"] = "Подтвердить"; $a->strings["Hide this contact"] = "Скрыть этот контакт"; $a->strings["Welcome home %s."] = "Добро пожаловать домой, %s!"; $a->strings["Please confirm your introduction/connection request to %s."] = "Пожалуйста, подтвердите краткую информацию / запрос на подключение к %s."; -$a->strings["Confirm"] = "Подтвердить"; $a->strings["Please enter your 'Identity Address' from one of the following supported communications networks:"] = "Пожалуйста, введите ваш 'идентификационный адрес' одной из следующих поддерживаемых социальных сетей:"; -$a->strings["If you are not yet a member of the free social web, follow this link to find a public Friendica site and join us today."] = "Если вы еще не являетесь членом свободной социальной сети, перейдите по этой ссылке, чтобы найти публичный сервер Friendica и присоединиться к нам сейчас ."; +$a->strings["If you are not yet a member of the free social web, follow this link to find a public Friendica site and join us today."] = ""; $a->strings["Friend/Connection Request"] = "Запрос в друзья / на подключение"; $a->strings["Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca"] = "Примеры: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca"; -$a->strings["Please answer the following:"] = "Пожалуйста, ответьте следующее:"; -$a->strings["Does %s know you?"] = "%s знает вас?"; -$a->strings["Add a personal note:"] = "Добавить личную заметку:"; $a->strings["Friendica"] = "Friendica"; $a->strings["StatusNet/Federated Social Web"] = "StatusNet / Federated Social Web"; $a->strings[" - please do not use this form. Instead, enter %s into your Diaspora search bar."] = "Участники сети Diaspora: пожалуйста, не пользуйтесь этой формой. Вместо этого введите %s в строке поиска Diaspora"; -$a->strings["Your Identity Address:"] = "Ваш идентификационный адрес:"; -$a->strings["Submit Request"] = "Отправить запрос"; $a->strings["Registration successful. Please check your email for further instructions."] = "Регистрация успешна. Пожалуйста, проверьте свою электронную почту для получения дальнейших инструкций."; $a->strings["Failed to send email message. Here your accout details:
            login: %s
            password: %s

            You can change your password after login."] = ""; +$a->strings["Registration successful."] = ""; $a->strings["Your registration can not be processed."] = "Ваша регистрация не может быть обработана."; $a->strings["Your registration is pending approval by the site owner."] = "Ваша регистрация в ожидании одобрения владельцем сайта."; $a->strings["This site has exceeded the number of allowed daily account registrations. Please try again tomorrow."] = "Этот сайт превысил допустимое количество ежедневных регистраций. Пожалуйста, повторите попытку завтра."; @@ -1013,24 +1152,27 @@ $a->strings["Your OpenID (optional): "] = "Ваш OpenID (необязатель $a->strings["Include your profile in member directory?"] = "Включить ваш профиль в каталог участников?"; $a->strings["Membership on this site is by invitation only."] = "Членство на сайте только по приглашению."; $a->strings["Your invitation ID: "] = "ID вашего приглашения:"; -$a->strings["Your Full Name (e.g. Joe Smith): "] = "Ваше полное имя (например, Joe Smith): "; +$a->strings["Your Full Name (e.g. Joe Smith, real or real-looking): "] = ""; $a->strings["Your Email Address: "] = "Ваш адрес электронной почты: "; +$a->strings["Leave empty for an auto generated password."] = ""; $a->strings["Choose a profile nickname. This must begin with a text character. Your profile address on this site will then be 'nickname@\$sitename'."] = "Выбор псевдонима профиля. Он должен начинаться с буквы. Адрес вашего профиля на данном сайте будет в этом случае 'nickname@\$sitename'."; $a->strings["Choose a nickname: "] = "Выберите псевдоним: "; $a->strings["Register"] = "Регистрация"; $a->strings["Import"] = "Импорт"; $a->strings["Import your profile to this friendica instance"] = "Импорт своего профиля в этот экземпляр friendica"; $a->strings["System down for maintenance"] = "Система закрыта на техническое обслуживание"; +$a->strings["Only logged in users are permitted to perform a search."] = ""; +$a->strings["Too Many Requests"] = ""; +$a->strings["Only one search per minute is permitted for not logged in users."] = ""; $a->strings["Search"] = "Поиск"; -$a->strings["Global Directory"] = "Глобальный каталог"; -$a->strings["Find on this site"] = "Найти на этом сайте"; -$a->strings["Site Directory"] = "Каталог сайта"; -$a->strings["Age: "] = "Возраст: "; -$a->strings["Gender: "] = "Пол: "; -$a->strings["Gender:"] = "Пол:"; +$a->strings["Items tagged with: %s"] = ""; +$a->strings["Search results for: %s"] = ""; $a->strings["Status:"] = "Статус:"; $a->strings["Homepage:"] = "Домашняя страничка:"; -$a->strings["About:"] = "О себе:"; +$a->strings["Global Directory"] = "Глобальный каталог"; +$a->strings["Find on this site"] = "Найти на этом сайте"; +$a->strings["Finding:"] = ""; +$a->strings["Site Directory"] = "Каталог сайта"; $a->strings["No entries (some entries may be hidden)."] = "Нет записей (некоторые записи могут быть скрыты)."; $a->strings["No potential page delegates located."] = ""; $a->strings["Delegate Page Management"] = "Делегировать управление страницей"; @@ -1040,7 +1182,6 @@ $a->strings["Existing Page Delegates"] = "Существующие уполно $a->strings["Potential Delegates"] = "Возможные доверенные лица"; $a->strings["Add"] = "Добавить"; $a->strings["No entries."] = "Нет записей."; -$a->strings["Common Friends"] = "Общие друзья"; $a->strings["No contacts in common."] = "Нет общих контактов."; $a->strings["Export account"] = "Экспорт аккаунта"; $a->strings["Export your account info and contacts. Use this to make a backup of your account and/or to move it to another server."] = "Экспорт ваших регистрационных данные и контактов. Используйте, чтобы создать резервную копию вашего аккаунта и/или переместить его на другой сервер."; @@ -1050,9 +1191,9 @@ $a->strings["%1\$s is currently %2\$s"] = ""; $a->strings["Mood"] = "Настроение"; $a->strings["Set your current mood and tell your friends"] = "Напишите о вашем настроении и расскажите своим друзьям"; $a->strings["Do you really want to delete this suggestion?"] = "Вы действительно хотите удалить это предложение?"; -$a->strings["Friend Suggestions"] = "Предложения друзей"; $a->strings["No suggestions available. If this is a new site, please try again in 24 hours."] = "Нет предложений. Если это новый сайт, пожалуйста, попробуйте снова через 24 часа."; $a->strings["Ignore/Hide"] = "Проигнорировать/Скрыть"; +$a->strings["Friend Suggestions"] = "Предложения друзей"; $a->strings["Profile deleted."] = "Профиль удален."; $a->strings["Profile-"] = "Профиль-"; $a->strings["New profile created."] = "Новый профиль создан."; @@ -1079,6 +1220,7 @@ $a->strings[" - Visit %1\$s's %2\$s"] = " - Посетить профиль %1\$ $a->strings["%1\$s has an updated %2\$s, changing %3\$s."] = ""; $a->strings["Hide contacts and friends:"] = ""; $a->strings["Hide your contact/friend list from viewers of this profile?"] = "Скрывать ваш список контактов / друзей от посетителей этого профиля?"; +$a->strings["Show more profile fields:"] = ""; $a->strings["Edit Profile Details"] = "Редактировать детали профиля"; $a->strings["Change Profile Photo"] = "Изменить фото профиля"; $a->strings["View this profile"] = "Просмотреть этот профиль"; @@ -1094,7 +1236,7 @@ $a->strings["Profile Name:"] = "Имя профиля:"; $a->strings["Your Full Name:"] = "Ваше полное имя:"; $a->strings["Title/Description:"] = "Заголовок / Описание:"; $a->strings["Your Gender:"] = "Ваш пол:"; -$a->strings["Birthday (%s):"] = "День рождения (%s):"; +$a->strings["Birthday :"] = ""; $a->strings["Street Address:"] = "Адрес:"; $a->strings["Locality/City:"] = "Город / Населенный пункт:"; $a->strings["Postal/Zip Code:"] = "Почтовый индекс:"; @@ -1127,6 +1269,7 @@ $a->strings["Love/romance"] = "Любовь / романтика"; $a->strings["Work/employment"] = "Работа / занятость"; $a->strings["School/education"] = "Школа / образование"; $a->strings["This is your public profile.
            It may be visible to anybody using the internet."] = "Это ваш публичный профиль.
            Он может быть виден каждому через Интернет."; +$a->strings["Age: "] = "Возраст: "; $a->strings["Edit/Manage Profiles"] = "Редактировать профиль"; $a->strings["Change profile photo"] = "Изменить фото профиля"; $a->strings["Create New Profile"] = "Создать новый профиль"; @@ -1136,7 +1279,7 @@ $a->strings["Edit visibility"] = "Редактировать видимость" $a->strings["Item not found"] = "Элемент не найден"; $a->strings["Edit post"] = "Редактировать сообщение"; $a->strings["upload photo"] = "загрузить фото"; -$a->strings["Attach file"] = "Приложить файл"; +$a->strings["Attach file"] = "Прикрепить файл"; $a->strings["attach file"] = "приложить файл"; $a->strings["web link"] = "веб-ссылка"; $a->strings["Insert video link"] = "Вставить ссылку видео"; @@ -1157,6 +1300,7 @@ $a->strings["This is Friendica, version"] = "Это Friendica, версия"; $a->strings["running at web location"] = "работает на веб-узле"; $a->strings["Please visit Friendica.com to learn more about the Friendica project."] = "Пожалуйста, посетите сайт Friendica.com, чтобы узнать больше о проекте Friendica."; $a->strings["Bug reports and issues: please visit"] = "Отчет об ошибках и проблемах: пожалуйста, посетите"; +$a->strings["the bugtracker at github"] = ""; $a->strings["Suggestions, praise, donations, etc. - please email \"Info\" at Friendica - dot com"] = "Предложения, похвала, пожертвования? Пишите на \"info\" на Friendica - точка com"; $a->strings["Installed plugins/addons/apps:"] = "Установленные плагины / добавки / приложения:"; $a->strings["No installed plugins/addons/apps"] = "Нет установленных плагинов / добавок / приложений"; @@ -1179,6 +1323,8 @@ $a->strings["poke, prod or do other things to somebody"] = ""; $a->strings["Recipient"] = "Получатель"; $a->strings["Choose what you wish to do to recipient"] = "Выберите действия для получателя"; $a->strings["Make this post private"] = "Сделать эту запись личной"; +$a->strings["Resubscribing to OStatus contacts"] = ""; +$a->strings["Error"] = "Ошибка"; $a->strings["Total invitation limit exceeded."] = "Превышен общий лимит приглашений."; $a->strings["%s : Not a valid email address."] = "%s: Неверный адрес электронной почты."; $a->strings["Please join us on Friendica"] = "Пожалуйста, присоединяйтесь к нам на Friendica"; @@ -1188,6 +1334,7 @@ $a->strings["%d message sent."] = array( 0 => "%d сообщение отправлено.", 1 => "%d сообщений отправлено.", 2 => "%d сообщений отправлено.", + 3 => "%d сообщений отправлено.", ); $a->strings["You have no more invitations available"] = "У вас нет больше приглашений"; $a->strings["Visit %s for a list of public sites that you can join. Friendica members on other sites can all connect with each other, as well as with members of many other social networks."] = "Посетите %s со списком общедоступных сайтов, к которым вы можете присоединиться. Все участники Friendica на других сайтах могут соединиться друг с другом, а также с участниками многих других социальных сетей."; @@ -1201,7 +1348,7 @@ $a->strings["You will need to supply this invitation code: \$invite_code"] = "В $a->strings["Once you have registered, please connect with me via my profile page at:"] = "После того как вы зарегистрировались, пожалуйста, свяжитесь со мной через мою страницу профиля по адресу:"; $a->strings["For more information about the Friendica project and why we feel it is important, please visit http://friendica.com"] = "Для получения более подробной информации о проекте Friendica, пожалуйста, посетите http://friendica.com"; $a->strings["Photo Albums"] = "Фотоальбомы"; -$a->strings["Contact Photos"] = "Фотографии контакта"; +$a->strings["Recent Photos"] = "Последние фото"; $a->strings["Upload New Photos"] = "Загрузить новые фото"; $a->strings["Contact information unavailable"] = "Информация о контакте недоступна"; $a->strings["Album not found."] = "Альбом не найден."; @@ -1211,7 +1358,6 @@ $a->strings["Delete Photo"] = "Удалить фото"; $a->strings["Do you really want to delete this photo?"] = "Вы действительно хотите удалить эту фотографию?"; $a->strings["%1\$s was tagged in %2\$s by %3\$s"] = "%1\$s отмечен/а/ в %2\$s by %3\$s"; $a->strings["a photo"] = "фото"; -$a->strings["Image exceeds size limit of "] = "Размер фото превышает лимит "; $a->strings["Image file is empty."] = "Файл изображения пуст."; $a->strings["No photos selected"] = "Не выбрано фото."; $a->strings["You have used %1$.2f Mbytes of %2$.2f Mbytes photo storage."] = "Вы использовали %1$.2f мегабайт из %2$.2f возможных для хранения фотографий."; @@ -1234,23 +1380,33 @@ $a->strings["Use as profile photo"] = "Использовать как фото $a->strings["View Full Size"] = "Просмотреть полный размер"; $a->strings["Tags: "] = "Ключевые слова: "; $a->strings["[Remove any tag]"] = "[Удалить любое ключевое слово]"; -$a->strings["Rotate CW (right)"] = "Поворот по часовой стрелке (направо)"; -$a->strings["Rotate CCW (left)"] = "Поворот против часовой стрелки (налево)"; $a->strings["New album name"] = "Название нового альбома"; $a->strings["Caption"] = "Подпись"; $a->strings["Add a Tag"] = "Добавить ключевое слово (таг)"; $a->strings["Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"] = "Пример: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"; +$a->strings["Do not rotate"] = ""; +$a->strings["Rotate CW (right)"] = "Поворот по часовой стрелке (направо)"; +$a->strings["Rotate CCW (left)"] = "Поворот против часовой стрелки (налево)"; $a->strings["Private photo"] = "Личное фото"; $a->strings["Public photo"] = "Публичное фото"; $a->strings["Share"] = "Поделиться"; -$a->strings["Recent Photos"] = "Последние фото"; +$a->strings["Attending"] = array( + 0 => "", + 1 => "", + 2 => "", + 3 => "", +); +$a->strings["Not attending"] = ""; +$a->strings["Might attend"] = ""; +$a->strings["Map"] = "Карта"; +$a->strings["Not Extended"] = ""; $a->strings["Account approved."] = "Аккаунт утвержден."; $a->strings["Registration revoked for %s"] = "Регистрация отменена для %s"; $a->strings["Please login."] = "Пожалуйста, войдите с паролем."; $a->strings["Move account"] = "Удалить аккаунт"; $a->strings["You can import an account from another Friendica server."] = "Вы можете импортировать учетную запись с другого сервера Friendica."; $a->strings["You need to export your account from the old server and upload it here. We will recreate your old account here with all your contacts. We will try also to inform your friends that you moved here."] = "Вам нужно экспортировать свой ​​аккаунт со старого сервера и загрузить его сюда. Мы восстановим ваш ​​старый аккаунт здесь со всеми вашими контактами. Мы постараемся также сообщить друзьям, что вы переехали сюда."; -$a->strings["This feature is experimental. We can't import contacts from the OStatus network (statusnet/identi.ca) or from Diaspora"] = "Это экспериментальная функция. Мы не можем импортировать контакты из сети OStatus (StatusNet / identi.ca) или из Diaspora"; +$a->strings["This feature is experimental. We can't import contacts from the OStatus network (GNU Social/Statusnet) or from Diaspora"] = ""; $a->strings["Account file"] = "Файл аккаунта"; $a->strings["To export your account, go to \"Settings->Export your personal data\" and select \"Export account\""] = "Для экспорта аккаунта, перейдите в \"Настройки->Экспортировать ваши данные\" и выберите \"Экспорт аккаунта\""; $a->strings["Item not available."] = "Пункт не доступен."; @@ -1269,31 +1425,13 @@ $a->strings["Website Terms of Service"] = "Правила сайта"; $a->strings["terms of service"] = "правила"; $a->strings["Website Privacy Policy"] = "Политика конфиденциальности сервера"; $a->strings["privacy policy"] = "политика конфиденциальности"; -$a->strings["Requested account is not available."] = "Запрашиваемый профиль недоступен."; -$a->strings["Edit profile"] = "Редактировать профиль"; -$a->strings["Message"] = "Сообщение"; -$a->strings["Profiles"] = "Профили"; -$a->strings["Manage/edit profiles"] = "Управление / редактирование профилей"; -$a->strings["Network:"] = ""; -$a->strings["g A l F d"] = "g A l F d"; -$a->strings["F d"] = "F d"; -$a->strings["[today]"] = "[сегодня]"; -$a->strings["Birthday Reminders"] = "Напоминания о днях рождения"; -$a->strings["Birthdays this week:"] = "Дни рождения на этой неделе:"; -$a->strings["[No description]"] = "[без описания]"; -$a->strings["Event Reminders"] = "Напоминания о мероприятиях"; -$a->strings["Events this week:"] = "Мероприятия на этой неделе:"; -$a->strings["Status"] = "Статус"; -$a->strings["Status Messages and Posts"] = "Сообщение статуса и посты"; -$a->strings["Profile Details"] = "Детали профиля"; -$a->strings["Videos"] = "Видео"; -$a->strings["Events and Calendar"] = "Календарь и события"; -$a->strings["Only You Can See This"] = "Только вы можете это видеть"; $a->strings["This entry was edited"] = "Эта запись была отредактирована"; +$a->strings["I will attend"] = ""; +$a->strings["I will not attend"] = ""; +$a->strings["I might attend"] = ""; $a->strings["ignore thread"] = ""; $a->strings["unignore thread"] = ""; $a->strings["toggle ignore status"] = ""; -$a->strings["ignored"] = ""; $a->strings["Categories:"] = "Категории:"; $a->strings["Filed under:"] = "В рубрике:"; $a->strings["via"] = "через"; @@ -1311,10 +1449,10 @@ $a->strings["%d invitation available"] = array( 0 => "%d приглашение доступно", 1 => "%d приглашений доступно", 2 => "%d приглашений доступно", + 3 => "%d приглашений доступно", ); $a->strings["Find People"] = "Поиск людей"; $a->strings["Enter name or interest"] = "Введите имя или интерес"; -$a->strings["Connect/Follow"] = "Подключиться/Следовать"; $a->strings["Examples: Robert Morgenstein, Fishing"] = "Примеры: Роберт Morgenstein, Рыбалка"; $a->strings["Similar Interests"] = "Похожие интересы"; $a->strings["Random Profile"] = "Случайный профиль"; @@ -1324,19 +1462,29 @@ $a->strings["All Networks"] = "Все сети"; $a->strings["Saved Folders"] = "Сохранённые папки"; $a->strings["Everything"] = "Всё"; $a->strings["Categories"] = "Категории"; +$a->strings["%d contact in common"] = array( + 0 => "%d Контакт", + 1 => "%d Контактов", + 2 => "%d Контактов", + 3 => "%d Контактов", +); $a->strings["General Features"] = "Основные возможности"; $a->strings["Multiple Profiles"] = "Несколько профилей"; $a->strings["Ability to create multiple profiles"] = "Возможность создания нескольких профилей"; -$a->strings["Post Composition Features"] = ""; +$a->strings["Photo Location"] = ""; +$a->strings["Photo metadata is normally stripped. This extracts the location (if present) prior to stripping metadata and links it to a map."] = ""; +$a->strings["Post Composition Features"] = "Составление сообщений"; $a->strings["Richtext Editor"] = "Редактор RTF"; $a->strings["Enable richtext editor"] = "Включить редактор RTF"; -$a->strings["Post Preview"] = "предварительный просмотр"; +$a->strings["Post Preview"] = "Предварительный просмотр"; $a->strings["Allow previewing posts and comments before publishing them"] = "Разрешить предпросмотр сообщения и комментария перед их публикацией"; $a->strings["Auto-mention Forums"] = ""; $a->strings["Add/remove mention when a fourm page is selected/deselected in ACL window."] = ""; $a->strings["Network Sidebar Widgets"] = "Виджет боковой панели \"Сеть\""; $a->strings["Search by Date"] = "Поиск по датам"; $a->strings["Ability to select posts by date ranges"] = "Возможность выбора постов по диапазону дат"; +$a->strings["List Forums"] = ""; +$a->strings["Enable widget to display the forums your are connected with"] = ""; $a->strings["Group Filter"] = "Фильтр групп"; $a->strings["Enable widget to display Network posts only from selected group"] = "Включить виджет для отображения сообщений сети только от выбранной группы"; $a->strings["Network Filter"] = "Фильтр сети"; @@ -1365,6 +1513,8 @@ $a->strings["Star Posts"] = "Популярные посты"; $a->strings["Ability to mark special posts with a star indicator"] = "Возможность отметить специальные сообщения индикатором популярности"; $a->strings["Mute Post Notifications"] = ""; $a->strings["Ability to mute notifications for a thread"] = ""; +$a->strings["Advanced Profile Settings"] = "Расширенные настройки профиля"; +$a->strings["Show visitors public community forums at the Advanced Profile Page"] = ""; $a->strings["Connect URL missing."] = "Connect-URL отсутствует."; $a->strings["This site is not configured to allow communications with other networks."] = "Данный сайт не настроен так, чтобы держать связь с другими сетями."; $a->strings["No compatible communication protocols or feeds were discovered."] = "Обнаружены несовместимые протоколы связи или каналы."; @@ -1381,18 +1531,17 @@ $a->strings["A deleted group with this name was revived. Existing item permissio $a->strings["Default privacy group for new contacts"] = "Группа доступа по умолчанию для новых контактов"; $a->strings["Everybody"] = "Каждый"; $a->strings["edit"] = "редактировать"; +$a->strings["Edit groups"] = ""; $a->strings["Edit group"] = "Редактировать группу"; $a->strings["Create a new group"] = "Создать новую группу"; $a->strings["Contacts not in any group"] = "Контакты не состоят в группе"; $a->strings["Miscellaneous"] = "Разное"; -$a->strings["year"] = "год"; -$a->strings["month"] = "мес."; -$a->strings["day"] = "день"; +$a->strings["YYYY-MM-DD or MM-DD"] = ""; $a->strings["never"] = "никогда"; $a->strings["less than a second ago"] = "менее сек. назад"; +$a->strings["year"] = "год"; $a->strings["years"] = "лет"; $a->strings["months"] = "мес."; -$a->strings["week"] = "неделя"; $a->strings["weeks"] = "недель"; $a->strings["days"] = "дней"; $a->strings["hour"] = "час"; @@ -1404,26 +1553,68 @@ $a->strings["seconds"] = "сек."; $a->strings["%1\$d %2\$s ago"] = "%1\$d %2\$s назад"; $a->strings["%s's birthday"] = "день рождения %s"; $a->strings["Happy Birthday %s"] = "С днём рождения %s"; +$a->strings["Requested account is not available."] = "Запрашиваемый профиль недоступен."; +$a->strings["Edit profile"] = "Редактировать профиль"; +$a->strings["Atom feed"] = ""; +$a->strings["Message"] = "Сообщение"; +$a->strings["Profiles"] = "Профили"; +$a->strings["Manage/edit profiles"] = "Управление / редактирование профилей"; +$a->strings["g A l F d"] = "g A l F d"; +$a->strings["F d"] = "F d"; +$a->strings["[today]"] = "[сегодня]"; +$a->strings["Birthday Reminders"] = "Напоминания о днях рождения"; +$a->strings["Birthdays this week:"] = "Дни рождения на этой неделе:"; +$a->strings["[No description]"] = "[без описания]"; +$a->strings["Event Reminders"] = "Напоминания о мероприятиях"; +$a->strings["Events this week:"] = "Мероприятия на этой неделе:"; +$a->strings["j F, Y"] = "j F, Y"; +$a->strings["j F"] = "j F"; +$a->strings["Birthday:"] = "День рождения:"; +$a->strings["Age:"] = "Возраст:"; +$a->strings["for %1\$d %2\$s"] = ""; +$a->strings["Religion:"] = "Религия:"; +$a->strings["Hobbies/Interests:"] = "Хобби / Интересы:"; +$a->strings["Contact information and Social Networks:"] = "Информация о контакте и социальных сетях:"; +$a->strings["Musical interests:"] = "Музыкальные интересы:"; +$a->strings["Books, literature:"] = "Книги, литература:"; +$a->strings["Television:"] = "Телевидение:"; +$a->strings["Film/dance/culture/entertainment:"] = "Кино / Танцы / Культура / Развлечения:"; +$a->strings["Love/Romance:"] = "Любовь / Романтика:"; +$a->strings["Work/employment:"] = "Работа / Занятость:"; +$a->strings["School/education:"] = "Школа / Образование:"; +$a->strings["Forums:"] = ""; +$a->strings["Videos"] = "Видео"; +$a->strings["Events and Calendar"] = "Календарь и события"; +$a->strings["Only You Can See This"] = "Только вы можете это видеть"; +$a->strings["event"] = "мероприятие"; +$a->strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s нравится %3\$s от %2\$s "; +$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s не нравится %3\$s от %2\$s "; +$a->strings["%1\$s is attending %2\$s's %3\$s"] = ""; +$a->strings["%1\$s is not attending %2\$s's %3\$s"] = ""; +$a->strings["%1\$s may attend %2\$s's %3\$s"] = ""; +$a->strings["Post to Email"] = "Отправить на Email"; +$a->strings["Connectors disabled, since \"%s\" is enabled."] = ""; $a->strings["Visible to everybody"] = "Видимо всем"; $a->strings["show"] = "показывать"; $a->strings["don't show"] = "не показывать"; +$a->strings["Close"] = "Закрыть"; $a->strings["[no subject]"] = "[без темы]"; $a->strings["stopped following"] = "остановлено следование"; -$a->strings["Poke"] = ""; $a->strings["View Status"] = "Просмотреть статус"; -$a->strings["View Profile"] = "Просмотреть профиль"; $a->strings["View Photos"] = "Просмотреть фото"; $a->strings["Network Posts"] = "Посты сети"; $a->strings["Edit Contact"] = "Редактировать контакт"; $a->strings["Drop Contact"] = "Удалить контакт"; $a->strings["Send PM"] = "Отправить ЛС"; +$a->strings["Poke"] = ""; $a->strings["Welcome "] = "Добро пожаловать, "; $a->strings["Please upload a profile photo."] = "Пожалуйста, загрузите фотографию профиля."; $a->strings["Welcome back "] = "Добро пожаловать обратно, "; $a->strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = "Ключ формы безопасности неправильный. Вероятно, это произошло потому, что форма была открыта слишком долго (более 3 часов) до её отправки."; -$a->strings["event"] = "мероприятие"; +$a->strings["%1\$s attends %2\$s's %3\$s"] = ""; +$a->strings["%1\$s doesn't attend %2\$s's %3\$s"] = ""; +$a->strings["%1\$s attends maybe %2\$s's %3\$s"] = ""; $a->strings["%1\$s poked %2\$s"] = ""; -$a->strings["poked"] = ""; $a->strings["post/item"] = "пост/элемент"; $a->strings["%1\$s marked %2\$s's %3\$s as favorite"] = "%1\$s пометил %2\$s %3\$s как Фаворит"; $a->strings["remove"] = "удалить"; @@ -1431,24 +1622,58 @@ $a->strings["Delete Selected Items"] = "Удалить выбранные поз $a->strings["Follow Thread"] = ""; $a->strings["%s likes this."] = "%s нравится это."; $a->strings["%s doesn't like this."] = "%s не нравится это."; -$a->strings["%2\$d people like this"] = "%2\$d людям нравится это"; -$a->strings["%2\$d people don't like this"] = "%2\$d людям не нравится это"; +$a->strings["%s attends."] = ""; +$a->strings["%s doesn't attend."] = ""; +$a->strings["%s attends maybe."] = ""; $a->strings["and"] = "и"; $a->strings[", and %d other people"] = ", и %d других чел."; -$a->strings["%s like this."] = "%s нравится это."; -$a->strings["%s don't like this."] = "%s не нравится это."; +$a->strings["%2\$d people like this"] = "%2\$d людям нравится это"; +$a->strings["%s like this."] = ""; +$a->strings["%2\$d people don't like this"] = "%2\$d людям не нравится это"; +$a->strings["%s don't like this."] = ""; +$a->strings["%2\$d people attend"] = ""; +$a->strings["%s attend."] = ""; +$a->strings["%2\$d people don't attend"] = ""; +$a->strings["%s don't attend."] = ""; +$a->strings["%2\$d people anttend maybe"] = ""; +$a->strings["%s anttend maybe."] = ""; $a->strings["Visible to everybody"] = "Видимое всем"; $a->strings["Please enter a video link/URL:"] = "Введите ссылку на видео link/URL:"; $a->strings["Please enter an audio link/URL:"] = "Введите ссылку на аудио link/URL:"; $a->strings["Tag term:"] = ""; $a->strings["Where are you right now?"] = "И где вы сейчас?"; $a->strings["Delete item(s)?"] = "Удалить елемент(ты)?"; -$a->strings["Post to Email"] = "Отправить на Email"; -$a->strings["Connectors disabled, since \"%s\" is enabled."] = ""; $a->strings["permissions"] = "разрешения"; $a->strings["Post to Groups"] = "Пост для групп"; $a->strings["Post to Contacts"] = "Пост для контактов"; $a->strings["Private post"] = "Личное сообщение"; +$a->strings["View all"] = ""; +$a->strings["Like"] = array( + 0 => "", + 1 => "", + 2 => "", + 3 => "", +); +$a->strings["Dislike"] = array( + 0 => "", + 1 => "", + 2 => "", + 3 => "", +); +$a->strings["Not Attending"] = array( + 0 => "", + 1 => "", + 2 => "", + 3 => "", +); +$a->strings["Undecided"] = array( + 0 => "", + 1 => "", + 2 => "", + 3 => "", +); +$a->strings["Forums"] = ""; +$a->strings["External link to forum"] = ""; $a->strings["view full size"] = "посмотреть в полный размер"; $a->strings["newer"] = "новее"; $a->strings["older"] = "старее"; @@ -1456,13 +1681,20 @@ $a->strings["prev"] = "пред."; $a->strings["first"] = "первый"; $a->strings["last"] = "последний"; $a->strings["next"] = "след."; +$a->strings["Loading more entries..."] = ""; +$a->strings["The end"] = ""; $a->strings["No contacts"] = "Нет контактов"; $a->strings["%d Contact"] = array( 0 => "%d контакт", 1 => "%d контактов", 2 => "%d контактов", + 3 => "%d контактов", ); +$a->strings["View Contacts"] = "Просмотр контактов"; +$a->strings["Full Text"] = "Контент"; +$a->strings["Tags"] = "Тэги"; $a->strings["poke"] = "poke"; +$a->strings["poked"] = ""; $a->strings["ping"] = "пинг"; $a->strings["pinged"] = "пингуется"; $a->strings["prod"] = ""; @@ -1493,29 +1725,10 @@ $a->strings["frustrated"] = ""; $a->strings["motivated"] = ""; $a->strings["relaxed"] = ""; $a->strings["surprised"] = ""; -$a->strings["Monday"] = "Понедельник"; -$a->strings["Tuesday"] = "Вторник"; -$a->strings["Wednesday"] = "Среда"; -$a->strings["Thursday"] = "Четверг"; -$a->strings["Friday"] = "Пятница"; -$a->strings["Saturday"] = "Суббота"; -$a->strings["Sunday"] = "Воскресенье"; -$a->strings["January"] = "Январь"; -$a->strings["February"] = "Февраль"; -$a->strings["March"] = "Март"; -$a->strings["April"] = "Апрель"; -$a->strings["May"] = "Май"; -$a->strings["June"] = "Июнь"; -$a->strings["July"] = "Июль"; -$a->strings["August"] = "Август"; -$a->strings["September"] = "Сентябрь"; -$a->strings["October"] = "Октябрь"; -$a->strings["November"] = "Ноябрь"; -$a->strings["December"] = "Декабрь"; $a->strings["bytes"] = "байт"; $a->strings["Click to open/close"] = "Нажмите, чтобы открыть / закрыть"; -$a->strings["default"] = "значение по умолчанию"; -$a->strings["Select an alternate language"] = "Выбор альтернативного языка"; +$a->strings["View on separate page"] = ""; +$a->strings["view on separate page"] = ""; $a->strings["activity"] = "активность"; $a->strings["post"] = "сообщение"; $a->strings["Item filed"] = ""; @@ -1524,8 +1737,6 @@ $a->strings["%2\$s %3\$s"] = ""; $a->strings["%s wrote the following post"] = ""; $a->strings["$1 wrote:"] = "$1 написал:"; $a->strings["Encrypted content"] = "Зашифрованный контент"; -$a->strings["(no subject)"] = "(без темы)"; -$a->strings["noreply"] = "без ответа"; $a->strings["Cannot locate DNS info for database server '%s'"] = "Не могу найти информацию для DNS-сервера базы данных '%s'"; $a->strings["Unknown | Not categorised"] = "Неизвестно | Не определено"; $a->strings["Block immediately"] = "Блокировать немедленно"; @@ -1537,6 +1748,7 @@ $a->strings["Weekly"] = "Еженедельно"; $a->strings["Monthly"] = "Ежемесячно"; $a->strings["OStatus"] = "OStatus"; $a->strings["RSS/Atom"] = "RSS/Atom"; +$a->strings["Facebook"] = "Facebook"; $a->strings["Zot!"] = "Zot!"; $a->strings["LinkedIn"] = "LinkedIn"; $a->strings["XMPP/IM"] = "XMPP/IM"; @@ -1545,33 +1757,18 @@ $a->strings["Google+"] = "Google+"; $a->strings["pump.io"] = "pump.io"; $a->strings["Twitter"] = "Twitter"; $a->strings["Diaspora Connector"] = ""; -$a->strings["Statusnet"] = ""; +$a->strings["GNU Social"] = ""; $a->strings["App.net"] = ""; +$a->strings["Redmatrix"] = ""; $a->strings[" on Last.fm"] = "на Last.fm"; $a->strings["Starts:"] = "Начало:"; $a->strings["Finishes:"] = "Окончание:"; -$a->strings["j F, Y"] = "j F, Y"; -$a->strings["j F"] = "j F"; -$a->strings["Birthday:"] = "День рождения:"; -$a->strings["Age:"] = "Возраст:"; -$a->strings["for %1\$d %2\$s"] = ""; -$a->strings["Tags:"] = "Ключевые слова: "; -$a->strings["Religion:"] = "Религия:"; -$a->strings["Hobbies/Interests:"] = "Хобби / Интересы:"; -$a->strings["Contact information and Social Networks:"] = "Информация о контакте и социальных сетях:"; -$a->strings["Musical interests:"] = "Музыкальные интересы:"; -$a->strings["Books, literature:"] = "Книги, литература:"; -$a->strings["Television:"] = "Телевидение:"; -$a->strings["Film/dance/culture/entertainment:"] = "Кино / Танцы / Культура / Развлечения:"; -$a->strings["Love/Romance:"] = "Любовь / Романтика:"; -$a->strings["Work/employment:"] = "Работа / Занятость:"; -$a->strings["School/education:"] = "Школа / Образование:"; $a->strings["Click here to upgrade."] = "Нажмите для обновления."; $a->strings["This action exceeds the limits set by your subscription plan."] = "Это действие превышает лимиты, установленные вашим тарифным планом."; $a->strings["This action is not available under your subscription plan."] = "Это действие не доступно в соответствии с вашим планом подписки."; -$a->strings["End this session"] = "Конец этой сессии"; -$a->strings["Your posts and conversations"] = "Ваши сообщения и беседы"; -$a->strings["Your profile page"] = "Страница Вашего профиля"; +$a->strings["End this session"] = "Завершить эту сессию"; +$a->strings["Your posts and conversations"] = "Данные вашей учётной записи"; +$a->strings["Your profile page"] = "Информация о вас"; $a->strings["Your photos"] = "Ваши фотографии"; $a->strings["Your videos"] = ""; $a->strings["Your events"] = "Ваши события"; @@ -1588,9 +1785,9 @@ $a->strings["Conversations on this site"] = "Беседы на этом сайт $a->strings["Conversations on the network"] = ""; $a->strings["Directory"] = "Каталог"; $a->strings["People directory"] = "Каталог участников"; -$a->strings["Information"] = ""; +$a->strings["Information"] = "Информация"; $a->strings["Information about this friendica instance"] = ""; -$a->strings["Conversations from your friends"] = "Беседы с друзьями"; +$a->strings["Conversations from your friends"] = "Посты ваших друзей"; $a->strings["Network Reset"] = "Перезагрузка сети"; $a->strings["Load Network page with no filters"] = "Загрузить страницу сети без фильтров"; $a->strings["Friend Requests"] = "Запросы на добавление в список друзей"; @@ -1604,19 +1801,12 @@ $a->strings["Manage other pages"] = "Управление другими стр $a->strings["Account settings"] = "Настройки аккаунта"; $a->strings["Manage/Edit Profiles"] = "Управление/редактирование профилей"; $a->strings["Manage/edit friends and contacts"] = "Управление / редактирование друзей и контактов"; -$a->strings["Site setup and configuration"] = "Установка и конфигурация сайта"; +$a->strings["Site setup and configuration"] = "Конфигурация сайта"; $a->strings["Navigation"] = "Навигация"; $a->strings["Site map"] = "Карта сайта"; -$a->strings["User not found."] = "Пользователь не найден."; $a->strings["Daily posting limit of %d posts reached. The post was rejected."] = ""; $a->strings["Weekly posting limit of %d posts reached. The post was rejected."] = ""; $a->strings["Monthly posting limit of %d posts reached. The post was rejected."] = ""; -$a->strings["There is no status with this id."] = "Нет статуса с таким id."; -$a->strings["There is no conversation with this id."] = ""; -$a->strings["Invalid request."] = ""; -$a->strings["Invalid item."] = ""; -$a->strings["Invalid action. "] = ""; -$a->strings["DB error"] = ""; $a->strings["An invitation is required."] = "Требуется приглашение."; $a->strings["Invitation could not be verified."] = "Приглашение не может быть проверено."; $a->strings["Invalid OpenID url"] = "Неверный URL OpenID"; @@ -1627,17 +1817,20 @@ $a->strings["That doesn't appear to be your full (First Last) name."] = "Каж $a->strings["Your email domain is not among those allowed on this site."] = "Домен вашего адреса электронной почты не относится к числу разрешенных на этом сайте."; $a->strings["Not a valid email address."] = "Неверный адрес электронной почты."; $a->strings["Cannot use that email."] = "Нельзя использовать этот Email."; -$a->strings["Your \"nickname\" can only contain \"a-z\", \"0-9\", \"-\", and \"_\", and must also begin with a letter."] = "Ваш \"ник\" может содержать только \"a-z\", \"0-9\", \"-\", и \"_\", а также должен начинаться с буквы."; +$a->strings["Your \"nickname\" can only contain \"a-z\", \"0-9\" and \"_\"."] = ""; $a->strings["Nickname is already registered. Please choose another."] = "Такой ник уже зарегистрирован. Пожалуйста, выберите другой."; $a->strings["Nickname was once registered here and may not be re-used. Please choose another."] = "Ник уже зарегистрирован на этом сайте и не может быть изменён. Пожалуйста, выберите другой ник."; $a->strings["SERIOUS ERROR: Generation of security keys failed."] = "СЕРЬЕЗНАЯ ОШИБКА: генерация ключей безопасности не удалась."; $a->strings["An error occurred during registration. Please try again."] = "Ошибка при регистрации. Пожалуйста, попробуйте еще раз."; +$a->strings["default"] = "значение по умолчанию"; $a->strings["An error occurred creating your default profile. Please try again."] = "Ошибка создания вашего профиля. Пожалуйста, попробуйте еще раз."; $a->strings["Friends"] = "Друзья"; $a->strings["\n\t\tDear %1\$s,\n\t\t\tThank you for registering at %2\$s. Your account has been created.\n\t"] = ""; $a->strings["\n\t\tThe login details are as follows:\n\t\t\tSite Location:\t%3\$s\n\t\t\tLogin Name:\t%1\$s\n\t\t\tPassword:\t%5\$s\n\n\t\tYou may change your password from your account \"Settings\" page after logging\n\t\tin.\n\n\t\tPlease take a few moments to review the other account settings on that page.\n\n\t\tYou may also wish to add some basic information to your default profile\n\t\t(on the \"Profiles\" page) so that other people can easily find you.\n\n\t\tWe recommend setting your full name, adding a profile photo,\n\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n\t\tperhaps what country you live in; if you do not wish to be more specific\n\t\tthan that.\n\n\t\tWe fully respect your right to privacy, and none of these items are necessary.\n\t\tIf you are new and do not know anybody here, they may help\n\t\tyou to make some new and interesting friends.\n\n\n\t\tThank you and welcome to %2\$s."] = ""; $a->strings["Sharing notification from Diaspora network"] = "Делиться уведомлениями из сети Diaspora"; $a->strings["Attachments:"] = "Вложения:"; +$a->strings["(no subject)"] = "(без темы)"; +$a->strings["noreply"] = "без ответа"; $a->strings["Do you really want to delete this item?"] = "Вы действительно хотите удалить этот элемент?"; $a->strings["Archives"] = "Архивы"; $a->strings["Male"] = "Мужчина"; @@ -1653,7 +1846,6 @@ $a->strings["Hermaphrodite"] = "Гермафродит"; $a->strings["Neuter"] = "Средний род"; $a->strings["Non-specific"] = "Не определен"; $a->strings["Other"] = "Другой"; -$a->strings["Undecided"] = "Не решено"; $a->strings["Males"] = "Мужчины"; $a->strings["Females"] = "Женщины"; $a->strings["Gay"] = "Гей"; @@ -1700,6 +1892,7 @@ $a->strings["Ask me"] = "Спросите меня"; $a->strings["Friendica Notification"] = "Friendica уведомления"; $a->strings["Thank You,"] = "Спасибо,"; $a->strings["%s Administrator"] = "%s администратор"; +$a->strings["%1\$s, %2\$s Administrator"] = ""; $a->strings["%s "] = "%s "; $a->strings["[Friendica:Notify] New mail received at %s"] = "[Friendica: Оповещение] Новое сообщение, пришедшее на %s"; $a->strings["%1\$s sent you a new private message at %2\$s."] = "%1\$s отправил вам новое личное сообщение на %2\$s."; @@ -1743,7 +1936,7 @@ $a->strings["Name:"] = "Имя:"; $a->strings["Photo:"] = "Фото:"; $a->strings["Please visit %s to approve or reject the suggestion."] = "Пожалуйста, посетите %s для подтверждения, или отказа запроса."; $a->strings["[Friendica:Notify] Connection accepted"] = ""; -$a->strings["'%1\$s' has acepted your connection request at %2\$s"] = ""; +$a->strings["'%1\$s' has accepted your connection request at %2\$s"] = ""; $a->strings["%2\$s has accepted your [url=%1\$s]connection request[/url]."] = ""; $a->strings["You are now mutual friends and may exchange status updates, photos, and email\n\twithout restriction."] = ""; $a->strings["Please visit %s if you wish to make any changes to this relationship."] = ""; @@ -1766,10 +1959,10 @@ $a->strings["%d contact not imported"] = array( 0 => "%d контакт не импортирован", 1 => "%d контакты не импортированы", 2 => "%d контакты не импортированы", + 3 => "%d контакты не импортированы", ); $a->strings["Done. You can now login with your username and password"] = "Завершено. Теперь вы можете войти с вашим логином и паролем"; $a->strings["toggle mobile"] = "мобильная версия"; -$a->strings["Theme settings"] = "Настройки темы"; $a->strings["Set resize level for images in posts and comments (width and height)"] = "Установить уровень изменения размера изображений в постах и ​​комментариях (ширина и высота)"; $a->strings["Set font-size for posts and comments"] = "Установить шрифт-размер для постов и комментариев"; $a->strings["Set theme width"] = "Установить ширину темы"; @@ -1800,7 +1993,9 @@ $a->strings["Your personal photos"] = "Ваши личные фотографи $a->strings["Local Directory"] = "Локальный каталог"; $a->strings["Set zoomfactor for Earth Layers"] = "Установить масштаб карты"; $a->strings["Show/hide boxes at right-hand column:"] = "Показать/скрыть блоки в правой колонке:"; +$a->strings["Comma separated list of helper forums"] = ""; $a->strings["Set style"] = ""; +$a->strings["Quick Start"] = "Быстрый запуск"; $a->strings["greenzero"] = ""; $a->strings["purplezero"] = ""; $a->strings["easterbunny"] = ""; diff --git a/view/templates/acl_selector.tpl b/view/templates/acl_selector.tpl index bf9470b64e..7c68a73064 100644 --- a/view/templates/acl_selector.tpl +++ b/view/templates/acl_selector.tpl @@ -29,7 +29,8 @@ $(document).ready(function() { acl = new ACL( baseurl+"/acl", [ {{$allowcid}},{{$allowgid}},{{$denycid}},{{$denygid}} ], - {{$features.aclautomention}} + {{$features.aclautomention}}, + {{if $APP->is_mobile}}true{{else}}false{{/if}} ); } }); diff --git a/view/templates/admin_federation.tpl b/view/templates/admin_federation.tpl index fad87da5b5..ee33df09b2 100644 --- a/view/templates/admin_federation.tpl +++ b/view/templates/admin_federation.tpl @@ -19,7 +19,7 @@ {{/if}}
            - {{foreach $field.4 as $opt=>$val}}{{/foreach}} - {{$field.3}} + {{$field.3}} {{if $field.5}}
            {{/if}}
            diff --git a/view/templates/field_yesno.tpl b/view/templates/field_yesno.tpl index de70c5ae6d..155d0488b3 100644 --- a/view/templates/field_yesno.tpl +++ b/view/templates/field_yesno.tpl @@ -2,7 +2,7 @@
            - + {{if $field.4}}{{$field.4.0}}{{else}}OFF{{/if}} @@ -10,5 +10,5 @@ {{if $field.4}}{{$field.4.1}}{{else}}ON{{/if}}
            - {{$field.3}} + {{$field.3}}
            diff --git a/view/templates/head.tpl b/view/templates/head.tpl index afb7c1df27..31555aae2b 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -2,17 +2,17 @@ - - - - - + + + + + @@ -28,21 +28,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -28,7 +28,9 @@
            module === 'settings' || $a->module === 'message' || $a->module === 'profile') && x($page,'aside')) echo $page['aside']; ?> -
            +
            + +
            @@ -41,4 +43,3 @@ - diff --git a/view/theme/frost-mobile/js/main.js b/view/theme/frost-mobile/js/main.js index d24e41e8c6..3ec2421df2 100644 --- a/view/theme/frost-mobile/js/main.js +++ b/view/theme/frost-mobile/js/main.js @@ -13,7 +13,6 @@ if($(listID).is(":visible")) { $(listID).hide(); $(listID+"-wrapper").show(); - alert($(listID+"-wrapper").attr("id")); } else { $(listID).show(); diff --git a/view/theme/frost-mobile/style.css b/view/theme/frost-mobile/style.css index 24373eda54..36b621b766 100644 --- a/view/theme/frost-mobile/style.css +++ b/view/theme/frost-mobile/style.css @@ -139,6 +139,47 @@ blockquote { margin-right: 5px; } +.btn { + outline: none; + -moz-box-shadow: inset 0px 1px 0px 0px #ffffff; + -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff; + box-shadow: inset 0px 1px 0px 0px #ffffff; + background-color: #ededed; + text-indent: 0; + border: 1px solid #dcdcdc; + display: inline-block; + color: #777777; + padding: 5px 10px; + text-align: center; + border-radius: 8px; +} + +.menu-popup { + width: auto; + border: 2px solid #444444; + background: #FFFFFF; + position: absolute; + margin: 2px 0 0; + display: none; + z-index: 10000; +} + +.menu-popup li a { + display: block; + padding: 2px; +} + +.menu-popup li a:hover { + color: #FFFFFF; + background: #3465A4; + text-decoration: none; +} +ul.menu-popup li.divider { + height: 1px; + margin: 3px 0; + overflow: hidden; + background-color: #2d2d2d; +} /* nav */ @@ -2045,6 +2086,19 @@ input#profile-jot-email { margin-left: 15px; } +#contact-edit-status-wrapper { + padding: 10px; + border: 1px solid #aaa; + border-radius: 8px; +} + +#contact-edit-contact-status { + font-weight: bold; +} +#contact-edit-actions { + float: right; + display: inline-block; +} #contact-edit-wrapper { margin-top: 10px; } @@ -2059,14 +2113,10 @@ input#profile-jot-email { } #contact-edit-last-update-text { - float: left; - clear: left; margin-top: 30px; } #contact-edit-poll-text { - float: left; - clear: left; margin-top: 15px; margin-bottom: 0px; } diff --git a/view/theme/frost-mobile/templates/contact_edit.tpl b/view/theme/frost-mobile/templates/contact_edit.tpl index e6401de606..79dc7da408 100644 --- a/view/theme/frost-mobile/templates/contact_edit.tpl +++ b/view/theme/frost-mobile/templates/contact_edit.tpl @@ -6,12 +6,6 @@ {{$tab_str}} - - - -
            {{$name}}
            {{$name}}
            @@ -20,41 +14,49 @@
            @@ -63,12 +65,6 @@
            - {{if $poll_enabled}} -
            -
            {{$lastupdtext}} {{$last_update}}
            - {{$updpub}} {{$poll_interval}} {{$udnow}} -
            - {{/if}}
            {{include file="field_checkbox.tpl" field=$hidden}} diff --git a/view/theme/frost-mobile/theme.php b/view/theme/frost-mobile/theme.php index beec924934..29a990f7b8 100644 --- a/view/theme/frost-mobile/theme.php +++ b/view/theme/frost-mobile/theme.php @@ -10,7 +10,6 @@ */ function frost_mobile_init(&$a) { - $a->theme_info = array(); $a->sourcename = 'Friendica mobile web'; $a->videowidth = 250; $a->videoheight = 200; diff --git a/view/theme/frost/default.php b/view/theme/frost/default.php index 95a9b1e8c7..c67bdcf20e 100644 --- a/view/theme/frost/default.php +++ b/view/theme/frost/default.php @@ -1,5 +1,5 @@ - + <?php if(x($page,'title')) echo $page['title'] ?> @@ -28,7 +28,9 @@
            -
            +
            + +
            @@ -39,4 +41,3 @@ - diff --git a/view/theme/frost/style.css b/view/theme/frost/style.css index fe07ad2651..82a89f93d2 100644 --- a/view/theme/frost/style.css +++ b/view/theme/frost/style.css @@ -113,6 +113,51 @@ blockquote { .pull-right { float: right } +.btn { + outline: none; + -moz-box-shadow: inset 0px 1px 0px 0px #ffffff; + -webkit-box-shadow: inset 0px 1px 0px 0px #ffffff; + box-shadow: inset 0px 1px 0px 0px #ffffff; + background-color: #ededed; + text-indent: 0; + border: 1px solid #dcdcdc; + display: inline-block; + color: #777777; + padding: 5px 10px; + text-align: center; + border-radius: 8px; +} +a.btn { + text-decoration: none; + color: inherit; +} + +.menu-popup { + width: auto; + border: 2px solid #444444; + background: #FFFFFF; + position: absolute; + margin: 2px 0 0; + display: none; + z-index: 10000; +} + +.menu-popup li a { + display: block; + padding: 2px; +} + +.menu-popup li a:hover { + color: #FFFFFF; + background: #3465A4; + text-decoration: none; +} +ul.menu-popup li.divider { + height: 1px; + margin: 3px 0; + overflow: hidden; + background-color: #2d2d2d; +} /* nav */ @@ -1952,6 +1997,20 @@ input#dfrn-url { margin-left: 15px; } +#contact-edit-status-wrapper { + padding: 10px; + border: 1px solid #aaa; + border-radius: 8px; +} + +#contact-edit-contact-status { + font-weight: bold; +} +#contact-edit-actions { + float: right; + display: inline-block; +} + #contact-edit-wrapper { margin-top: 10px; } @@ -1989,11 +2048,6 @@ input#dfrn-url { margin-top: 5px; } -#contact-edit-drop-link { - float: right; - margin-right: 20px; -} - #contact-edit-nav-end { clear: both; } diff --git a/view/theme/frost/templates/contact_edit.tpl b/view/theme/frost/templates/contact_edit.tpl index 731c5e0d4c..44e55b1cd8 100644 --- a/view/theme/frost/templates/contact_edit.tpl +++ b/view/theme/frost/templates/contact_edit.tpl @@ -6,50 +6,51 @@ {{$tab_str}} - - - - -
            @@ -58,12 +59,6 @@ - {{if $poll_enabled}} -
            -
            {{$lastupdtext}} {{$last_update}}
            - {{$updpub}} {{$poll_interval}} {{$udnow}} -
            - {{/if}}
            {{include file="field_checkbox.tpl" field=$hidden}} diff --git a/view/theme/frost/theme.php b/view/theme/frost/theme.php index 868a840dee..1093a04729 100644 --- a/view/theme/frost/theme.php +++ b/view/theme/frost/theme.php @@ -10,7 +10,6 @@ */ function frost_init(&$a) { - $a->theme_info = array(); $a->videowidth = 400; $a->videoheight = 330; $a->theme_thread_allow = false; diff --git a/view/theme/quattro/dark/style.css b/view/theme/quattro/dark/style.css index 04e7a2ad54..53864c12f5 100644 --- a/view/theme/quattro/dark/style.css +++ b/view/theme/quattro/dark/style.css @@ -1668,6 +1668,9 @@ span[id^="showmore-wrap"] { overflow: hidden; text-overflow: ellipsis; } +#contact-edit-status-wrapper { + border-color: #364e59; +} /* editor */ .jothidden { display: none; diff --git a/view/theme/quattro/green/style.css b/view/theme/quattro/green/style.css index 13f328d026..e099a31d6e 100644 --- a/view/theme/quattro/green/style.css +++ b/view/theme/quattro/green/style.css @@ -1668,6 +1668,9 @@ span[id^="showmore-wrap"] { overflow: hidden; text-overflow: ellipsis; } +#contact-edit-status-wrapper { + border-color: #9ade00; +} /* editor */ .jothidden { display: none; diff --git a/view/theme/quattro/lilac/style.css b/view/theme/quattro/lilac/style.css index 5ad7e20284..631b0233d6 100644 --- a/view/theme/quattro/lilac/style.css +++ b/view/theme/quattro/lilac/style.css @@ -1668,6 +1668,9 @@ span[id^="showmore-wrap"] { overflow: hidden; text-overflow: ellipsis; } +#contact-edit-status-wrapper { + border-color: #86608e; +} /* editor */ .jothidden { display: none; diff --git a/view/theme/quattro/quattro.less b/view/theme/quattro/quattro.less index 3360ce3373..5d25b0fb40 100644 --- a/view/theme/quattro/quattro.less +++ b/view/theme/quattro/quattro.less @@ -418,19 +418,19 @@ aside { .group-delete-wrapper { float: right; margin-right: 50px; - .drophide { - background-image: url('../../../images/icons/22/delete.png'); - display: block; width: 22px; height: 22px; - opacity: 0.3; - position: relative; - top: -50px; - } - .drop { - background-image: url('../../../images/icons/22/delete.png'); - display: block; width: 22px; height: 22px; - position: relative; - top: -50px; - } + .drophide { + background-image: url('../../../images/icons/22/delete.png'); + display: block; width: 22px; height: 22px; + opacity: 0.3; + position: relative; + top: -50px; + } + .drop { + background-image: url('../../../images/icons/22/delete.png'); + display: block; width: 22px; height: 22px; + position: relative; + top: -50px; + } } /* #group-members { @@ -512,7 +512,7 @@ section { } .sparkle { - cursor: url('icons/lock.cur'), pointer; + cursor: url('icons/lock.cur'), pointer; } /* wall item */ @@ -969,6 +969,7 @@ span[id^="showmore-wrap"] { text-overflow: ellipsis; } +#contact-edit-status-wrapper { border-color: @JotToolsOverBackgroundColor;} /* editor */ .jothidden { display: none; } #jot { diff --git a/view/theme/quattro/theme.php b/view/theme/quattro/theme.php index a1cd29ee72..0b67c6b49a 100644 --- a/view/theme/quattro/theme.php +++ b/view/theme/quattro/theme.php @@ -8,8 +8,6 @@ */ function quattro_init(&$a) { - $a->theme_info = array(); - $a->page['htmlhead'] .= ''; $a->page['htmlhead'] .= '';; } diff --git a/view/theme/smoothly/default.php b/view/theme/smoothly/default.php index 9ac49820aa..405e1cad34 100644 --- a/view/theme/smoothly/default.php +++ b/view/theme/smoothly/default.php @@ -19,7 +19,9 @@ -
            +
            + +
            @@ -41,4 +43,3 @@ - diff --git a/view/theme/smoothly/style.css b/view/theme/smoothly/style.css index 1415949606..e38a7ef6dc 100644 --- a/view/theme/smoothly/style.css +++ b/view/theme/smoothly/style.css @@ -236,6 +236,39 @@ section { color: #efefef; } +ul.menu-popup { + position: absolute; + display: none; + width: auto; + margin: 2px 0 0; + padding: 0px; + list-style: none; + z-index: 100000; + color: #2e3436; + border-top: 1px; + background: #eeeeee; + border: 1px solid #7C7D7B; + border-radius: 0px 0px 5px 5px; + -webkit-border-radius: 0px 0px 5px 5px; + -moz-border-radius: 0px 0px 5px 5px; + box-shadow: 5px 5px 10px #242424; + -moz-box-shadow: 5px 5px 10px #242424; + -webkit-box-shadow: 5px 5px 10px #242424; +} +ul.menu-popup li a { + white-space: nowrap; + display: block; + padding: 5px 2px; + color: #2e3436; +} +ul.menu-popup li a:hover { + color: #efefef; + background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #1873a2), color-stop(1, #6da6c4) ); + background: -moz-linear-gradient( center top, #1873a2 5%, #6da6c4 100% ); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#1873a2', endColorstr='#6da6c4'); + background-color: #1873a2; +} + /* ========= */ /* = Login = */ /* ========= */ @@ -4271,16 +4304,6 @@ a.active { .nav-notify.show { display: block; } -ul.menu-popup { - position: absolute; - display: none; - width: 10em; - margin: 0px; - padding: 0px; - list-style: none; - z-index: 100000; - top: 40px; -} #nav-notifications-menu { width: 320px; max-height: 400px; @@ -4298,6 +4321,7 @@ ul.menu-popup { box-shadow: 5px 5px 10px #242424; -moz-box-shadow: 5px 5px 10px #242424; -webkit-box-shadow: 5px 5px 10px #242424; + top: 40px; } #nav-notifications-menu .contactname { @@ -4413,6 +4437,10 @@ ul.menu-popup { background: #000000; } +.notify-seen a { + color: #efefef !important; +} + /* Pages profile widget ----------------------------------------------------------- */ #page-profile, diff --git a/view/theme/smoothly/theme.php b/view/theme/smoothly/theme.php index d3ebbc1d15..0dae3a6e47 100644 --- a/view/theme/smoothly/theme.php +++ b/view/theme/smoothly/theme.php @@ -11,7 +11,6 @@ */ function smoothly_init(&$a) { - $a->theme_info = array(); set_template_engine($a, 'smarty3'); $cssFile = null; diff --git a/view/theme/vier/css/font2.css b/view/theme/vier/css/font2.css index 4300a910c0..a62cffc00c 100644 --- a/view/theme/vier/css/font2.css +++ b/view/theme/vier/css/font2.css @@ -228,6 +228,7 @@ li.icon.icon-large:before { .icon-key:before { content: "\f084"; } .icon.gears:before { content: "\f085"; } .icon-comments:before { content: "\f086"; } +.icon-commenting:before { content: "\f27a"; } .icon.like:before { content: "\f087"; } .icon.dislike:before { content: "\f088"; } .icon-star-half:before { content: "\f089"; } diff --git a/view/theme/vier/dark.css b/view/theme/vier/dark.css index 8e128ae27f..99850f2826 100644 --- a/view/theme/vier/dark.css +++ b/view/theme/vier/dark.css @@ -8,6 +8,12 @@ hr { background-color: #343434 !important; } a, .wall-item-name, .fakelink { color: #989898 !important; } +.btn, .btn:hover{ + color: #989898; + border: 2px solid #0C1116; + background-color: #0C1116; + text-shadow: none; +} nav { color: #989898 !important; @@ -35,6 +41,11 @@ body, section, blockquote, blockquote.shared_content, #profile-jot-form, background-color: #171B1F !important; } +#profile-jot-acl-wrapper, #event-notice, #event-wrapper, +#cboxLoadedContent, .contact-photo-menu, #contact-edit-status-wrapper { + background-color: #252C33 !important; +} + div.rte, .mceContentBody { background:none repeat scroll 0 0 #333333!important; color:#FFF!important; @@ -63,3 +74,35 @@ li :hover { #viewcontact_wrapper-network { background-color: #343434; } + +table.smiley-preview{ + background-color: #252C33 !important; +} + +/* ACL permission popup */ + .acl-list-item.groupshow { + border-color: #9ade00 !important; +} + +.acl-list-item.grouphide { + border-color: #ff4141 !important; +} + +/* Notifications */ +li.notify-unseen { + background-color: #252C33; +} + +li.notify-seen { + background-color: #1C2126; +} + +.photo-top-album-name { + background-color: #252C33; +} + +#panel { + background-color: #252C33; + border: 3px solid #364e59; + color: #989898; +} diff --git a/view/theme/vier/font/FontAwesome.otf b/view/theme/vier/font/FontAwesome.otf index 81c9ad949b..3ed7f8b48a 100644 Binary files a/view/theme/vier/font/FontAwesome.otf and b/view/theme/vier/font/FontAwesome.otf differ diff --git a/view/theme/vier/font/fontawesome-webfont.eot b/view/theme/vier/font/fontawesome-webfont.eot old mode 100755 new mode 100644 index 84677bc0c5..9b6afaedc0 Binary files a/view/theme/vier/font/fontawesome-webfont.eot and b/view/theme/vier/font/fontawesome-webfont.eot differ diff --git a/view/theme/vier/font/fontawesome-webfont.svg b/view/theme/vier/font/fontawesome-webfont.svg old mode 100755 new mode 100644 index d907b25ae6..d05688e9e2 --- a/view/theme/vier/font/fontawesome-webfont.svg +++ b/view/theme/vier/font/fontawesome-webfont.svg @@ -147,14 +147,14 @@ - + - + @@ -219,8 +219,8 @@ - - + + @@ -275,7 +275,7 @@ - + @@ -362,7 +362,7 @@ - + @@ -399,7 +399,7 @@ - + @@ -410,9 +410,9 @@ - - - + + + @@ -438,7 +438,7 @@ - + @@ -454,12 +454,12 @@ - + - + @@ -483,13 +483,13 @@ - + - + @@ -513,8 +513,143 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/view/theme/vier/font/fontawesome-webfont.ttf b/view/theme/vier/font/fontawesome-webfont.ttf old mode 100755 new mode 100644 index 96a3639cdd..26dea7951a Binary files a/view/theme/vier/font/fontawesome-webfont.ttf and b/view/theme/vier/font/fontawesome-webfont.ttf differ diff --git a/view/theme/vier/font/fontawesome-webfont.woff b/view/theme/vier/font/fontawesome-webfont.woff old mode 100755 new mode 100644 index 628b6a52a8..dc35ce3c2c Binary files a/view/theme/vier/font/fontawesome-webfont.woff and b/view/theme/vier/font/fontawesome-webfont.woff differ diff --git a/view/theme/vier/font/fontawesome-webfont.woff2 b/view/theme/vier/font/fontawesome-webfont.woff2 new file mode 100644 index 0000000000..500e517253 Binary files /dev/null and b/view/theme/vier/font/fontawesome-webfont.woff2 differ diff --git a/view/theme/vier/mobile.css b/view/theme/vier/mobile.css index f6ec2b8ccb..b67dc5f771 100644 --- a/view/theme/vier/mobile.css +++ b/view/theme/vier/mobile.css @@ -8,11 +8,11 @@ aside, header, #nav-events-link, #search-box, #nav-admin-link, #activitiy-by-dat } section { - /* left: calc((100% - (784px)) / 2); */ + box-sizing: border-box; left: 0px; - width: calc(100% - 20px); + width: 100%; max-width: 100%; - padding: 10px; + padding: 5px; } body, section, nav .nav-menu, div.pager, ul.tabs { @@ -92,8 +92,8 @@ nav ul { .wall-item-container.thread_level_5, .wall-item-container.thread_level_6, .wall-item-container.thread_level_7 { - margin-left: 60px; - width: calc(100% - 70px); + margin-left: 0; + width: calc(100% - 10px); } .wall-item-container.thread_level_2 .wall-item-content, @@ -105,6 +105,10 @@ nav ul { max-width: 100%; } +.wall-item-container .wall-item-info { width: auto; } +.contact-photo-wrapper { width: 55px; } +.wwto { left: 0px; } + /* aside in/out */ .mobile-aside-toggle { display: block !important; @@ -161,3 +165,116 @@ aside.show { } .tabs.show::after { display: none; } .tabs.show .tab { display: block; } + +/* jot buttons */ +#profile-jot-submit, +#jot-preview-link { + float: none; + display: block; + width: 100%; + margin: 0 0 1em 0; +} +#profile-jot-submit-wrapper > div { + margin: 0 1em 1em 0; +} +#profile-jot-submit-wrapper > div#profile-jot-perms { margin: 0; } + +/* ACL window */ +#profile-jot-acl-wrapper, #profile-jot-acl-wrapper * { box-sizing: border-box; } +#acl-wrapper { width: 100%; float: none; } +#acl-search { width: 100%; float: none; padding-right: 0px; margin-bottom: 1em; } +#acl-showall { width: 100%; height: 48px; margin-bottom: 1em; } +.acl-list-item { width: auto; float: none; height: auto; overflow: hidden; position: relative;} +.acl-list-item img { width: 48px; height: 48px; } +.acl-list-item p { height: auto; font-size: inherit; } +.acl-list-item a { + float: none; + position: absolute; + top: 5px; + right: 5px; + height: 48px; + padding: 10px 2px 2px 2px; + font-size: 12px; + width: 20%; + text-align: center; + background-position: center 5px; +} +.acl-list-item a.acl-button-hide { right: 25%; } +/* flexbox for ACL window */ +#cboxLoadedContent, +#cboxLoadedContent > div, +#acl-wrapper { + display: -ms-Flexbox !important; + -ms-box-orient: vertival; + + display: -webkit-flex !important; + display: -moz-flex !important; + display: -ms-flex !important; + display: flex !important; + + -webkit-flex-flow: column; + -moz-flex-flow: column; + -ms-flex-flow: column; + flex-flow: column; + + -webkit-flex: 1 100%; + -moz-flex: 1 100%; + -ms-flex: 1 100%; + flex: 1 100%; +} +#acl-list { + -webkit-flex: 1 1 auto; + -moz-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +/** input elements **/ +input, +textarea, +select { + font-size: 18px; + border: 1px solid #888; + padding: 0.2em; +} +input:focus, +textarea:focus, +select:focus { + box-shadow: 1px 1px 10px rgba(46, 151, 255, 0.62); +} + +.field, .field > * { box-sizing: border-box; } +.field label { width: 100%; float: none; display: block; } +.field input, .field textarea, .field select { max-width: 100%; width: 100%; } +.field.yesno .onoff, +.field.checkbox input { width: auto; float: right; } +.field.yesno label, +.field.checkbox label { width: 70%; float: left; } +.field .field_help { margin: 0; } + +/** event **/ +.event-start, .event-end { width: auto; } +.event-start .dtstart, .event-end .dtend { float: none; display: block; } + +/** oembed **/ +.embed_video { + float: none; + margin: 0px; + height: 120px; + width: 100%; + overflow: hidden; + display: block; +} +.embed_video > img { + display: block; + width: 100% !important; + height: auto !important; + max-width: 100% !important; +} +.embed_video > div { + background-image: none !important; background-color: transparent !important; + width: 100% !important; height: 110px !important; +} + +.type-link > a { height: 120px; width: 100%; overflow: hidden; display: block; } +.type-link > a > img { display: block; max-width: 100%!important; width: 100% !important; height: auto !important; } \ No newline at end of file diff --git a/view/theme/vier/style.css b/view/theme/vier/style.css index 7a73e03e83..f853079f18 100644 --- a/view/theme/vier/style.css +++ b/view/theme/vier/style.css @@ -24,72 +24,72 @@ img { } #pending-update { - float:right; - color: #ffffff; - font-weight: bold; - background-color: #FF0000; - padding: 0em 0.3em; + float:right; + color: #ffffff; + font-weight: bold; + background-color: #FF0000; + padding: 0em 0.3em; } .admin.linklist { - border: 0px; - padding: 0px; - list-style: none; - margin-top: 0px; + border: 0px; + padding: 0px; + list-style: none; + margin-top: 0px; } .admin.link { - list-style-position: inside; - font-size: 1em; -/* padding-left: 5px; - margin: 5px; */ + list-style-position: inside; + font-size: 1em; +/* padding-left: 5px; + margin: 5px; */ } #adminpage dl { - clear: left; - margin-bottom: 2px; - padding-bottom: 2px; - border-bottom: 1px solid black; + clear: left; + margin-bottom: 2px; + padding-bottom: 2px; + border-bottom: 1px solid black; } #adminpage dt { - width: 200px; - float: left; - font-weight: bold; + width: 200px; + float: left; + font-weight: bold; } #adminpage dd { - margin-left: 200px; + margin-left: 200px; } #adminpage h3 { - border-bottom: 1px solid #898989; - margin-bottom: 5px; - margin-top: 10px; + border-bottom: 1px solid #898989; + margin-bottom: 5px; + margin-top: 10px; } #adminpage .submit { - clear:left; + clear:left; } #adminpage #pluginslist { - margin: 0px; padding: 0px; + margin: 0px; padding: 0px; } #adminpage .plugin { - list-style: none; - display: block; - /* border: 1px solid #888888; */ - padding: 1em; - margin-bottom: 5px; - clear: left; + list-style: none; + display: block; + /* border: 1px solid #888888; */ + padding: 1em; + margin-bottom: 5px; + clear: left; } #adminpage .toggleplugin { - float:left; - margin-right: 1em; + float:left; + margin-right: 1em; } -#adminpage table {width:100%; border-bottom: 1p solid #000000; margin: 5px 0px;} +#adminpage table {width:100%; border-bottom: 1px solid #000000; margin: 5px 0px;} #adminpage table th { text-align: left;} #adminpage td .icon { float: left;} #adminpage table#users img { width: 16px; height: 16px; } @@ -127,8 +127,8 @@ img { .icon { background-color: transparent ; background-repeat: no-repeat; - width: 18px; - height: 18px; + width: 20px; + height: 20px; /* display: block; */ display: inline-block; overflow: hidden; @@ -247,15 +247,6 @@ div.pager { float: left; } -#contact-edit-drop-link-end { - /* clear: both; */ -} - -#contact-edit-links ul { - list-style: none; - list-style-type: none; -} - .hide-comments-outer { margin-left: 80px; margin-bottom: 5px; @@ -269,7 +260,7 @@ div.pager { /* global */ body { /* font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; */ - font-family: system,-apple-system,".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Helvetica,Arial,sans-serif; + font-family: -apple-system,".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande",Helvetica,Arial,sans-serif; font-size: 14px; /* font-size: 13px; line-height: 19.5px; */ @@ -385,6 +376,14 @@ code { overflow: auto; padding: 0px; } +.menu-popup .divider { + width: 90%; + height: 1px; + margin: 3px auto; + overflow: hidden; + background-color: #737373; + opacity: 0.4; +} #saved-search-ul .tool:hover, #nets-sidebar .tool:hover, #sidebar-group-list .tool:hover { @@ -793,7 +792,8 @@ nav #nav-user-linklabel:hover #nav-user-menu, nav #nav-user-linkmenu:hover #nav-user-menu, nav #nav-apps-link:hover #nav-apps-menu, nav #nav-site-linkmenu:hover #nav-site-menu, -nav #nav-notifications-linkmenu:hover #nav-notifications-menu { +nav #nav-notifications-linkmenu:hover #nav-notifications-menu, +#contact-edit-actions:hover #contact-actions-menu { display:block; visibility:visible; opacity:1; @@ -1070,6 +1070,9 @@ aside { box-shadow: 1px 2px 0px 0px #D8D8D8; color: #737373; } +aside .vcard .tool { + clear: both; +} aside .vcard .fn { font-size: 18px; font-weight: bold; @@ -1078,7 +1081,6 @@ aside .vcard .fn { } aside .vcard .title { margin-bottom: 5px; - float: left; } aside .vcard dl { height: auto; @@ -1167,6 +1169,10 @@ aside h4, right_aside h4 { font-size: 1.17em; } +aside img { + max-width: 175px; +} + .nets-ul { margin-top: 0px; } @@ -1454,8 +1460,8 @@ section.minimal { } .children .wall-item-container .wall-item-item .wall-item-content img { /* max-width: 650px; */ - /* max-width: 580px; */ - max-width: 100%; + max-width: 520px; + /* max-width: 100%; */ } .wall-item-container .wall-item-links, .wall-item-container .wall-item-actions { display: table-cell; @@ -1611,63 +1617,34 @@ section.minimal { width: calc(100% - 165px); } -.wall-item-container.comment .wall-item-content { - max-width: 585px; -} -.wall-item-container.thread_level_3 .wall-item-content { - max-width: 570px; -} -.wall-item-container.thread_level_4 .wall-item-content { - max-width: 555px; -} -.wall-item-container.thread_level_5 .wall-item-content { - max-width: 540px; -} -.wall-item-container.thread_level_6 .wall-item-content { - max-width: 525px; -} +.wall-item-container.comment .wall-item-content, +.wall-item-container.thread_level_3 .wall-item-content, +.wall-item-container.thread_level_4 .wall-item-content, +.wall-item-container.thread_level_5 .wall-item-content, +.wall-item-container.thread_level_6 .wall-item-content, .wall-item-container.thread_level_7 .wall-item-content { - max-width: 510px; + max-width: calc(100% - 1px); } -.children .wall-item-comment-wrapper textarea { - width: 570px; -} -.wall-item-container.thread_level_3 .wall-item-comment-wrapper textarea { - width: 555px; -} -.wall-item-container.thread_level_4 .wall-item-comment-wrapper textarea { - width: 540px; -} -.wall-item-container.thread_level_5 .wall-item-comment-wrapper textarea { - width: 525px; -} -.wall-item-container.thread_level_6 .wall-item-comment-wrapper textarea { - width: 510px; -} +.children .wall-item-comment-wrapper textarea, +.wall-item-container.thread_level_3 .wall-item-comment-wrapper textarea, +.wall-item-container.thread_level_4 .wall-item-comment-wrapper textarea, +.wall-item-container.thread_level_5 .wall-item-comment-wrapper textarea, +.wall-item-container.thread_level_6 .wall-item-comment-wrapper textarea, .wall-item-container.thread_level_7 .wall-item-comment-wrapper textarea { - width: 495px; + width: calc(100% - 11px); } -.children .wall-item-bottom .comment-edit-preview { - width: 575px; -} -.wall-item-container.thread_level_3 .wall-item-bottom .comment-edit-preview { - width: 560px; -} -.wall-item-container.thread_level_4 .wall-item-bottom .comment-edit-preview { - width: 545px; -} -.wall-item-container.thread_level_5 .wall-item-bottom .comment-edit-preview { - width: 530px; -} -.wall-item-container.thread_level_6 .wall-item-bottom .comment-edit-preview { - width: 515px; -} +.children .wall-item-bottom .comment-edit-preview, +.wall-item-container.thread_level_3 .wall-item-bottom .comment-edit-preview, +.wall-item-container.thread_level_4 .wall-item-bottom .comment-edit-preview, +.wall-item-container.thread_level_5 .wall-item-bottom .comment-edit-preview, +.wall-item-container.thread_level_6 .wall-item-bottom .comment-edit-preview, .wall-item-container.thread_level_7 .wall-item-bottom .comment-edit-preview { - width: 500px; + width: calc(100% - 6px); } + .wall-item-container.comment .contact-photo { width: 32px; height: 32px; @@ -2079,6 +2056,15 @@ section.minimal { cursor: pointer; margin-top: 3px; height: 10px; + display: inline-block; +} +#smileybutton { + position: absolute; + z-index: 99; +} +table.smiley-preview{ + background-color: #FFF; + box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.7); } #jot-perms-icon { float: right; @@ -2776,7 +2762,9 @@ a.mail-list-link { margin-bottom: 15px; } -.vevent .event-description, .vevent .event-location { +.vevent .event-summary, +.vevent .event-description, +.vevent .event-location { margin-left: 10px; margin-right: 10px; } @@ -2975,35 +2963,83 @@ a.mail-list-link { color: #999999; } +/* contact edit page */ +#contact-edit-nav-wrapper { + margin-top: 24px; +} +#contact-edit-status-wrapper { + border-color: #c9d8f6; + background-color: #e0e8fa; + border-radius: 3px; +} + +#contact-edit-contact-status { + font-weight: bold; +} + +#contact-edit-drop-link-end { + /* clear: both; */ +} + +#contact-edit-links ul { + list-style: none; + list-style-type: none; +} + +#contact-edit-settings { + margin-top: 10px; +} + +a.btn#contact-edit-actions-button { + cursor: pointer; + border-radius: 3px; + font-size: inherit; + font-weight: normal; + height: auto; + line-height: inherit; + padding: 5px 10px; +} + +#lost-contact-message, #insecure-message, +#block-message, #ignore-message, #archive-message { + color: #CB4437; +} + +/* photo album page */ .photo-top-image-wrapper { - position: relative; - float: left; - margin-top: 15px; - margin-right: 15px; - width: 200px; height: 200px; - overflow: hidden; + position: relative; + float: left; + margin-top: 15px; + margin-right: 15px; + width: 200px; height: 200px; + overflow: hidden; } .photo-top-album-name { - width: 100%; - min-height: 2em; - position: absolute; - bottom: 0px; - padding: 0px 3px; - padding-top: 0.5em; - background-color: rgb(255, 255, 255); + width: 100%; + min-height: 2em; + position: absolute; + bottom: 0px; + padding: 0px 3px; + padding-top: 0.5em; + background-color: rgb(255, 255, 255); } #photo-top-end { - clear: both; + clear: both; } #photo-top-links { - margin-bottom: 30px; - margin-left: 30px; + margin-bottom: 30px; + margin-left: 30px; } #photos-upload-newalbum-div { - float: left; - width: 175px; + float: left; + width: 175px; +} +img.photo-top-photo { + width: 100%; + height: 100%; + object-fit: cover; } .menu-profile-list{ diff --git a/view/theme/vier/templates/contact_edit.tpl b/view/theme/vier/templates/contact_edit.tpl new file mode 100644 index 0000000000..ce3cfbf808 --- /dev/null +++ b/view/theme/vier/templates/contact_edit.tpl @@ -0,0 +1,98 @@ + +{{if $header}}

            {{$header}}

            {{/if}} + +
            + + {{* Insert Tab-Nav *}} + {{$tab_str}} + + +
            + {{* End of contact-edit-links *}} + + + +
            + + +
            + + + +
            + {{include file="field_checkbox.tpl" field=$notify}} + {{if $fetch_further_information}} + {{include file="field_select.tpl" field=$fetch_further_information}} + {{if $fetch_further_information.2 == 2 }} {{include file="field_textarea.tpl" field=$ffi_keyword_blacklist}} {{/if}} + {{/if}} + {{include file="field_checkbox.tpl" field=$hidden}} + +
            +

            {{$lbl_info1}}

            + + +
            +
            + + {{if $profile_select}} +
            +

            {{$lbl_vis1}}

            +

            {{$lbl_vis2}}

            +
            + {{$profile_select}} +
            + + {{/if}} + +
            +
            {{* End of contact-edit-nav-wrapper *}} +
            diff --git a/view/theme/vier/templates/wall_thread.tpl b/view/theme/vier/templates/wall_thread.tpl index 267b35df77..c9ee770819 100644 --- a/view/theme/vier/templates/wall_thread.tpl +++ b/view/theme/vier/templates/wall_thread.tpl @@ -91,7 +91,7 @@ {{if $item.threaded}} {{/if}} {{if $item.comment}} - {{$item.switchcomment}} + {{$item.switchcomment}} {{/if}} {{if $item.isevent}} diff --git a/view/theme/vier/theme.php b/view/theme/vier/theme.php index 4afc5409c0..925ac76a1f 100644 --- a/view/theme/vier/theme.php +++ b/view/theme/vier/theme.php @@ -19,10 +19,6 @@ function vier_init(&$a) { set_template_engine($a, 'smarty3'); - $baseurl = $a->get_baseurl(); - - $a->theme_info = array(); - if ($a->argv[0].$a->argv[1] === "profile".$a->user['nickname'] or $a->argv[0] === "network" && local_user()) { vier_community_info(); @@ -160,7 +156,7 @@ function vier_community_info() { $entry = replace_macros($tpl,array( '$id' => $rr['id'], //'$profile_link' => zrl($rr['url']), - '$profile_link' => $a->get_baseurl().'/follow/?url='.urlencode($rr['url']), + '$profile_link' => 'follow/?url='.urlencode($rr['url']), '$photo' => proxy_url($rr['photo'], false, PROXY_SIZE_MICRO), '$alt_text' => $rr['name'], )); @@ -186,11 +182,11 @@ function vier_community_info() { $aside['$lastusers_items'] = array(); foreach($r as $rr) { - $profile_link = $a->get_baseurl() . '/profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); + $profile_link = 'profile/' . ((strlen($rr['nickname'])) ? $rr['nickname'] : $rr['profile_uid']); $entry = replace_macros($tpl,array( '$id' => $rr['id'], '$profile_link' => $profile_link, - '$photo' => $a->get_cached_avatar_image($rr['thumb']), + '$photo' => $a->remove_baseurl($rr['thumb']), '$alt_text' => $rr['name'])); $aside['$lastusers_items'][] = $entry; } @@ -207,7 +203,7 @@ function vier_community_info() { $nv['suggest'] = Array('suggest', t('Friend Suggestions'), "", ""); $nv['invite'] = Array('invite', t('Invite Friends'), "", ""); - $nv['search'] = '
            + $nv['search'] = ' @@ -220,7 +216,7 @@ function vier_community_info() { //Community_Pages at right_aside if($show_pages AND local_user()) { - require_once('include/forums.php'); + require_once('include/ForumManager.php'); if(x($_GET['cid']) && intval($_GET['cid']) != 0) $cid = $_GET['cid']; @@ -228,7 +224,7 @@ function vier_community_info() { //sort by last updated item $lastitem = true; - $contacts = get_forumlist($a->user['uid'],true,$lastitem, true); + $contacts = ForumManager::get_list($a->user['uid'],true,$lastitem, true); $total = count($contacts); $visible_forums = 10; @@ -241,12 +237,12 @@ function vier_community_info() { $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;