Merge remote-tracking branch 'upstream/develop' into 1601-api-statuses-lookup

This commit is contained in:
Michael Vogel 2016-04-05 23:35:33 +02:00
commit 542be50e26
152 changed files with 8519 additions and 7585 deletions

104
boot.php
View file

@ -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) {

View file

@ -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;
--

View file

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

View file

@ -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:
<pre>[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.</pre>
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:
<pre>
[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 ...
</pre>
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:
<pre>
[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 ... </pre>
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:
<pre>[noparse][b]bold[/b][/noparse]</pre> : [b]bold[/b]

View file

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

View file

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

View file

@ -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)

View file

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

View file

@ -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 (*)

View file

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

View file

@ -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)

View file

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

View file

@ -131,8 +131,7 @@ Au&szlig;erdem kann *url* die genaue url zu einer ogg Datei sein, die dann per H
<pre>[url]*url*[/url]</pre>
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:
<pre>[abstract]Total spannend! Unbedingt diesen Link anklicken![/abstract]
Hier erzähle ich euch eine total langweilige Geschichte, die ihr noch
nie hören wolltet.</pre>
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:
<pre>
[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 ...
</pre>
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:
<pre>
[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 ... </pre>
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 &uuml;ber BBCode Tags in einer Nachricht schreiben m&ouml;chtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu sch&uuml;tzen:
<pre>[noparse][b]fett[/b][/noparse]</pre> : [b]fett[/b]

View file

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

View file

@ -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 ##

View file

@ -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
*

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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]);

View file

@ -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;
}
?>

View file

@ -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`

View file

@ -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'),

View file

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

View file

@ -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();

View file

@ -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(

View file

@ -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;

View file

@ -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,'&amp;')))
$r->link = str_replace('&','&amp;', $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"])

View file

@ -1,1663 +1,1122 @@
<?php
/**
* @file include/diaspora.php
*
* @todo GET /people/9aed8882b9f64896/stream
* @brief The implementation of the diaspora protocol
*
* Checklist:
*
* Checked:
* - send status
* - send comment
* - send like
* - send mail
* - send status retraction
* - send comment retraction on own post
* - send like retraction on own post
* - send comment retraction on diaspora post
* - send like retraction on diaspora post
* - receive status
* - receive reshare
* - receive comment
* - receive like
* - receive connect request
* - receive profile data
* - receive mail
* - receive comment retraction
* - receive like retraction
* - relay comment
* - relay like
* - relay comment retraction from diaspora
* - relay comment retraction from friendica
* - relay like retraction from diaspora
* - relay like retraction from friendica
* - send share
*
* Should work:
* - receive account deletion
* - send unshare
*
* Unchecked:
*/
require_once('include/crypto.php');
require_once('include/items.php');
require_once('include/bb2diaspora.php');
require_once('include/contact_selectors.php');
require_once('include/queue_fn.php');
require_once('include/lock.php');
require_once('include/threads.php');
require_once('mod/share.php');
require_once('include/enotify.php');
require_once("include/items.php");
require_once("include/bb2diaspora.php");
require_once("include/Scrape.php");
require_once("include/Contact.php");
require_once("include/Photo.php");
require_once("include/socgraph.php");
require_once("include/group.php");
require_once("include/xml.php");
require_once("include/datetime.php");
require_once("include/queue_fn.php");
function diaspora_dispatch_public($msg) {
/**
* @brief This class contain functions to create and send Diaspora XML files
*
*/
class diaspora {
$enabled = intval(get_config('system','diaspora_enabled'));
if(! $enabled) {
logger('mod-diaspora: disabled');
return;
}
/**
* @brief Return a list of relay servers
*
* This is an experimental Diaspora feature.
*
* @return array of relay servers
*/
public static function relay_list() {
// Use a dummy importer to import the data for the public copy
$importer = array("uid" => 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('<activity_streams-photo>','</activity_streams-photo>'),
array('<asphoto>','</asphoto>'),
$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
<?xml version='1.0' encoding='UTF-8'?>
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
<header>
<author_id>$handle</author_id>
</header>
<me:env>
<me:encoding>base64url</me:encoding>
<me:alg>RSA-SHA256</me:alg>
<me:data type="application/xml">$data</me:data>
<me:sig>$sig</me:sig>
</me:env>
</diaspora>
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
<decrypted_header>
<iv>$b_inner_iv</iv>
<aes_key>$b_inner_aes_key</aes_key>
<author_id>$handle</author_id>
</decrypted_header>
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 = '<encrypted_header>' . $cipher_json . '</encrypted_header>';
$magic_env = <<< EOT
<?xml version='1.0' encoding='UTF-8'?>
<diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env" >
$encrypted_header
<me:env>
<me:encoding>base64url</me:encoding>
<me:alg>RSA-SHA256</me:alg>
<me:data type="application/xml">$data</me:data>
<me:sig>$sig</me:sig>
</me:env>
</diaspora>
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
*
* <decrypted_header>
* <iv>8e+G2+ET8l5BPuW0sVTnQw==</iv>
* <aes_key>UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU=</aes_key>
***** OBSOLETE
* <author>
* <name>Ryan Hughes</name>
* <uri>acct:galaxor@diaspora.pirateship.org</uri>
* </author>
***** CURRENT
* <author_id>galaxor@diaspora.priateship.org</author_id>
***** END DIFFS
* </decrypted_header>
*/
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'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>'
. '<id>' . $contact['url'] . '/' . $contact['name'] . '</id>';
$arr['object'] .= '<link>' . xmlify('<link rel="alternate" type="text/html" href="' . $contact['url'] . '" />' . "\n");
$arr['object'] .= xmlify('<link rel="photo" type="image/jpeg" href="' . $contact['thumb'] . '" />' . "\n");
$arr['object'] .= '</link></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('<activity_streams-photo>','</activity_streams-photo>'),array('<asphoto>','</asphoto>'),$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 = '<link rel="alternate" type="text/html" href="'.App::get_baseurl()."/display/".$importer["nickname"]."/".$parent_item["id"].'" />';
$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 = '<link rel="alternate" type="text/html" href="'.$contact["url"].'" />'."\n".
'<link rel="photo" type="image/jpeg" href="'.$contact["thumb"].'" />'."\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('<link rel="alternate" type="text/html" href="' . $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $parent_item['id'] . '" />' . "\n") ;
$body = $parent_item['body'];
$obj = <<< EOT
<object>
<type>$objtype</type>
<local>1</local>
<id>{$parent_item['uri']}</id>
<link>$link</link>
<title></title>
<content>$body</content>
</object>
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;
}
?>

View file

@ -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'));

View file

@ -1,55 +0,0 @@
<?php
require_once("boot.php");
require_once('include/diaspora.php');
function dsprphotoq_run($argv, $argc){
global $a, $db;
if(is_null($a)){
$a = new App;
}
if(is_null($db)){
@include(".htconfig.php");
require_once("include/dba.php");
$db = new dba($db_host, $db_user, $db_pass, $db_data);
unset($db_host, $db_user, $db_pass, $db_data);
};
logger("diaspora photo queue: running", LOGGER_DEBUG);
$r = q("SELECT * FROM dsprphotoq");
if(!$r)
return;
$dphotos = $r;
logger("diaspora photo queue: processing " . count($dphotos) . " photos");
foreach($dphotos as $dphoto) {
$r = array();
if ($dphoto['uid'] == 0)
$r[0] = array("uid" => 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();
}

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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){

View file

@ -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;
}

View file

@ -1,4 +1,5 @@
<?php
require_once("include/diaspora.php");
/**
* @brief add/remove activity to an item
@ -151,9 +152,6 @@ function do_like($item_id, $verb) {
intval($like_item['id'])
);
// Save the author information for the unlike in case we need to relay to Diaspora
store_diaspora_like_retract_sig($activity, $item, $like_item, $contact);
$like_item_id = $like_item['id'];
proc_run('php',"include/notifier.php","like","$like_item_id");
@ -196,6 +194,7 @@ EOT;
$arr = array();
$arr['guid'] = get_guid(32);
$arr['uri'] = $uri;
$arr['uid'] = $owner_uid;
$arr['contact-id'] = $contact['id'];
@ -240,7 +239,7 @@ EOT;
// Save the author information for the like in case we need to relay to Diaspora
store_diaspora_like_sig($activity, $post_type, $contact, $post_id);
diaspora::store_like_signature($contact, $post_id);
$arr['id'] = $post_id;
@ -250,149 +249,3 @@ EOT;
return true;
}
function store_diaspora_like_retract_sig($activity, $item, $like_item, $contact) {
// 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
//
// If $item['resource-id'] exists, it means the item is a photo. Diaspora doesn't support
// likes on photos, so don't bother.
$enabled = intval(get_config('system','diaspora_enabled'));
if(! $enabled) {
logger('mod_like: diaspora support disabled, not storing like retraction signature', LOGGER_DEBUG);
return;
}
logger('mod_like: storing diaspora like retraction signature');
if(($activity === ACTIVITY_LIKE) && (! $item['resource-id'])) {
$signed_text = $like_item['guid'] . ';' . 'Like';
// 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)
);
*/
// 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)
$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;
}

View file

@ -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'));

View file

@ -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"

View file

@ -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)

View file

@ -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();

View file

@ -1,4 +1,8 @@
<?php
/**
* @file include/ostatus.php
*/
require_once("include/Contact.php");
require_once("include/threads.php");
require_once("include/html2bbcode.php");
@ -12,484 +16,486 @@ require_once("include/Scrape.php");
require_once("include/follow.php");
require_once("include/api.php");
require_once("mod/proxy.php");
require_once("include/xml.php");
define('OSTATUS_DEFAULT_POLL_INTERVAL', 30); // given in minutes
define('OSTATUS_DEFAULT_POLL_TIMEFRAME', 1440); // given in minutes
define('OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS', 14400); // given in minutes
/**
* @brief This class contain functions for the OStatus protocol
*
*/
class ostatus {
const OSTATUS_DEFAULT_POLL_INTERVAL = 30; // given in minutes
const OSTATUS_DEFAULT_POLL_TIMEFRAME = 1440; // given in minutes
const OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS = 14400; // given in minutes
function ostatus_check_follow_friends() {
$r = q("SELECT `uid`,`v` FROM `pconfig` WHERE `cat`='system' AND `k`='ostatus_legacy_contact' AND `v` != ''");
/**
* @brief Fetches author data
*
* @param object $xpath The xpath object
* @param object $context The xml context of the author detals
* @param array $importer user record of the importing user
* @param array $contact Called by reference, will contain the fetched contact
* @param bool $onlyfetch Only fetch the header without updating the contact entries
*
* @return array Array of author related entries for the item
*/
private function fetchauthor($xpath, $context, $importer, &$contact, $onlyfetch) {
if (!$r)
return;
$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;
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:
* <a href="https://status.pirati.ca/attachment/572819" title="https://status.pirati.ca/file/heluecht-20151202T222602-rd3u49p.gif"
* class="attachment thumbnail" id="attachment-572819" rel="nofollow external">https://status.pirati.ca/attachment/572819</a>
*
*/
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:
* <a href="https://status.pirati.ca/attachment/572819" title="https://status.pirati.ca/file/heluecht-20151202T222602-rd3u49p.gif"
* class="attachment thumbnail" id="attachment-572819" rel="nofollow external">https://status.pirati.ca/attachment/572819</a>
*
*/
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:
//
// <activity:object-type>http://activitystrea.ms/schema/1.0/bookmark</activity:object-type>
// <title>Historic Rocket Landing</title>
// <summary>Nur ein Testbeitrag.</summary>
// <link rel="related" href="https://www.youtube.com/watch?v=9pillaOxGCo"/>
// <link rel="preview" href="https://pirati.cc/file/thumb-4526-450x338-b48c8055f0c2fed0c3f67adc234c4b99484a90c42ed3cac73dc1081a4d0a7bc1.jpg.jpg" media:width="450" media:height="338"/>
//
// 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()));
}
?>

View file

@ -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'));

View file

@ -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",

141
include/post_update.php Normal file
View file

@ -0,0 +1,141 @@
<?php
/**
* @file include/post_update.php
*/
/**
* @brief Calls the post update functions
*/
function post_update() {
if (!post_update_1192())
return;
if (!post_update_1194())
return;
}
/**
* @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.
*
* @return bool "true" when the job is done
*/
function post_update_1192() {
// Was the script completed?
if (get_config("system", "post_update_version") >= 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);
}
?>

View file

@ -1,96 +1,6 @@
<?php
require_once('include/datetime.php');
require_once('include/diaspora.php');
require_once('include/queue_fn.php');
require_once('include/Contact.php');
function profile_change() {
$a = get_app();
if(! local_user())
return;
// $url = $a->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());
}

View file

@ -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'));

View file

@ -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']);

View file

@ -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;
}}

View file

@ -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'",

View file

@ -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(
'<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="&lt;3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;/3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-smile.gif" alt=":-)" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-wink.gif" alt=";-)" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-frown.gif" alt=":-(" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-P" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-p" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\"" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-x" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-X" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-laughing.gif" alt=":-D" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-|" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt=":-O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-thumbsup.gif" alt="\\o/" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o.O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O.o" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o_O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O_o" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-cry.gif" alt=":\'(" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-foot-in-mouth.gif" alt=":-!" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-undecided.gif" alt=":-/" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-embarassed.gif" alt=":-[" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-cool.gif" alt="8-)" />',
'<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":beer" />',
'<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":homebrew" />',
'<img class="smiley" src="' . z_root() . '/images/coffee.gif" alt=":coffee" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" />',
'<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" />',
'<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" />',
'<a href="http://friendica.com">~friendica <img class="smiley" src="' . z_root() . '/images/friendica-16.png" alt="~friendica" /></a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red" />matrix</a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red" />matrix</a>'
'<img class="smiley" src="' . z_root() . '/images/smiley-heart.gif" alt="&lt;3" title="&lt;3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;/3" title="&lt;/3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" title="&lt;\\3" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-smile.gif" alt=":-)" title=":-)" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-wink.gif" alt=";-)" title=";-)" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-frown.gif" alt=":-(" title=":-(" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-P" title=":-P" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-tongue-out.gif" alt=":-p" title=":-P" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-x" title=":-x" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-kiss.gif" alt=":-X" title=":-X" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-laughing.gif" alt=":-D" title=":-D" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-|" title="8-|" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt="8-O" title="8-O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-surprised.gif" alt=":-O" title="8-O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-thumbsup.gif" alt="\\o/" title="\\o/" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o.O" title="o.O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O.o" title="O.o" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="o_O" title="o_O" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-Oo.gif" alt="O_o" title="O_o" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-cry.gif" alt=":\'(" title=":\'("/>',
'<img class="smiley" src="' . z_root() . '/images/smiley-foot-in-mouth.gif" alt=":-!" title=":-!" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-undecided.gif" alt=":-/" title=":-/" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-embarassed.gif" alt=":-[" title=":-[" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-cool.gif" alt="8-)" title="8-)" />',
'<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":beer" title=":beer" />',
'<img class="smiley" src="' . z_root() . '/images/beer_mug.gif" alt=":homebrew" title=":homebrew" />',
'<img class="smiley" src="' . z_root() . '/images/coffee.gif" alt=":coffee" title=":coffee" />',
'<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" title=":facepalm" />',
'<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" title=":like" />',
'<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" title=":dislike" />',
'<a href="http://friendica.com">~friendica <img class="smiley" src="' . z_root() . '/images/friendica-16.png" alt="~friendica" title="~friendica" /></a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red#" title="red#" />matrix</a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="red#matrix" title="red#matrix" />matrix</a>'
);
$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');
}

View file

@ -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));

131
include/xml.php Normal file
View file

@ -0,0 +1,131 @@
<?php
/**
* @file include/xml.php
*/
/**
* @brief This class contain functions to work with XML data
*
*/
class xml {
/**
* @brief Creates an XML structure out of a given array
*
* @param array $array The array of the XML structure that will be generated
* @param object $xml The createdXML will be returned by reference
* @param bool $remove_header Should the XML header be removed or not?
* @param array $namespaces List of namespaces
* @param bool $root - interally used parameter. Mustn't be used from outside.
*
* @return string The created XML
*/
public static function from_array($array, &$xml, $remove_header = false, $namespaces = array(), $root = true) {
if ($root) {
foreach($array as $key => $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);
}
}
?>

View file

@ -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'] .= '<script>var homebase="' . $homebase . '" ; </script>';
@ -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,

View file

@ -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 <scheme>://<domain>"));
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("<a href='http://embed.ly'>Embedly</a> 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')))
);
}
}

View file

@ -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'))

View file

@ -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;
}

View file

@ -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']) : '');

View file

@ -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'] = '<object><type>' . ACTIVITY_OBJ_PERSON . '</type><title>' . $contact['name'] . '</title>'
@ -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

View file

@ -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:'),

View file

@ -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'] . '<br />' : '');
@ -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,

View file

@ -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'])
);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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,

View file

@ -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);

View file

@ -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
}
}

View file

@ -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),

View file

@ -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 */
);

View file

@ -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("&", "&amp;", $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"];

View file

@ -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, '<div class="icon prev"></div>') ;
$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,
));

View file

@ -207,7 +207,7 @@ function ping_init(&$a) {
call_hooks('ping_xmlize', $n);
$notsxml = '<note id="%d" href="%s" name="%s" url="%s" photo="%s" date="%s" seen="%s" timestamp="%s" >%s</note>'."\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'])
);

View file

@ -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);

View file

@ -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']) ? '<strong>' . t('visible to everybody') . '</strong>'
: '<a href="' . $a->get_baseurl(true) . '/profperm/' . $rr['id'] . '" />' . t('Edit visibility') . '</a>')
: '<a href="'.'profperm/'.$rr['id'].'" />' . t('Edit visibility') . '</a>')
));
}
}

View file

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

View file

@ -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);
}

View file

@ -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)) {

View file

@ -1,5 +1,6 @@
<?php
require_once('include/group.php');
function get_theme_config_file($theme){
$a = get_app();
@ -39,7 +40,7 @@ function settings_init(&$a) {
$tabs = array(
array(
'label' => 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 .= '<span class="field_help">'.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.').'</span>';
$settings_connectors .= '</div>';
$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'] = '<meta http-equiv="refresh" content="0; URL='.$a->get_baseurl().'/ostatus_subscribe?url='.urlencode($legacy_contact).'">';
@ -1152,7 +1157,7 @@ function settings_content(&$a) {
info( t('Profile is <strong>not published</strong>.') . EOL );
//$subdir = ((strlen($a->get_path())) ? '<br />' . t('or') . ' ' . $a->get_baseurl(true) . '/profile/' . $nickname : '');
//$subdir = ((strlen($a->get_path())) ? '<br />' . t('or') . ' ' . 'profile/' . $nickname : '');
$tpl_addr = get_markup_template("settings_nick_set.tpl");

View file

@ -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]';

View file

@ -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('&#x2317;') . '[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]';

View file

@ -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 ",

View file

@ -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'),

94
util/createdoxygen.php Normal file
View file

@ -0,0 +1,94 @@
#!/usr/bin/php
<?php
/**
* @file util/createdoxygen.php
* @brief Adds a doxygen header to functions
*/
if (count($_SERVER["argv"]) < 2)
die("usage: createdoxygen.php file\n");
$file = $_SERVER["argv"][1];
$data = file_get_contents($file);
$lines = explode("\n", $data);
$previous = "";
foreach ($lines AS $line) {
$line = rtrim(trim($line, "\r"));
if (strstr(strtolower($line), "function")) {
$detect = strtolower(trim($line));
$detect = implode(" ", explode(" ", $detect));
$found = false;
if (substr($detect, 0, 9) == "function ")
$found = true;
if (substr($detect, 0, 17) == "private function ")
$found = true;
if (substr($detect, 0, 23) == "public static function ")
$found = true;
if (substr($detect, 0, 10) == "function (")
$found = false;
if ($found and (trim($previous) == "*/"))
$found = false;
if ($found and !strstr($detect, "{"))
$found = false;
if ($found) {
echo add_documentation($line);
}
}
echo $line."\n";
$previous = $line;
}
/**
* @brief Adds a doxygen header
*
* @param string $line The current line of the document
*
* @return string added doxygen header
*/
function add_documentation($line) {
$trimmed = ltrim($line);
$length = strlen($line) - strlen($trimmed);
$space = substr($line, 0, $length);
$block = $space."/**\n".
$space." * @brief \n".
$space." *\n"; /**/
$left = strpos($line, "(");
$line = substr($line, $left + 1);
$right = strpos($line, ")");
$line = trim(substr($line, 0, $right));
if ($line != "") {
$parameters = explode(",", $line);
foreach ($parameters AS $parameter) {
$parameter = trim($parameter);
$splitted = explode("=", $parameter);
$block .= $space." * @param ".trim($splitted[0], "& ")."\n";
}
if (count($parameters) > 0)
$block .= $space." *\n";
}
$block .= $space." * @return \n".
$space." */\n";
return $block;
}
?>

View file

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 <strong>may</strong> 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 ""
"<strong>WARNING: This is highly advanced</strong> 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 <strong>now</strong> 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 <em>Auto Discovered Contact Directory</em> 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 <scheme>://<domain>"
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 - <a href='#' "
"id='cnftheme'>change theme settings</a>"
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</a> 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 ""
"<a href='http://embed.ly'>Embedly</a> 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 <strong>not published</strong>."
msgstr ""
#: mod/settings.php:1160
#: mod/settings.php:1164
#, php-format
msgid "Your Identity Address is <strong>'%s'</strong> 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 <em>interesting</em> 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 <strong>this</"
"strong> 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, <a href=\"%s/siteinfo"
"\">follow this link to find a public Friendica site and join us today</a>."
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 &ldquo;%3$s&rdquo;"
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 "<span class=\"heart\">&hearts;</span> 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 <strong>public</strong> profile.<br />It <strong>may</strong> "
"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</a>"
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 ""

View file

@ -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;
}

View file

@ -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 <strk@keybit.net>\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 ""

View file

@ -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 <em>Auto Discovered Contact Directory</em> 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:"] = "";

View file

@ -19,7 +19,7 @@
<script>
var FedData = [
{{foreach $counts as $c}}
{ value: {{$c[0]['total']}}, label: "{{$c[0]['platform']}}", color: "#90EE90", highlight: "#EE90A1", },
{ value: {{$c[0]['total']}}, label: "{{$c[0]['platform']}}", color: '{{$c[3]}}', highlight: "#EE90A1", },
{{/foreach}}
];
var ctx = document.getElementById("FederationChart").getContext("2d");
@ -40,7 +40,7 @@ var myDoughnutChart = new Chart(ctx).Doughnut(FedData, { animateRotate : false,
<script>
var {{$c[2]}}data = [
{{foreach $c[1] as $v}}
{ value: {{$v['total']}}, label: '{{$v['version']}}', color: "#90EE90", highlight: "#EE90A1",},
{ value: {{$v['total']}}, label: '{{$v['version']}}', color: "{{$c[3]}}", highlight: "#EE90A1",},
{{/foreach}}
];
var ctx = document.getElementById("{{$c[2]}}Chart").getContext("2d");

View file

@ -87,6 +87,7 @@
{{if $thread_allow.2}}
{{include file="field_checkbox.tpl" field=$ostatus_disabled}}
{{include file="field_select.tpl" field=$ostatus_poll_interval}}
{{include file="field_checkbox.tpl" field=$ostatus_full_threads}}
{{else}}
<div class='field checkbox' id='div_id_{{$ostatus_disabled.0}}'>
<label for='id_{{$ostatus_disabled.0}}'>{{$ostatus_disabled.1}}</label>
@ -153,6 +154,12 @@
{{include file="field_checkbox.tpl" field=$old_pager}}
<div class="submit"><input type="submit" name="page_site" value="{{$submit|escape:'html'}}" /></div>
<h3>{{$worker_title}}</h3>
{{include file="field_checkbox.tpl" field=$worker}}
{{include file="field_input.tpl" field=$worker_queues}}
{{include file="field_checkbox.tpl" field=$worker_dont_fork}}
<div class="submit"><input type="submit" name="page_site" value="{{$submit|escape:'html'}}" /></div>
</form>
{{* separate form for relocate... *}}

View file

@ -1,104 +1,98 @@
{{if $header}}<h2>{{$header}}</h2>{{/if}}
<div id="contact-edit-wrapper" >
{{* Insert Tab-Nav *}}
{{$tab_str}}
<div id="contact-edit-drop-link" >
<a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();" title="{{$delete}}" onmouseover="imgbright(this);" onmouseout="imgdull(this);"></a>
</div>
<div id="contact-edit-drop-link-end"></div>
<div id="contact-edit-nav-wrapper" >
<div id="contact-edit-links">
<ul>
{{if $relation_text}}
<li><div id="contact-edit-rel">{{$relation_text}}</div></li>
{{/if}}
{{if $lost_contact}}
<li><div id="lost-contact-message">{{$lost_contact}}</div></li>
{{/if}}
{{if $insecure}}
<li><div id="insecure-message">{{$insecure}}</div></li>
{{/if}}
{{if $blocked}}
<li><div id="block-message">{{$blocked}}</div></li>
{{/if}}
{{if $ignored}}
<li><div id="ignore-message">{{$ignored}}</div></li>
{{/if}}
{{if $archived}}
<li><div id="archive-message">{{$archived}}</div></li>
{{/if}}
</ul>
<div id="contact-edit-status-wrapper">
<span id="contact-edit-contact-status">{{$contact_status}}</span>
<ul>
{{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
<div id="contact-edit-actions">
<a class="btn" rel="#contact-actions-menu" href="#" id="contact-edit-actions-button">{{$contact_action_button}}</a>
{{if $common_text}}
<li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
{{/if}}
{{if $all_friends}}
<li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
{{/if}}
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
{{if $lblsuggest}}<li role="menuitem"><a href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
{{if $poll_enabled}}<li role="menuitem"><a href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
<li class="divider"></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
</ul>
</div>
{{* Block with status information about the contact *}}
<ul>
{{if $relation_text}}<li><div id="contact-edit-rel">{{$relation_text}}</div></li>{{/if}}
<!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
{{if $lblsuggest}}
<li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
{{/if}}
{{if $follow}}
<li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>
{{/if}}
{{if $poll_enabled}}
<li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
{{if $poll_interval}}
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
{{/if}}
</li>
{{/if}}
</ul>
{{if $lost_contact}}<li><div id="lost-contact-message">{{$lost_contact}}</div></li>{{/if}}
{{if $insecure}}<li><div id="insecure-message">{{$insecure}}</div></li> {{/if}}
{{if $blocked}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
{{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
{{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
</ul>
<ul>
<!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
{{if $follow}}<li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>{{/if}}
</ul>
</div> {{* End of contact-edit-status-wrapper *}}
{{* Some information about the contact from the profile *}}
<dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$url}}">{{$profileurl}}</a></dd></dl>
{{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location}}</dd></dl>{{/if}}
{{if $keywords}}<dl><dt>{{$keywords_label}}</dt><dd>{{$keywords}}</dd></dl>{{/if}}
{{if $about}}<dl><dt>{{$about_label}}</dt><dd>{{$about}}</dd></dl>{{/if}}
</div>
</div>
<div id="contact-edit-nav-end"></div>
</div>{{* End of contact-edit-links *}}
<hr />
<div id="contact-edit-links-end"></div>
<form action="contacts/{{$contact_id}}" method="post" >
<input type="hidden" name="contact_id" value="{{$contact_id}}">
<hr />
<div id="contact-edit-poll-wrapper">
{{if $poll_enabled}}
<div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
{{if $poll_interval}}
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
<h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
<div id="contact-edit-settings">
<form action="contacts/{{$contact_id}}" method="post" >
<input type="hidden" name="contact_id" value="{{$contact_id}}">
<div id="contact-edit-end" ></div>
{{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}}
<div id="contact-edit-info-wrapper">
<h4>{{$lbl_info1}}</h4>
<textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
</div>
<div id="contact-edit-info-end"></div>
{{if $profile_select}}
<div id="contact-edit-profile-select-text">
<h4>{{$lbl_vis1}}</h4>
<p>{{$lbl_vis2}}</p>
</div>
{{$profile_select}}
<div id="contact-edit-profile-select-end"></div>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
{{/if}}
<span id="contact-edit-update-now" class="button"><a href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
{{/if}}
</div>
<div id="contact-edit-end" ></div>
{{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}}
<div id="contact-edit-info-wrapper">
<h4>{{$lbl_info1}}</h4>
<textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
</div>
<div id="contact-edit-info-end"></div>
{{if $profile_select}}
<div id="contact-edit-profile-select-text">
<h4>{{$lbl_vis1}}</h4>
<p>{{$lbl_vis2}}</p>
</div>
{{$profile_select}}
<div id="contact-edit-profile-select-end"></div>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
{{/if}}
</form>
</form>
</div>
</div>{{* End of contact-edit-nav-wrapper *}}
</div>

View file

@ -18,13 +18,13 @@
{{/if}}
{{if $request}}
<form action="{{$request}}" method="post" />
<form action="{{$request}}" method="post">
{{else}}
<form action="dfrn_request/{{$nickname}}" method="post" />
<form action="dfrn_request/{{$nickname}}" method="post">
{{/if}}
{{if $photo}}
<img src="{{$photo}}" alt="" id="dfrn-request-photo">
<img src="{{$photo}}" alt="" id="dfrn-request-photo" />
{{/if}}
{{if $url}}<dl><dt>{{$url_label}}</dt><dd><a target="blank" href="{{$zrl}}">{{$url}}</a></dd></dl>{{/if}}

View file

@ -1,9 +0,0 @@
<decrypted_hdeader>
<iv>{{$inner_iv}}</iv>
<aes_key>{{$inner_key}}</aes_key>
<author>
<name>{{$author_name}}</name>
<uri>{{$author_uri}}</uri>
</author>
</decrypted_header>

View file

@ -1,12 +0,0 @@
<XML>
<post>
<comment>
<guid>{{$guid}}</guid>
<parent_guid>{{$parent_guid}}</parent_guid>
<author_signature>{{$authorsig}}</author_signature>
<text>{{$body}}</text>
<diaspora_handle>{{$handle}}</diaspora_handle>
</comment>
</post>
</XML>

View file

@ -1,13 +0,0 @@
<XML>
<post>
<comment>
<guid>{{$guid}}</guid>
<parent_guid>{{$parent_guid}}</parent_guid>
<parent_author_signature>{{$parentsig}}</parent_author_signature>
<author_signature>{{$authorsig}}</author_signature>
<text>{{$body}}</text>
<diaspora_handle>{{$handle}}</diaspora_handle>
</comment>
</post>
</XML>

View file

@ -1,30 +0,0 @@
<XML>
<post>
<conversation>
<guid>{{$conv.guid}}</guid>
<subject>{{$conv.subject}}</subject>
<created_at>{{$conv.created_at}}</created_at>
{{foreach $conv.messages as $msg}}
<message>
<guid>{{$msg.guid}}</guid>
<parent_guid>{{$msg.parent_guid}}</parent_guid>
{{if $msg.parent_author_signature}}
<parent_author_signature>{{$msg.parent_author_signature}}</parent_author_signature>
{{/if}}
<author_signature>{{$msg.author_signature}}</author_signature>
<text>{{$msg.text}}</text>
<created_at>{{$msg.created_at}}</created_at>
<diaspora_handle>{{$msg.diaspora_handle}}</diaspora_handle>
<conversation_guid>{{$msg.conversation_guid}}</conversation_guid>
</message>
{{/foreach}}
<diaspora_handle>{{$conv.diaspora_handle}}</diaspora_handle>
<participant_handles>{{$conv.participant_handles}}</participant_handles>
</conversation>
</post>
</XML>

View file

@ -1,13 +0,0 @@
<XML>
<post>
<like>
<positive>{{$positive}}</positive>
<guid>{{$guid}}</guid>
<target_type>{{$target_type}}</target_type>
<parent_guid>{{$parent_guid}}</parent_guid>
<author_signature>{{$authorsig}}</author_signature>
<diaspora_handle>{{$handle}}</diaspora_handle>
</like>
</post>
</XML>

View file

@ -1,14 +0,0 @@
<XML>
<post>
<like>
<positive>{{$positive}}</positive>
<guid>{{$guid}}</guid>
<target_type>{{$target_type}}</target_type>
<parent_guid>{{$parent_guid}}</parent_guid>
<parent_author_signature>{{$parentsig}}</parent_author_signature>
<author_signature>{{$authorsig}}</author_signature>
<diaspora_handle>{{$handle}}</diaspora_handle>
</like>
</post>
</XML>

View file

@ -1,17 +0,0 @@
<XML>
<post>
<message>
<guid>{{$msg.guid}}</guid>
<parent_guid>{{$msg.parent_guid}}</parent_guid>
{{if $msg.parent_author_signature}}
<parent_author_signature>{{$msg.parent_author_signature}}</parent_author_signature>
{{/if}}
<author_signature>{{$msg.author_signature}}</author_signature>
<text>{{$msg.text}}</text>
<created_at>{{$msg.created_at}}</created_at>
<diaspora_handle>{{$msg.diaspora_handle}}</diaspora_handle>
<conversation_guid>{{$msg.conversation_guid}}</conversation_guid>
</message>
</post>
</XML>

View file

@ -1,14 +0,0 @@
<XML>
<post>
<photo>
<guid>{{$guid}}</guid>
<diaspora_handle>{{$handle}}</diaspora_handle>
<public>{{$public}}</public>
<created_at>{{$created_at}}</created_at>
<remote_photo_path>{{$path}}</remote_photo_path>
<remote_photo_name>{{$filename}}</remote_photo_name>
<status_message_guid>{{$msg_guid}}</status_message_guid>
</photo>
</post>
</XML>

View file

@ -1,13 +0,0 @@
<XML>
<post>
<status_message>
<raw_message>{{$body}}</raw_message>
<guid>{{$guid}}</guid>
<diaspora_handle>{{$handle}}</diaspora_handle>
<public>{{$public}}</public>
<created_at>{{$created}}</created_at>
<provider_display_name>{{$provider}}</provider_display_name>
</status_message>
</post>
</XML>

View file

@ -1,17 +0,0 @@
<XML>
<post><profile>
<diaspora_handle>{{$handle}}</diaspora_handle>
<first_name>{{$first}}</first_name>
<last_name>{{$last}}</last_name>
<image_url>{{$large}}</image_url>
<image_url_medium>{{$medium}}</image_url_medium>
<image_url_small>{{$small}}</image_url_small>
<birthday>{{$dob}}</birthday>
<gender>{{$gender}}</gender>
<bio>{{$about}}</bio>
<location>{{$location}}</location>
<searchable>{{$searchable}}</searchable>
<tag_string>{{$tags}}</tag_string>
</profile></post>
</XML>

View file

@ -1,11 +0,0 @@
<XML>
<post>
<relayable_retraction>
<target_type>{{$type}}</target_type>
<target_guid>{{$guid}}</target_guid>
<target_author_signature>{{$signature}}</target_author_signature>
<sender_handle>{{$handle}}</sender_handle>
</relayable_retraction>
</post>
</XML>

View file

@ -1,12 +0,0 @@
<XML>
<post>
<relayable_retraction>
<parent_author_signature>{{$parentsig}}</parent_author_signature>
<target_guid>{{$guid}}</target_guid>
<target_type>{{$target_type}}</target_type>
<sender_handle>{{$handle}}</sender_handle>
<target_author_signature>{{$authorsig}}</target_author_signature>
</relayable_retraction>
</post>
</XML>

View file

@ -1,14 +0,0 @@
<XML>
<post>
<reshare>
<root_diaspora_id>{{$root_handle}}</root_diaspora_id>
<root_guid>{{$root_guid}}</root_guid>
<guid>{{$guid}}</guid>
<diaspora_handle>{{$handle}}</diaspora_handle>
<public>{{$public}}</public>
<created_at>{{$created}}</created_at>
<provider_display_name>{{$provider}}</provider_display_name>
</reshare>
</post>
</XML>

View file

@ -1,10 +0,0 @@
<XML>
<post>
<retraction>
<post_guid>{{$guid}}</post_guid>
<diaspora_handle>{{$handle}}</diaspora_handle>
<type>{{$type}}</type>
</retraction>
</post>
</XML>

View file

@ -1,9 +0,0 @@
<XML>
<post>
<request>
<sender_handle>{{$sender}}</sender_handle>
<recipient_handle>{{$recipient}}</recipient_handle>
</request>
</post>
</XML>

View file

@ -1,11 +0,0 @@
<XML>
<post>
<signed_retraction>
<target_guid>{{$guid}}</target_guid>
<target_type>{{$type}}</target_type>
<sender_handle>{{$handle}}</sender_handle>
<target_author_signature>{{$signature}}</target_author_signature>
</signed_retraction>
</post>
</XML>

View file

@ -1,5 +1,5 @@
<div class='field checkbox' id='div_id_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}}>
<span class='field_help'>{{$field.3}}</span>
<input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip' value="1" {{if $field.2}}checked="checked"{{/if}}>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -7,12 +7,12 @@
{{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{/foreach}}
</datalist> *}}
<input id="id_{{$field.0}}" type="text" value="{{$field.2}}">
<input id="id_{{$field.0}}" type="text" value="{{$field.2}}" aria-describedby='{{$field.0}}_tip'>
<select id="select_{{$field.0}}" onChange="$('#id_{{$field.0}}').val($(this).val())">
<option value="">{{$field.5}}</option>
{{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{$val}}</option>{{/foreach}}
</select>
<span class='field_help'>{{$field.3}}</span>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -1,6 +1,6 @@
<div class='field input' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}}>
<span class='field_help'>{{$field.3}}</span>
<input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,6 +2,6 @@
<div class='field checkbox'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}}>
<span class='field_help'>{{$field.4}}</span>
<input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}} aria-describedby='{{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.4}}</span>
</div>

View file

@ -1,6 +1,6 @@
<div class='field input openid' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<input name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}">
<span class='field_help'>{{$field.3}}</span>
<input name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -1,6 +1,6 @@
<div class='field password' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}}>
<span class='field_help'>{{$field.3}}</span>
<input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,6 +2,6 @@
<div class='field radio'>
<label for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label>
<input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked="true"{{/if}}>
<span class='field_help'>{{$field.3}}</span>
<input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked="true"{{/if}} aria-describedby={{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,6 +2,6 @@
<div class='field richtext'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext">{{$field.2}}</textarea>
<span class='field_help'>{{$field.3}}</span>
<textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext" aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,8 +2,8 @@
<div class='field select'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<select name='{{$field.0}}' id='id_{{$field.0}}'>
<select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
{{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
</select>
<span class='field_help'>{{$field.3}}</span>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,8 +2,8 @@
<div class='field select'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<select name='{{$field.0}}' id='id_{{$field.0}}'>
<select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
{{$field.4}}
</select>
<span class='field_help'>{{$field.3}}</span>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,6 +2,6 @@
<div class='field textarea'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<textarea name='{{$field.0}}' id='id_{{$field.0}}'>{{$field.2}}</textarea>
<span class='field_help'>{{$field.3}}</span>
<textarea name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,9 +2,9 @@
{{if $field.5}}<script>$(function(){ previewTheme($("#id_{{$field.0}}")[0]); });</script>{{/if}}
<div class='field select'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} >
<select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} aria-describedby='{{$field.0}}_tip'>
{{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
</select>
<span class='field_help'>{{$field.3}}</span>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
{{if $field.5}}<div id="theme-preview"></div>{{/if}}
</div>

View file

@ -2,7 +2,7 @@
<div class='field yesno'>
<label for='id_{{$field.0}}'>{{$field.1}}</label>
<div class='onoff' id="id_{{$field.0}}_onoff">
<input type="hidden" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}">
<input type="hidden" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'>
<a href="#" class='off'>
{{if $field.4}}{{$field.4.0}}{{else}}OFF{{/if}}
</a>
@ -10,5 +10,5 @@
{{if $field.4}}{{$field.4.1}}{{else}}ON{{/if}}
</a>
</div>
<span class='field_help'>{{$field.3}}</span>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
</div>

View file

@ -2,17 +2,17 @@
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<base href="{{$baseurl}}/" />
<meta name="generator" content="{{$generator}}" />
<link rel="stylesheet" href="{{$baseurl}}/view/global.css" type="text/css" media="all" />
<link rel="stylesheet" href="{{$baseurl}}/library/colorbox/colorbox.css" type="text/css" media="screen" />
<link rel="stylesheet" href="{{$baseurl}}/library/jgrowl/jquery.jgrowl.css" type="text/css" media="screen" />
<link rel="stylesheet" href="{{$baseurl}}/library/datetimepicker/jquery.datetimepicker.css" type="text/css" media="screen" />
<link rel="stylesheet" href="{{$baseurl}}/library/perfect-scrollbar/perfect-scrollbar.min.css" type="text/css" media="screen" />
<link rel="stylesheet" href="view/global.css" type="text/css" media="all" />
<link rel="stylesheet" href="library/colorbox/colorbox.css" type="text/css" media="screen" />
<link rel="stylesheet" href="library/jgrowl/jquery.jgrowl.css" type="text/css" media="screen" />
<link rel="stylesheet" href="library/datetimepicker/jquery.datetimepicker.css" type="text/css" media="screen" />
<link rel="stylesheet" href="library/perfect-scrollbar/perfect-scrollbar.min.css" type="text/css" media="screen" />
<link rel="stylesheet" type="text/css" href="{{$stylesheet}}" media="all" />
<!--
<link rel="shortcut icon" href="{{$baseurl}}/images/friendica-32.png" />
<link rel="apple-touch-icon" href="{{$baseurl}}/images/friendica-128.png"/>
<link rel="shortcut icon" href="images/friendica-32.png" />
<link rel="apple-touch-icon" href="images/friendica-128.png"/>
-->
<link rel="shortcut icon" href="{{$shortcut_icon}}" />
<link rel="apple-touch-icon" href="{{$touch_icon}}"/>
@ -28,20 +28,20 @@
<!--[if IE]>
<script type="text/javascript" src="https://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript" src="{{$baseurl}}/js/modernizr.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/jquery.js" ></script>
<!-- <script type="text/javascript" src="{{$baseurl}}/js/jquery-migrate.js" ></script>-->
<script type="text/javascript" src="{{$baseurl}}/js/jquery-migrate.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/jquery.textinputs.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/fk.autocomplete.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/library/colorbox/jquery.colorbox-min.js"></script>
<script type="text/javascript" src="{{$baseurl}}/library/jgrowl/jquery.jgrowl_minimized.js"></script>
<script type="text/javascript" src="{{$baseurl}}/library/datetimepicker/jquery.datetimepicker.js"></script>
<script type="text/javascript" src="{{$baseurl}}/library/tinymce/jscripts/tiny_mce/tiny_mce_src.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/library/perfect-scrollbar/perfect-scrollbar.jquery.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/acl.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/webtoolkit.base64.js" ></script>
<script type="text/javascript" src="{{$baseurl}}/js/main.js" ></script>
<script type="text/javascript" src="js/modernizr.js" ></script>
<script type="text/javascript" src="js/jquery.js" ></script>
<!-- <script type="text/javascript" src="js/jquery-migrate.js" ></script>-->
<script type="text/javascript" src="js/jquery-migrate.js" ></script>
<script type="text/javascript" src="js/jquery.textinputs.js" ></script>
<script type="text/javascript" src="js/fk.autocomplete.js" ></script>
<script type="text/javascript" src="library/colorbox/jquery.colorbox-min.js"></script>
<script type="text/javascript" src="library/jgrowl/jquery.jgrowl_minimized.js"></script>
<script type="text/javascript" src="library/datetimepicker/jquery.datetimepicker.js"></script>
<script type="text/javascript" src="library/tinymce/jscripts/tiny_mce/tiny_mce_src.js" ></script>
<script type="text/javascript" src="library/perfect-scrollbar/perfect-scrollbar.jquery.js" ></script>
<script type="text/javascript" src="js/acl.js" ></script>
<script type="text/javascript" src="js/webtoolkit.base64.js" ></script>
<script type="text/javascript" src="js/main.js" ></script>
<script>
var updateInterval = {{$update_interval}};

View file

@ -1,6 +1,7 @@
<form action="{{$dest_url}}" method="post" >
<fieldset>
<input type="hidden" name="auth-params" value="login" />
<div id="login_standard">
@ -29,7 +30,7 @@
<input type="hidden" name="{{$k}}" value="{{$v|escape:'html'}}" />
{{/foreach}}
</fieldset>
</form>

View file

@ -1,10 +1,10 @@
<h3>{{$title}}</h3>
{{foreach $options as $o}}
<dl>
<dt><a href="{{$baseurl}}/{{$o.0}}">{{$o.1}}</a></dt>
<dd>{{$o.2}}</dd>
</dl>
{{/foreach}}
<h3>{{$title}}</h3>
{{foreach $options as $o}}
<dl>
<dt><a href="{{$o.0}}">{{$o.1}}</a></dt>
<dd>{{$o.2}}</dd>
</dl>
{{/foreach}}

View file

@ -10,7 +10,6 @@
*/
function decaf_mobile_init(&$a) {
$a->theme_info = array();
$a->sourcename = 'Friendica mobile web';
$a->videowidth = 250;
$a->videoheight = 200;

View file

@ -83,6 +83,26 @@ blockquote {
margin-right: 5px;
}
ul.menu-popup {
position: absolute;
display: none;
width: auto;
margin: 2px 0 0;
padding: 0px;
list-style: none;
z-index: 100000;
border: 2px solid #444444;
background: #FFFFFF;
}
.menu-popup li a {
padding: 2px;
white-space: nowrap;
}
a.btn, a.btn:hover {
text-decoration: none;
color: inherit;
}
/* nav */
@ -140,12 +160,12 @@ nav #banner #logo-text a:hover { text-decoration: none; }
.nav-commlink, .nav-login-link {
display: block;
height: 15px;
display: block;
height: 15px;
margin-top: 67px;
margin-right: 2px;
//padding: 6px 10px;
padding: 6px 3px;
/*padding: 6px 10px;*/
padding: 6px 3px;
float: left;
bottom: 140px;
border: 1px solid #babdb6;
@ -244,7 +264,7 @@ section {
display:block;
float:left;
padding: 0.4em;
//margin-right: 1em;
/*margin-right: 1em;*/
margin-right: 3px ;
}
.tab.active {
@ -3371,17 +3391,6 @@ div.jGrowl div.info {
.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: 90px;
left: 200px;
}
#nav-notifications-menu {
width: 320px;
max-height: 400px;
@ -3391,6 +3400,8 @@ ul.menu-popup {
-webkit-border-radius: 5px;
border-radius:5px;
border: 1px solid #888;
top: 90px;
left: 200px;
}
#nav-notifications-menu .contactname { font-weight: bold; font-size: 0.9em; }
#nav-notifications-menu img { float: left; margin-right: 5px; }

View file

@ -2,7 +2,6 @@
function duepuntozero_init(&$a) {
$a->theme_info = array();
set_template_engine($a, 'smarty3');
$colorset = get_pconfig( local_user(), 'duepuntozero','colorset');

View file

@ -1,7 +1,6 @@
<?php
function facepark_init(&$a) {
$a->theme_info = array();
set_template_engine($a, 'smarty3');
$a->page['htmlhead'] .= <<< EOT

View file

@ -13,7 +13,6 @@
if($(listID).is(":visible")) {
$(listID).hide();
$(listID+"-wrapper").show();
alert($(listID+"-wrapper").attr("id"));
}
else {
$(listID).show();

View file

@ -139,6 +139,47 @@ blockquote {
margin-right: 5px;
}
.btn {
outline: none;
-moz-box-shadow: inset 0px 1px 0px 0px #ffffff;
-webkit-box-shadow: inset 0px 1px 0px 0px #ffffff;
box-shadow: inset 0px 1px 0px 0px #ffffff;
background-color: #ededed;
text-indent: 0;
border: 1px solid #dcdcdc;
display: inline-block;
color: #777777;
padding: 5px 10px;
text-align: center;
border-radius: 8px;
}
.menu-popup {
width: auto;
border: 2px solid #444444;
background: #FFFFFF;
position: absolute;
margin: 2px 0 0;
display: none;
z-index: 10000;
}
.menu-popup li a {
display: block;
padding: 2px;
}
.menu-popup li a:hover {
color: #FFFFFF;
background: #3465A4;
text-decoration: none;
}
ul.menu-popup li.divider {
height: 1px;
margin: 3px 0;
overflow: hidden;
background-color: #2d2d2d;
}
/* nav */
@ -2045,6 +2086,19 @@ input#profile-jot-email {
margin-left: 15px;
}
#contact-edit-status-wrapper {
padding: 10px;
border: 1px solid #aaa;
border-radius: 8px;
}
#contact-edit-contact-status {
font-weight: bold;
}
#contact-edit-actions {
float: right;
display: inline-block;
}
#contact-edit-wrapper {
margin-top: 10px;
}
@ -2059,14 +2113,10 @@ input#profile-jot-email {
}
#contact-edit-last-update-text {
float: left;
clear: left;
margin-top: 30px;
}
#contact-edit-poll-text {
float: left;
clear: left;
margin-top: 15px;
margin-bottom: 0px;
}

View file

@ -6,12 +6,6 @@
{{$tab_str}}
<div id="contact-edit-drop-link" >
<a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();" title="{{$delete}}" {{*onmouseover="imgbright(this);" onmouseout="imgdull(this);"*}}></a>
</div>
<div id="contact-edit-drop-link-end"></div>
<div class="vcard">
<div class="fn">{{$name}}</div>
<div id="profile-photo-wrapper"><img class="photo" style="width: 175px; height: 175px;" src="{{$photo}}" alt="{{$name}}" /></div>
@ -20,41 +14,49 @@
<div id="contact-edit-nav-wrapper" >
<div id="contact-edit-links">
<ul>
<li><div id="contact-edit-rel">{{$relation_text}}</div></li>
<li><div id="contact-edit-nettype">{{$nettype}}</div></li>
{{if $lost_contact}}
<li><div id="lost-contact-message">{{$lost_contact}}</div></li>
{{/if}}
{{if $insecure}}
<li><div id="insecure-message">{{$insecure}}</div></li>
{{/if}}
{{if $blocked}}
<li><div id="block-message">{{$blocked}}</div></li>
{{/if}}
{{if $ignored}}
<li><div id="ignore-message">{{$ignored}}</div></li>
{{/if}}
{{if $archived}}
<li><div id="archive-message">{{$archived}}</div></li>
{{/if}}
<div id="contact-edit-status-wrapper">
<span id="contact-edit-contact-status">{{$contact_status}}</span>
<li>&nbsp;</li>
<div id="contact-edit-actions">
<div class="btn" id="contact-edit-actions-button" onclick="openClose('contact-actions-menu')">{{$contact_action_button}}</div>
{{if $common_text}}
<li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
{{/if}}
{{if $all_friends}}
<li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
{{/if}}
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
{{if $lblsuggest}}<li role="menuitem"><a href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
{{if $poll_enabled}}<li role="menuitem"><a href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
<li class="divider"></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
</ul>
</div>
<ul>
<li><div id="contact-edit-rel">{{$relation_text}}</div></li>
<li><div id="contact-edit-nettype">{{$nettype}}</div></li>
{{if $poll_enabled}}
<div id="contact-edit-poll-wrapper">
<div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
</div>
{{/if}}
{{if $lost_contact}}
<li><div id="lost-contact-message">{{$lost_contact}}</div></li>
{{/if}}
{{if $insecure}}
<li><div id="insecure-message">{{$insecure}}</div></li>
{{/if}}
{{if $blocked}}
<li><div id="block-message">{{$blocked}}</div></li>
{{/if}}
{{if $ignored}}
<li><div id="ignore-message">{{$ignored}}</div></li>
{{/if}}
{{if $archived}}
<li><div id="archive-message">{{$archived}}</div></li>
{{/if}}
<li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li>
{{if $lblsuggest}}
<li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
{{/if}}
</ul>
</ul>
</div>
</div>
</div>
<div id="contact-edit-nav-end"></div>
@ -63,12 +65,6 @@
<form action="contacts/{{$contact_id}}" method="post" >
<input type="hidden" name="contact_id" value="{{$contact_id}}">
{{if $poll_enabled}}
<div id="contact-edit-poll-wrapper">
<div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
<span id="contact-edit-poll-text">{{$updpub}} {{$poll_interval}}</span> <span id="contact-edit-update-now" class="button"><a id="update_now_link" href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
</div>
{{/if}}
<div id="contact-edit-end" ></div>
{{include file="field_checkbox.tpl" field=$hidden}}

View file

@ -10,7 +10,6 @@
*/
function frost_mobile_init(&$a) {
$a->theme_info = array();
$a->sourcename = 'Friendica mobile web';
$a->videowidth = 250;
$a->videoheight = 200;

View file

@ -113,6 +113,51 @@ blockquote {
.pull-right { float: right }
.btn {
outline: none;
-moz-box-shadow: inset 0px 1px 0px 0px #ffffff;
-webkit-box-shadow: inset 0px 1px 0px 0px #ffffff;
box-shadow: inset 0px 1px 0px 0px #ffffff;
background-color: #ededed;
text-indent: 0;
border: 1px solid #dcdcdc;
display: inline-block;
color: #777777;
padding: 5px 10px;
text-align: center;
border-radius: 8px;
}
a.btn {
text-decoration: none;
color: inherit;
}
.menu-popup {
width: auto;
border: 2px solid #444444;
background: #FFFFFF;
position: absolute;
margin: 2px 0 0;
display: none;
z-index: 10000;
}
.menu-popup li a {
display: block;
padding: 2px;
}
.menu-popup li a:hover {
color: #FFFFFF;
background: #3465A4;
text-decoration: none;
}
ul.menu-popup li.divider {
height: 1px;
margin: 3px 0;
overflow: hidden;
background-color: #2d2d2d;
}
/* nav */
@ -1952,6 +1997,20 @@ input#dfrn-url {
margin-left: 15px;
}
#contact-edit-status-wrapper {
padding: 10px;
border: 1px solid #aaa;
border-radius: 8px;
}
#contact-edit-contact-status {
font-weight: bold;
}
#contact-edit-actions {
float: right;
display: inline-block;
}
#contact-edit-wrapper {
margin-top: 10px;
}
@ -1989,11 +2048,6 @@ input#dfrn-url {
margin-top: 5px;
}
#contact-edit-drop-link {
float: right;
margin-right: 20px;
}
#contact-edit-nav-end {
clear: both;
}

View file

@ -6,50 +6,51 @@
{{$tab_str}}
<div id="contact-edit-drop-link" >
<a href="contacts/{{$contact_id}}/drop" class="icon drophide" id="contact-edit-drop-link" onclick="return confirmDelete();" title="{{$delete}}" {{*onmouseover="imgbright(this);" onmouseout="imgdull(this);"*}}></a>
</div>
<div id="contact-edit-drop-link-end"></div>
<div id="contact-edit-nav-wrapper" >
<div id="contact-edit-links">
<ul>
<li><div id="contact-edit-rel">{{$relation_text}}</div></li>
<li><div id="contact-edit-nettype">{{$nettype}}</div></li>
{{if $lost_contact}}
<li><div id="lost-contact-message">{{$lost_contact}}</div></li>
{{/if}}
{{if $insecure}}
<li><div id="insecure-message">{{$insecure}}</div></li>
{{/if}}
{{if $blocked}}
<li><div id="block-message">{{$blocked}}</div></li>
{{/if}}
{{if $ignored}}
<li><div id="ignore-message">{{$ignored}}</div></li>
{{/if}}
{{if $archived}}
<li><div id="archive-message">{{$archived}}</div></li>
{{/if}}
<div id="contact-edit-status-wrapper">
<span id="contact-edit-contact-status">{{$contact_status}}</span>
<li>&nbsp;</li>
<div id="contact-edit-actions">
<a class="btn" rel="#contact-actions-menu" href="#" id="contact-edit-actions-button">{{$contact_action_button}}</a>
{{if $common_text}}
<li><div id="contact-edit-common"><a href="{{$common_link}}">{{$common_text}}</a></div></li>
{{/if}}
{{if $all_friends}}
<li><div id="contact-edit-allfriends"><a href="allfriends/{{$contact_id}}">{{$all_friends}}</a></div></li>
{{/if}}
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
{{if $lblsuggest}}<li role="menuitem"><a href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
{{if $poll_enabled}}<li role="menuitem"><a href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
<li class="divider"></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
</ul>
</div>
<ul>
<li><div id="contact-edit-rel">{{$relation_text}}</div></li>
<li><div id="contact-edit-nettype">{{$nettype}}</div></li>
{{if $poll_enabled}}
<div id="contact-edit-poll-wrapper">
<div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
</div>
{{/if}}
{{if $lost_contact}}
<li><div id="lost-contact-message">{{$lost_contact}}</div></li>
{{/if}}
{{if $insecure}}
<li><div id="insecure-message">{{$insecure}}</div></li>
{{/if}}
{{if $blocked}}
<li><div id="block-message">{{$blocked}}</div></li>
{{/if}}
{{if $ignored}}
<li><div id="ignore-message">{{$ignored}}</div></li>
{{/if}}
{{if $archived}}
<li><div id="archive-message">{{$archived}}</div></li>
{{/if}}
<li><a href="network/?cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li>
{{if $lblsuggest}}
<li><a href="fsuggest/{{$contact_id}}" id="contact-edit-suggest">{{$lblsuggest}}</a></li>
{{/if}}
</ul>
</ul>
</div>
</div>
</div>
<div id="contact-edit-nav-end"></div>
@ -58,12 +59,6 @@
<form action="contacts/{{$contact_id}}" method="post" >
<input type="hidden" name="contact_id" value="{{$contact_id}}">
{{if $poll_enabled}}
<div id="contact-edit-poll-wrapper">
<div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}} <span id="contact-edit-update-now" class="button"><a href="contacts/{{$contact_id}}/update" >{{$udnow}}</a></span>
</div>
{{/if}}
<div id="contact-edit-end" ></div>
{{include file="field_checkbox.tpl" field=$hidden}}

View file

@ -10,7 +10,6 @@
*/
function frost_init(&$a) {
$a->theme_info = array();
$a->videowidth = 400;
$a->videoheight = 330;
$a->theme_thread_allow = false;

View file

@ -463,7 +463,7 @@ a:hover {
text-decoration: underline;
}
blockquote {
background: #FFFFFF;
background: #ffffff;
padding: 1em;
margin-left: 1em;
border-left: 1em solid #e6e6e6;
@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
overflow: hidden;
text-overflow: ellipsis;
}
#contact-edit-status-wrapper {
border-color: #364e59;
}
/* editor */
.jothidden {
display: none;

View file

@ -463,7 +463,7 @@ a:hover {
text-decoration: underline;
}
blockquote {
background: #FFFFFF;
background: #ffffff;
padding: 1em;
margin-left: 1em;
border-left: 1em solid #e6e6e6;
@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
overflow: hidden;
text-overflow: ellipsis;
}
#contact-edit-status-wrapper {
border-color: #9ade00;
}
/* editor */
.jothidden {
display: none;

View file

@ -420,7 +420,7 @@
body {
font-family: Liberation Sans, helvetica, arial, clean, sans-serif;
font-size: 11px;
background-color: #F6ECF9;
background-color: #f6ecf9;
color: #2d2d2d;
margin: 50px 0 0 0;
display: table;
@ -463,7 +463,7 @@ a:hover {
text-decoration: underline;
}
blockquote {
background: #FFFFFF;
background: #ffffff;
padding: 1em;
margin-left: 1em;
border-left: 1em solid #e6e6e6;
@ -1655,6 +1655,9 @@ span[id^="showmore-wrap"] {
overflow: hidden;
text-overflow: ellipsis;
}
#contact-edit-status-wrapper {
border-color: #86608e;
}
/* editor */
.jothidden {
display: none;
@ -1753,7 +1756,7 @@ span[id^="showmore-wrap"] {
height: 20px;
width: 500px;
font-weight: bold;
border: 1px solid #F6ECF9;
border: 1px solid #f6ecf9;
}
#jot #jot-title:-webkit-input-placeholder {
font-weight: normal;
@ -1780,7 +1783,7 @@ span[id^="showmore-wrap"] {
margin: 0;
height: 20px;
width: 200px;
border: 1px solid #F6ECF9;
border: 1px solid #f6ecf9;
}
#jot #jot-category:hover {
border: 1px solid #999999;

View file

@ -408,19 +408,19 @@ aside {
.group-delete-wrapper {
float: right;
margin-right: 50px;
.drophide {
background-image: url('../../../images/icons/22/delete.png');
display: block; width: 22px; height: 22px;
opacity: 0.3;
position: relative;
top: -50px;
}
.drop {
background-image: url('../../../images/icons/22/delete.png');
display: block; width: 22px; height: 22px;
position: relative;
top: -50px;
}
.drophide {
background-image: url('../../../images/icons/22/delete.png');
display: block; width: 22px; height: 22px;
opacity: 0.3;
position: relative;
top: -50px;
}
.drop {
background-image: url('../../../images/icons/22/delete.png');
display: block; width: 22px; height: 22px;
position: relative;
top: -50px;
}
}
/*
#group-members {
@ -502,7 +502,7 @@ section {
}
.sparkle {
cursor: url('icons/lock.cur'), pointer;
cursor: url('icons/lock.cur'), pointer;
}
/* wall item */
@ -959,6 +959,7 @@ span[id^="showmore-wrap"] {
text-overflow: ellipsis;
}
#contact-edit-status-wrapper { border-color: @JotToolsOverBackgroundColor;}
/* editor */
.jothidden { display: none; }
#jot {

View file

@ -8,8 +8,6 @@
*/
function quattro_init(&$a) {
$a->theme_info = array();
$a->page['htmlhead'] .= '<script src="'.$a->get_baseurl().'/view/theme/quattro/tinycon.min.js"></script>';
$a->page['htmlhead'] .= '<script src="'.$a->get_baseurl().'/view/theme/quattro/js/quattro.js"></script>';;
}

View file

@ -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,

View file

@ -11,7 +11,6 @@
*/
function smoothly_init(&$a) {
$a->theme_info = array();
set_template_engine($a, 'smarty3');
$cssFile = null;

View file

@ -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"; }

View file

@ -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;
}

Binary file not shown.

BIN
view/theme/vier/font/fontawesome-webfont.eot Executable file → Normal file

Binary file not shown.

169
view/theme/vier/font/fontawesome-webfont.svg Executable file → Normal file
View file

@ -147,14 +147,14 @@
<glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
<glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1536 0q0 -52 -38 -90t-90 -38t-90 38t-38 90t38 90t90 38t90 -38t38 -90zM1664 1088v-512q0 -24 -16.5 -42.5t-40.5 -21.5l-1044 -122q13 -60 13 -70q0 -16 -24 -64h920q26 0 45 -19t19 -45 t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 11 8 31.5t16 36t21.5 40t15.5 29.5l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t19.5 -15.5t13 -24.5t8 -26t5.5 -29.5t4.5 -26h1201q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
<glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf082;" d="M1536 160q0 -119 -84.5 -203.5t-203.5 -84.5h-192v608h203l30 224h-233v143q0 54 28 83t96 29l132 1v207q-96 9 -180 9q-136 0 -218 -80.5t-82 -225.5v-166h-224v-224h224v-608h-544q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5v-960z" />
<glyph unicode="&#xf082;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-188v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-532q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960z" />
<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
@ -219,8 +219,8 @@
<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
<glyph unicode="&#xf0d4;" d="M829 318q0 -76 -58.5 -112.5t-139.5 -36.5q-41 0 -80.5 9.5t-75.5 28.5t-58 53t-22 78q0 46 25 80t65.5 51.5t82 25t84.5 7.5q20 0 31 -2q2 -1 23 -16.5t26 -19t23 -18t24.5 -22t19 -22.5t17 -26t9 -26.5t4.5 -31.5zM755 863q0 -60 -33 -99.5t-92 -39.5q-53 0 -93 42.5 t-57.5 96.5t-17.5 106q0 61 32 104t92 43q53 0 93.5 -45t58 -101t17.5 -107zM861 1120l88 64h-265q-85 0 -161 -32t-127.5 -98t-51.5 -153q0 -93 64.5 -154.5t158.5 -61.5q22 0 43 3q-13 -29 -13 -54q0 -44 40 -94q-175 -12 -257 -63q-47 -29 -75.5 -73t-28.5 -95 q0 -43 18.5 -77.5t48.5 -56.5t69 -37t77.5 -21t76.5 -6q60 0 120.5 15.5t113.5 46t86 82.5t33 117q0 49 -20 89.5t-49 66.5t-58 47.5t-49 44t-20 44.5t15.5 42.5t37.5 39.5t44 42t37.5 59.5t15.5 82.5q0 60 -22.5 99.5t-72.5 90.5h83zM1152 672h128v64h-128v128h-64v-128 h-128v-64h128v-160h64v160zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M735 740q0 -36 32 -70.5t77.5 -68t90.5 -73.5t77 -104t32 -142q0 -90 -48 -173q-72 -122 -211 -179.5t-298 -57.5q-132 0 -246.5 41.5t-171.5 137.5q-37 60 -37 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 42 -47.5 74t-15.5 73q0 36 21 85q-46 -4 -68 -4 q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q77 66 182.5 98t217.5 32h418l-138 -88h-131q74 -63 112 -133t38 -160q0 -72 -24.5 -129.5t-59 -93t-69.5 -65t-59.5 -61.5t-24.5 -66zM589 836q38 0 78 16.5t66 43.5q53 57 53 159q0 58 -17 125t-48.5 129.5 t-84.5 103.5t-117 41q-42 0 -82.5 -19.5t-65.5 -52.5q-47 -59 -47 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26zM591 -37q58 0 111.5 13t99 39t73 73t27.5 109q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -48 2 q-53 0 -105 -7t-107.5 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -70 35 -123.5t91.5 -83t119 -44t127.5 -14.5zM1401 839h213v-108h-213v-219h-105v219h-212v108h212v217h105v-217z" />
<glyph unicode="&#xf0d4;" d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf0d5;" horiz-adv-x="2304" d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
@ -275,7 +275,7 @@
<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
<glyph unicode="&#xf110;" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
<glyph unicode="&#xf110;" horiz-adv-x="1792" d="M526 142q0 -53 -37.5 -90.5t-90.5 -37.5q-52 0 -90 38t-38 90q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 -64q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1522 142q0 -52 -38 -90t-90 -38q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM558 1138q0 -66 -47 -113t-113 -47t-113 47t-47 113t47 113t113 47t113 -47t47 -113z M1728 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1088 1344q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1618 1138q0 -93 -66 -158.5t-158 -65.5q-93 0 -158.5 65.5t-65.5 158.5 q0 92 65.5 158t158.5 66q92 0 158 -66t66 -158z" />
<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
@ -362,7 +362,7 @@
<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
<glyph unicode="&#xf16c;" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
@ -399,7 +399,7 @@
<glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
<glyph unicode="&#xf194;" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf194;" d="M1292 898q10 216 -161 222q-231 8 -312 -261q44 19 82 19q85 0 74 -96q-4 -57 -74 -167t-105 -110q-43 0 -82 169q-13 54 -45 255q-30 189 -160 177q-59 -7 -164 -100l-81 -72l-81 -72l52 -67q76 52 87 52q57 0 107 -179q15 -55 45 -164.5t45 -164.5q68 -179 164 -179 q157 0 383 294q220 283 226 444zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
<glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
@ -410,9 +410,9 @@
<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
<glyph unicode="&#xf1a0;" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
<glyph unicode="&#xf1a1;" horiz-adv-x="1984" d="M831 572q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41t96.5 -41t40.5 -98zM1292 711q56 0 96.5 -41t40.5 -98q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41zM1984 722q0 -62 -31 -114t-83 -82q5 -33 5 -61 q0 -121 -68.5 -230.5t-197.5 -193.5q-125 -82 -285.5 -125.5t-335.5 -43.5q-176 0 -336.5 43.5t-284.5 125.5q-129 84 -197.5 193t-68.5 231q0 29 5 66q-48 31 -77 81.5t-29 109.5q0 94 66 160t160 66q83 0 148 -55q248 158 592 164l134 423q4 14 17.5 21.5t28.5 4.5 l347 -82q22 50 68.5 81t102.5 31q77 0 131.5 -54.5t54.5 -131.5t-54.5 -132t-131.5 -55q-76 0 -130.5 54t-55.5 131l-315 74l-116 -366q327 -14 560 -166q64 58 151 58q94 0 160 -66t66 -160zM1664 1459q-45 0 -77 -32t-32 -77t32 -77t77 -32t77 32t32 77t-32 77t-77 32z M77 722q0 -67 51 -111q49 131 180 235q-36 25 -82 25q-62 0 -105.5 -43.5t-43.5 -105.5zM1567 105q112 73 171.5 166t59.5 194t-59.5 193.5t-171.5 165.5q-116 75 -265.5 115.5t-313.5 40.5t-313.5 -40.5t-265.5 -115.5q-112 -73 -171.5 -165.5t-59.5 -193.5t59.5 -194 t171.5 -166q116 -75 265.5 -115.5t313.5 -40.5t313.5 40.5t265.5 115.5zM1850 605q57 46 57 117q0 62 -43.5 105.5t-105.5 43.5q-49 0 -86 -28q131 -105 178 -238zM1258 237q11 11 27 11t27 -11t11 -27.5t-11 -27.5q-99 -99 -319 -99h-2q-220 0 -319 99q-11 11 -11 27.5 t11 27.5t27 11t27 -11q77 -77 265 -77h2q188 0 265 77z" />
<glyph unicode="&#xf1a2;" d="M950 393q7 7 17.5 7t17.5 -7t7 -18t-7 -18q-65 -64 -208 -64h-1h-1q-143 0 -207 64q-8 7 -8 18t8 18q7 7 17.5 7t17.5 -7q49 -51 172 -51h1h1q122 0 173 51zM671 613q0 -37 -26 -64t-63 -27t-63 27t-26 64t26 63t63 26t63 -26t26 -63zM1214 1049q-29 0 -50 21t-21 50 q0 30 21 51t50 21q30 0 51 -21t21 -51q0 -29 -21 -50t-51 -21zM1216 1408q132 0 226 -94t94 -227v-894q0 -133 -94 -227t-226 -94h-896q-132 0 -226 94t-94 227v894q0 133 94 227t226 94h896zM1321 596q35 14 57 45.5t22 70.5q0 51 -36 87.5t-87 36.5q-60 0 -98 -48 q-151 107 -375 115l83 265l206 -49q1 -50 36.5 -85t84.5 -35q50 0 86 35.5t36 85.5t-36 86t-86 36q-36 0 -66 -20.5t-45 -53.5l-227 54q-9 2 -17.5 -2.5t-11.5 -14.5l-95 -302q-224 -4 -381 -113q-36 43 -93 43q-51 0 -87 -36.5t-36 -87.5q0 -37 19.5 -67.5t52.5 -45.5 q-7 -25 -7 -54q0 -98 74 -181.5t201.5 -132t278.5 -48.5q150 0 277.5 48.5t201.5 132t74 181.5q0 27 -6 54zM971 702q37 0 63 -26t26 -63t-26 -64t-63 -27t-63 27t-26 64t26 63t63 26z" />
<glyph unicode="&#xf1a0;" d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
<glyph unicode="&#xf1a1;" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf1a2;" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
<glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
@ -438,7 +438,7 @@
<glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
<glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
<glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf1bd;" d="M1397 1408q58 0 98.5 -40.5t40.5 -98.5v-1258q0 -58 -40.5 -98.5t-98.5 -40.5h-1258q-58 0 -98.5 40.5t-40.5 98.5v1258q0 58 40.5 98.5t98.5 40.5h1258zM1465 11v1258q0 28 -20 48t-48 20h-1258q-28 0 -48 -20t-20 -48v-1258q0 -28 20 -48t48 -20h1258q28 0 48 20t20 48 zM694 749l188 -387l533 145v-496q0 -7 -5.5 -12.5t-12.5 -5.5h-1258q-7 0 -12.5 5.5t-5.5 12.5v141l711 195l-212 439q4 1 12 2.5t12 1.5q170 32 303.5 21.5t221 -46t143.5 -94.5q27 -28 -25 -42q-64 -16 -256 -62l-97 198q-111 7 -240 -16zM1397 1287q7 0 12.5 -5.5 t5.5 -12.5v-428q-85 30 -188 52q-294 64 -645 12l-18 -3l-65 134h-233l85 -190q-132 -51 -230 -137v560q0 7 5.5 12.5t12.5 5.5h1258zM286 387q-14 -3 -26 4.5t-14 21.5q-24 203 166 305l129 -270z" />
<glyph unicode="&#xf1bd;" horiz-adv-x="1024" d="M1024 1233l-303 -582l24 -31h279v-415h-507l-44 -30l-142 -273l-30 -30h-301v303l303 583l-24 30h-279v415h507l44 30l142 273l30 30h301v-303z" />
<glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
<glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
<glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
@ -454,12 +454,12 @@
<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348q0 222 101 414.5t276.5 317t390.5 155.5v-260q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 q0 230 -145.5 406t-366.5 221v260q215 -31 390.5 -155.5t276.5 -317t101 -414.5z" />
<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
<glyph unicode="&#xf1d4;" d="M825 547l343 588h-150q-21 -39 -63.5 -118.5t-68 -128.5t-59.5 -118.5t-60 -128.5h-3q-21 48 -44.5 97t-52 105.5t-46.5 92t-54 104.5t-49 95h-150l323 -589v-435h134v436zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf1d4;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
<glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
<glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
@ -483,13 +483,13 @@
<glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1ed;" horiz-adv-x="1792" d="M1112 1090q0 159 -237 159h-70q-32 0 -59.5 -21.5t-34.5 -52.5l-63 -276q-2 -5 -2 -16q0 -24 17 -39.5t41 -15.5h53q69 0 128.5 13t112.5 41t83.5 81.5t30.5 126.5zM1716 938q0 -265 -220 -428q-219 -161 -612 -161h-61q-32 0 -59 -21.5t-34 -52.5l-73 -316 q-8 -36 -40.5 -61.5t-69.5 -25.5h-213q-31 0 -53 20t-22 51q0 10 13 65h151q34 0 64 23.5t38 56.5l73 316q8 33 37.5 57t63.5 24h61q390 0 607 160t217 421q0 129 -51 207q183 -92 183 -335zM1533 1123q0 -264 -221 -428q-218 -161 -612 -161h-60q-32 0 -59.5 -22t-34.5 -53 l-73 -315q-8 -36 -40 -61.5t-69 -25.5h-214q-31 0 -52.5 19.5t-21.5 51.5q0 8 2 20l300 1301q8 36 40.5 61.5t69.5 25.5h444q68 0 125 -4t120.5 -15t113.5 -30t96.5 -50.5t77.5 -74t49.5 -103.5t18.5 -136z" />
<glyph unicode="&#xf1ed;" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
<glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M322 689h-15q-19 0 -19 18q0 28 19 85q5 15 15 19.5t28 4.5q77 0 77 -49q0 -41 -30.5 -59.5t-74.5 -18.5zM664 528q-47 0 -47 29q0 62 123 62l3 -3q-5 -88 -79 -88zM1438 687h-15q-19 0 -19 19q0 28 19 85q5 15 14.5 19t28.5 4q77 0 77 -49q0 -41 -30.5 -59.5 t-74.5 -18.5zM1780 527q-47 0 -47 30q0 62 123 62l3 -3q-5 -89 -79 -89zM373 894h-128q-8 0 -14.5 -4t-8.5 -7.5t-7 -12.5q-3 -7 -45 -190t-42 -192q0 -7 5.5 -12.5t13.5 -5.5h62q25 0 32.5 34.5l15 69t32.5 34.5q47 0 87.5 7.5t80.5 24.5t63.5 52.5t23.5 84.5 q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM719 798q-38 0 -74 -6q-2 0 -8.5 -1t-9 -1.5l-7.5 -1.5t-7.5 -2t-6.5 -3t-6.5 -4t-5 -5t-4.5 -7t-4 -9q-9 -29 -9 -39t9 -10q5 0 21.5 5t19.5 6q30 8 58 8q74 0 74 -36q0 -11 -10 -14q-8 -2 -18 -3t-21.5 -1.5t-17.5 -1.5 q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5q0 -38 26 -59.5t64 -21.5q24 0 45.5 6.5t33 13t38.5 23.5q-3 -7 -3 -15t5.5 -13.5t12.5 -5.5h56q1 1 7 3.5t7.5 3.5t5 3.5t5 5.5t2.5 8l45 194q4 13 4 30q0 81 -145 81zM1247 793h-74q-22 0 -39 -23q-5 -7 -29.5 -51 t-46.5 -81.5t-26 -38.5l-5 4q0 77 -27 166q-1 5 -3.5 8.5t-6 6.5t-6.5 5t-8.5 3t-8.5 1.5t-9.5 1t-9 0.5h-10h-8.5q-38 0 -38 -21l1 -5q5 -53 25 -151t25 -143q2 -16 2 -24q0 -19 -30.5 -61.5t-30.5 -58.5q0 -13 40 -13q61 0 76 25l245 415q10 20 10 26q0 9 -8 9zM1489 892 h-129q-18 0 -29 -23q-6 -13 -46.5 -191.5t-40.5 -190.5q0 -20 43 -20h7.5h9h9t9.5 1t8.5 2t8.5 3t6.5 4.5t5.5 6t3 8.5l21 91q2 10 10.5 17t19.5 7q47 0 87.5 7t80.5 24.5t63.5 52.5t23.5 84q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM1835 798q-26 0 -74 -6 q-38 -6 -48 -16q-7 -8 -11 -19q-8 -24 -8 -39q0 -10 8 -10q1 0 41 12q30 8 58 8q74 0 74 -36q0 -12 -10 -14q-4 -1 -57 -7q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5t26 -58.5t64 -21.5q24 0 45 6t34 13t38 24q-3 -15 -3 -16q0 -5 2 -8.5t6.5 -5.5t8 -3.5 t10.5 -2t9.5 -0.5h9.5h8q42 0 48 25l45 194q3 15 3 31q0 81 -145 81zM2157 889h-55q-25 0 -33 -40q-10 -44 -36.5 -167t-42.5 -190v-5q0 -16 16 -18h1h57q10 0 18.5 6.5t10.5 16.5l83 374h-1l1 5q0 7 -5.5 12.5t-13.5 5.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048 q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M745 630q0 -37 -25.5 -61.5t-62.5 -24.5q-29 0 -46.5 16t-17.5 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM1530 779q0 -42 -22 -57t-66 -15l-32 -1l17 107q2 11 13 11h18q22 0 35 -2t25 -12.5t12 -30.5zM1881 630q0 -36 -25.5 -61t-61.5 -25q-29 0 -47 16 t-18 44q0 37 25 62.5t62 25.5q28 0 46.5 -16.5t18.5 -45.5zM513 801q0 59 -38.5 85.5t-100.5 26.5h-160q-19 0 -21 -19l-65 -408q-1 -6 3 -11t10 -5h76q20 0 22 19l18 110q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM822 489l41 261q1 6 -3 11t-10 5h-76 q-14 0 -17 -33q-27 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q28 0 58 12t48 32q-4 -12 -4 -21q0 -16 13 -16h69q19 0 22 19zM1269 752q0 5 -4 9.5t-9 4.5h-77q-11 0 -18 -10l-106 -156l-44 150q-5 16 -22 16h-75q-5 0 -9 -4.5t-4 -9.5q0 -2 19.5 -59 t42 -123t23.5 -70q-82 -112 -82 -120q0 -13 13 -13h77q11 0 18 10l255 368q2 2 2 7zM1649 801q0 59 -38.5 85.5t-100.5 26.5h-159q-20 0 -22 -19l-65 -408q-1 -6 3 -11t10 -5h82q12 0 16 13l18 116q1 8 7 13t15 6.5t17 1.5t19 -1t14 -1q86 0 135 48.5t49 134.5zM1958 489 l41 261q1 6 -3 11t-10 5h-76q-14 0 -17 -33q-26 40 -95 40q-72 0 -122.5 -54t-50.5 -127q0 -59 34.5 -94t92.5 -35q29 0 59 12t47 32q0 -1 -2 -9t-2 -12q0 -16 13 -16h69q19 0 22 19zM2176 898v1q0 14 -13 14h-74q-11 0 -13 -11l-65 -416l-1 -2q0 -5 4 -9.5t10 -4.5h66 q19 0 21 19zM392 764q-5 -35 -26 -46t-60 -11l-33 -1l17 107q2 11 13 11h19q40 0 58 -11.5t12 -48.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
<glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
@ -513,8 +513,143 @@
<glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
<glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
<glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
<glyph unicode="&#xf20d;" horiz-adv-x="1792" />
<glyph unicode="&#xf20e;" horiz-adv-x="1792" />
<glyph unicode="&#xf20d;" d="M915 450h-294l147 551zM1001 128h311l-324 1024h-440l-324 -1024h311l383 314zM1536 1120v-960q0 -118 -85 -203t-203 -85h-960q-118 0 -203 85t-85 203v960q0 118 85 203t203 85h960q118 0 203 -85t85 -203z" />
<glyph unicode="&#xf20e;" horiz-adv-x="2048" d="M2048 641q0 -21 -13 -36.5t-33 -19.5l-205 -356q3 -9 3 -18q0 -20 -12.5 -35.5t-32.5 -19.5l-193 -337q3 -8 3 -16q0 -23 -16.5 -40t-40.5 -17q-25 0 -41 18h-400q-17 -20 -43 -20t-43 20h-399q-17 -20 -43 -20q-23 0 -40 16.5t-17 40.5q0 8 4 20l-193 335 q-20 4 -32.5 19.5t-12.5 35.5q0 9 3 18l-206 356q-20 5 -32.5 20.5t-12.5 35.5q0 21 13.5 36.5t33.5 19.5l199 344q0 1 -0.5 3t-0.5 3q0 36 34 51l209 363q-4 10 -4 18q0 24 17 40.5t40 16.5q26 0 44 -21h396q16 21 43 21t43 -21h398q18 21 44 21q23 0 40 -16.5t17 -40.5 q0 -6 -4 -18l207 -358q23 -1 39 -17.5t16 -38.5q0 -13 -7 -27l187 -324q19 -4 31.5 -19.5t12.5 -35.5zM1063 -158h389l-342 354h-143l-342 -354h360q18 16 39 16t39 -16zM112 654q1 -4 1 -13q0 -10 -2 -15l208 -360q2 0 4.5 -1t5.5 -2.5l5 -2.5l188 199v347l-187 194 q-13 -8 -29 -10zM986 1438h-388l190 -200l554 200h-280q-16 -16 -38 -16t-38 16zM1689 226q1 6 5 11l-64 68l-17 -79h76zM1583 226l22 105l-252 266l-296 -307l63 -64h463zM1495 -142l16 28l65 310h-427l333 -343q8 4 13 5zM578 -158h5l342 354h-373v-335l4 -6q14 -5 22 -13 zM552 226h402l64 66l-309 321l-157 -166v-221zM359 226h163v189l-168 -177q4 -8 5 -12zM358 1051q0 -1 0.5 -2t0.5 -2q0 -16 -8 -29l171 -177v269zM552 1121v-311l153 -157l297 314l-223 236zM556 1425l-4 -8v-264l205 74l-191 201q-6 -2 -10 -3zM1447 1438h-16l-621 -224 l213 -225zM1023 946l-297 -315l311 -319l296 307zM688 634l-136 141v-284zM1038 270l-42 -44h85zM1374 618l238 -251l132 624l-3 5l-1 1zM1718 1018q-8 13 -8 29v2l-216 376q-5 1 -13 5l-437 -463l310 -327zM522 1142v223l-163 -282zM522 196h-163l163 -283v283zM1607 196 l-48 -227l130 227h-82zM1729 266l207 361q-2 10 -2 14q0 1 3 16l-171 296l-129 -612l77 -82q5 3 15 7z" />
<glyph unicode="&#xf210;" d="M0 856q0 131 91.5 226.5t222.5 95.5h742l352 358v-1470q0 -132 -91.5 -227t-222.5 -95h-780q-131 0 -222.5 95t-91.5 227v790zM1232 102l-176 180v425q0 46 -32 79t-78 33h-484q-46 0 -78 -33t-32 -79v-492q0 -46 32.5 -79.5t77.5 -33.5h770z" />
<glyph unicode="&#xf211;" d="M934 1386q-317 -121 -556 -362.5t-358 -560.5q-20 89 -20 176q0 208 102.5 384.5t278.5 279t384 102.5q82 0 169 -19zM1203 1267q93 -65 164 -155q-389 -113 -674.5 -400.5t-396.5 -676.5q-93 72 -155 162q112 386 395 671t667 399zM470 -67q115 356 379.5 622t619.5 384 q40 -92 54 -195q-292 -120 -516 -345t-343 -518q-103 14 -194 52zM1536 -125q-193 50 -367 115q-135 -84 -290 -107q109 205 274 370.5t369 275.5q-21 -152 -101 -284q65 -175 115 -370z" />
<glyph unicode="&#xf212;" horiz-adv-x="2048" d="M1893 1144l155 -1272q-131 0 -257 57q-200 91 -393 91q-226 0 -374 -148q-148 148 -374 148q-193 0 -393 -91q-128 -57 -252 -57h-5l155 1272q224 127 482 127q233 0 387 -106q154 106 387 106q258 0 482 -127zM1398 157q129 0 232 -28.5t260 -93.5l-124 1021 q-171 78 -368 78q-224 0 -374 -141q-150 141 -374 141q-197 0 -368 -78l-124 -1021q105 43 165.5 65t148.5 39.5t178 17.5q202 0 374 -108q172 108 374 108zM1438 191l-55 907q-211 -4 -359 -155q-152 155 -374 155q-176 0 -336 -66l-114 -941q124 51 228.5 76t221.5 25 q209 0 374 -102q172 107 374 102z" />
<glyph unicode="&#xf213;" horiz-adv-x="2048" d="M1500 165v733q0 21 -15 36t-35 15h-93q-20 0 -35 -15t-15 -36v-733q0 -20 15 -35t35 -15h93q20 0 35 15t15 35zM1216 165v531q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-531q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM924 165v429q0 20 -15 35t-35 15h-101 q-20 0 -35 -15t-15 -35v-429q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM632 165v362q0 20 -15 35t-35 15h-101q-20 0 -35 -15t-15 -35v-362q0 -20 15 -35t35 -15h101q20 0 35 15t15 35zM2048 311q0 -166 -118 -284t-284 -118h-1244q-166 0 -284 118t-118 284 q0 116 63 214.5t168 148.5q-10 34 -10 73q0 113 80.5 193.5t193.5 80.5q102 0 180 -67q45 183 194 300t338 117q149 0 275 -73.5t199.5 -199.5t73.5 -275q0 -66 -14 -122q135 -33 221 -142.5t86 -247.5z" />
<glyph unicode="&#xf214;" d="M0 1536h1536v-1392l-776 -338l-760 338v1392zM1436 209v926h-1336v-926l661 -294zM1436 1235v201h-1336v-201h1336zM181 937v-115h-37v115h37zM181 789v-115h-37v115h37zM181 641v-115h-37v115h37zM181 493v-115h-37v115h37zM181 345v-115h-37v115h37zM207 202l15 34 l105 -47l-15 -33zM343 142l15 34l105 -46l-15 -34zM478 82l15 34l105 -46l-15 -34zM614 23l15 33l104 -46l-15 -34zM797 10l105 46l15 -33l-105 -47zM932 70l105 46l15 -34l-105 -46zM1068 130l105 46l15 -34l-105 -46zM1203 189l105 47l15 -34l-105 -46zM259 1389v-36h-114 v36h114zM421 1389v-36h-115v36h115zM583 1389v-36h-115v36h115zM744 1389v-36h-114v36h114zM906 1389v-36h-114v36h114zM1068 1389v-36h-115v36h115zM1230 1389v-36h-115v36h115zM1391 1389v-36h-114v36h114zM181 1049v-79h-37v115h115v-36h-78zM421 1085v-36h-115v36h115z M583 1085v-36h-115v36h115zM744 1085v-36h-114v36h114zM906 1085v-36h-114v36h114zM1068 1085v-36h-115v36h115zM1230 1085v-36h-115v36h115zM1355 970v79h-78v36h115v-115h-37zM1355 822v115h37v-115h-37zM1355 674v115h37v-115h-37zM1355 526v115h37v-115h-37zM1355 378 v115h37v-115h-37zM1355 230v115h37v-115h-37zM760 265q-129 0 -221 91.5t-92 221.5q0 129 92 221t221 92q130 0 221.5 -92t91.5 -221q0 -130 -91.5 -221.5t-221.5 -91.5zM595 646q0 -36 19.5 -56.5t49.5 -25t64 -7t64 -2t49.5 -9t19.5 -30.5q0 -49 -112 -49q-97 0 -123 51 h-3l-31 -63q67 -42 162 -42q29 0 56.5 5t55.5 16t45.5 33t17.5 53q0 46 -27.5 69.5t-67.5 27t-79.5 3t-67 5t-27.5 25.5q0 21 20.5 33t40.5 15t41 3q34 0 70.5 -11t51.5 -34h3l30 58q-3 1 -21 8.5t-22.5 9t-19.5 7t-22 7t-20 4.5t-24 4t-23 1q-29 0 -56.5 -5t-54 -16.5 t-43 -34t-16.5 -53.5z" />
<glyph unicode="&#xf215;" horiz-adv-x="2048" d="M863 504q0 112 -79.5 191.5t-191.5 79.5t-191 -79.5t-79 -191.5t79 -191t191 -79t191.5 79t79.5 191zM1726 505q0 112 -79 191t-191 79t-191.5 -79t-79.5 -191q0 -113 79.5 -192t191.5 -79t191 79.5t79 191.5zM2048 1314v-1348q0 -44 -31.5 -75.5t-76.5 -31.5h-1832 q-45 0 -76.5 31.5t-31.5 75.5v1348q0 44 31.5 75.5t76.5 31.5h431q44 0 76 -31.5t32 -75.5v-161h754v161q0 44 32 75.5t76 31.5h431q45 0 76.5 -31.5t31.5 -75.5z" />
<glyph unicode="&#xf216;" horiz-adv-x="2048" d="M1430 953zM1690 749q148 0 253 -98.5t105 -244.5q0 -157 -109 -261.5t-267 -104.5q-85 0 -162 27.5t-138 73.5t-118 106t-109 126.5t-103.5 132.5t-108.5 126t-117 106t-136 73.5t-159 27.5q-154 0 -251.5 -91.5t-97.5 -244.5q0 -157 104 -250t263 -93q100 0 208 37.5 t193 98.5q5 4 21 18.5t30 24t22 9.5q14 0 24.5 -10.5t10.5 -24.5q0 -24 -60 -77q-101 -88 -234.5 -142t-260.5 -54q-133 0 -245.5 58t-180 165t-67.5 241q0 205 141.5 341t347.5 136q120 0 226.5 -43.5t185.5 -113t151.5 -153t139 -167.5t133.5 -153.5t149.5 -113 t172.5 -43.5q102 0 168.5 61.5t66.5 162.5q0 95 -64.5 159t-159.5 64q-30 0 -81.5 -18.5t-68.5 -18.5q-20 0 -35.5 15t-15.5 35q0 18 8.5 57t8.5 59q0 159 -107.5 263t-266.5 104q-58 0 -111.5 -18.5t-84 -40.5t-55.5 -40.5t-33 -18.5q-15 0 -25.5 10.5t-10.5 25.5 q0 19 25 46q59 67 147 103.5t182 36.5q191 0 318 -125.5t127 -315.5q0 -37 -4 -66q57 15 115 15z" />
<glyph unicode="&#xf217;" horiz-adv-x="1664" d="M1216 832q0 26 -19 45t-45 19h-128v128q0 26 -19 45t-45 19t-45 -19t-19 -45v-128h-128q-26 0 -45 -19t-19 -45t19 -45t45 -19h128v-128q0 -26 19 -45t45 -19t45 19t19 45v128h128q26 0 45 19t19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf218;" horiz-adv-x="1664" d="M1280 832q0 26 -19 45t-45 19t-45 -19l-147 -146v293q0 26 -19 45t-45 19t-45 -19t-19 -45v-293l-147 146q-19 19 -45 19t-45 -19t-19 -45t19 -45l256 -256q19 -19 45 -19t45 19l256 256q19 19 19 45zM640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920 q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf219;" horiz-adv-x="2048" d="M212 768l623 -665l-300 665h-323zM1024 -4l349 772h-698zM538 896l204 384h-262l-288 -384h346zM1213 103l623 665h-323zM683 896h682l-204 384h-274zM1510 896h346l-288 384h-262zM1651 1382l384 -512q14 -18 13 -41.5t-17 -40.5l-960 -1024q-18 -20 -47 -20t-47 20 l-960 1024q-16 17 -17 40.5t13 41.5l384 512q18 26 51 26h1152q33 0 51 -26z" />
<glyph unicode="&#xf21a;" horiz-adv-x="2048" d="M1811 -19q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83q19 19 45 19t45 -19l83 -83l83 83 q19 19 45 19t45 -19l83 -83zM237 19q-19 -19 -45 -19t-45 19l-128 128l90 90l83 -82l83 82q19 19 45 19t45 -19l83 -82l64 64v293l-210 314q-17 26 -7 56.5t40 40.5l177 58v299h128v128h256v128h256v-128h256v-128h128v-299l177 -58q30 -10 40 -40.5t-7 -56.5l-210 -314 v-293l19 18q19 19 45 19t45 -19l83 -82l83 82q19 19 45 19t45 -19l128 -128l-90 -90l-83 83l-83 -83q-18 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83l-83 -83 q-19 -19 -45 -19t-45 19l-83 83l-83 -83q-19 -19 -45 -19t-45 19l-83 83zM640 1152v-128l384 128l384 -128v128h-128v128h-512v-128h-128z" />
<glyph unicode="&#xf21b;" d="M576 0l96 448l-96 128l-128 64zM832 0l128 640l-128 -64l-96 -128zM992 1010q-2 4 -4 6q-10 8 -96 8q-70 0 -167 -19q-7 -2 -21 -2t-21 2q-97 19 -167 19q-86 0 -96 -8q-2 -2 -4 -6q2 -18 4 -27q2 -3 7.5 -6.5t7.5 -10.5q2 -4 7.5 -20.5t7 -20.5t7.5 -17t8.5 -17t9 -14 t12 -13.5t14 -9.5t17.5 -8t20.5 -4t24.5 -2q36 0 59 12.5t32.5 30t14.5 34.5t11.5 29.5t17.5 12.5h12q11 0 17.5 -12.5t11.5 -29.5t14.5 -34.5t32.5 -30t59 -12.5q13 0 24.5 2t20.5 4t17.5 8t14 9.5t12 13.5t9 14t8.5 17t7.5 17t7 20.5t7.5 20.5q2 7 7.5 10.5t7.5 6.5 q2 9 4 27zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 61 4.5 118t19 125.5t37.5 123.5t63.5 103.5t93.5 74.5l-90 220h214q-22 64 -22 128q0 12 2 32q-194 40 -194 96q0 57 210 99q17 62 51.5 134t70.5 114q32 37 76 37q30 0 84 -31t84 -31t84 31 t84 31q44 0 76 -37q36 -42 70.5 -114t51.5 -134q210 -42 210 -99q0 -56 -194 -96q7 -81 -20 -160h214l-82 -225q63 -33 107.5 -96.5t65.5 -143.5t29 -151.5t8 -148.5z" />
<glyph unicode="&#xf21c;" horiz-adv-x="2304" d="M2301 500q12 -103 -22 -198.5t-99 -163.5t-158.5 -106t-196.5 -31q-161 11 -279.5 125t-134.5 274q-12 111 27.5 210.5t118.5 170.5l-71 107q-96 -80 -151 -194t-55 -244q0 -27 -18.5 -46.5t-45.5 -19.5h-256h-69q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5 t-131.5 316.5t131.5 316.5t316.5 131.5q76 0 152 -27l24 45q-123 110 -304 110h-64q-26 0 -45 19t-19 45t19 45t45 19h128q78 0 145 -13.5t116.5 -38.5t71.5 -39.5t51 -36.5h512h115l-85 128h-222q-30 0 -49 22.5t-14 52.5q4 23 23 38t43 15h253q33 0 53 -28l70 -105 l114 114q19 19 46 19h101q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-179l115 -172q131 63 275 36q143 -26 244 -134.5t118 -253.5zM448 128q115 0 203 72.5t111 183.5h-314q-35 0 -55 31q-18 32 -1 63l147 277q-47 13 -91 13q-132 0 -226 -94t-94 -226t94 -226 t226 -94zM1856 128q132 0 226 94t94 226t-94 226t-226 94q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94z" />
<glyph unicode="&#xf21d;" d="M1408 0q0 -63 -61.5 -113.5t-164 -81t-225 -46t-253.5 -15.5t-253.5 15.5t-225 46t-164 81t-61.5 113.5q0 49 33 88.5t91 66.5t118 44.5t131 29.5q26 5 48 -10.5t26 -41.5q5 -26 -10.5 -48t-41.5 -26q-58 -10 -106 -23.5t-76.5 -25.5t-48.5 -23.5t-27.5 -19.5t-8.5 -12 q3 -11 27 -26.5t73 -33t114 -32.5t160.5 -25t201.5 -10t201.5 10t160.5 25t114 33t73 33.5t27 27.5q-1 4 -8.5 11t-27.5 19t-48.5 23.5t-76.5 25t-106 23.5q-26 4 -41.5 26t-10.5 48q4 26 26 41.5t48 10.5q71 -12 131 -29.5t118 -44.5t91 -66.5t33 -88.5zM1024 896v-384 q0 -26 -19 -45t-45 -19h-64v-384q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v384h-64q-26 0 -45 19t-19 45v384q0 53 37.5 90.5t90.5 37.5h384q53 0 90.5 -37.5t37.5 -90.5zM928 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5 t158.5 -65.5t65.5 -158.5z" />
<glyph unicode="&#xf21e;" horiz-adv-x="1792" d="M1280 512h305q-5 -6 -10 -10.5t-9 -7.5l-3 -4l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-5 2 -21 20h369q22 0 39.5 13.5t22.5 34.5l70 281l190 -667q6 -20 23 -33t39 -13q21 0 38 13t23 33l146 485l56 -112q18 -35 57 -35zM1792 940q0 -145 -103 -300h-369l-111 221 q-8 17 -25.5 27t-36.5 8q-45 -5 -56 -46l-129 -430l-196 686q-6 20 -23.5 33t-39.5 13t-39 -13.5t-22 -34.5l-116 -464h-423q-103 155 -103 300q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124 t127 -344z" />
<glyph unicode="&#xf221;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292 q11 134 80.5 249t182 188t245.5 88q170 19 319 -54t236 -212t87 -306zM128 960q0 -185 131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5z" />
<glyph unicode="&#xf222;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-382 -383q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5 q203 0 359 -126l382 382h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf223;" horiz-adv-x="1280" d="M830 1220q145 -72 233.5 -210.5t88.5 -305.5q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5 t-147.5 384.5q0 167 88.5 305.5t233.5 210.5q-165 96 -228 273q-6 16 3.5 29.5t26.5 13.5h69q21 0 29 -20q44 -106 140 -171t214 -65t214 65t140 171q8 20 37 20h61q17 0 26.5 -13.5t3.5 -29.5q-63 -177 -228 -273zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf224;" d="M1024 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-149 16 -270.5 103t-186.5 223.5t-53 291.5q16 204 160 353.5t347 172.5q118 14 228 -19t198 -103l255 254h-134q-14 0 -23 9t-9 23v64zM576 256q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf225;" horiz-adv-x="1792" d="M1280 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q126 -158 126 -359q0 -221 -147.5 -384.5t-364.5 -187.5v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64 q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-217 24 -364.5 187.5t-147.5 384.5q0 201 126 359l-52 53l-101 -111q-9 -10 -22 -10.5t-23 7.5l-48 44q-10 8 -10.5 21.5t8.5 23.5l105 115l-111 112v-134q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9 t-9 23v288q0 26 19 45t45 19h288q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-133l106 -107l86 94q9 10 22 10.5t23 -7.5l48 -44q10 -8 10.5 -21.5t-8.5 -23.5l-90 -99l57 -56q158 126 359 126t359 -126l255 254h-134q-14 0 -23 9t-9 23v64zM832 256q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf226;" horiz-adv-x="1792" d="M1790 1007q12 -155 -52.5 -292t-186 -224t-271.5 -103v-260h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-512v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23 t23 9h224v260q-150 16 -271.5 103t-186 224t-52.5 292q17 206 164.5 356.5t352.5 169.5q206 21 377 -94q171 115 377 94q205 -19 352.5 -169.5t164.5 -356.5zM896 647q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM576 512q115 0 218 57q-154 165 -154 391 q0 224 154 391q-103 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5zM1152 128v260q-137 15 -256 94q-119 -79 -256 -94v-260h512zM1216 512q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5q-115 0 -218 -57q154 -167 154 -391 q0 -226 -154 -391q103 -57 218 -57z" />
<glyph unicode="&#xf227;" horiz-adv-x="1920" d="M1536 1120q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-31 -182 -166 -312t-318 -156q-210 -29 -384.5 80t-241.5 300q-117 6 -221 57.5t-177.5 133t-113.5 192.5t-32 230 q9 135 78 252t182 191.5t248 89.5q118 14 227.5 -19t198.5 -103l255 254h-134q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q59 -74 93 -169q182 -9 328 -124l255 254h-134q-14 0 -23 9 t-9 23v64zM1024 704q0 20 -4 58q-162 -25 -271 -150t-109 -292q0 -20 4 -58q162 25 271 150t109 292zM128 704q0 -168 111 -294t276 -149q-3 29 -3 59q0 210 135 369.5t338 196.5q-53 120 -163.5 193t-245.5 73q-185 0 -316.5 -131.5t-131.5 -316.5zM1088 -128 q185 0 316.5 131.5t131.5 316.5q0 168 -111 294t-276 149q3 -29 3 -59q0 -210 -135 -369.5t-338 -196.5q53 -120 163.5 -193t245.5 -73z" />
<glyph unicode="&#xf228;" horiz-adv-x="2048" d="M1664 1504q0 14 9 23t23 9h288q26 0 45 -19t19 -45v-288q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v134l-254 -255q76 -95 107.5 -214t9.5 -247q-32 -180 -164.5 -310t-313.5 -157q-223 -34 -409 90q-117 -78 -256 -93v-132h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23 t-23 -9h-96v-96q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v96h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96v132q-155 17 -279.5 109.5t-187 237.5t-39.5 307q25 187 159.5 322.5t320.5 164.5q224 34 410 -90q146 97 320 97q201 0 359 -126l255 254h-134q-14 0 -23 9 t-9 23v64zM896 391q128 131 128 313t-128 313q-128 -131 -128 -313t128 -313zM128 704q0 -185 131.5 -316.5t316.5 -131.5q117 0 218 57q-154 167 -154 391t154 391q-101 57 -218 57q-185 0 -316.5 -131.5t-131.5 -316.5zM1216 256q185 0 316.5 131.5t131.5 316.5 t-131.5 316.5t-316.5 131.5q-117 0 -218 -57q154 -167 154 -391t-154 -391q101 -57 218 -57z" />
<glyph unicode="&#xf229;" d="M1472 1408q26 0 45 -19t19 -45v-416q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v262l-213 -214l140 -140q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-140 141l-78 -79q126 -156 126 -359q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5 t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123t223.5 45.5q203 0 359 -126l78 78l-172 172q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l172 -172l213 213h-261q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h416zM576 0q185 0 316.5 131.5t131.5 316.5t-131.5 316.5 t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf22a;" horiz-adv-x="1280" d="M640 892q217 -24 364.5 -187.5t147.5 -384.5q0 -167 -87 -306t-236 -212t-319 -54q-133 15 -245.5 88t-182 188t-80.5 249q-12 155 52.5 292t186 224t271.5 103v132h-160q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h160v165l-92 -92q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22 t9 23l202 201q19 19 45 19t45 -19l202 -201q9 -10 9 -23t-9 -22l-46 -46q-9 -9 -22 -9t-23 9l-92 92v-165h160q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-160v-132zM576 -128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5 t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf22b;" horiz-adv-x="2048" d="M1901 621q19 -19 19 -45t-19 -45l-294 -294q-9 -10 -22.5 -10t-22.5 10l-45 45q-10 9 -10 22.5t10 22.5l185 185h-294v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-132q-24 -217 -187.5 -364.5t-384.5 -147.5q-167 0 -306 87t-212 236t-54 319q15 133 88 245.5 t188 182t249 80.5q155 12 292 -52.5t224 -186t103 -271.5h132v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h294l-185 185q-10 9 -10 22.5t10 22.5l45 45q9 10 22.5 10t22.5 -10zM576 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5 t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf22c;" horiz-adv-x="1280" d="M1152 960q0 -221 -147.5 -384.5t-364.5 -187.5v-612q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v612q-217 24 -364.5 187.5t-147.5 384.5q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM576 512q185 0 316.5 131.5 t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
<glyph unicode="&#xf22d;" horiz-adv-x="1280" d="M1024 576q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1152 576q0 -117 -45.5 -223.5t-123 -184t-184 -123t-223.5 -45.5t-223.5 45.5t-184 123t-123 184t-45.5 223.5t45.5 223.5t123 184t184 123 t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5z" />
<glyph unicode="&#xf22e;" horiz-adv-x="1792" />
<glyph unicode="&#xf22f;" horiz-adv-x="1792" />
<glyph unicode="&#xf230;" d="M1451 1408q35 0 60 -25t25 -60v-1366q0 -35 -25 -60t-60 -25h-391v595h199l30 232h-229v148q0 56 23.5 84t91.5 28l122 1v207q-63 9 -178 9q-136 0 -217.5 -80t-81.5 -226v-171h-200v-232h200v-595h-735q-35 0 -60 25t-25 60v1366q0 35 25 60t60 25h1366z" />
<glyph unicode="&#xf231;" horiz-adv-x="1280" d="M0 939q0 108 37.5 203.5t103.5 166.5t152 123t185 78t202 26q158 0 294 -66.5t221 -193.5t85 -287q0 -96 -19 -188t-60 -177t-100 -149.5t-145 -103t-189 -38.5q-68 0 -135 32t-96 88q-10 -39 -28 -112.5t-23.5 -95t-20.5 -71t-26 -71t-32 -62.5t-46 -77.5t-62 -86.5 l-14 -5l-9 10q-15 157 -15 188q0 92 21.5 206.5t66.5 287.5t52 203q-32 65 -32 169q0 83 52 156t132 73q61 0 95 -40.5t34 -102.5q0 -66 -44 -191t-44 -187q0 -63 45 -104.5t109 -41.5q55 0 102 25t78.5 68t56 95t38 110.5t20 111t6.5 99.5q0 173 -109.5 269.5t-285.5 96.5 q-200 0 -334 -129.5t-134 -328.5q0 -44 12.5 -85t27 -65t27 -45.5t12.5 -30.5q0 -28 -15 -73t-37 -45q-2 0 -17 3q-51 15 -90.5 56t-61 94.5t-32.5 108t-11 106.5z" />
<glyph unicode="&#xf232;" d="M985 562q13 0 97.5 -44t89.5 -53q2 -5 2 -15q0 -33 -17 -76q-16 -39 -71 -65.5t-102 -26.5q-57 0 -190 62q-98 45 -170 118t-148 185q-72 107 -71 194v8q3 91 74 158q24 22 52 22q6 0 18 -1.5t19 -1.5q19 0 26.5 -6.5t15.5 -27.5q8 -20 33 -88t25 -75q0 -21 -34.5 -57.5 t-34.5 -46.5q0 -7 5 -15q34 -73 102 -137q56 -53 151 -101q12 -7 22 -7q15 0 54 48.5t52 48.5zM782 32q127 0 243.5 50t200.5 134t134 200.5t50 243.5t-50 243.5t-134 200.5t-200.5 134t-243.5 50t-243.5 -50t-200.5 -134t-134 -200.5t-50 -243.5q0 -203 120 -368l-79 -233 l242 77q158 -104 345 -104zM782 1414q153 0 292.5 -60t240.5 -161t161 -240.5t60 -292.5t-60 -292.5t-161 -240.5t-240.5 -161t-292.5 -60q-195 0 -365 94l-417 -134l136 405q-108 178 -108 389q0 153 60 292.5t161 240.5t240.5 161t292.5 60z" />
<glyph unicode="&#xf233;" horiz-adv-x="1792" d="M128 128h1024v128h-1024v-128zM128 640h1024v128h-1024v-128zM1696 192q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM128 1152h1024v128h-1024v-128zM1696 704q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1696 1216 q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1792 384v-384h-1792v384h1792zM1792 896v-384h-1792v384h1792zM1792 1408v-384h-1792v384h1792z" />
<glyph unicode="&#xf234;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1664 512h352q13 0 22.5 -9.5t9.5 -22.5v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-352q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5 t-9.5 22.5v352h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v352q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5v-352zM928 288q0 -52 38 -90t90 -38h256v-238q-68 -50 -171 -50h-874q-121 0 -194 69t-73 190q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q79 -61 154.5 -91.5t164.5 -30.5t164.5 30.5t154.5 91.5q20 17 39 17q132 0 217 -96h-223q-52 0 -90 -38t-38 -90v-192z" />
<glyph unicode="&#xf235;" horiz-adv-x="2048" d="M704 640q-159 0 -271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5t-112.5 -271.5t-271.5 -112.5zM1781 320l249 -249q9 -9 9 -23q0 -13 -9 -22l-136 -136q-9 -9 -22 -9q-14 0 -23 9l-249 249l-249 -249q-9 -9 -23 -9q-13 0 -22 9l-136 136 q-9 9 -9 22q0 14 9 23l249 249l-249 249q-9 9 -9 23q0 13 9 22l136 136q9 9 22 9q14 0 23 -9l249 -249l249 249q9 9 23 9q13 0 22 -9l136 -136q9 -9 9 -22q0 -14 -9 -23zM1283 320l-181 -181q-37 -37 -37 -91q0 -53 37 -90l83 -83q-21 -3 -44 -3h-874q-121 0 -194 69 t-73 190q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q19 0 39 -17q154 -122 319 -122t319 122q20 17 39 17q28 0 57 -6q-28 -27 -41 -50t-13 -56q0 -54 37 -91z" />
<glyph unicode="&#xf236;" horiz-adv-x="2048" d="M256 512h1728q26 0 45 -19t19 -45v-448h-256v256h-1536v-256h-256v1216q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-704zM832 832q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM2048 576v64q0 159 -112.5 271.5t-271.5 112.5h-704 q-26 0 -45 -19t-19 -45v-384h1152z" />
<glyph unicode="&#xf237;" d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
<glyph unicode="&#xf238;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
<glyph unicode="&#xf239;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
<glyph unicode="&#xf23b;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
<glyph unicode="&#xf23c;" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
<glyph unicode="&#xf23d;" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
<glyph unicode="&#xf23e;" horiz-adv-x="1792" d="M896 1472q-169 0 -323 -66t-265.5 -177.5t-177.5 -265.5t-66 -323t66 -323t177.5 -265.5t265.5 -177.5t323 -66t323 66t265.5 177.5t177.5 265.5t66 323t-66 323t-177.5 265.5t-265.5 177.5t-323 66zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348 t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM496 704q16 0 16 -16v-480q0 -16 -16 -16h-32q-16 0 -16 16v480q0 16 16 16h32zM896 640q53 0 90.5 -37.5t37.5 -90.5q0 -35 -17.5 -64t-46.5 -46v-114q0 -14 -9 -23 t-23 -9h-64q-14 0 -23 9t-9 23v114q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5zM896 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM544 928v-96 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 93 65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5v-96q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v96q0 146 -103 249t-249 103t-249 -103t-103 -249zM1408 192v512q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-512 q0 -26 19 -45t45 -19h896q26 0 45 19t19 45z" />
<glyph unicode="&#xf240;" horiz-adv-x="2304" d="M1920 1024v-768h-1664v768h1664zM2048 448h128v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288zM2304 832v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113 v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160q53 0 90.5 -37.5t37.5 -90.5z" />
<glyph unicode="&#xf241;" horiz-adv-x="2304" d="M256 256v768h1280v-768h-1280zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
<glyph unicode="&#xf242;" horiz-adv-x="2304" d="M256 256v768h896v-768h-896zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
<glyph unicode="&#xf243;" horiz-adv-x="2304" d="M256 256v768h512v-768h-512zM2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9 h-1856q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
<glyph unicode="&#xf244;" horiz-adv-x="2304" d="M2176 960q53 0 90.5 -37.5t37.5 -90.5v-384q0 -53 -37.5 -90.5t-90.5 -37.5v-160q0 -66 -47 -113t-113 -47h-1856q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1856q66 0 113 -47t47 -113v-160zM2176 448v384h-128v288q0 14 -9 23t-23 9h-1856q-14 0 -23 -9t-9 -23 v-960q0 -14 9 -23t23 -9h1856q14 0 23 9t9 23v288h128z" />
<glyph unicode="&#xf245;" horiz-adv-x="1280" d="M1133 493q31 -30 14 -69q-17 -40 -59 -40h-382l201 -476q10 -25 0 -49t-34 -35l-177 -75q-25 -10 -49 0t-35 34l-191 452l-312 -312q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v1504q0 42 40 59q12 5 24 5q27 0 45 -19z" />
<glyph unicode="&#xf246;" horiz-adv-x="1024" d="M832 1408q-320 0 -320 -224v-416h128v-128h-128v-544q0 -224 320 -224h64v-128h-64q-272 0 -384 146q-112 -146 -384 -146h-64v128h64q320 0 320 224v544h-128v128h128v416q0 224 -320 224h-64v128h64q272 0 384 -146q112 146 384 146h64v-128h-64z" />
<glyph unicode="&#xf247;" horiz-adv-x="2048" d="M2048 1152h-128v-1024h128v-384h-384v128h-1280v-128h-384v384h128v1024h-128v384h384v-128h1280v128h384v-384zM1792 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 -128v128h-128v-128h128zM1664 0v128h128v1024h-128v128h-1280v-128h-128v-1024h128v-128 h1280zM1920 -128v128h-128v-128h128zM1280 896h384v-768h-896v256h-384v768h896v-256zM512 512h640v512h-640v-512zM1536 256v512h-256v-384h-384v-128h640z" />
<glyph unicode="&#xf248;" horiz-adv-x="2304" d="M2304 768h-128v-640h128v-384h-384v128h-896v-128h-384v384h128v128h-384v-128h-384v384h128v640h-128v384h384v-128h896v128h384v-384h-128v-128h384v128h384v-384zM2048 1024v-128h128v128h-128zM1408 1408v-128h128v128h-128zM128 1408v-128h128v128h-128zM256 256 v128h-128v-128h128zM1536 384h-128v-128h128v128zM384 384h896v128h128v640h-128v128h-896v-128h-128v-640h128v-128zM896 -128v128h-128v-128h128zM2176 -128v128h-128v-128h128zM2048 128v640h-128v128h-384v-384h128v-384h-384v128h-384v-128h128v-128h896v128h128z" />
<glyph unicode="&#xf249;" d="M1024 288v-416h-928q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68v-928h-416q-40 0 -68 -28t-28 -68zM1152 256h381q-15 -82 -65 -132l-184 -184q-50 -50 -132 -65v381z" />
<glyph unicode="&#xf24a;" d="M1400 256h-248v-248q29 10 41 22l185 185q12 12 22 41zM1120 384h288v896h-1280v-1280h896v288q0 40 28 68t68 28zM1536 1312v-1024q0 -40 -20 -88t-48 -76l-184 -184q-28 -28 -76 -48t-88 -20h-1024q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1344q40 0 68 -28t28 -68 z" />
<glyph unicode="&#xf24b;" horiz-adv-x="2304" d="M1951 538q0 -26 -15.5 -44.5t-38.5 -23.5q-8 -2 -18 -2h-153v140h153q10 0 18 -2q23 -5 38.5 -23.5t15.5 -44.5zM1933 751q0 -25 -15 -42t-38 -21q-3 -1 -15 -1h-139v129h139q3 0 8.5 -0.5t6.5 -0.5q23 -4 38 -21.5t15 -42.5zM728 587v308h-228v-308q0 -58 -38 -94.5 t-105 -36.5q-108 0 -229 59v-112q53 -15 121 -23t109 -9l42 -1q328 0 328 217zM1442 403v113q-99 -52 -200 -59q-108 -8 -169 41t-61 142t61 142t169 41q101 -7 200 -58v112q-48 12 -100 19.5t-80 9.5l-28 2q-127 6 -218.5 -14t-140.5 -60t-71 -88t-22 -106t22 -106t71 -88 t140.5 -60t218.5 -14q101 4 208 31zM2176 518q0 54 -43 88.5t-109 39.5v3q57 8 89 41.5t32 79.5q0 55 -41 88t-107 36q-3 0 -12 0.5t-14 0.5h-455v-510h491q74 0 121.5 36.5t47.5 96.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90 t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf24c;" horiz-adv-x="2304" d="M858 295v693q-106 -41 -172 -135.5t-66 -211.5t66 -211.5t172 -134.5zM1362 641q0 117 -66 211.5t-172 135.5v-694q106 41 172 135.5t66 211.5zM1577 641q0 -159 -78.5 -294t-213.5 -213.5t-294 -78.5q-119 0 -227.5 46.5t-187 125t-125 187t-46.5 227.5q0 159 78.5 294 t213.5 213.5t294 78.5t294 -78.5t213.5 -213.5t78.5 -294zM1960 634q0 139 -55.5 261.5t-147.5 205.5t-213.5 131t-252.5 48h-301q-176 0 -323.5 -81t-235 -230t-87.5 -335q0 -171 87 -317.5t236 -231.5t323 -85h301q129 0 251.5 50.5t214.5 135t147.5 202.5t55.5 246z M2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf24d;" horiz-adv-x="1792" d="M1664 -96v1088q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5zM1792 992v-1088q0 -66 -47 -113t-113 -47h-1088q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113 zM1408 1376v-160h-128v160q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5v-1088q0 -13 9.5 -22.5t22.5 -9.5h160v-128h-160q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1088q66 0 113 -47t47 -113z" />
<glyph unicode="&#xf24e;" horiz-adv-x="2304" d="M1728 1088l-384 -704h768zM448 1088l-384 -704h768zM1269 1280q-14 -40 -45.5 -71.5t-71.5 -45.5v-1291h608q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1344q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h608v1291q-40 14 -71.5 45.5t-45.5 71.5h-491q-14 0 -23 9t-9 23v64 q0 14 9 23t23 9h491q21 57 70 92.5t111 35.5t111 -35.5t70 -92.5h491q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-491zM1088 1264q33 0 56.5 23.5t23.5 56.5t-23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5zM2176 384q0 -73 -46.5 -131t-117.5 -91 t-144.5 -49.5t-139.5 -16.5t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81zM896 384q0 -73 -46.5 -131t-117.5 -91t-144.5 -49.5t-139.5 -16.5 t-139.5 16.5t-144.5 49.5t-117.5 91t-46.5 131q0 11 35 81t92 174.5t107 195.5t102 184t56 100q18 33 56 33t56 -33q4 -7 56 -100t102 -184t107 -195.5t92 -174.5t35 -81z" />
<glyph unicode="&#xf250;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-77 -29 -149 -92.5 t-129.5 -152.5t-92.5 -210t-35 -253h1024q0 132 -35 253t-92.5 210t-129.5 152.5t-149 92.5q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
<glyph unicode="&#xf251;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -66 9 -128h1006q9 61 9 128zM1280 -128q0 130 -34 249.5t-90.5 208t-126.5 152t-146 94.5h-230q-76 -31 -146 -94.5t-126.5 -152t-90.5 -208t-34 -249.5h1024z" />
<glyph unicode="&#xf252;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM1280 1408h-1024q0 -206 85 -384h854q85 178 85 384zM1223 192q-54 141 -145.5 241.5t-194.5 142.5h-230q-103 -42 -194.5 -142.5t-145.5 -241.5h910z" />
<glyph unicode="&#xf253;" d="M1408 1408q0 -261 -106.5 -461.5t-266.5 -306.5q160 -106 266.5 -306.5t106.5 -461.5h96q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h96q0 261 106.5 461.5t266.5 306.5q-160 106 -266.5 306.5t-106.5 461.5h-96q-14 0 -23 9 t-9 23v64q0 14 9 23t23 9h1472q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-96zM874 700q77 29 149 92.5t129.5 152.5t92.5 210t35 253h-1024q0 -132 35 -253t92.5 -210t129.5 -152.5t149 -92.5q19 -7 30.5 -23.5t11.5 -36.5t-11.5 -36.5t-30.5 -23.5q-137 -51 -244 -196 h700q-107 145 -244 196q-19 7 -30.5 23.5t-11.5 36.5t11.5 36.5t30.5 23.5z" />
<glyph unicode="&#xf254;" d="M1504 -64q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472zM130 0q3 55 16 107t30 95t46 87t53.5 76t64.5 69.5t66 60t70.5 55t66.5 47.5t65 43q-43 28 -65 43t-66.5 47.5t-70.5 55t-66 60t-64.5 69.5t-53.5 76t-46 87 t-30 95t-16 107h1276q-3 -55 -16 -107t-30 -95t-46 -87t-53.5 -76t-64.5 -69.5t-66 -60t-70.5 -55t-66.5 -47.5t-65 -43q43 -28 65 -43t66.5 -47.5t70.5 -55t66 -60t64.5 -69.5t53.5 -76t46 -87t30 -95t16 -107h-1276zM1504 1536q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9 h-1472q-14 0 -23 9t-9 23v128q0 14 9 23t23 9h1472z" />
<glyph unicode="&#xf255;" d="M768 1152q-53 0 -90.5 -37.5t-37.5 -90.5v-128h-32v93q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-429l-32 30v172q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-224q0 -47 35 -82l310 -296q39 -39 39 -102q0 -26 19 -45t45 -19h640q26 0 45 19t19 45v25 q0 41 10 77l108 436q10 36 10 77v246q0 48 -32 81.5t-80 33.5q-46 0 -79 -33t-33 -79v-32h-32v125q0 40 -25 72.5t-64 40.5q-14 2 -23 2q-46 0 -79 -33t-33 -79v-128h-32v122q0 51 -32.5 89.5t-82.5 43.5q-5 1 -13 1zM768 1280q84 0 149 -50q57 34 123 34q59 0 111 -27 t86 -76q27 7 59 7q100 0 170 -71.5t70 -171.5v-246q0 -51 -13 -108l-109 -436q-6 -24 -6 -71q0 -80 -56 -136t-136 -56h-640q-84 0 -138 58.5t-54 142.5l-308 296q-76 73 -76 175v224q0 99 70.5 169.5t169.5 70.5q11 0 16 -1q6 95 75.5 160t164.5 65q52 0 98 -21 q72 69 174 69z" />
<glyph unicode="&#xf256;" horiz-adv-x="1792" d="M880 1408q-46 0 -79 -33t-33 -79v-656h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528v-256l-154 205q-38 51 -102 51q-53 0 -90.5 -37.5t-37.5 -90.5q0 -43 26 -77l384 -512q38 -51 102 -51h688q34 0 61 22t34 56l76 405q5 32 5 59v498q0 46 -33 79t-79 33t-79 -33 t-33 -79v-272h-32v528q0 46 -33 79t-79 33t-79 -33t-33 -79v-528h-32v656q0 46 -33 79t-79 33zM880 1536q68 0 125.5 -35.5t88.5 -96.5q19 4 42 4q99 0 169.5 -70.5t70.5 -169.5v-17q105 6 180.5 -64t75.5 -175v-498q0 -40 -8 -83l-76 -404q-14 -79 -76.5 -131t-143.5 -52 h-688q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 106 75 181t181 75q78 0 128 -34v434q0 99 70.5 169.5t169.5 70.5q23 0 42 -4q31 61 88.5 96.5t125.5 35.5z" />
<glyph unicode="&#xf257;" horiz-adv-x="1792" d="M1073 -128h-177q-163 0 -226 141q-23 49 -23 102v5q-62 30 -98.5 88.5t-36.5 127.5q0 38 5 48h-261q-106 0 -181 75t-75 181t75 181t181 75h113l-44 17q-74 28 -119.5 93.5t-45.5 145.5q0 106 75 181t181 75q46 0 91 -17l628 -239h401q106 0 181 -75t75 -181v-668 q0 -88 -54 -157.5t-140 -90.5l-339 -85q-92 -23 -186 -23zM1024 583l-155 -71l-163 -74q-30 -14 -48 -41.5t-18 -60.5q0 -46 33 -79t79 -33q26 0 46 10l338 154q-49 10 -80.5 50t-31.5 90v55zM1344 272q0 46 -33 79t-79 33q-26 0 -46 -10l-290 -132q-28 -13 -37 -17 t-30.5 -17t-29.5 -23.5t-16 -29t-8 -40.5q0 -50 31.5 -82t81.5 -32q20 0 38 9l352 160q30 14 48 41.5t18 60.5zM1112 1024l-650 248q-24 8 -46 8q-53 0 -90.5 -37.5t-37.5 -90.5q0 -40 22.5 -73t59.5 -47l526 -200v-64h-640q-53 0 -90.5 -37.5t-37.5 -90.5t37.5 -90.5 t90.5 -37.5h535l233 106v198q0 63 46 106l111 102h-69zM1073 0q82 0 155 19l339 85q43 11 70 45.5t27 78.5v668q0 53 -37.5 90.5t-90.5 37.5h-308l-136 -126q-36 -33 -36 -82v-296q0 -46 33 -77t79 -31t79 35t33 81v208h32v-208q0 -70 -57 -114q52 -8 86.5 -48.5t34.5 -93.5 q0 -42 -23 -78t-61 -53l-310 -141h91z" />
<glyph unicode="&#xf258;" horiz-adv-x="2048" d="M1151 1536q61 0 116 -28t91 -77l572 -781q118 -159 118 -359v-355q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v177l-286 143h-546q-80 0 -136 56t-56 136v32q0 119 84.5 203.5t203.5 84.5h420l42 128h-686q-100 0 -173.5 67.5t-81.5 166.5q-65 79 -65 182v32 q0 80 56 136t136 56h959zM1920 -64v355q0 157 -93 284l-573 781q-39 52 -103 52h-959q-26 0 -45 -19t-19 -45q0 -32 1.5 -49.5t9.5 -40.5t25 -43q10 31 35.5 50t56.5 19h832v-32h-832q-26 0 -45 -19t-19 -45q0 -44 3 -58q8 -44 44 -73t81 -29h640h91q40 0 68 -28t28 -68 q0 -15 -5 -30l-64 -192q-10 -29 -35 -47.5t-56 -18.5h-443q-66 0 -113 -47t-47 -113v-32q0 -26 19 -45t45 -19h561q16 0 29 -7l317 -158q24 -13 38.5 -36t14.5 -50v-197q0 -26 19 -45t45 -19h384q26 0 45 19t19 45z" />
<glyph unicode="&#xf259;" horiz-adv-x="2048" d="M816 1408q-48 0 -79.5 -34t-31.5 -82q0 -14 3 -28l150 -624h-26l-116 482q-9 38 -39.5 62t-69.5 24q-47 0 -79 -34t-32 -81q0 -11 4 -29q3 -13 39 -161t68 -282t32 -138v-227l-307 230q-34 26 -77 26q-52 0 -89.5 -36.5t-37.5 -88.5q0 -67 56 -110l507 -379 q34 -26 76 -26h694q33 0 59 20.5t34 52.5l100 401q8 30 10 88t9 86l116 478q3 12 3 26q0 46 -33 79t-80 33q-38 0 -69 -25.5t-40 -62.5l-99 -408h-26l132 547q3 14 3 28q0 47 -32 80t-80 33q-38 0 -68.5 -24t-39.5 -62l-145 -602h-127l-164 682q-9 38 -39.5 62t-68.5 24z M1461 -256h-694q-85 0 -153 51l-507 380q-50 38 -78.5 94t-28.5 118q0 105 75 179t180 74q25 0 49.5 -5.5t41.5 -11t41 -20.5t35 -23t38.5 -29.5t37.5 -28.5l-123 512q-7 35 -7 59q0 93 60 162t152 79q14 87 80.5 144.5t155.5 57.5q83 0 148 -51.5t85 -132.5l103 -428 l83 348q20 81 85 132.5t148 51.5q87 0 152.5 -54t82.5 -139q93 -10 155 -78t62 -161q0 -30 -7 -57l-116 -477q-5 -22 -5 -67q0 -51 -13 -108l-101 -401q-19 -75 -79.5 -122.5t-137.5 -47.5z" />
<glyph unicode="&#xf25a;" horiz-adv-x="1792" d="M640 1408q-53 0 -90.5 -37.5t-37.5 -90.5v-512v-384l-151 202q-41 54 -107 54q-52 0 -89 -38t-37 -90q0 -43 26 -77l384 -512q38 -51 102 -51h718q22 0 39.5 13.5t22.5 34.5l92 368q24 96 24 194v217q0 41 -28 71t-68 30t-68 -28t-28 -68h-32v61q0 48 -32 81.5t-80 33.5 q-46 0 -79 -33t-33 -79v-64h-32v90q0 55 -37 94.5t-91 39.5q-53 0 -90.5 -37.5t-37.5 -90.5v-96h-32v570q0 55 -37 94.5t-91 39.5zM640 1536q107 0 181.5 -77.5t74.5 -184.5v-220q22 2 32 2q99 0 173 -69q47 21 99 21q113 0 184 -87q27 7 56 7q94 0 159 -67.5t65 -161.5 v-217q0 -116 -28 -225l-92 -368q-16 -64 -68 -104.5t-118 -40.5h-718q-60 0 -114.5 27.5t-90.5 74.5l-384 512q-51 68 -51 154q0 105 74.5 180.5t179.5 75.5q71 0 130 -35v547q0 106 75 181t181 75zM768 128v384h-32v-384h32zM1024 128v384h-32v-384h32zM1280 128v384h-32 v-384h32z" />
<glyph unicode="&#xf25b;" d="M1288 889q60 0 107 -23q141 -63 141 -226v-177q0 -94 -23 -186l-85 -339q-21 -86 -90.5 -140t-157.5 -54h-668q-106 0 -181 75t-75 181v401l-239 628q-17 45 -17 91q0 106 75 181t181 75q80 0 145.5 -45.5t93.5 -119.5l17 -44v113q0 106 75 181t181 75t181 -75t75 -181 v-261q27 5 48 5q69 0 127.5 -36.5t88.5 -98.5zM1072 896q-33 0 -60.5 -18t-41.5 -48l-74 -163l-71 -155h55q50 0 90 -31.5t50 -80.5l154 338q10 20 10 46q0 46 -33 79t-79 33zM1293 761q-22 0 -40.5 -8t-29 -16t-23.5 -29.5t-17 -30.5t-17 -37l-132 -290q-10 -20 -10 -46 q0 -46 33 -79t79 -33q33 0 60.5 18t41.5 48l160 352q9 18 9 38q0 50 -32 81.5t-82 31.5zM128 1120q0 -22 8 -46l248 -650v-69l102 111q43 46 106 46h198l106 233v535q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5v-640h-64l-200 526q-14 37 -47 59.5t-73 22.5 q-53 0 -90.5 -37.5t-37.5 -90.5zM1180 -128q44 0 78.5 27t45.5 70l85 339q19 73 19 155v91l-141 -310q-17 -38 -53 -61t-78 -23q-53 0 -93.5 34.5t-48.5 86.5q-44 -57 -114 -57h-208v32h208q46 0 81 33t35 79t-31 79t-77 33h-296q-49 0 -82 -36l-126 -136v-308 q0 -53 37.5 -90.5t90.5 -37.5h668z" />
<glyph unicode="&#xf25c;" horiz-adv-x="1973" d="M857 992v-117q0 -13 -9.5 -22t-22.5 -9h-298v-812q0 -13 -9 -22.5t-22 -9.5h-135q-13 0 -22.5 9t-9.5 23v812h-297q-13 0 -22.5 9t-9.5 22v117q0 14 9 23t23 9h793q13 0 22.5 -9.5t9.5 -22.5zM1895 995l77 -961q1 -13 -8 -24q-10 -10 -23 -10h-134q-12 0 -21 8.5 t-10 20.5l-46 588l-189 -425q-8 -19 -29 -19h-120q-20 0 -29 19l-188 427l-45 -590q-1 -12 -10 -20.5t-21 -8.5h-135q-13 0 -23 10q-9 10 -9 24l78 961q1 12 10 20.5t21 8.5h142q20 0 29 -19l220 -520q10 -24 20 -51q3 7 9.5 24.5t10.5 26.5l221 520q9 19 29 19h141 q13 0 22 -8.5t10 -20.5z" />
<glyph unicode="&#xf25d;" horiz-adv-x="1792" d="M1042 833q0 88 -60 121q-33 18 -117 18h-123v-281h162q66 0 102 37t36 105zM1094 548l205 -373q8 -17 -1 -31q-8 -16 -27 -16h-152q-20 0 -28 17l-194 365h-155v-350q0 -14 -9 -23t-23 -9h-134q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h294q128 0 190 -24q85 -31 134 -109 t49 -180q0 -92 -42.5 -165.5t-115.5 -109.5q6 -10 9 -16zM896 1376q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM1792 640 q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf25e;" horiz-adv-x="1792" d="M605 303q153 0 257 104q14 18 3 36l-45 82q-6 13 -24 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78 q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-148 0 -246 -96.5t-98 -240.5q0 -146 97 -241.5t247 -95.5zM1235 303q153 0 257 104q14 18 4 36l-45 82q-8 14 -25 17q-16 2 -27 -11l-4 -3q-4 -4 -11.5 -10t-17.5 -13t-23.5 -14.5t-28.5 -13.5t-33.5 -9.5 t-37.5 -3.5q-76 0 -125 50t-49 127q0 76 48 125.5t122 49.5q37 0 71.5 -14t50.5 -28l16 -14q11 -11 26 -10q16 2 24 14l53 78q13 20 -2 39q-3 4 -11 12t-30 23.5t-48.5 28t-67.5 22.5t-86 10q-147 0 -245.5 -96.5t-98.5 -240.5q0 -146 97 -241.5t247 -95.5zM896 1376 q-150 0 -286 -58.5t-234.5 -157t-157 -234.5t-58.5 -286t58.5 -286t157 -234.5t234.5 -157t286 -58.5t286 58.5t234.5 157t157 234.5t58.5 286t-58.5 286t-157 234.5t-234.5 157t-286 58.5zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71z" />
<glyph unicode="&#xf260;" horiz-adv-x="2048" d="M736 736l384 -384l-384 -384l-672 672l672 672l168 -168l-96 -96l-72 72l-480 -480l480 -480l193 193l-289 287zM1312 1312l672 -672l-672 -672l-168 168l96 96l72 -72l480 480l-480 480l-193 -193l289 -287l-96 -96l-384 384z" />
<glyph unicode="&#xf261;" horiz-adv-x="1792" d="M717 182l271 271l-279 279l-88 -88l192 -191l-96 -96l-279 279l279 279l40 -40l87 87l-127 128l-454 -454zM1075 190l454 454l-454 454l-271 -271l279 -279l88 88l-192 191l96 96l279 -279l-279 -279l-40 40l-87 -88zM1792 640q0 -182 -71 -348t-191 -286t-286 -191 t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf262;" horiz-adv-x="2304" d="M651 539q0 -39 -27.5 -66.5t-65.5 -27.5q-39 0 -66.5 27.5t-27.5 66.5q0 38 27.5 65.5t66.5 27.5q38 0 65.5 -27.5t27.5 -65.5zM1805 540q0 -39 -27.5 -66.5t-66.5 -27.5t-66.5 27.5t-27.5 66.5t27.5 66t66.5 27t66.5 -27t27.5 -66zM765 539q0 79 -56.5 136t-136.5 57 t-136.5 -56.5t-56.5 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM1918 540q0 80 -56.5 136.5t-136.5 56.5q-79 0 -136 -56.5t-57 -136.5t56.5 -136.5t136.5 -56.5t136.5 56.5t56.5 136.5zM850 539q0 -116 -81.5 -197.5t-196.5 -81.5q-116 0 -197.5 82t-81.5 197 t82 196.5t197 81.5t196.5 -81.5t81.5 -196.5zM2004 540q0 -115 -81.5 -196.5t-197.5 -81.5q-115 0 -196.5 81.5t-81.5 196.5t81.5 196.5t196.5 81.5q116 0 197.5 -81.5t81.5 -196.5zM1040 537q0 191 -135.5 326.5t-326.5 135.5q-125 0 -231 -62t-168 -168.5t-62 -231.5 t62 -231.5t168 -168.5t231 -62q191 0 326.5 135.5t135.5 326.5zM1708 1110q-254 111 -556 111q-319 0 -573 -110q117 0 223 -45.5t182.5 -122.5t122 -183t45.5 -223q0 115 43.5 219.5t118 180.5t177.5 123t217 50zM2187 537q0 191 -135 326.5t-326 135.5t-326.5 -135.5 t-135.5 -326.5t135.5 -326.5t326.5 -135.5t326 135.5t135 326.5zM1921 1103h383q-44 -51 -75 -114.5t-40 -114.5q110 -151 110 -337q0 -156 -77 -288t-209 -208.5t-287 -76.5q-133 0 -249 56t-196 155q-47 -56 -129 -179q-11 22 -53.5 82.5t-74.5 97.5 q-80 -99 -196.5 -155.5t-249.5 -56.5q-155 0 -287 76.5t-209 208.5t-77 288q0 186 110 337q-9 51 -40 114.5t-75 114.5h365q149 100 355 156.5t432 56.5q224 0 421 -56t348 -157z" />
<glyph unicode="&#xf263;" horiz-adv-x="1280" d="M640 629q-188 0 -321 133t-133 320q0 188 133 321t321 133t321 -133t133 -321q0 -187 -133 -320t-321 -133zM640 1306q-92 0 -157.5 -65.5t-65.5 -158.5q0 -92 65.5 -157.5t157.5 -65.5t157.5 65.5t65.5 157.5q0 93 -65.5 158.5t-157.5 65.5zM1163 574q13 -27 15 -49.5 t-4.5 -40.5t-26.5 -38.5t-42.5 -37t-61.5 -41.5q-115 -73 -315 -94l73 -72l267 -267q30 -31 30 -74t-30 -73l-12 -13q-31 -30 -74 -30t-74 30q-67 68 -267 268l-267 -268q-31 -30 -74 -30t-73 30l-12 13q-31 30 -31 73t31 74l267 267l72 72q-203 21 -317 94 q-39 25 -61.5 41.5t-42.5 37t-26.5 38.5t-4.5 40.5t15 49.5q10 20 28 35t42 22t56 -2t65 -35q5 -4 15 -11t43 -24.5t69 -30.5t92 -24t113 -11q91 0 174 25.5t120 50.5l38 25q33 26 65 35t56 2t42 -22t28 -35z" />
<glyph unicode="&#xf264;" d="M927 956q0 -66 -46.5 -112.5t-112.5 -46.5t-112.5 46.5t-46.5 112.5t46.5 112.5t112.5 46.5t112.5 -46.5t46.5 -112.5zM1141 593q-10 20 -28 32t-47.5 9.5t-60.5 -27.5q-10 -8 -29 -20t-81 -32t-127 -20t-124 18t-86 36l-27 18q-31 25 -60.5 27.5t-47.5 -9.5t-28 -32 q-22 -45 -2 -74.5t87 -73.5q83 -53 226 -67l-51 -52q-142 -142 -191 -190q-22 -22 -22 -52.5t22 -52.5l9 -9q22 -22 52.5 -22t52.5 22l191 191q114 -115 191 -191q22 -22 52.5 -22t52.5 22l9 9q22 22 22 52.5t-22 52.5l-191 190l-52 52q141 14 225 67q67 44 87 73.5t-2 74.5 zM1092 956q0 134 -95 229t-229 95t-229 -95t-95 -229t95 -229t229 -95t229 95t95 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf265;" horiz-adv-x="1720" d="M1565 1408q65 0 110 -45.5t45 -110.5v-519q0 -176 -68 -336t-182.5 -275t-274 -182.5t-334.5 -67.5q-176 0 -335.5 67.5t-274.5 182.5t-183 275t-68 336v519q0 64 46 110t110 46h1409zM861 344q47 0 82 33l404 388q37 35 37 85q0 49 -34.5 83.5t-83.5 34.5q-47 0 -82 -33 l-323 -310l-323 310q-35 33 -81 33q-49 0 -83.5 -34.5t-34.5 -83.5q0 -51 36 -85l405 -388q33 -33 81 -33z" />
<glyph unicode="&#xf266;" horiz-adv-x="2304" d="M1494 -103l-295 695q-25 -49 -158.5 -305.5t-198.5 -389.5q-1 -1 -27.5 -0.5t-26.5 1.5q-82 193 -255.5 587t-259.5 596q-21 50 -66.5 107.5t-103.5 100.5t-102 43q0 5 -0.5 24t-0.5 27h583v-50q-39 -2 -79.5 -16t-66.5 -43t-10 -64q26 -59 216.5 -499t235.5 -540 q31 61 140 266.5t131 247.5q-19 39 -126 281t-136 295q-38 69 -201 71v50l513 -1v-47q-60 -2 -93.5 -25t-12.5 -69q33 -70 87 -189.5t86 -187.5q110 214 173 363q24 55 -10 79.5t-129 26.5q1 7 1 25v24q64 0 170.5 0.5t180 1t92.5 0.5v-49q-62 -2 -119 -33t-90 -81 l-213 -442q13 -33 127.5 -290t121.5 -274l441 1017q-14 38 -49.5 62.5t-65 31.5t-55.5 8v50l460 -4l1 -2l-1 -44q-139 -4 -201 -145q-526 -1216 -559 -1291h-49z" />
<glyph unicode="&#xf267;" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
<glyph unicode="&#xf268;" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
<glyph unicode="&#xf269;" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
<glyph unicode="&#xf26a;" horiz-adv-x="1792" d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z " />
<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
<glyph unicode="&#xf26c;" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
<glyph unicode="&#xf26d;" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
<glyph unicode="&#xf26e;" d="M1401 -11l-6 -6q-113 -114 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 32 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 32.5 -6t30.5 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
<glyph unicode="&#xf270;" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
<glyph unicode="&#xf271;" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
<glyph unicode="&#xf272;" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf273;" horiz-adv-x="1792" d="M1111 151l-46 -46q-9 -9 -22 -9t-23 9l-188 189l-188 -189q-10 -9 -23 -9t-22 9l-46 46q-9 9 -9 22t9 23l189 188l-189 188q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l188 -188l188 188q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23l-188 -188l188 -188q9 -10 9 -23t-9 -22z M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf274;" horiz-adv-x="1792" d="M1303 572l-512 -512q-10 -9 -23 -9t-23 9l-288 288q-9 10 -9 23t9 22l46 46q9 9 22 9t23 -9l220 -220l444 444q10 9 23 9t22 -9l46 -46q9 -9 9 -22t-9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23 t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf275;" horiz-adv-x="1792" d="M448 1536q26 0 45 -19t19 -45v-891l536 429q17 14 40 14q26 0 45 -19t19 -45v-379l536 429q17 14 40 14q26 0 45 -19t19 -45v-1152q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h384z" />
<glyph unicode="&#xf276;" horiz-adv-x="1024" d="M512 448q66 0 128 15v-655q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v655q61 -15 128 -15zM512 1536q212 0 362 -150t150 -362t-150 -362t-362 -150t-362 150t-150 362t150 362t362 150zM512 1312q14 0 23 9t9 23t-9 23t-23 9q-146 0 -249 -103t-103 -249 q0 -14 9 -23t23 -9t23 9t9 23q0 119 84.5 203.5t203.5 84.5z" />
<glyph unicode="&#xf277;" horiz-adv-x="1792" d="M1745 1239q10 -10 10 -23t-10 -23l-141 -141q-28 -28 -68 -28h-1344q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h576v64q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-64h512q40 0 68 -28zM768 320h256v-512q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v512zM1600 768 q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-1344q-40 0 -68 28l-141 141q-10 10 -10 23t10 23l141 141q28 28 68 28h512v192h256v-192h576z" />
<glyph unicode="&#xf278;" horiz-adv-x="2048" d="M2020 1525q28 -20 28 -53v-1408q0 -20 -11 -36t-29 -23l-640 -256q-24 -11 -48 0l-616 246l-616 -246q-10 -5 -24 -5q-19 0 -36 11q-28 20 -28 53v1408q0 20 11 36t29 23l640 256q24 11 48 0l616 -246l616 246q32 13 60 -6zM736 1390v-1270l576 -230v1270zM128 1173 v-1270l544 217v1270zM1920 107v1270l-544 -217v-1270z" />
<glyph unicode="&#xf279;" horiz-adv-x="1792" d="M512 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472q0 20 17 28l480 256q7 4 15 4zM1760 1536q13 0 22.5 -9.5t9.5 -22.5v-1472q0 -20 -17 -28l-480 -256q-7 -4 -15 -4q-13 0 -22.5 9.5t-9.5 22.5v1472 q0 20 17 28l480 256q7 4 15 4zM640 1536q8 0 14 -3l512 -256q18 -10 18 -29v-1472q0 -13 -9.5 -22.5t-22.5 -9.5q-8 0 -14 3l-512 256q-18 10 -18 29v1472q0 13 9.5 22.5t22.5 9.5z" />
<glyph unicode="&#xf27a;" horiz-adv-x="1792" d="M640 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 640q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-110 0 -211 18q-173 -173 -435 -229q-52 -10 -86 -13q-12 -1 -22 6t-13 18q-4 15 20 37q5 5 23.5 21.5t25.5 23.5t23.5 25.5t24 31.5t20.5 37 t20 48t14.5 57.5t12.5 72.5q-146 90 -229.5 216.5t-83.5 269.5q0 174 120 321.5t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
<glyph unicode="&#xf27b;" horiz-adv-x="1792" d="M640 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1024 640q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 -53 -37.5 -90.5t-90.5 -37.5 t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5 t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51 t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 130 71 248.5t191 204.5t286 136.5t348 50.5t348 -50.5t286 -136.5t191 -204.5t71 -248.5z" />
<glyph unicode="&#xf27c;" horiz-adv-x="1024" d="M512 345l512 295v-591l-512 -296v592zM0 640v-591l512 296zM512 1527v-591l-512 -296v591zM512 936l512 295v-591z" />
<glyph unicode="&#xf27d;" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
<glyph unicode="&#xf27e;" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
<glyph unicode="&#xf280;" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
<glyph unicode="&#xf281;" horiz-adv-x="1792" d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
<glyph unicode="&#xf282;" horiz-adv-x="1792" d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
<glyph unicode="&#xf283;" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
<glyph unicode="&#xf284;" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
<glyph unicode="&#xf285;" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
<glyph unicode="&#xf286;" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
<glyph unicode="&#xf287;" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
<glyph unicode="&#xf288;" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf289;" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
<glyph unicode="&#xf28a;" d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
<glyph unicode="&#xf28b;" d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf28c;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
<glyph unicode="&#xf28d;" d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
<glyph unicode="&#xf28e;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
<glyph unicode="&#xf290;" horiz-adv-x="1792" d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
<glyph unicode="&#xf291;" horiz-adv-x="2048" d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
<glyph unicode="&#xf292;" horiz-adv-x="1792" d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 q15 0 25 -12q9 -12 6 -28z" />
<glyph unicode="&#xf293;" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
<glyph unicode="&#xf294;" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
<glyph unicode="&#xf295;" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
<glyph unicode="&#xf296;" horiz-adv-x="1792" />
<glyph unicode="&#xf297;" horiz-adv-x="1792" />
<glyph unicode="&#xf298;" horiz-adv-x="1792" />
<glyph unicode="&#xf299;" horiz-adv-x="1792" />
<glyph unicode="&#xf29a;" horiz-adv-x="1792" />
<glyph unicode="&#xf29b;" horiz-adv-x="1792" />
<glyph unicode="&#xf29c;" horiz-adv-x="1792" />
<glyph unicode="&#xf29d;" horiz-adv-x="1792" />
<glyph unicode="&#xf29e;" horiz-adv-x="1792" />
<glyph unicode="&#xf500;" horiz-adv-x="1792" />
</font>
</defs></svg>

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 357 KiB

Before After
Before After

BIN
view/theme/vier/font/fontawesome-webfont.ttf Executable file → Normal file

Binary file not shown.

BIN
view/theme/vier/font/fontawesome-webfont.woff Executable file → Normal file

Binary file not shown.

Binary file not shown.

View file

@ -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;

View file

@ -0,0 +1,98 @@
{{if $header}}<h2>{{$header}}</h2>{{/if}}
<div id="contact-edit-wrapper" >
{{* Insert Tab-Nav *}}
{{$tab_str}}
<div id="contact-edit-nav-wrapper" >
<div id="contact-edit-links">
<div id="contact-edit-status-wrapper">
<span id="contact-edit-contact-status">{{$contact_status}}</span>
{{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
<div id="contact-edit-actions">
<a class="btn" id="contact-edit-actions-button">{{$contact_action_button}}</a>
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup" >
{{if $lblsuggest}}<li role="menuitem"><a href="#" title="{{$contact_actions.suggest.title}}" onclick="window.location.href='{{$contact_actions.suggest.url}}'; return false;">{{$contact_actions.suggest.label}}</a></li>{{/if}}
{{if $poll_enabled}}<li role="menuitem"><a href="#" title="{{$contact_actions.update.title}}" onclick="window.location.href='{{$contact_actions.update.url}}'; return false;">{{$contact_actions.update.label}}</a></li>{{/if}}
<li class="divider"></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.archive.title}}" onclick="window.location.href='{{$contact_actions.archive.url}}'; return false;">{{$contact_actions.archive.label}}</a></li>
<li role="menuitem"><a href="#" title="{{$contact_actions.delete.title}}" onclick="return confirmDelete();">{{$contact_actions.delete.label}}</a></li>
</ul>
</div>
{{* Block with status information about the contact *}}
<ul>
{{if $relation_text}}<li><div id="contact-edit-rel">{{$relation_text}}</div></li>{{/if}}
{{if $poll_enabled}}
<li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
{{if $poll_interval}}
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval}}
{{/if}}
</li>
{{/if}}
{{if $lost_contact}}<li><div id="lost-contact-message">{{$lost_contact}}</div></li>{{/if}}
{{if $insecure}}<li><div id="insecure-message">{{$insecure}}</div></li> {{/if}}
{{if $blocked}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
{{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
{{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
</ul>
<ul>
<!-- <li><a href="network/0?nets=all&cid={{$contact_id}}" id="contact-edit-view-recent">{{$lblrecent}}</a></li> -->
{{if $follow}}<li><div id="contact-edit-follow"><a href="{{$follow}}">{{$follow_text}}</a></div></li>{{/if}}
</ul>
</div> {{* End of contact-edit-status-wrapper *}}
{{* Some information about the contact from the profile *}}
<dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$url}}">{{$profileurl}}</a></dd></dl>
{{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location}}</dd></dl>{{/if}}
{{if $keywords}}<dl><dt>{{$keywords_label}}</dt><dd>{{$keywords}}</dd></dl>{{/if}}
{{if $about}}<dl><dt>{{$about_label}}</dt><dd>{{$about}}</dd></dl>{{/if}}
</div>{{* End of contact-edit-links *}}
<div id="contact-edit-links-end"></div>
<hr />
<h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
<div id="contact-edit-settings">
<form action="contacts/{{$contact_id}}" method="post" >
<input type="hidden" name="contact_id" value="{{$contact_id}}">
<div id="contact-edit-end" ></div>
{{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}}
<div id="contact-edit-info-wrapper">
<h4>{{$lbl_info1}}</h4>
<textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
</div>
<div id="contact-edit-info-end"></div>
{{if $profile_select}}
<div id="contact-edit-profile-select-text">
<h4>{{$lbl_vis1}}</h4>
<p>{{$lbl_vis2}}</p>
</div>
{{$profile_select}}
<div id="contact-edit-profile-select-end"></div>
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit|escape:'html'}}" />
{{/if}}
</form>
</div>
</div>{{* End of contact-edit-nav-wrapper *}}
</div>

View file

@ -91,7 +91,7 @@
{{if $item.threaded}}
{{/if}}
{{if $item.comment}}
<a role="button" id="comment-{{$item.id}}" class="fakelink togglecomment" onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" title="{{$item.switchcomment}}"><i class="icon-reply"><span class="sr-only">{{$item.switchcomment}}</span></i></a>
<a role="button" id="comment-{{$item.id}}" class="fakelink togglecomment" onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" title="{{$item.switchcomment}}"><i class="icon-commenting"><span class="sr-only">{{$item.switchcomment}}</span></i></a>
{{/if}}
{{if $item.isevent}}

View file

@ -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'] = '<form name="simple_bar" method="get" action="'.$a->get_baseurl().'/dirfind">
$nv['search'] = '<form name="simple_bar" method="get" action="dirfind">
<span class="sbox_l"></span>
<span class="sbox">
<input type="text" name="search" size="13" maxlength="50">
@ -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;