diff --git a/boot.php b/boot.php index 94e1e0ab8c..58b4bc0983 100644 --- a/boot.php +++ b/boot.php @@ -6,11 +6,11 @@ /** * 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 @@ -18,7 +18,7 @@ */ require_once('include/autoloader.php'); - + require_once('include/config.php'); require_once('include/network.php'); require_once('include/plugin.php'); @@ -30,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'); @@ -465,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; @@ -855,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( @@ -938,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 * @@ -1027,11 +1047,21 @@ 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; $callstack = $this->callstack(); + if (!isset($this->callstack[$value][$callstack])) { + // Prevent ugly E_NOTICE + $this->callstack[$value][$callstack] = 0; + } + $this->callstack[$value][$callstack] += (float)$duration; } @@ -1068,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; + } } /** @@ -1419,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"); @@ -1479,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(); } @@ -1738,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 25faf0f4c0..89b821e23a 100644 --- a/database.sql +++ b/database.sql @@ -201,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 -- @@ -912,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/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 c020f403ff..7d6f440c58 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1,6 +1,6 @@ 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. @@ -388,6 +388,18 @@ Friendica doesn't allow showing friends of other users. --- ### statusnet/config (*) +--- +### 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 (*) diff --git a/doc/database.md b/doc/database.md index e37df05e09..f48404c17d 100644 --- a/doc/database.md +++ b/doc/database.md @@ -15,7 +15,6 @@ Database Tables | [contact](help/database/db_contact) | contact table | | [conv](help/database/db_conv) | private messages | | [deliverq](help/database/db_deliverq) | | -| [dsprphotoq](help/database/db_dsprphotoq) | | | [event](help/database/db_event) | Events | | [fcontact](help/database/db_fcontact) | friend suggestion stuff | | [ffinder](help/database/db_ffinder) | friend suggestion stuff | diff --git a/doc/database/db_dsprphotoq.md b/doc/database/db_dsprphotoq.md deleted file mode 100644 index 6af4d030e0..0000000000 --- a/doc/database/db_dsprphotoq.md +++ /dev/null @@ -1,11 +0,0 @@ -Table dsprphotoq -================ - -| Field | Description | Type | Null | Key | Default | Extra | -|---------|------------------|------------------|------|-----|---------|----------------| -| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment | -| uid | | int(11) | NO | | 0 | | -| msg | | mediumtext | NO | | NULL | | -| attempt | | tinyint(4) | NO | | 0 | | - -Return to [database documentation](help/database) diff --git a/doc/database/db_sign.md b/doc/database/db_sign.md index 8de59ac675..6986613e59 100644 --- a/doc/database/db_sign.md +++ b/doc/database/db_sign.md @@ -5,7 +5,6 @@ Table sign | ------------ | ------------- | ---------------- | ---- | --- | ------- | --------------- | | id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment | | iid | item.id | int(10) unsigned | NO | MUL | 0 | | -| retract_iid | | int(10) unsigned | NO | MUL | 0 | | | signed_text | | mediumtext | NO | | NULL | | | signature | | text | NO | | NULL | | | signer | | varchar(255) | NO | | | | diff --git a/doc/de/BBCode.md b/doc/de/BBCode.md index d3e205f0fa..cd9fa7673e 100644 --- a/doc/de/BBCode.md +++ b/doc/de/BBCode.md @@ -131,8 +131,7 @@ Außerdem kann *url* die genaue url zu einer ogg Datei sein, die dann per H
[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/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 f9c92bfa08..a36e0bef22 100644 --- a/doc/htconfig.md +++ b/doc/htconfig.md @@ -64,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/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 index 49417d1831..6fede0204d 100644 --- a/include/ForumManager.php +++ b/include/ForumManager.php @@ -95,12 +95,12 @@ class ForumManager { $selected = (($cid == $contact['id']) ? ' forum-selected' : ''); $entry = array( - 'url' => z_root() . '/network?f=&cid=' . $contact['id'], - 'external_url' => z_root() . '/redir/' . $contact['id'], + 'url' => 'network?f=&cid=' . $contact['id'], + 'external_url' => 'redir/' . $contact['id'], 'name' => $contact['name'], 'cid' => $contact['id'], 'selected' => $selected, - 'micro' => proxy_url($contact['micro'], false, PROXY_SIZE_MICRO), + 'micro' => App::remove_baseurl(proxy_url($contact['micro'], false, PROXY_SIZE_MICRO)), 'id' => ++$id, ); $entries[] = $entry; @@ -187,4 +187,4 @@ class ForumManager { return $r; } -} \ No newline at end of file +} diff --git a/include/Scrape.php b/include/Scrape.php index e8e9a97a16..68926a997e 100644 --- a/include/Scrape.php +++ b/include/Scrape.php @@ -23,13 +23,15 @@ function scrape_dfrn($url, $dont_probe = false) { 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) { @@ -356,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); @@ -365,6 +367,7 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { return $result; } + $original_url = $url; $network = null; $diaspora = false; $diaspora_base = ''; @@ -393,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); @@ -440,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']); @@ -453,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; @@ -685,7 +705,14 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { if (($vcard["nick"] == "") AND ($data["header"]["author-nick"] != "")) $vcard["nick"] = $data["header"]["author-nick"]; - if(!$profile AND ($data["header"]["author-link"] != "") AND !in_array($network, array("", NETWORK_FEED))) + 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"]; } } @@ -769,6 +796,9 @@ function probe_url($url, $mode = PROBE_NORMAL, $level = 1) { if (($baseurl == "") AND ($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, "/"); if(strpos($url,'@') AND ($addr == "") AND ($network == NETWORK_DFRN)) @@ -816,8 +846,28 @@ 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; } diff --git a/include/api.php b/include/api.php index e5ca8ca787..887b449fc0 100644 --- a/include/api.php +++ b/include/api.php @@ -161,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']; @@ -216,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; @@ -332,7 +330,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); @@ -904,7 +903,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)); } } @@ -923,7 +923,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)); + } } @@ -942,7 +944,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)); } } @@ -1548,6 +1551,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); /** @@ -1689,13 +1693,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 ", @@ -1810,7 +1814,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]); diff --git a/include/bbcode.php b/include/bbcode.php index 6a44e19ec4..8545b2ff82 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -311,6 +311,9 @@ function tryoembed($match){ $o = oembed_fetch_url($url); + if (!is_object($o)) + return $match[0]; + if (isset($match[2])) $o->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 a884a6b52b..3bf68f764e 100644 --- a/include/contact_selectors.php +++ b/include/contact_selectors.php @@ -99,7 +99,7 @@ function network_to_name($s, $profile = "") { $networkname = str_replace($search,$replace,$s); - if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora_is_redmatrix($profile)) { + if (($s == NETWORK_DIASPORA) AND ($profile != "") AND diaspora::is_redmatrix($profile)) { $networkname = t("Hubzilla/Redmatrix"); $r = q("SELECT `gserver`.`platform` FROM `gcontact` 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/dbstructure.php b/include/dbstructure.php index ddf036f2c1..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"), @@ -1246,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" => ""), @@ -1254,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 021ceb9968..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; @@ -379,14 +371,14 @@ function delivery_run(&$argv, &$argc){ } } - 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 f7a05bdb63..14be747305 100644 --- a/include/dfrn.php +++ b/include/dfrn.php @@ -18,6 +18,8 @@ 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 @@ -84,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]; } } @@ -243,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 @@ -285,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); @@ -318,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); @@ -364,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); @@ -409,39 +411,39 @@ class dfrn { $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, "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); if ($public) { // DFRN itself doesn't uses this. But maybe someone else wants to subscribe to the public feed. - ostatus_hublinks($doc, $root); + 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); /// @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)); + xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME)); $author = self::add_author($doc, $owner, $authorelement, $public); $root->appendChild($author); @@ -467,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['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) @@ -501,25 +503,25 @@ class dfrn { 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); } @@ -527,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)); } @@ -535,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); } @@ -577,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 @@ -590,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", @@ -598,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; } @@ -621,13 +623,13 @@ 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); @@ -640,16 +642,16 @@ class dfrn { $attributes = array(); foreach ($link->attributes() AS $parameter => $value) $attributes[$parameter] = $value; - xml_add_element($doc, $entry, "link", "", $attributes); + 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; } @@ -683,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); } } } @@ -710,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"); @@ -720,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; @@ -741,66 +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)); // "dfrn:env" is used to read the content - xml_add_element($doc, $entry, "dfrn:env", base64url_encode($body, true)); + xml::add_element($doc, $entry, "dfrn:env", base64url_encode($body, true)); // 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)); + 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", + 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) @@ -815,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)) @@ -828,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)); } @@ -1319,7 +1324,7 @@ class dfrn { $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); + xml::add_element($obj_doc, $obj_element, "type", $activity_type); $id = $xpath->query("atom:id", $activity)->item(0); if (is_object($id)) @@ -1769,6 +1774,9 @@ class dfrn { * @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 @@ -1799,11 +1807,11 @@ class dfrn { return false; } } else { - if(($item["verb"] === ACTIVITY_LIKE) - || ($item["verb"] === ACTIVITY_DISLIKE) - || ($item["verb"] === ACTIVITY_ATTEND) - || ($item["verb"] === ACTIVITY_ATTENDNO) - || ($item["verb"] === ACTIVITY_ATTENDMAYBE)) { + 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; @@ -1829,7 +1837,7 @@ class dfrn { } else $is_like = false; - if(($item["verb"] === ACTIVITY_TAG) && ($item["object-type"] === ACTIVITY_OBJ_TAGTERM)) { + 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); @@ -2018,14 +2026,28 @@ class dfrn { $categories = $xpath->query("atom:category", $entry); if ($categories) { foreach ($categories AS $category) { - foreach($category->attributes AS $attributes) - if ($attributes->name == "term") { + $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"] .= "#[url=".App::get_baseurl()."/search?tag=".$term."]".$term."[/url]"; + $item["tag"] .= $termhash."[url=".$termurl."]".$term."[/url]"; } + } } } @@ -2243,15 +2265,17 @@ class dfrn { else return; - if($item["object-type"] === ACTIVITY_OBJ_EVENT) { + 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)) { + 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) { + + 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"]) 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/feed.php b/include/feed.php index 04cfba75a6..293de3cc96 100644 --- a/include/feed.php +++ b/include/feed.php @@ -54,8 +54,10 @@ function feed_import($xml,$importer,&$contact, &$hub, $simulate = false) { 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"] = $xpath->evaluate('/atom:feed/atom:author/atom:uri/text()')->item(0)->nodeValue; + $author["author-link"] = $author["author-id"]; if ($author["author-link"] == "") { $self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes; @@ -127,6 +129,7 @@ function feed_import($xml,$importer,&$contact, &$hub, $simulate = false) { // This is no field in the item table. So we have to unset it. unset($author["author-nick"]); + unset($author["author-id"]); } $header = array(); diff --git a/include/follow.php b/include/follow.php index 410e0e58aa..d0411a466a 100644 --- a/include/follow.php +++ b/include/follow.php @@ -258,12 +258,10 @@ function new_contact($uid,$url,$interactive = false) { $contact_id = $r[0]['id']; $result['cid'] = $contact_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, $contact["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); } require_once("include/Photo.php"); @@ -305,8 +303,8 @@ function new_contact($uid,$url,$interactive = false) { } if($contact['network'] == NETWORK_DIASPORA) { require_once('include/diaspora.php'); - $ret = diaspora_share($a->user,$contact); - logger('mod_follow: diaspora_share returns: ' . $ret); + $ret = diaspora::send_share($a->user,$contact); + logger('share returns: '.$ret); } } diff --git a/include/group.php b/include/group.php index 2b872f16a7..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; @@ -375,3 +378,28 @@ function groups_count_unseen() { 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 ec66225d0f..888a09ee6f 100644 --- a/include/identity.php +++ b/include/identity.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){ diff --git a/include/items.php b/include/items.php index 8d6b5b471c..4627b10ca2 100644 --- a/include/items.php +++ b/include/items.php @@ -383,9 +383,9 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa // Converting the plink if ($arr['network'] == NETWORK_OSTATUS) { if (isset($arr['plink'])) - $arr['plink'] = ostatus_convert_href($arr['plink']); + $arr['plink'] = ostatus::convert_href($arr['plink']); elseif (isset($arr['uri'])) - $arr['plink'] = ostatus_convert_href($arr['uri']); + $arr['plink'] = ostatus::convert_href($arr['uri']); } if(x($arr, 'gravity')) @@ -707,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); } @@ -1243,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; } @@ -1980,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']); @@ -2115,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 6512d35609..0fa671a27d 100644 --- a/include/nav.php +++ b/include/nav.php @@ -82,7 +82,7 @@ function nav_info(&$a) { // user info $r = q("SELECT micro FROM contact WHERE uid=%d AND self=1", intval($a->user['uid'])); $userinfo = array( - 'icon' => (count($r) ? $a->get_cached_avatar_image($r[0]['micro']) : $a->get_baseurl($ssl_state)."/images/person-48.jpg"), + 'icon' => (count($r) ? $a->remove_baseurl($r[0]['micro']) : "images/person-48.jpg"), 'name' => $a->user['username'], ); @@ -107,7 +107,7 @@ function nav_info(&$a) { if(($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user())) $nav['register'] = array('register',t('Register'), "", t('Create an account')); - $help_url = $a->get_baseurl($ssl_state) . '/help'; + $help_url = 'help'; if(! get_config('system','hide_help')) $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation')); diff --git a/include/network.php b/include/network.php index c6379e407b..27459112d6 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 5c5016d0fc..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; - foreach ($r AS $contact) { - ostatus_follow_friends($contact["uid"], $contact["v"]); - set_pconfig($contact["uid"], "system", "ostatus_legacy_contact", ""); - } -} + $aliaslink = $author["author-link"]; -// This function doesn't work reliable by now. -function ostatus_follow_friends($uid, $url) { - $contact = probe_url($url); + $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; - if (!$contact) - return; + $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"]; - $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"]; - $contact["generation"] = 2; - $contact["photo"] = $author["author-avatar"]; - update_gcontact($contact); - } + // Only update the contacts if it is an OStatus contact + if ($r AND !$onlyfetch AND ($contact["network"] == NETWORK_OSTATUS)) { - return($author); -} + // Update contact data -function ostatus_salmon_author($xml, $importer) { - $a = get_app(); + // This query doesn't seem to work + // $value = $xpath->query("atom:link[@rel='salmon']", $context)->item(0)->nodeValue; + // if ($value != "") + // $contact["notify"] = $value; - if ($xml == "") - return; + // 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; - $doc = new DOMDocument(); - @$doc->loadXML($xml); + $value = $xpath->evaluate('atom:author/atom:uri/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["alias"] = $value; - $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); + $value = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["name"] = $value; - $entries = $xpath->query('/atom:entry'); + $value = $xpath->evaluate('atom:author/poco:preferredUsername/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["nick"] = $value; - foreach ($entries AS $entry) { - // fetch the author - $author = ostatus_fetchauthor($xpath, $entry, $importer, $contact, true); - return $author; - } -} + $value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["about"] = html2bbcode($value); -function ostatus_import($xml,$importer,&$contact, &$hub) { + $value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue; + if ($value != "") + $contact["location"] = $value; - $a = get_app(); + 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"])) { - logger("Import OStatus message", LOGGER_DEBUG); + logger("Update contact data for contact ".$contact["id"], LOGGER_DEBUG); - if ($xml == "") - return; + 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"])); - $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); - - $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"]; @@ -498,1146 +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()); -} - -/** - * @brief Updates the gcontact table with actor data from the conversation - * - * @param object $actor The actor object that contains the contact data - */ -function ostatus_conv_fetch_actor($actor) { - - // We set the generation to "3" since the data here is not as reliable as the data we get on other occasions - $contact = array("network" => NETWORK_OSTATUS, "generation" => 3); - - if (isset($actor->url)) - $contact["url"] = $actor->url; - - if (isset($actor->displayName)) - $contact["name"] = $actor->displayName; - - if (isset($actor->portablecontacts_net->displayName)) - $contact["name"] = $actor->portablecontacts_net->displayName; - - if (isset($actor->portablecontacts_net->preferredUsername)) - $contact["nick"] = $actor->portablecontacts_net->preferredUsername; - - if (isset($actor->id)) - $contact["alias"] = $actor->id; - - if (isset($actor->summary)) - $contact["about"] = $actor->summary; - - if (isset($actor->portablecontacts_net->note)) - $contact["about"] = $actor->portablecontacts_net->note; - - if (isset($actor->portablecontacts_net->addresses->formatted)) - $contact["location"] = $actor->portablecontacts_net->addresses->formatted; - - - if (isset($actor->image->url)) - $contact["photo"] = $actor->image->url; - - if (isset($actor->image->width)) - $avatarwidth = $actor->image->width; - - if (is_array($actor->status_net->avatarLinks)) - foreach ($actor->status_net->avatarLinks AS $avatar) { - if ($avatarsize < $avatar->width) { - $contact["photo"] = $avatar->url; - $avatarsize = $avatar->width; - } - } - - update_gcontact($contact); -} - - -function ostatus_completion($conversation_url, $uid, $item = array()) { - - $a = get_app(); - - $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) { - - // Update the gcontact table - ostatus_conv_fetch_actor($single_conv->actor); - - // Test - remove before flight - //$tempfile = tempnam(get_temppath(), "conversation"); - //file_put_contents($tempfile, json_encode($single_conv)); - - $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(NAMESPACE_ATOM1, 'feed'); - $doc->appendChild($root); - - $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); - - $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 - * -*/ - -function ostatus_entry($doc, $item, $owner, $toplevel = false, $repeat = false) { - $a = get_app(); - - 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); + return $source; } - $is_repeat = false; + /** + * @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) { -/* if (!$repeat) { - $repeated_guid = get_reshared_guid($item); + $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 ($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(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); + 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 90e94ede9e..7ffd47aa68 100644 --- a/include/poller.php +++ b/include/poller.php @@ -29,17 +29,8 @@ function poller_run(&$argv, &$argc){ if (poller_max_connections_reached()) return; - $load = current_load(); - if($load) { - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - - 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)) { @@ -205,6 +196,12 @@ function poller_max_connections_reached() { */ 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..12551efc42 100644 --- a/include/session.php +++ b/include/session.php @@ -69,7 +69,7 @@ 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`"); + //q("OPTIMIZE TABLE `sess_data`"); return true; }} diff --git a/include/socgraph.php b/include/socgraph.php index bd5b1817f0..33d62dc5b9 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -438,44 +438,47 @@ function poco_last_updated($profile, $force = false) { $noscrape = json_decode($noscraperet["body"], true); - $contact = array("url" => $profile, - "network" => $server[0]["network"], - "generation" => $gcontacts[0]["generation"]); + if (is_array($noscrape)) { + $contact = array("url" => $profile, + "network" => $server[0]["network"], + "generation" => $gcontacts[0]["generation"]); - $contact["name"] = $noscrape["fn"]; - $contact["community"] = $noscrape["comm"]; + $contact["name"] = $noscrape["fn"]; + $contact["community"] = $noscrape["comm"]; - if (isset($noscrape["tags"])) { - $keywords = implode(" ", $noscrape["tags"]); - if ($keywords != "") - $contact["keywords"] = $keywords; + if (isset($noscrape["tags"])) { + $keywords = implode(" ", $noscrape["tags"]); + if ($keywords != "") + $contact["keywords"] = $keywords; + } + + $location = formatted_location($noscrape); + if ($location) + $contact["location"] = $location; + + $contact["notify"] = $noscrape["dfrn-notify"]; + + // 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"]); + + $contact = array_merge($contact, $noscrape); + + update_gcontact($contact); + + return $noscrape["updated"]; } - - $location = formatted_location($noscrape); - if ($location) - $contact["location"] = $location; - - $contact["notify"] = $noscrape["dfrn-notify"]; - - // 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"]); - - $contact = array_merge($contact, $noscrape); - update_gcontact($contact); - - return $noscrape["updated"]; } } } @@ -719,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; @@ -727,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; @@ -854,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'", diff --git a/include/text.php b/include/text.php index c7681a4d58..c868499cc6 100644 --- a/include/text.php +++ b/include/text.php @@ -285,7 +285,7 @@ function paginate_data(&$a, $count=null) { if (($a->page_offset != "") AND !preg_match('/[?&].offset=/', $stripped)) $stripped .= "&offset=".urlencode($a->page_offset); - $url = z_root() . '/' . $stripped; + $url = $stripped; $data = array(); function _l(&$d, $name, $url, $text, $class="") { @@ -923,7 +923,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { if($redirect) { $a = get_app(); - $redirect_url = z_root() . '/redir/' . $contact['id']; + $redirect_url = 'redir/' . $contact['id']; if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) { $redir = true; $url = $redirect_url; @@ -964,13 +964,13 @@ if(! function_exists('search')) { * @param string $url search url * @param boolean $savedsearch show save search button */ -function search($s,$id='search-box',$url='/search',$save = false, $aside = true) { +function search($s,$id='search-box',$url='search',$save = false, $aside = true) { $a = get_app(); $values = array( '$s' => $s, '$id' => $id, - '$action_url' => $a->get_baseurl((stristr($url,'network')) ? true : false) . $url, + '$action_url' => $url, '$search_label' => t('Search'), '$save_label' => t('Save'), '$savedsearch' => feature_enabled(local_user(),'savedsearch'), @@ -1148,41 +1148,41 @@ function smilies($s, $sample = false) { ); $icons = array( - '<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', - '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, 'string' => $s); @@ -1305,7 +1305,7 @@ function redir_private_images($a, &$item) { if((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { //logger("redir_private_images: redir"); - $img_url = z_root() . '/redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link']; + $img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link']; $item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']); } } @@ -1421,7 +1421,7 @@ function prepare_body(&$item,$attach = false, $preview = false) { $mime = $mtch[3]; if((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) - $the_url = z_root() . '/redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1]; + $the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1]; else $the_url = $mtch[1]; @@ -1596,7 +1596,7 @@ function get_cats_and_terms($item) { $categories[] = array( 'name' => xmlify(file_tag_decode($mtch[1])), 'url' => "#", - 'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""), + 'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&cat=' . xmlify(file_tag_decode($mtch[1])):""), 'first' => $first, 'last' => false ); @@ -1614,7 +1614,7 @@ function get_cats_and_terms($item) { $folders[] = array( 'name' => xmlify(file_tag_decode($mtch[1])), 'url' => "#", - 'removeurl' => ((local_user() == $item['uid'])?z_root() . '/filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""), + 'removeurl' => ((local_user() == $item['uid'])?'filerm/' . $item['id'] . '?f=&term=' . xmlify(file_tag_decode($mtch[1])):""), 'first' => $first, 'last' => false ); @@ -1639,15 +1639,15 @@ function get_plink($item) { if ($a->user['nickname'] != "") { $ret = array( - //'href' => z_root()."/display/".$a->user['nickname']."/".$item['id'], - 'href' => z_root()."/display/".$item['guid'], - 'orig' => z_root()."/display/".$item['guid'], + //'href' => "display/".$a->user['nickname']."/".$item['id'], + 'href' => "display/".$item['guid'], + 'orig' => "display/".$item['guid'], 'title' => t('View on separate page'), 'orig_title' => t('view on separate page'), ); if (x($item,'plink')) { - $ret["href"] = $item['plink']; + $ret["href"] = $a->remove_baseurl($item['plink']); $ret["title"] = t('link to source'); } 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 8471735d01..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'] .= ''; @@ -423,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/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_request.php b/mod/dfrn_request.php index 2741ad59b4..837eec2dd2 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -42,8 +42,10 @@ function dfrn_request_init(&$a) { if(! function_exists('dfrn_request_post')) { function dfrn_request_post(&$a) { - if(($a->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..5836efbe52 100644 --- a/mod/fbrowser.php +++ b/mod/fbrowser.php @@ -74,10 +74,18 @@ function fbrowser_content($a){ $filename_e = $rr['filename']; } + // Take the second largest picture as preview + $p = q("SELECT `scale` FROM `photo` WHERE `resource-id` = '%s' AND `scale` > %d ORDER BY `resource-id`, `scale` LIMIT 1", + dbesc($rr['resource-id']), intval($rr['hiq'])); + 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/item.php b/mod/item.php index 2ade524a05..14c8203c98 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) { @@ -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; @@ -1245,42 +1246,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 0010a3d824..a9f369a894 100644 --- a/mod/network.php +++ b/mod/network.php @@ -149,10 +149,10 @@ 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') ? ForumManager::widget(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'] .= 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 1f7105b769..4be1a740cd 100644 --- a/mod/noscrape.php +++ b/mod/noscrape.php @@ -28,7 +28,7 @@ function noscrape_init(&$a) { $json_info = array( 'fn' => $a->profile['name'], 'addr' => $a->profile['addr'], - 'nick' => $a->user['nickname'], + 'nick' => $which, 'key' => $a->profile['pubkey'], 'homepage' => $a->get_baseurl()."/profile/{$which}", 'comm' => (x($a->profile,'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY), 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/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 2eb94576b3..544aa446bb 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -207,7 +207,7 @@ function ping_init(&$a) { call_hooks('ping_xmlize', $n); $notsxml = '%s'."\n"; return sprintf ( $notsxml, intval($n['id']), - xmlify($n['href']), xmlify($n['name']), xmlify($n['url']), xmlify($n['photo']), + 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']) ); 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/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/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/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/global.css b/view/global.css index 8646bf8e44..41af643ecc 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; @@ -277,20 +303,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 { @@ -302,10 +328,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 { @@ -329,25 +355,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 b2b88bc72f..a9df298ca9 100644 --- a/view/it/messages.po +++ b/view/it/messages.po @@ -16,7 +16,7 @@ msgstr "" "Project-Id-Version: friendica\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-01-24 06:49+0100\n" -"PO-Revision-Date: 2016-01-30 08:43+0000\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" @@ -41,8 +41,8 @@ msgstr "Forum" #, php-format msgid "%d contact edited." msgid_plural "%d contacts edited." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%d contatto modificato." +msgstr[1] "%d contatti modificati" #: mod/contacts.php:159 mod/contacts.php:383 msgid "Could not access contact record." @@ -887,7 +887,7 @@ msgstr "Rimuovi" #: mod/ostatus_subscribe.php:14 msgid "Subscribing to OStatus contacts" -msgstr "" +msgstr "Iscrizione a contatti OStatus" #: mod/ostatus_subscribe.php:25 msgid "No contact provided." @@ -1943,7 +1943,7 @@ msgstr "Ispeziona Coda di invio" #: mod/admin.php:163 mod/admin.php:354 msgid "Federation Statistics" -msgstr "" +msgstr "Statistiche sulla Federazione" #: mod/admin.php:177 mod/admin.php:188 mod/admin.php:1792 msgid "Logs" @@ -1951,7 +1951,7 @@ msgstr "Log" #: mod/admin.php:178 mod/admin.php:1859 msgid "View Logs" -msgstr "" +msgstr "Vedi i log" #: mod/admin.php:179 msgid "probe address" @@ -1982,7 +1982,7 @@ 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 "" +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 "" diff --git a/view/it/strings.php b/view/it/strings.php index 606ae1e16c..131e03080b 100644 --- a/view/it/strings.php +++ b/view/it/strings.php @@ -8,8 +8,8 @@ function string_plural_select_it($n){ $a->strings["Network:"] = "Rete:"; $a->strings["Forum"] = "Forum"; $a->strings["%d contact edited."] = array( - 0 => "", - 1 => "", + 0 => "%d contatto modificato.", + 1 => "%d contatti modificati", ); $a->strings["Could not access contact record."] = "Non è possibile accedere al contatto."; $a->strings["Could not locate selected profile."] = "Non riesco a trovare il profilo selezionato."; @@ -186,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["Subscribing to OStatus contacts"] = ""; +$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."; @@ -419,16 +419,16 @@ $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"] = ""; +$a->strings["Federation Statistics"] = "Statistiche sulla Federazione"; $a->strings["Logs"] = "Log"; -$a->strings["View Logs"] = ""; +$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."] = ""; +$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:"] = ""; 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 17c459c4d8..fdf9a7716e 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -2,17 +2,17 @@ - - - - - + + + + + @@ -28,20 +28,20 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + '; $a->page['htmlhead'] .= '';; } diff --git a/view/theme/smoothly/style.css b/view/theme/smoothly/style.css index b9f094932d..87c7342c9d 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 { @@ -4406,6 +4430,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 023e419464..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; @@ -36,7 +42,7 @@ body, section, blockquote, blockquote.shared_content, #profile-jot-form, } #profile-jot-acl-wrapper, #event-notice, #event-wrapper, -#cboxLoadedContent, .contact-photo-menu { +#cboxLoadedContent, .contact-photo-menu, #contact-edit-status-wrapper { background-color: #252C33 !important; } 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/style.css b/view/theme/vier/style.css index e35556f541..e08e103b8a 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; } @@ -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; @@ -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; @@ -1432,8 +1432,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; @@ -2935,6 +2935,48 @@ 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; 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 c2669f5a93..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'] = ' @@ -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;