Merge remote-tracking branch 'upstream/develop' into dfrn-reshare
This commit is contained in:
commit
88353ce56f
291 changed files with 98692 additions and 89038 deletions
141
src/App.php
141
src/App.php
|
@ -212,12 +212,6 @@ class App
|
|||
. $this->basepath . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR
|
||||
. $this->basepath);
|
||||
|
||||
|
||||
if (is_array($_SERVER['argv']) && $_SERVER['argc'] > 1 && substr(end($_SERVER['argv']), 0, 4) == 'http') {
|
||||
$this->set_baseurl(array_pop($_SERVER['argv']));
|
||||
$_SERVER['argc'] --;
|
||||
}
|
||||
|
||||
if ((x($_SERVER, 'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'], 0, 9) === 'pagename=') {
|
||||
$this->query_string = substr($_SERVER['QUERY_STRING'], 9);
|
||||
|
||||
|
@ -868,9 +862,6 @@ class App
|
|||
|
||||
array_unshift($args, ((x($this->config, 'php_path')) && (strlen($this->config['php_path'])) ? $this->config['php_path'] : 'php'));
|
||||
|
||||
// add baseurl to args. cli scripts can't construct it
|
||||
$args[] = $this->get_baseurl();
|
||||
|
||||
for ($x = 0; $x < count($args); $x ++) {
|
||||
$args[$x] = escapeshellarg($args[$x]);
|
||||
}
|
||||
|
@ -944,4 +935,136 @@ class App
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
* @param mixed $default Default value if it isn't set
|
||||
*/
|
||||
public function getConfigValue($cat, $k, $default = null)
|
||||
{
|
||||
$return = $default;
|
||||
|
||||
if ($cat === 'config') {
|
||||
if (isset($this->config[$k])) {
|
||||
$return = $this->config[$k];
|
||||
}
|
||||
} else {
|
||||
if (isset($this->config[$cat][$k])) {
|
||||
$return = $this->config[$cat][$k];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the config cache. Accepts raw output from the config table
|
||||
*
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
* @param mixed $v Value to set
|
||||
*/
|
||||
public function setConfigValue($cat, $k, $v)
|
||||
{
|
||||
// Only arrays are serialized in database, so we have to unserialize sparingly
|
||||
$value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
|
||||
|
||||
if ($cat === 'config') {
|
||||
$this->config[$k] = $value;
|
||||
} else {
|
||||
$this->config[$cat][$k] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the config cache
|
||||
*
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
*/
|
||||
public function deleteConfigValue($cat, $k)
|
||||
{
|
||||
if ($cat === 'config') {
|
||||
if (isset($this->config[$k])) {
|
||||
unset($this->config[$k]);
|
||||
}
|
||||
} else {
|
||||
if (isset($this->config[$cat][$k])) {
|
||||
unset($this->config[$cat][$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a value from the user config cache
|
||||
*
|
||||
* @param int $uid User Id
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
* @param mixed $default Default value if key isn't set
|
||||
*/
|
||||
public function getPConfigValue($uid, $cat, $k, $default = null)
|
||||
{
|
||||
$return = $default;
|
||||
|
||||
if (isset($this->config[$uid][$cat][$k])) {
|
||||
$return = $this->config[$uid][$cat][$k];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the user config cache
|
||||
*
|
||||
* Accepts raw output from the pconfig table
|
||||
*
|
||||
* @param int $uid User Id
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
* @param mixed $v Value to set
|
||||
*/
|
||||
public function setPConfigValue($uid, $cat, $k, $v)
|
||||
{
|
||||
// Only arrays are serialized in database, so we have to unserialize sparingly
|
||||
$value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
|
||||
|
||||
$this->config[$uid][$cat][$k] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a value from the user config cache
|
||||
*
|
||||
* @param int $uid User Id
|
||||
* @param string $cat Config category
|
||||
* @param string $k Config key
|
||||
*/
|
||||
public function deletePConfigValue($uid, $cat, $k)
|
||||
{
|
||||
if (isset($this->config[$uid][$cat][$k])) {
|
||||
unset($this->config[$uid][$cat][$k]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the site's default sender email address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSenderEmailAddress()
|
||||
{
|
||||
$sender_email = Config::get('config', 'sender_email');
|
||||
if (empty($sender_email)) {
|
||||
$hostname = $this->get_hostname();
|
||||
if (strpos($hostname, ':')) {
|
||||
$hostname = substr($hostname, 0, strpos($hostname, ':'));
|
||||
}
|
||||
|
||||
$sender_email = 'noreply@' . $hostname;
|
||||
}
|
||||
|
||||
return $sender_email;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ namespace Friendica;
|
|||
* All modules in Friendica should extend BaseModule, although not all modules
|
||||
* need to extend all the methods described here
|
||||
*
|
||||
* The filename of the module in src/Module needs to match the class name
|
||||
* exactly to make the module available.
|
||||
*
|
||||
* @author Hypolite Petovan mrpetovan@gmail.com
|
||||
*/
|
||||
abstract class BaseModule extends BaseObject
|
||||
|
|
|
@ -27,7 +27,7 @@ class ContactSelector
|
|||
|
||||
$o .= "<select id=\"contact-profile-selector\" class=\"form-control\" $disabled name=\"profile-assign\" >\r\n";
|
||||
|
||||
$s = dba::select('profile', ['id', 'profile-name', 'is-default'], ['uid' => $$_SESSION['uid']]);
|
||||
$s = dba::select('profile', ['id', 'profile-name', 'is-default'], ['uid' => $_SESSION['uid']]);
|
||||
$r = dba::inArray($s);
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
|
@ -123,9 +123,9 @@ class ContactSelector
|
|||
{
|
||||
$o = '';
|
||||
$select = ['', L10n::t('Male'), L10n::t('Female'), L10n::t('Currently Male'), L10n::t('Currently Female'), L10n::t('Mostly Male'), L10n::t('Mostly Female'), L10n::t('Transgender'), L10n::t('Intersex'), L10n::t('Transsexual'), L10n::t('Hermaphrodite'), L10n::t('Neuter'), L10n::t('Non-specific'), L10n::t('Other'), L10n::t('Undecided')];
|
||||
|
||||
|
||||
Addon::callHooks('gender_selector', $select);
|
||||
|
||||
|
||||
$o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >";
|
||||
foreach ($select as $selection) {
|
||||
if ($selection !== 'NOTRANSLATION') {
|
||||
|
@ -136,7 +136,7 @@ class ContactSelector
|
|||
$o .= '</select>';
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $current optional, default empty
|
||||
* @param string $suffix optionsl, default empty
|
||||
|
@ -145,10 +145,10 @@ class ContactSelector
|
|||
{
|
||||
$o = '';
|
||||
$select = ['', L10n::t('Males'), L10n::t('Females'), L10n::t('Gay'), L10n::t('Lesbian'), L10n::t('No Preference'), L10n::t('Bisexual'), L10n::t('Autosexual'), L10n::t('Abstinent'), L10n::t('Virgin'), L10n::t('Deviant'), L10n::t('Fetish'), L10n::t('Oodles'), L10n::t('Nonsexual')];
|
||||
|
||||
|
||||
|
||||
|
||||
Addon::callHooks('sexpref_selector', $select);
|
||||
|
||||
|
||||
$o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >";
|
||||
foreach ($select as $selection) {
|
||||
if ($selection !== 'NOTRANSLATION') {
|
||||
|
@ -159,7 +159,7 @@ class ContactSelector
|
|||
$o .= '</select>';
|
||||
return $o;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $current optional, default empty
|
||||
*/
|
||||
|
@ -167,9 +167,9 @@ class ContactSelector
|
|||
{
|
||||
$o = '';
|
||||
$select = ['', L10n::t('Single'), L10n::t('Lonely'), L10n::t('Available'), L10n::t('Unavailable'), L10n::t('Has crush'), L10n::t('Infatuated'), L10n::t('Dating'), L10n::t('Unfaithful'), L10n::t('Sex Addict'), L10n::t('Friends'), L10n::t('Friends/Benefits'), L10n::t('Casual'), L10n::t('Engaged'), L10n::t('Married'), L10n::t('Imaginarily married'), L10n::t('Partners'), L10n::t('Cohabiting'), L10n::t('Common law'), L10n::t('Happy'), L10n::t('Not looking'), L10n::t('Swinger'), L10n::t('Betrayed'), L10n::t('Separated'), L10n::t('Unstable'), L10n::t('Divorced'), L10n::t('Imaginarily divorced'), L10n::t('Widowed'), L10n::t('Uncertain'), L10n::t('It\'s complicated'), L10n::t('Don\'t care'), L10n::t('Ask me')];
|
||||
|
||||
|
||||
Addon::callHooks('marital_selector', $select);
|
||||
|
||||
|
||||
$o .= '<select name="marital" id="marital-select" size="1" >';
|
||||
foreach ($select as $selection) {
|
||||
if ($selection !== 'NOTRANSLATION') {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace Friendica\Content\Text;
|
||||
|
||||
use DOMDocument;
|
||||
use DomXPath;
|
||||
use DOMXPath;
|
||||
use Exception;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Content\OEmbed;
|
||||
|
@ -20,6 +20,7 @@ use Friendica\Core\PConfig;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Event;
|
||||
use Friendica\Network\Probe;
|
||||
use Friendica\Object\Image;
|
||||
use Friendica\Util\Map;
|
||||
|
@ -27,8 +28,6 @@ use Friendica\Util\Network;
|
|||
use Friendica\Util\ParseUrl;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
|
||||
require_once "include/event.php";
|
||||
require_once "include/html2plain.php";
|
||||
require_once "mod/proxy.php";
|
||||
|
||||
class BBCode extends BaseObject
|
||||
|
@ -77,10 +76,12 @@ class BBCode extends BaseObject
|
|||
|
||||
$picturedata = Image::getInfoFromURL($matches[1]);
|
||||
|
||||
if (($picturedata[0] >= 500) && ($picturedata[0] >= $picturedata[1])) {
|
||||
$post["image"] = $matches[1];
|
||||
} else {
|
||||
$post["preview"] = $matches[1];
|
||||
if ($picturedata) {
|
||||
if (($picturedata[0] >= 500) && ($picturedata[0] >= $picturedata[1])) {
|
||||
$post["image"] = $matches[1];
|
||||
} else {
|
||||
$post["preview"] = $matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,6 +243,9 @@ class BBCode extends BaseObject
|
|||
$body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
|
||||
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
$body = preg_replace("/\[img\=([$URLSearchString]*)\](.*?)\[\/img\]/ism", '[img]$1[/img]', $body);
|
||||
|
||||
if (preg_match_all("(\[url=([$URLSearchString]*)\]\s*\[img\]([$URLSearchString]*)\[\/img\]\s*\[\/url\])ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
if ((count($pictures) == 1) && !$has_title) {
|
||||
// Checking, if the link goes to a picture
|
||||
|
@ -267,7 +271,7 @@ class BBCode extends BaseObject
|
|||
$post["text"] = str_replace($pictures[0][0], "", $body);
|
||||
} else {
|
||||
$imgdata = Image::getInfoFromURL($pictures[0][1]);
|
||||
if (substr($imgdata["mime"], 0, 6) == "image/") {
|
||||
if ($imgdata && substr($imgdata["mime"], 0, 6) == "image/") {
|
||||
$post["type"] = "photo";
|
||||
$post["image"] = $pictures[0][1];
|
||||
$post["preview"] = $pictures[0][2];
|
||||
|
@ -413,7 +417,7 @@ class BBCode extends BaseObject
|
|||
}
|
||||
|
||||
$html = self::convert($post["text"].$post["after"], false, $htmlmode);
|
||||
$msg = html2plain($html, 0, true);
|
||||
$msg = HTML::toPlaintext($html, 0, true);
|
||||
$msg = trim(html_entity_decode($msg, ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
$link = "";
|
||||
|
@ -681,7 +685,7 @@ class BBCode extends BaseObject
|
|||
|
||||
$return = '';
|
||||
if ($simplehtml == 7) {
|
||||
$return = self::convertUrlForMastodon($data["url"]);
|
||||
$return = self::convertUrlForOStatus($data["url"]);
|
||||
} elseif (($simplehtml != 4) && ($simplehtml != 0)) {
|
||||
$return = sprintf('<a href="%s" target="_blank">%s</a><br>', $data["url"], $data["title"]);
|
||||
} else {
|
||||
|
@ -709,9 +713,10 @@ class BBCode extends BaseObject
|
|||
}
|
||||
|
||||
if ($data["description"] != "" && $data["description"] != $data["title"]) {
|
||||
$return .= sprintf('<blockquote>%s</blockquote>', trim(self::convert($data["description"])));
|
||||
// Sanitize the HTML by converting it to BBCode
|
||||
$bbcode = HTML::toBBCode($data["description"]);
|
||||
$return .= sprintf('<blockquote>%s</blockquote>', trim(self::convert($bbcode)));
|
||||
}
|
||||
|
||||
if ($data["type"] == "link") {
|
||||
$return .= sprintf('<sup><a href="%s">%s</a></sup>', $data['url'], parse_url($data['url'], PHP_URL_HOST));
|
||||
}
|
||||
|
@ -758,33 +763,12 @@ class BBCode extends BaseObject
|
|||
if (($data["url"] != "") && ($data["title"] != "")) {
|
||||
$text .= "\n[url=" . $data["url"] . "]" . $data["title"] . "[/url]";
|
||||
} elseif (($data["url"] != "")) {
|
||||
$text .= "\n" . $data["url"];
|
||||
$text .= "\n[url]" . $data["url"] . "[/url]";
|
||||
}
|
||||
|
||||
return $text . "\n" . $data["after"];
|
||||
}
|
||||
|
||||
private static function cleanCss($input)
|
||||
{
|
||||
$cleaned = "";
|
||||
|
||||
$input = strtolower($input);
|
||||
|
||||
for ($i = 0; $i < strlen($input); $i++) {
|
||||
$char = substr($input, $i, 1);
|
||||
|
||||
if (($char >= "a") && ($char <= "z")) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
|
||||
if (!(strpos(" #;:0123456789-_.%", $char) === false)) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
return $cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts [url] BBCodes in a format that looks fine on Mastodon. (callback function)
|
||||
*
|
||||
|
@ -792,7 +776,7 @@ class BBCode extends BaseObject
|
|||
* @param array $match Array with the matching values
|
||||
* @return string reformatted link including HTML codes
|
||||
*/
|
||||
private static function convertUrlForMastodonCallback($match)
|
||||
private static function convertUrlForOStatusCallback($match)
|
||||
{
|
||||
$url = $match[1];
|
||||
|
||||
|
@ -805,34 +789,27 @@ class BBCode extends BaseObject
|
|||
return $match[0];
|
||||
}
|
||||
|
||||
return self::convertUrlForMastodon($url);
|
||||
return self::convertUrlForOStatus($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts [url] BBCodes in a format that looks fine on Mastodon and GNU Social.
|
||||
* @brief Converts [url] BBCodes in a format that looks fine on OStatus systems.
|
||||
* @param string $url URL that is about to be reformatted
|
||||
* @return string reformatted link including HTML codes
|
||||
*/
|
||||
private static function convertUrlForMastodon($url)
|
||||
private static function convertUrlForOStatus($url)
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
$scheme = $parts['scheme'] . '://';
|
||||
$styled_url = str_replace($scheme, '', $url);
|
||||
|
||||
$html = '<a href="%s" class="attachment" rel="nofollow noopener" target="_blank">' .
|
||||
'<span class="invisible">%s</span>';
|
||||
|
||||
if (strlen($styled_url) > 30) {
|
||||
$html .= '<span class="ellipsis">%s</span>' .
|
||||
'<span class="invisible">%s</span></a>';
|
||||
|
||||
$ellipsis = substr($styled_url, 0, 30);
|
||||
$rest = substr($styled_url, 30);
|
||||
return sprintf($html, $url, $scheme, $ellipsis, $rest);
|
||||
} else {
|
||||
$html .= '%s</a>';
|
||||
return sprintf($html, $url, $scheme, $styled_url);
|
||||
$styled_url = substr($styled_url, 0, 30) . "…";
|
||||
}
|
||||
|
||||
$html = '<a href="%s" target="_blank">%s</a>';
|
||||
|
||||
return sprintf($html, $url, $styled_url);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1127,13 +1104,13 @@ class BBCode extends BaseObject
|
|||
}
|
||||
|
||||
if (stripos(normalise_link($link), 'http://twitter.com/') === 0) {
|
||||
$text .= '<br /><a href="' . $link . '">' . $link . '</a>';
|
||||
} else {
|
||||
$text .= $headline . '<blockquote>' . trim($share[3]) . "</blockquote><br />";
|
||||
|
||||
if ($link != "") {
|
||||
$text .= '<br /><a href="' . $link . '">[l]</a>';
|
||||
}
|
||||
} else {
|
||||
$text .= '<br /><a href="' . $link . '">' . $link . '</a>';
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -1229,7 +1206,7 @@ class BBCode extends BaseObject
|
|||
|
||||
$doc = new DOMDocument();
|
||||
@$doc->loadHTML($body);
|
||||
$xpath = new DomXPath($doc);
|
||||
$xpath = new DOMXPath($doc);
|
||||
$list = $xpath->query("//meta[@name]");
|
||||
foreach ($list as $node) {
|
||||
$attr = [];
|
||||
|
@ -1318,13 +1295,17 @@ class BBCode extends BaseObject
|
|||
|
||||
private static function textHighlightCallback($match)
|
||||
{
|
||||
// Fallback in case the language doesn't exist
|
||||
$return = '[code]' . $match[2] . '[/code]';
|
||||
|
||||
if (in_array(strtolower($match[1]),
|
||||
['php', 'css', 'mysql', 'sql', 'abap', 'diff', 'html', 'perl', 'ruby',
|
||||
'vbscript', 'avrc', 'dtd', 'java', 'xml', 'cpp', 'python', 'javascript', 'js', 'sh'])
|
||||
'vbscript', 'avrc', 'dtd', 'java', 'xml', 'cpp', 'python', 'javascript', 'js', 'sh', 'bash'])
|
||||
) {
|
||||
return text_highlight($match[2], strtolower($match[1]));
|
||||
$return = text_highlight($match[2], strtolower($match[1]));
|
||||
}
|
||||
return $match[0];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1401,7 +1382,7 @@ class BBCode extends BaseObject
|
|||
// After we're finished processing the bbcode we'll
|
||||
// replace all of the event code with a reformatted version.
|
||||
|
||||
$ev = bbtoevent($text);
|
||||
$ev = Event::fromBBCode($text);
|
||||
|
||||
// Replace any html brackets with HTML Entities to prevent executing HTML or script
|
||||
// Don't use strip_tags here because it breaks [url] search by replacing & with amp
|
||||
|
@ -1461,8 +1442,8 @@ class BBCode extends BaseObject
|
|||
$autolink_regex = "/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism";
|
||||
$text = preg_replace($autolink_regex, '$1[url]$2[/url]', $text);
|
||||
if ($simple_html == 7) {
|
||||
$text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForMastodonCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForMastodonCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForOStatusCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForOStatusCallback', $text);
|
||||
}
|
||||
} else {
|
||||
$text = preg_replace("(\[url\]([$URLSearchString]*)\[\/url\])ism", " $1 ", $text);
|
||||
|
@ -1562,10 +1543,8 @@ class BBCode extends BaseObject
|
|||
if (strpos($text, '[/map]') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map\](.*?)\[\/map\]/ism",
|
||||
function ($match) {
|
||||
// the extra space in the following line is intentional
|
||||
// Whyyy? - @MrPetovan
|
||||
return str_replace($match[0], '<div class="map" >' . Map::byLocation($match[1]) . '</div>', $match[0]);
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byLocation($match[1], $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
@ -1573,16 +1552,14 @@ class BBCode extends BaseObject
|
|||
if (strpos($text, '[map=') !== false) {
|
||||
$text = preg_replace_callback(
|
||||
"/\[map=(.*?)\]/ism",
|
||||
function ($match) {
|
||||
// the extra space in the following line is intentional
|
||||
// Whyyy? - @MrPetovan
|
||||
return str_replace($match[0], '<div class="map" >' . Map::byCoordinates(str_replace('/', ' ', $match[1])) . '</div>', $match[0]);
|
||||
function ($match) use ($simple_html) {
|
||||
return str_replace($match[0], '<p class="map">' . Map::byCoordinates(str_replace('/', ' ', $match[1]), $simple_html) . '</p>', $match[0]);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
if (strpos($text, '[map]') !== false) {
|
||||
$text = preg_replace("/\[map\]/", '<div class="map"></div>', $text);
|
||||
$text = preg_replace("/\[map\]/", '<p class="map"></p>', $text);
|
||||
}
|
||||
|
||||
// Check for headers
|
||||
|
@ -1606,7 +1583,7 @@ class BBCode extends BaseObject
|
|||
$text = preg_replace("(\[u\](.*?)\[\/u\])ism", '<u>$1</u>', $text);
|
||||
|
||||
// Check for strike-through text
|
||||
$text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<strike>$1</strike>', $text);
|
||||
$text = preg_replace("(\[s\](.*?)\[\/s\])ism", '<s>$1</s>', $text);
|
||||
|
||||
// Check for over-line text
|
||||
$text = preg_replace("(\[o\](.*?)\[\/o\])ism", '<span class="overline">$1</span>', $text);
|
||||
|
@ -1629,7 +1606,7 @@ class BBCode extends BaseObject
|
|||
$text = preg_replace_callback(
|
||||
"(\[style=(.*?)\](.*?)\[\/style\])ism",
|
||||
function ($match) {
|
||||
return "<span style=\"" . self::cleanCss($match[1]) . ";\">" . $match[2] . "</span>";
|
||||
return "<span style=\"" . HTML::sanitizeCSS($match[1]) . ";\">" . $match[2] . "</span>";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
@ -1638,7 +1615,7 @@ class BBCode extends BaseObject
|
|||
$text = preg_replace_callback(
|
||||
"(\[class=(.*?)\](.*?)\[\/class\])ism",
|
||||
function ($match) {
|
||||
return "<span class=\"" . self::cleanCss($match[1]) . "\">" . $match[2] . "</span>";
|
||||
return "<span class=\"" . HTML::sanitizeCSS($match[1]) . "\">" . $match[2] . "</span>";
|
||||
},
|
||||
$text
|
||||
);
|
||||
|
@ -1747,6 +1724,14 @@ class BBCode extends BaseObject
|
|||
$text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: $1px;" >', $text);
|
||||
$text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: $1px;" >', $text);
|
||||
|
||||
$text = preg_replace_callback("/\[img\=([$URLSearchString]*)\](.*?)\[\/img\]/ism",
|
||||
function ($matches) {
|
||||
$matches[1] = proxy_url($matches[1]);
|
||||
$matches[2] = htmlspecialchars($matches[2], ENT_COMPAT);
|
||||
return '<img src="' . $matches[1] . '" alt="' . $matches[2] . '">';
|
||||
},
|
||||
$text);
|
||||
|
||||
// Images
|
||||
// [img]pathtoimage[/img]
|
||||
$text = preg_replace_callback(
|
||||
|
@ -1843,7 +1828,7 @@ class BBCode extends BaseObject
|
|||
// start which is always required). Allow desc with a missing summary for compatibility.
|
||||
|
||||
if ((x($ev, 'desc') || x($ev, 'summary')) && x($ev, 'start')) {
|
||||
$sub = format_event_html($ev, $simple_html);
|
||||
$sub = Event::getHTML($ev, $simple_html);
|
||||
|
||||
$text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text);
|
||||
$text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $text);
|
||||
|
@ -1878,10 +1863,12 @@ class BBCode extends BaseObject
|
|||
$text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'self::unescapeNoparseCallback', $text);
|
||||
$text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'self::unescapeNoparseCallback', $text);
|
||||
|
||||
|
||||
/// @todo What is the meaning of these lines?
|
||||
$text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/', '&$1;', $text);
|
||||
$text = preg_replace('/\&\#039\;/', '\'', $text);
|
||||
$text = preg_replace('/\"\;/', '"', $text);
|
||||
|
||||
// Currently deactivated, it made problems with " inside of alt texts.
|
||||
//$text = preg_replace('/\"\;/', '"', $text);
|
||||
|
||||
// fix any escaped ampersands that may have been converted into links
|
||||
$text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/ism', '<$1$2=$3&$4>', $text);
|
||||
|
|
677
src/Content/Text/HTML.php
Normal file
677
src/Content/Text/HTML.php
Normal file
|
@ -0,0 +1,677 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file src/Content/Text/HTML.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Content\Text;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
class HTML
|
||||
{
|
||||
public static function sanitizeCSS($input)
|
||||
{
|
||||
$cleaned = "";
|
||||
|
||||
$input = strtolower($input);
|
||||
|
||||
for ($i = 0; $i < strlen($input); $i++) {
|
||||
$char = substr($input, $i, 1);
|
||||
|
||||
if (($char >= "a") && ($char <= "z")) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
|
||||
if (!(strpos(" #;:0123456789-_.%", $char) === false)) {
|
||||
$cleaned .= $char;
|
||||
}
|
||||
}
|
||||
|
||||
return $cleaned;
|
||||
}
|
||||
|
||||
private static function tagToBBCode(DOMDocument $doc, $tag, $attributes, $startbb, $endbb)
|
||||
{
|
||||
do {
|
||||
$done = self::tagToBBCodeSub($doc, $tag, $attributes, $startbb, $endbb);
|
||||
} while ($done);
|
||||
}
|
||||
|
||||
private static function tagToBBCodeSub(DOMDocument $doc, $tag, $attributes, $startbb, $endbb)
|
||||
{
|
||||
$savestart = str_replace('$', '\x01', $startbb);
|
||||
$replace = false;
|
||||
|
||||
$xpath = new DOMXPath($doc);
|
||||
|
||||
$list = $xpath->query("//" . $tag);
|
||||
foreach ($list as $node) {
|
||||
$attr = [];
|
||||
if ($node->attributes->length) {
|
||||
foreach ($node->attributes as $attribute) {
|
||||
$attr[$attribute->name] = $attribute->value;
|
||||
}
|
||||
}
|
||||
|
||||
$replace = true;
|
||||
|
||||
$startbb = $savestart;
|
||||
|
||||
$i = 0;
|
||||
|
||||
foreach ($attributes as $attribute => $value) {
|
||||
$startbb = str_replace('\x01' . ++$i, '$1', $startbb);
|
||||
if (strpos('*' . $startbb, '$1') > 0) {
|
||||
if ($replace && (@$attr[$attribute] != '')) {
|
||||
$startbb = preg_replace($value, $startbb, $attr[$attribute], -1, $count);
|
||||
|
||||
// If nothing could be changed
|
||||
if ($count == 0) {
|
||||
$replace = false;
|
||||
}
|
||||
} else {
|
||||
$replace = false;
|
||||
}
|
||||
} else {
|
||||
if (@$attr[$attribute] != $value) {
|
||||
$replace = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($replace) {
|
||||
$StartCode = $doc->createTextNode($startbb);
|
||||
$EndCode = $doc->createTextNode($endbb);
|
||||
|
||||
$node->parentNode->insertBefore($StartCode, $node);
|
||||
|
||||
if ($node->hasChildNodes()) {
|
||||
foreach ($node->childNodes as $child) {
|
||||
$newNode = $child->cloneNode(true);
|
||||
$node->parentNode->insertBefore($newNode, $node);
|
||||
}
|
||||
}
|
||||
|
||||
$node->parentNode->insertBefore($EndCode, $node);
|
||||
$node->parentNode->removeChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
return $replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Made by: ike@piratenpartei.de
|
||||
* Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom
|
||||
* https://github.com/annando/Syncom
|
||||
*
|
||||
* @brief Converter for HTML to BBCode
|
||||
* @param string $message
|
||||
* @param string $basepath
|
||||
* @return string
|
||||
*/
|
||||
public static function toBBCode($message, $basepath = '')
|
||||
{
|
||||
$message = str_replace("\r", "", $message);
|
||||
|
||||
// Removing code blocks before the whitespace removal processing below
|
||||
$codeblocks = [];
|
||||
$message = preg_replace_callback(
|
||||
'#<pre><code(?: class="([^"]*)")?>(.*)</code></pre>#iUs',
|
||||
function ($matches) use (&$codeblocks) {
|
||||
$return = '[codeblock-' . count($codeblocks) . ']';
|
||||
|
||||
$prefix = '[code]';
|
||||
if ($matches[1] != '') {
|
||||
$prefix = '[code=' . $matches[1] . ']';
|
||||
}
|
||||
|
||||
$codeblocks[] = $prefix . trim($matches[2]) . '[/code]';
|
||||
return $return;
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
||||
$message = str_replace(
|
||||
[
|
||||
"<li><p>",
|
||||
"</p></li>",
|
||||
], [
|
||||
"<li>",
|
||||
"</li>",
|
||||
], $message
|
||||
);
|
||||
|
||||
// remove namespaces
|
||||
$message = preg_replace('=<(\w+):(.+?)>=', '<removeme>', $message);
|
||||
$message = preg_replace('=</(\w+):(.+?)>=', '</removeme>', $message);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message);
|
||||
|
||||
XML::deleteNode($doc, 'style');
|
||||
XML::deleteNode($doc, 'head');
|
||||
XML::deleteNode($doc, 'title');
|
||||
XML::deleteNode($doc, 'meta');
|
||||
XML::deleteNode($doc, 'xml');
|
||||
XML::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
$node->nodeValue = str_replace("\n", "\r", $node->nodeValue);
|
||||
}
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br />", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
@$doc->loadHTML($message);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], "", "");
|
||||
self::tagToBBCode($doc, 'body', [], "", "");
|
||||
|
||||
// Outlook-Quote - Variant 1
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal', 'style' => 'margin-left:35.4pt'], '[quote]', '[/quote]');
|
||||
|
||||
// Outlook-Quote - Variant 2
|
||||
self::tagToBBCode($doc, 'div', ['style' => 'border:none;border-left:solid blue 1.5pt;padding:0cm 0cm 0cm 4.0pt'],
|
||||
'[quote]', '[/quote]');
|
||||
|
||||
// MyBB-Stuff
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'text-decoration: underline;'], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-style: italic;'], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'span', ['style' => 'font-weight: bold;'], '[b]', '[/b]');
|
||||
|
||||
/* self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[font=$1][size=$2][color=$3]', '[/color][/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/', 'color'=>'/(.+)/'), '[size=$1][color=$2]', '[/color][/size]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'size'=>'/(.+)/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/', 'color'=>'/(.+)/'), '[font=$1][color=$3]', '[/color][/font]');
|
||||
self::node2BBCode($doc, 'font', array('face'=>'/([\w ]+)/'), '[font=$1]', '[/font]');
|
||||
self::node2BBCode($doc, 'font', array('size'=>'/(\d+)/'), '[size=$1]', '[/size]');
|
||||
self::node2BBCode($doc, 'font', array('color'=>'/(.+)/'), '[color=$1]', '[/color]');
|
||||
*/
|
||||
// Untested
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*font-family:\s*(.+?)[,;].*color:\s*(.+?)[,;].*/'), '[size=$1][font=$2][color=$3]', '[/color][/font][/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(\d+)[,;].*/'), '[size=$1]', '[/size]');
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-size:\s*(.+?)[,;].*/'), '[size=$1]', '[/size]');
|
||||
|
||||
self::tagToBBCode($doc, 'span', ['style' => '/.*color:\s*(.+?)[,;].*/'], '[color="$1"]', '[/color]');
|
||||
|
||||
//self::node2BBCode($doc, 'span', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)pt.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*font-size:\s*(\d+?)px.*/'), '[font=$1][size=$2]', '[/size][/font]');
|
||||
//self::node2BBCode($doc, 'div', array('style'=>'/.*font-family:\s*(.+?)[,;].*/'), '[font=$1]', '[/font]');
|
||||
// Importing the classes - interesting for importing of posts from third party networks that were exported from friendica
|
||||
// Test
|
||||
//self::node2BBCode($doc, 'span', array('class'=>'/([\w ]+)/'), '[class=$1]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-link'], '[class=type-link]', '[/class]');
|
||||
self::tagToBBCode($doc, 'span', ['class' => 'type-video'], '[class=type-video]', '[/class]');
|
||||
|
||||
self::tagToBBCode($doc, 'strong', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'em', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'b', [], '[b]', '[/b]');
|
||||
self::tagToBBCode($doc, 'i', [], '[i]', '[/i]');
|
||||
self::tagToBBCode($doc, 'u', [], '[u]', '[/u]');
|
||||
self::tagToBBCode($doc, 's', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'del', [], '[s]', '[/s]');
|
||||
self::tagToBBCode($doc, 'strike', [], '[s]', '[/s]');
|
||||
|
||||
self::tagToBBCode($doc, 'big', [], "[size=large]", "[/size]");
|
||||
self::tagToBBCode($doc, 'small', [], "[size=small]", "[/size]");
|
||||
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', '[/quote]');
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'p', ['class' => 'MsoNormal'], "\n", "");
|
||||
self::tagToBBCode($doc, 'div', ['class' => 'MsoNormal'], "\r", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'ul', [], "[list]", "[/list]");
|
||||
self::tagToBBCode($doc, 'ol', [], "[list=1]", "[/list]");
|
||||
self::tagToBBCode($doc, 'li', [], "[*]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "[hr]", "");
|
||||
|
||||
self::tagToBBCode($doc, 'table', [], "", "");
|
||||
self::tagToBBCode($doc, 'tr', [], "\n", "");
|
||||
self::tagToBBCode($doc, 'td', [], "\t", "");
|
||||
//self::node2BBCode($doc, 'table', array(), "[table]", "[/table]");
|
||||
//self::node2BBCode($doc, 'th', array(), "[th]", "[/th]");
|
||||
//self::node2BBCode($doc, 'tr', array(), "[tr]", "[/tr]");
|
||||
//self::node2BBCode($doc, 'td', array(), "[td]", "[/td]");
|
||||
//self::node2BBCode($doc, 'h1', array(), "\n\n[size=xx-large][b]", "[/b][/size]\n");
|
||||
//self::node2BBCode($doc, 'h2', array(), "\n\n[size=x-large][b]", "[/b][/size]\n");
|
||||
//self::node2BBCode($doc, 'h3', array(), "\n\n[size=large][b]", "[/b][/size]\n");
|
||||
//self::node2BBCode($doc, 'h4', array(), "\n\n[size=medium][b]", "[/b][/size]\n");
|
||||
//self::node2BBCode($doc, 'h5', array(), "\n\n[size=small][b]", "[/b][/size]\n");
|
||||
//self::node2BBCode($doc, 'h6', array(), "\n\n[size=x-small][b]", "[/b][/size]\n");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "[h1]", "[/h1]");
|
||||
self::tagToBBCode($doc, 'h2', [], "[h2]", "[/h2]");
|
||||
self::tagToBBCode($doc, 'h3', [], "[h3]", "[/h3]");
|
||||
self::tagToBBCode($doc, 'h4', [], "[h4]", "[/h4]");
|
||||
self::tagToBBCode($doc, 'h5', [], "[h5]", "[/h5]");
|
||||
self::tagToBBCode($doc, 'h6', [], "[h6]", "[/h6]");
|
||||
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/mailto:(.+)/'], '[mail=$1]', '[/mail]');
|
||||
self::tagToBBCode($doc, 'a', ['href' => '/(.+)/'], '[url=$1]', '[/url]');
|
||||
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/', 'width' => '/(\d+)/', 'height' => '/(\d+)/'], '[img=$2x$3]$1',
|
||||
'[/img]');
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], '[img]$1', '[/img]');
|
||||
|
||||
|
||||
self::tagToBBCode($doc, 'video', ['src' => '/(.+)/'], '[video]$1', '[/video]');
|
||||
self::tagToBBCode($doc, 'audio', ['src' => '/(.+)/'], '[audio]$1', '[/audio]');
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], '[iframe]$1', '[/iframe]');
|
||||
|
||||
self::tagToBBCode($doc, 'key', [], '[code]', '[/code]');
|
||||
self::tagToBBCode($doc, 'code', [], '[code]', '[/code]');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
// I'm removing something really disturbing
|
||||
// Don't know exactly what it is
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// removing multiple DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
Addon::callHooks('html2bbcode', $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$message = str_replace(["<"], ["<"], $message);
|
||||
|
||||
// remove quotes if they don't make sense
|
||||
$message = preg_replace('=\[/quote\][\s]*\[quote\]=i', "\n", $message);
|
||||
|
||||
$message = preg_replace('=\[quote\]\s*=i', "[quote]", $message);
|
||||
$message = preg_replace('=\s*\[/quote\]=i', "[/quote]", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n \n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace(
|
||||
[
|
||||
"[/size]\n\n",
|
||||
"\n[hr]",
|
||||
"[hr]\n",
|
||||
"\n[list",
|
||||
"[/list]\n",
|
||||
"\n[/",
|
||||
"[list]\n",
|
||||
"[list=1]\n",
|
||||
"\n[*]"],
|
||||
[
|
||||
"[/size]\n",
|
||||
"[hr]",
|
||||
"[hr]",
|
||||
"[list",
|
||||
"[/list]",
|
||||
"[/",
|
||||
"[list]",
|
||||
"[list=1]",
|
||||
"[*]"], $message
|
||||
);
|
||||
} while ($message != $oldmessage);
|
||||
|
||||
$message = str_replace(
|
||||
['[b][b]', '[/b][/b]', '[i][i]', '[/i][/i]'], ['[b]', '[/b]', '[i]', '[/i]'], $message
|
||||
);
|
||||
|
||||
// Handling Yahoo style of mails
|
||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||
|
||||
// Restore code blocks
|
||||
$message = preg_replace_callback(
|
||||
'#\[codeblock-([0-9]+)\]#iU',
|
||||
function ($matches) use ($codeblocks) {
|
||||
$return = '';
|
||||
if (isset($codeblocks[intval($matches[1])])) {
|
||||
$return = $codeblocks[$matches[1]];
|
||||
}
|
||||
return $return;
|
||||
},
|
||||
$message
|
||||
);
|
||||
|
||||
$message = trim($message);
|
||||
|
||||
if ($basepath != '') {
|
||||
$message = self::qualifyURLs($message, $basepath);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sub function to complete incomplete URL
|
||||
*
|
||||
* @param array $matches Result of preg_replace_callback
|
||||
* @param string $basepath Basepath that is used to complete the URL
|
||||
*
|
||||
* @return string The expanded URL
|
||||
*/
|
||||
private static function qualifyURLsSub($matches, $basepath)
|
||||
{
|
||||
$base = parse_url($basepath);
|
||||
unset($base['query']);
|
||||
unset($base['fragment']);
|
||||
|
||||
$link = $matches[0];
|
||||
$url = $matches[1];
|
||||
|
||||
$parts = array_merge($base, parse_url($url));
|
||||
$url2 = Network::unparseURL($parts);
|
||||
|
||||
return str_replace($url, $url2, $link);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Complete incomplete URLs in BBCode
|
||||
*
|
||||
* @param string $body Body with URLs
|
||||
* @param string $basepath Base path that is used to complete the URL
|
||||
*
|
||||
* @return string Body with expanded URLs
|
||||
*/
|
||||
private static function qualifyURLs($body, $basepath)
|
||||
{
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
$matches = ["/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism",
|
||||
"/\[url\]([$URLSearchString]*)\[\/url\]/ism",
|
||||
"/\[img\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism",
|
||||
"/\[img\](.*?)\[\/img\]/ism",
|
||||
"/\[zmg\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism",
|
||||
"/\[zmg\](.*?)\[\/zmg\]/ism",
|
||||
"/\[video\](.*?)\[\/video\]/ism",
|
||||
"/\[audio\](.*?)\[\/audio\]/ism",
|
||||
];
|
||||
|
||||
foreach ($matches as $match) {
|
||||
$body = preg_replace_callback(
|
||||
$match, function ($match) use ($basepath) {
|
||||
return self::qualifyURLsSub($match, $basepath);
|
||||
},
|
||||
$body
|
||||
);
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
private static function breakLines($line, $level, $wraplength = 75)
|
||||
{
|
||||
if ($wraplength == 0) {
|
||||
$wraplength = 2000000;
|
||||
}
|
||||
|
||||
$wraplen = $wraplength - $level;
|
||||
|
||||
$newlines = [];
|
||||
|
||||
do {
|
||||
$oldline = $line;
|
||||
|
||||
$subline = substr($line, 0, $wraplen);
|
||||
|
||||
$pos = strrpos($subline, ' ');
|
||||
|
||||
if ($pos == 0) {
|
||||
$pos = strpos($line, ' ');
|
||||
}
|
||||
|
||||
if (($pos > 0) && strlen($line) > $wraplen) {
|
||||
$newline = trim(substr($line, 0, $pos));
|
||||
if ($level > 0) {
|
||||
$newline = str_repeat(">", $level) . ' ' . $newline;
|
||||
}
|
||||
|
||||
$newlines[] = $newline . " ";
|
||||
$line = substr($line, $pos + 1);
|
||||
}
|
||||
} while ((strlen($line) > $wraplen) && !($oldline == $line));
|
||||
|
||||
if ($level > 0) {
|
||||
$line = str_repeat(">", $level) . ' ' . $line;
|
||||
}
|
||||
|
||||
$newlines[] = $line;
|
||||
|
||||
return implode($newlines, "\n");
|
||||
}
|
||||
|
||||
private static function quoteLevel($message, $wraplength = 75)
|
||||
{
|
||||
$lines = explode("\n", $message);
|
||||
|
||||
$newlines = [];
|
||||
$level = 0;
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
$startquote = false;
|
||||
while (strpos("*" . $line, '[quote]') > 0) {
|
||||
$level++;
|
||||
$pos = strpos($line, '[quote]');
|
||||
$line = substr($line, 0, $pos) . substr($line, $pos + 7);
|
||||
$startquote = true;
|
||||
}
|
||||
|
||||
$currlevel = $level;
|
||||
|
||||
while (strpos("*" . $line, '[/quote]') > 0) {
|
||||
$level--;
|
||||
if ($level < 0) {
|
||||
$level = 0;
|
||||
}
|
||||
|
||||
$pos = strpos($line, '[/quote]');
|
||||
$line = substr($line, 0, $pos) . substr($line, $pos + 8);
|
||||
}
|
||||
|
||||
if (!$startquote || ($line != '')) {
|
||||
$newlines[] = self::breakLines($line, $currlevel, $wraplength);
|
||||
}
|
||||
}
|
||||
|
||||
return implode($newlines, "\n");
|
||||
}
|
||||
|
||||
private static function collectURLs($message)
|
||||
{
|
||||
$pattern = '/<a.*?href="(.*?)".*?>(.*?)<\/a>/is';
|
||||
preg_match_all($pattern, $message, $result, PREG_SET_ORDER);
|
||||
|
||||
$urls = [];
|
||||
foreach ($result as $treffer) {
|
||||
$ignore = false;
|
||||
|
||||
// A list of some links that should be ignored
|
||||
$list = ["/user/", "/tag/", "/group/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/",
|
||||
"//facebook.com/profile.php?id=", "//plus.google.com/", "//twitter.com/"];
|
||||
foreach ($list as $listitem) {
|
||||
if (strpos($treffer[1], $listitem) !== false) {
|
||||
$ignore = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((strpos($treffer[1], "//twitter.com/") !== false) && (strpos($treffer[1], "/status/") !== false)) {
|
||||
$ignore = false;
|
||||
}
|
||||
|
||||
if ((strpos($treffer[1], "//plus.google.com/") !== false) && (strpos($treffer[1], "/posts") !== false)) {
|
||||
$ignore = false;
|
||||
}
|
||||
|
||||
if ((strpos($treffer[1], "//plus.google.com/") !== false) && (strpos($treffer[1], "/photos") !== false)) {
|
||||
$ignore = false;
|
||||
}
|
||||
|
||||
if (!$ignore) {
|
||||
$urls[$treffer[1]] = $treffer[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
public static function toPlaintext($html, $wraplength = 75, $compact = false)
|
||||
{
|
||||
global $lang;
|
||||
|
||||
$message = str_replace("\r", "", $html);
|
||||
|
||||
$doc = new DOMDocument();
|
||||
$doc->preserveWhiteSpace = false;
|
||||
|
||||
$message = mb_convert_encoding($message, 'HTML-ENTITIES', "UTF-8");
|
||||
|
||||
@$doc->loadHTML($message);
|
||||
|
||||
$xpath = new DOMXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
foreach ($list as $node) {
|
||||
$node->nodeValue = str_replace("\n", "\r", $node->nodeValue);
|
||||
}
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
$message = str_replace(["\n<", ">\n", "\r", "\n", "\xC3\x82\xC2\xA0"], ["<", ">", "<br>", " ", ""], $message);
|
||||
$message = preg_replace('= [\s]*=i', " ", $message);
|
||||
|
||||
// Collecting all links
|
||||
$urls = self::collectURLs($message);
|
||||
|
||||
@$doc->loadHTML($message);
|
||||
|
||||
self::tagToBBCode($doc, 'html', [], '', '');
|
||||
self::tagToBBCode($doc, 'body', [], '', '');
|
||||
|
||||
// MyBB-Auszeichnungen
|
||||
/*
|
||||
self::node2BBCode($doc, 'span', array('style'=>'text-decoration: underline;'), '_', '_');
|
||||
self::node2BBCode($doc, 'span', array('style'=>'font-style: italic;'), '/', '/');
|
||||
self::node2BBCode($doc, 'span', array('style'=>'font-weight: bold;'), '*', '*');
|
||||
|
||||
self::node2BBCode($doc, 'strong', array(), '*', '*');
|
||||
self::node2BBCode($doc, 'b', array(), '*', '*');
|
||||
self::node2BBCode($doc, 'i', array(), '/', '/');
|
||||
self::node2BBCode($doc, 'u', array(), '_', '_');
|
||||
*/
|
||||
|
||||
if ($compact) {
|
||||
self::tagToBBCode($doc, 'blockquote', [], "»", "«");
|
||||
} else {
|
||||
self::tagToBBCode($doc, 'blockquote', [], '[quote]', "[/quote]\n");
|
||||
}
|
||||
|
||||
self::tagToBBCode($doc, 'br', [], "\n", '');
|
||||
|
||||
self::tagToBBCode($doc, 'span', [], "", "");
|
||||
self::tagToBBCode($doc, 'pre', [], "", "");
|
||||
self::tagToBBCode($doc, 'div', [], "\r", "\r");
|
||||
self::tagToBBCode($doc, 'p', [], "\n", "\n");
|
||||
|
||||
//self::node2BBCode($doc, 'ul', array(), "\n[list]", "[/list]\n");
|
||||
//self::node2BBCode($doc, 'ol', array(), "\n[list=1]", "[/list]\n");
|
||||
self::tagToBBCode($doc, 'li', [], "\n* ", "\n");
|
||||
|
||||
self::tagToBBCode($doc, 'hr', [], "\n" . str_repeat("-", 70) . "\n", "");
|
||||
|
||||
self::tagToBBCode($doc, 'tr', [], "\n", "");
|
||||
self::tagToBBCode($doc, 'td', [], "\t", "");
|
||||
|
||||
self::tagToBBCode($doc, 'h1', [], "\n\n*", "*\n");
|
||||
self::tagToBBCode($doc, 'h2', [], "\n\n*", "*\n");
|
||||
self::tagToBBCode($doc, 'h3', [], "\n\n*", "*\n");
|
||||
self::tagToBBCode($doc, 'h4', [], "\n\n*", "*\n");
|
||||
self::tagToBBCode($doc, 'h5', [], "\n\n*", "*\n");
|
||||
self::tagToBBCode($doc, 'h6', [], "\n\n*", "*\n");
|
||||
|
||||
// Problem: there is no reliable way to detect if it is a link to a tag or profile
|
||||
//self::node2BBCode($doc, 'a', array('href'=>'/(.+)/'), ' $1 ', ' ', true);
|
||||
//self::node2BBCode($doc, 'a', array('href'=>'/(.+)/', 'rel'=>'oembed'), ' $1 ', '', true);
|
||||
//self::node2BBCode($doc, 'img', array('alt'=>'/(.+)/'), '$1', '');
|
||||
//self::node2BBCode($doc, 'img', array('title'=>'/(.+)/'), '$1', '');
|
||||
//self::node2BBCode($doc, 'img', array(), '', '');
|
||||
if (!$compact) {
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], ' [img]$1', '[/img] ');
|
||||
} else {
|
||||
self::tagToBBCode($doc, 'img', ['src' => '/(.+)/'], ' ', ' ');
|
||||
}
|
||||
|
||||
self::tagToBBCode($doc, 'iframe', ['src' => '/(.+)/'], ' $1 ', '');
|
||||
|
||||
$message = $doc->saveHTML();
|
||||
|
||||
if (!$compact) {
|
||||
$message = str_replace("[img]", "", $message);
|
||||
$message = str_replace("[/img]", "", $message);
|
||||
}
|
||||
|
||||
// was ersetze ich da?
|
||||
// Irgendein stoerrisches UTF-Zeug
|
||||
$message = str_replace(chr(194) . chr(160), ' ', $message);
|
||||
|
||||
$message = str_replace(" ", " ", $message);
|
||||
|
||||
// Aufeinanderfolgende DIVs
|
||||
$message = preg_replace('=\r *\r=i', "\n", $message);
|
||||
$message = str_replace("\r", "\n", $message);
|
||||
|
||||
$message = strip_tags($message);
|
||||
|
||||
$message = html_entity_decode($message, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if (!$compact && ($message != '')) {
|
||||
foreach ($urls as $id => $url) {
|
||||
if ($url != '' && strpos($message, $url) === false) {
|
||||
$message .= "\n" . $url . ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$message = str_replace("\n«", "«\n", $message);
|
||||
$message = str_replace("»\n", "\n»", $message);
|
||||
|
||||
do {
|
||||
$oldmessage = $message;
|
||||
$message = str_replace("\n\n\n", "\n\n", $message);
|
||||
} while ($oldmessage != $message);
|
||||
|
||||
$message = self::quoteLevel(trim($message), $wraplength);
|
||||
|
||||
return trim($message);
|
||||
}
|
||||
}
|
|
@ -9,8 +9,7 @@ namespace Friendica\Content\Text;
|
|||
use Friendica\BaseObject;
|
||||
use Friendica\Model\Contact;
|
||||
use Michelf\MarkdownExtra;
|
||||
|
||||
require_once 'include/html2bbcode.php';
|
||||
use Friendica\Content\Text\HTML;
|
||||
|
||||
/**
|
||||
* Friendica-specific usage of Markdown
|
||||
|
@ -94,7 +93,7 @@ class Markdown extends BaseObject
|
|||
|
||||
$s = str_replace('#', '#', $s);
|
||||
|
||||
$s = html2bbcode($s);
|
||||
$s = HTML::toBBCode($s);
|
||||
|
||||
// protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands
|
||||
$s = str_replace('♲', html_entity_decode('♲', ENT_QUOTES, 'UTF-8'), $s);
|
||||
|
|
69
src/Content/Widget/CalendarExport.php
Normal file
69
src/Content/Widget/CalendarExport.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* @file src/Content/Widget/CalendarExport.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Content\Widget;
|
||||
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Core\L10n;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/text.php';
|
||||
|
||||
/**
|
||||
* TagCloud widget
|
||||
*
|
||||
* @author Rabuzarus
|
||||
*/
|
||||
class CalendarExport
|
||||
{
|
||||
/**
|
||||
* @brief Get the events widget.
|
||||
*
|
||||
* @return string Formated HTML of the calendar widget.
|
||||
*/
|
||||
public static function getHTML() {
|
||||
$a = get_app();
|
||||
|
||||
$owner_uid = $a->data['user']['uid'];
|
||||
|
||||
// The permission testing is a little bit tricky because we have to respect many cases.
|
||||
|
||||
// It's not the private events page (we don't get the $owner_uid for /events).
|
||||
if (!local_user() && !$owner_uid) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a kind of profile page (intval($owner_uid)) return if the user not logged in and
|
||||
* export feature isn't enabled.
|
||||
*/
|
||||
/*
|
||||
* Cal logged in user (test permission at foreign profile page).
|
||||
* If the $owner uid is available we know it is part of one of the profile pages (like /cal).
|
||||
* So we have to test if if it's the own profile page of the logged in user
|
||||
* or a foreign one. For foreign profile pages we need to check if the feature
|
||||
* for exporting the cal is enabled (otherwise the widget would appear for logged in users
|
||||
* on foreigen profile pages even if the widget is disabled).
|
||||
*/
|
||||
if (local_user() != $owner_uid && !Feature::isEnabled($owner_uid, "export_calendar")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// $a->data is only available if the profile page is visited. If the visited page is not part
|
||||
// of the profile page it should be the personal /events page. So we can use $a->user.
|
||||
$user = defaults($a->data['user'], 'nickname', $a->user['nickname']);
|
||||
|
||||
$tpl = get_markup_template("events_aside.tpl");
|
||||
$return = replace_macros($tpl, [
|
||||
'$etitle' => L10n::t("Export"),
|
||||
'$export_ical' => L10n::t("Export calendar as ical"),
|
||||
'$export_csv' => L10n::t("Export calendar as csv"),
|
||||
'$user' => $user
|
||||
]);
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
727
src/Core/ACL.php
727
src/Core/ACL.php
|
@ -1,358 +1,369 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file src/Core/Acl.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Util\Network;
|
||||
use const CONTACT_IS_FRIEND;
|
||||
use const NETWORK_DFRN;
|
||||
use const NETWORK_DIASPORA;
|
||||
use const NETWORK_FACEBOOK;
|
||||
use const NETWORK_MAIL;
|
||||
use const NETWORK_OSTATUS;
|
||||
use const PHP_EOL;
|
||||
use function dbesc;
|
||||
use function defaults;
|
||||
use function get_markup_template;
|
||||
use function get_server;
|
||||
use function local_user;
|
||||
use function remote_user;
|
||||
use function replace_macros;
|
||||
|
||||
/**
|
||||
* Handle ACL management and display
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class ACL extends BaseObject
|
||||
{
|
||||
/**
|
||||
* Returns a select input tag with all the contact of the local user
|
||||
*
|
||||
* @param string $selname Name attribute of the select input tag
|
||||
* @param string $selclass Class attribute of the select input tag
|
||||
* @param array $options Available options:
|
||||
* - size: length of the select box
|
||||
* - mutual_friends: Only used for the hook
|
||||
* - single: Only used for the hook
|
||||
* - exclude: Only used for the hook
|
||||
* @param array $preselected Contact ID that should be already selected
|
||||
* @return string
|
||||
*/
|
||||
public static function getSuggestContactSelectHTML($selname, $selclass, array $options = [], array $preselected = [])
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$networks = null;
|
||||
|
||||
$size = defaults($options, 'size', 4);
|
||||
$mutual = !empty($options['mutual_friends']);
|
||||
$single = !empty($options['single']) && empty($options['multiple']);
|
||||
$exclude = defaults($options, 'exclude', false);
|
||||
|
||||
switch (defaults($options, 'networks', Protocol::PHANTOM)) {
|
||||
case 'DFRN_ONLY':
|
||||
$networks = [NETWORK_DFRN];
|
||||
break;
|
||||
case 'PRIVATE':
|
||||
if (!empty($a->user['prvnets'])) {
|
||||
$networks = [NETWORK_DFRN, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
} else {
|
||||
$networks = [NETWORK_DFRN, NETWORK_FACEBOOK, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
}
|
||||
break;
|
||||
case 'TWO_WAY':
|
||||
if (!empty($a->user['prvnets'])) {
|
||||
$networks = [NETWORK_DFRN, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
} else {
|
||||
$networks = [NETWORK_DFRN, NETWORK_FACEBOOK, NETWORK_MAIL, NETWORK_DIASPORA, NETWORK_OSTATUS];
|
||||
}
|
||||
break;
|
||||
default: /// @TODO Maybe log this call?
|
||||
break;
|
||||
}
|
||||
|
||||
$x = ['options' => $options, 'size' => $size, 'single' => $single, 'mutual' => $mutual, 'exclude' => $exclude, 'networks' => $networks];
|
||||
|
||||
Addon::callHooks('contact_select_options', $x);
|
||||
|
||||
$o = '';
|
||||
|
||||
$sql_extra = '';
|
||||
|
||||
if (!empty($x['mutual'])) {
|
||||
$sql_extra .= sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND));
|
||||
}
|
||||
|
||||
if (!empty($x['exclude'])) {
|
||||
$sql_extra .= sprintf(" AND `id` != %d ", intval($x['exclude']));
|
||||
}
|
||||
|
||||
if (!empty($x['networks'])) {
|
||||
/// @TODO rewrite to foreach()
|
||||
array_walk($x['networks'], function (&$value) {
|
||||
$value = "'" . dbesc($value) . "'";
|
||||
});
|
||||
$str_nets = implode(',', $x['networks']);
|
||||
$sql_extra .= " AND `network` IN ( $str_nets ) ";
|
||||
}
|
||||
|
||||
$tabindex = (!empty($options['tabindex']) ? 'tabindex="' . $options["tabindex"] . '"' : '');
|
||||
|
||||
if (!empty($x['single'])) {
|
||||
$o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"" . $x['size'] . "\" $tabindex >\r\n";
|
||||
} else {
|
||||
$o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"" . $x['size'] . "$\" $tabindex >\r\n";
|
||||
}
|
||||
|
||||
$stmt = dba::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
|
||||
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
|
||||
$sql_extra
|
||||
ORDER BY `name` ASC ", intval(local_user())
|
||||
);
|
||||
|
||||
$contacts = dba::inArray($stmt);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
|
||||
Addon::callHooks($a->module . '_pre_' . $selname, $arr);
|
||||
|
||||
if (DBM::is_result($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (in_array($contact['id'], $preselected)) {
|
||||
$selected = ' selected="selected" ';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
|
||||
$trimmed = mb_substr($contact['name'], 0, 20);
|
||||
|
||||
$o .= "<option value=\"{$contact['id']}\" $selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</select>' . PHP_EOL;
|
||||
|
||||
Addon::callHooks($a->module . '_post_' . $selname, $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a select input tag with all the contact of the local user
|
||||
*
|
||||
* @param string $selname Name attribute of the select input tag
|
||||
* @param string $selclass Class attribute of the select input tag
|
||||
* @param array $preselected Contact IDs that should be already selected
|
||||
* @param int $size Length of the select box
|
||||
* @param int $tabindex Select input tag tabindex attribute
|
||||
* @return string
|
||||
*/
|
||||
public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$o = '';
|
||||
|
||||
// When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
|
||||
// to one recipient. By default our selector allows multiple selects amongst all contacts.
|
||||
$sql_extra = sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND));
|
||||
$sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", NETWORK_DFRN, NETWORK_DIASPORA);
|
||||
|
||||
$tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
|
||||
|
||||
$hidepreselected = '';
|
||||
if ($preselected) {
|
||||
$sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
|
||||
$hidepreselected = ' style="display: none;"';
|
||||
}
|
||||
|
||||
$o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
|
||||
|
||||
$stmt = dba::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
|
||||
WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
|
||||
$sql_extra
|
||||
ORDER BY `name` ASC ", intval(local_user())
|
||||
);
|
||||
|
||||
$contacts = dba::inArray($stmt);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
|
||||
Addon::callHooks($a->module . '_pre_' . $selname, $arr);
|
||||
|
||||
$receiverlist = [];
|
||||
|
||||
if (DBM::is_result($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (in_array($contact['id'], $preselected)) {
|
||||
$selected = ' selected="selected"';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
|
||||
$trimmed = Protocol::formatMention($contact['url'], $contact['name']);
|
||||
|
||||
$receiverlist[] = $trimmed;
|
||||
|
||||
$o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</select>' . PHP_EOL;
|
||||
|
||||
if ($preselected) {
|
||||
$o .= implode(', ', $receiverlist);
|
||||
}
|
||||
|
||||
Addon::callHooks($a->module . '_post_' . $selname, $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default permission of the provided user array
|
||||
*
|
||||
* @param array $user
|
||||
* @return array Hash of contact id lists
|
||||
*/
|
||||
public static function getDefaultUserPermissions(array $user = null)
|
||||
{
|
||||
$matches = [];
|
||||
|
||||
$acl_regex = '/<([0-9]+)>/i';
|
||||
|
||||
preg_match_all($acl_regex, defaults($user, 'allow_cid', ''), $matches);
|
||||
$allow_cid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'allow_gid', ''), $matches);
|
||||
$allow_gid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'deny_cid', ''), $matches);
|
||||
$deny_cid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'deny_gid', ''), $matches);
|
||||
$deny_gid = $matches[1];
|
||||
|
||||
Contact::pruneUnavailable($allow_cid);
|
||||
|
||||
return [
|
||||
'allow_cid' => $allow_cid,
|
||||
'allow_gid' => $allow_gid,
|
||||
'deny_cid' => $deny_cid,
|
||||
'deny_gid' => $deny_gid,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full jot ACL selector HTML
|
||||
*
|
||||
* @param array $user
|
||||
* @param bool $show_jotnets
|
||||
* @return string
|
||||
*/
|
||||
public static function getFullSelectorHTML(array $user = null, $show_jotnets = false)
|
||||
{
|
||||
$perms = self::getDefaultUserPermissions($user);
|
||||
|
||||
$jotnets = '';
|
||||
if ($show_jotnets) {
|
||||
$imap_disabled = !function_exists('imap_open') || Config::get('system', 'imap_disabled');
|
||||
|
||||
$mail_enabled = false;
|
||||
$pubmail_enabled = false;
|
||||
|
||||
if (!$imap_disabled) {
|
||||
$mailacct = dba::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
|
||||
if (DBM::is_result($mailacct)) {
|
||||
$mail_enabled = true;
|
||||
$pubmail_enabled = !empty($mailacct['pubmail']);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($user['hidewall'])) {
|
||||
if ($mail_enabled) {
|
||||
$selected = $pubmail_enabled ? ' checked="checked"' : '';
|
||||
$jotnets .= '<div class="profile-jot-net"><input type="checkbox" name="pubmail_enable"' . $selected . ' value="1" /> ' . L10n::t("Post to Email") . '</div>';
|
||||
}
|
||||
|
||||
Addon::callHooks('jot_networks', $jotnets);
|
||||
} else {
|
||||
$jotnets .= L10n::t('Connectors disabled, since "%s" is enabled.',
|
||||
L10n::t('Hide your profile details from unknown viewers?'));
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('acl_selector.tpl');
|
||||
$o = replace_macros($tpl, [
|
||||
'$showall' => L10n::t('Visible to everybody'),
|
||||
'$show' => L10n::t('show'),
|
||||
'$hide' => L10n::t('don\'t show'),
|
||||
'$allowcid' => json_encode($perms['allow_cid']),
|
||||
'$allowgid' => json_encode($perms['allow_gid']),
|
||||
'$denycid' => json_encode($perms['deny_cid']),
|
||||
'$denygid' => json_encode($perms['deny_gid']),
|
||||
'$networks' => $show_jotnets,
|
||||
'$emailcc' => L10n::t('CC: email addresses'),
|
||||
'$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
|
||||
'$jotnets' => $jotnets,
|
||||
'$aclModalTitle' => L10n::t('Permissions'),
|
||||
'$aclModalDismiss' => L10n::t('Close'),
|
||||
'$features' => [
|
||||
'aclautomention' => Feature::isEnabled($user['uid'], 'aclautomention') ? 'true' : 'false'
|
||||
],
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searching for global contacts for autocompletion
|
||||
*
|
||||
* @brief Searching for global contacts for autocompletion
|
||||
* @param string $search Name or part of a name or nick
|
||||
* @param string $mode Search mode (e.g. "community")
|
||||
* @return array with the search results
|
||||
*/
|
||||
public static function contactAutocomplete($search, $mode)
|
||||
{
|
||||
if ((Config::get('system', 'block_public')) && (!local_user()) && (!remote_user())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// don't search if search term has less than 2 characters
|
||||
if (!$search || mb_strlen($search) < 2) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (substr($search, 0, 1) === '@') {
|
||||
$search = substr($search, 1);
|
||||
}
|
||||
|
||||
// check if searching in the local global contact table is enabled
|
||||
if (Config::get('system', 'poco_local_search')) {
|
||||
$return = GContact::searchByName($search, $mode);
|
||||
} else {
|
||||
$a = self::getApp();
|
||||
$p = $a->pager['page'] != 1 ? '&p=' . $a->pager['page'] : '';
|
||||
|
||||
$response = Network::curl(get_server() . '/lsearch?f=' . $p . '&search=' . urlencode($search));
|
||||
if ($response['success']) {
|
||||
$lsearch = json_decode($response['body'], true);
|
||||
if (!empty($lsearch['results'])) {
|
||||
$return = $lsearch['results'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaults($return, []);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file src/Core/Acl.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Util\Network;
|
||||
use const CONTACT_IS_FRIEND;
|
||||
use const NETWORK_DFRN;
|
||||
use const NETWORK_DIASPORA;
|
||||
use const NETWORK_FACEBOOK;
|
||||
use const NETWORK_MAIL;
|
||||
use const NETWORK_OSTATUS;
|
||||
use const PHP_EOL;
|
||||
use function dbesc;
|
||||
use function defaults;
|
||||
use function get_markup_template;
|
||||
use function get_server;
|
||||
use function local_user;
|
||||
use function remote_user;
|
||||
use function replace_macros;
|
||||
|
||||
/**
|
||||
* Handle ACL management and display
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class ACL extends BaseObject
|
||||
{
|
||||
/**
|
||||
* Returns a select input tag with all the contact of the local user
|
||||
*
|
||||
* @param string $selname Name attribute of the select input tag
|
||||
* @param string $selclass Class attribute of the select input tag
|
||||
* @param array $options Available options:
|
||||
* - size: length of the select box
|
||||
* - mutual_friends: Only used for the hook
|
||||
* - single: Only used for the hook
|
||||
* - exclude: Only used for the hook
|
||||
* @param array $preselected Contact ID that should be already selected
|
||||
* @return string
|
||||
*/
|
||||
public static function getSuggestContactSelectHTML($selname, $selclass, array $options = [], array $preselected = [])
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$networks = null;
|
||||
|
||||
$size = defaults($options, 'size', 4);
|
||||
$mutual = !empty($options['mutual_friends']);
|
||||
$single = !empty($options['single']) && empty($options['multiple']);
|
||||
$exclude = defaults($options, 'exclude', false);
|
||||
|
||||
switch (defaults($options, 'networks', Protocol::PHANTOM)) {
|
||||
case 'DFRN_ONLY':
|
||||
$networks = [NETWORK_DFRN];
|
||||
break;
|
||||
case 'PRIVATE':
|
||||
if (!empty($a->user['prvnets'])) {
|
||||
$networks = [NETWORK_DFRN, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
} else {
|
||||
$networks = [NETWORK_DFRN, NETWORK_FACEBOOK, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
}
|
||||
break;
|
||||
case 'TWO_WAY':
|
||||
if (!empty($a->user['prvnets'])) {
|
||||
$networks = [NETWORK_DFRN, NETWORK_MAIL, NETWORK_DIASPORA];
|
||||
} else {
|
||||
$networks = [NETWORK_DFRN, NETWORK_FACEBOOK, NETWORK_MAIL, NETWORK_DIASPORA, NETWORK_OSTATUS];
|
||||
}
|
||||
break;
|
||||
default: /// @TODO Maybe log this call?
|
||||
break;
|
||||
}
|
||||
|
||||
$x = ['options' => $options, 'size' => $size, 'single' => $single, 'mutual' => $mutual, 'exclude' => $exclude, 'networks' => $networks];
|
||||
|
||||
Addon::callHooks('contact_select_options', $x);
|
||||
|
||||
$o = '';
|
||||
|
||||
$sql_extra = '';
|
||||
|
||||
if (!empty($x['mutual'])) {
|
||||
$sql_extra .= sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND));
|
||||
}
|
||||
|
||||
if (!empty($x['exclude'])) {
|
||||
$sql_extra .= sprintf(" AND `id` != %d ", intval($x['exclude']));
|
||||
}
|
||||
|
||||
if (!empty($x['networks'])) {
|
||||
/// @TODO rewrite to foreach()
|
||||
array_walk($x['networks'], function (&$value) {
|
||||
$value = "'" . dbesc($value) . "'";
|
||||
});
|
||||
$str_nets = implode(',', $x['networks']);
|
||||
$sql_extra .= " AND `network` IN ( $str_nets ) ";
|
||||
}
|
||||
|
||||
$tabindex = (!empty($options['tabindex']) ? 'tabindex="' . $options["tabindex"] . '"' : '');
|
||||
|
||||
if (!empty($x['single'])) {
|
||||
$o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"" . $x['size'] . "\" $tabindex >\r\n";
|
||||
} else {
|
||||
$o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"" . $x['size'] . "$\" $tabindex >\r\n";
|
||||
}
|
||||
|
||||
$stmt = dba::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
|
||||
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
|
||||
$sql_extra
|
||||
ORDER BY `name` ASC ", intval(local_user())
|
||||
);
|
||||
|
||||
$contacts = dba::inArray($stmt);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
|
||||
Addon::callHooks($a->module . '_pre_' . $selname, $arr);
|
||||
|
||||
if (DBM::is_result($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (in_array($contact['id'], $preselected)) {
|
||||
$selected = ' selected="selected" ';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
|
||||
$trimmed = mb_substr($contact['name'], 0, 20);
|
||||
|
||||
$o .= "<option value=\"{$contact['id']}\" $selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</select>' . PHP_EOL;
|
||||
|
||||
Addon::callHooks($a->module . '_post_' . $selname, $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a select input tag with all the contact of the local user
|
||||
*
|
||||
* @param string $selname Name attribute of the select input tag
|
||||
* @param string $selclass Class attribute of the select input tag
|
||||
* @param array $preselected Contact IDs that should be already selected
|
||||
* @param int $size Length of the select box
|
||||
* @param int $tabindex Select input tag tabindex attribute
|
||||
* @return string
|
||||
*/
|
||||
public static function getMessageContactSelectHTML($selname, $selclass, array $preselected = [], $size = 4, $tabindex = null)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$o = '';
|
||||
|
||||
// When used for private messages, we limit correspondence to mutual DFRN/Friendica friends and the selector
|
||||
// to one recipient. By default our selector allows multiple selects amongst all contacts.
|
||||
$sql_extra = sprintf(" AND `rel` = %d ", intval(CONTACT_IS_FRIEND));
|
||||
$sql_extra .= sprintf(" AND `network` IN ('%s' , '%s') ", NETWORK_DFRN, NETWORK_DIASPORA);
|
||||
|
||||
$tabindex_attr = !empty($tabindex) ? ' tabindex="' . intval($tabindex) . '"' : '';
|
||||
|
||||
$hidepreselected = '';
|
||||
if ($preselected) {
|
||||
$sql_extra .= " AND `id` IN (" . implode(",", $preselected) . ")";
|
||||
$hidepreselected = ' style="display: none;"';
|
||||
}
|
||||
|
||||
$o .= "<select name=\"$selname\" id=\"$selclass\" class=\"$selclass\" size=\"$size\"$tabindex_attr$hidepreselected>\r\n";
|
||||
|
||||
$stmt = dba::p("SELECT `id`, `name`, `url`, `network` FROM `contact`
|
||||
WHERE `uid` = ? AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
|
||||
$sql_extra
|
||||
ORDER BY `name` ASC ", intval(local_user())
|
||||
);
|
||||
|
||||
$contacts = dba::inArray($stmt);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
// e.g. 'network_pre_contact_deny', 'profile_pre_contact_allow'
|
||||
Addon::callHooks($a->module . '_pre_' . $selname, $arr);
|
||||
|
||||
$receiverlist = [];
|
||||
|
||||
if (DBM::is_result($contacts)) {
|
||||
foreach ($contacts as $contact) {
|
||||
if (in_array($contact['id'], $preselected)) {
|
||||
$selected = ' selected="selected"';
|
||||
} else {
|
||||
$selected = '';
|
||||
}
|
||||
|
||||
$trimmed = Protocol::formatMention($contact['url'], $contact['name']);
|
||||
|
||||
$receiverlist[] = $trimmed;
|
||||
|
||||
$o .= "<option value=\"{$contact['id']}\"$selected title=\"{$contact['name']}|{$contact['url']}\" >$trimmed</option>\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</select>' . PHP_EOL;
|
||||
|
||||
if ($preselected) {
|
||||
$o .= implode(', ', $receiverlist);
|
||||
}
|
||||
|
||||
Addon::callHooks($a->module . '_post_' . $selname, $o);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
private static function fixACL(&$item)
|
||||
{
|
||||
$item = intval(str_replace(['<', '>'], ['', ''], $item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default permission of the provided user array
|
||||
*
|
||||
* @param array $user
|
||||
* @return array Hash of contact id lists
|
||||
*/
|
||||
public static function getDefaultUserPermissions(array $user = null)
|
||||
{
|
||||
$matches = [];
|
||||
|
||||
$acl_regex = '/<([0-9]+)>/i';
|
||||
|
||||
preg_match_all($acl_regex, defaults($user, 'allow_cid', ''), $matches);
|
||||
$allow_cid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'allow_gid', ''), $matches);
|
||||
$allow_gid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'deny_cid', ''), $matches);
|
||||
$deny_cid = $matches[1];
|
||||
preg_match_all($acl_regex, defaults($user, 'deny_gid', ''), $matches);
|
||||
$deny_gid = $matches[1];
|
||||
|
||||
// Reformats the ACL data so that it is accepted by the JS frontend
|
||||
array_walk($allow_cid, 'self::fixACL');
|
||||
array_walk($allow_gid, 'self::fixACL');
|
||||
array_walk($deny_cid, 'self::fixACL');
|
||||
array_walk($deny_gid, 'self::fixACL');
|
||||
|
||||
Contact::pruneUnavailable($allow_cid);
|
||||
|
||||
return [
|
||||
'allow_cid' => $allow_cid,
|
||||
'allow_gid' => $allow_gid,
|
||||
'deny_cid' => $deny_cid,
|
||||
'deny_gid' => $deny_gid,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full jot ACL selector HTML
|
||||
*
|
||||
* @param array $user
|
||||
* @param bool $show_jotnets
|
||||
* @return string
|
||||
*/
|
||||
public static function getFullSelectorHTML(array $user = null, $show_jotnets = false)
|
||||
{
|
||||
$perms = self::getDefaultUserPermissions($user);
|
||||
|
||||
$jotnets = '';
|
||||
if ($show_jotnets) {
|
||||
$imap_disabled = !function_exists('imap_open') || Config::get('system', 'imap_disabled');
|
||||
|
||||
$mail_enabled = false;
|
||||
$pubmail_enabled = false;
|
||||
|
||||
if (!$imap_disabled) {
|
||||
$mailacct = dba::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
|
||||
if (DBM::is_result($mailacct)) {
|
||||
$mail_enabled = true;
|
||||
$pubmail_enabled = !empty($mailacct['pubmail']);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($user['hidewall'])) {
|
||||
if ($mail_enabled) {
|
||||
$selected = $pubmail_enabled ? ' checked="checked"' : '';
|
||||
$jotnets .= '<div class="profile-jot-net"><input type="checkbox" name="pubmail_enable"' . $selected . ' value="1" /> ' . L10n::t("Post to Email") . '</div>';
|
||||
}
|
||||
|
||||
Addon::callHooks('jot_networks', $jotnets);
|
||||
} else {
|
||||
$jotnets .= L10n::t('Connectors disabled, since "%s" is enabled.',
|
||||
L10n::t('Hide your profile details from unknown viewers?'));
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('acl_selector.tpl');
|
||||
$o = replace_macros($tpl, [
|
||||
'$showall' => L10n::t('Visible to everybody'),
|
||||
'$show' => L10n::t('show'),
|
||||
'$hide' => L10n::t('don\'t show'),
|
||||
'$allowcid' => json_encode($perms['allow_cid']),
|
||||
'$allowgid' => json_encode($perms['allow_gid']),
|
||||
'$denycid' => json_encode($perms['deny_cid']),
|
||||
'$denygid' => json_encode($perms['deny_gid']),
|
||||
'$networks' => $show_jotnets,
|
||||
'$emailcc' => L10n::t('CC: email addresses'),
|
||||
'$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
|
||||
'$jotnets' => $jotnets,
|
||||
'$aclModalTitle' => L10n::t('Permissions'),
|
||||
'$aclModalDismiss' => L10n::t('Close'),
|
||||
'$features' => [
|
||||
'aclautomention' => Feature::isEnabled($user['uid'], 'aclautomention') ? 'true' : 'false'
|
||||
],
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searching for global contacts for autocompletion
|
||||
*
|
||||
* @brief Searching for global contacts for autocompletion
|
||||
* @param string $search Name or part of a name or nick
|
||||
* @param string $mode Search mode (e.g. "community")
|
||||
* @return array with the search results
|
||||
*/
|
||||
public static function contactAutocomplete($search, $mode)
|
||||
{
|
||||
if ((Config::get('system', 'block_public')) && (!local_user()) && (!remote_user())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// don't search if search term has less than 2 characters
|
||||
if (!$search || mb_strlen($search) < 2) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (substr($search, 0, 1) === '@') {
|
||||
$search = substr($search, 1);
|
||||
}
|
||||
|
||||
// check if searching in the local global contact table is enabled
|
||||
if (Config::get('system', 'poco_local_search')) {
|
||||
$return = GContact::searchByName($search, $mode);
|
||||
} else {
|
||||
$a = self::getApp();
|
||||
$p = $a->pager['page'] != 1 ? '&p=' . $a->pager['page'] : '';
|
||||
|
||||
$response = Network::curl(get_server() . '/lsearch?f=' . $p . '&search=' . urlencode($search));
|
||||
if ($response['success']) {
|
||||
$lsearch = json_decode($response['body'], true);
|
||||
if (!empty($lsearch['results'])) {
|
||||
$return = $lsearch['results'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return defaults($return, []);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ use Friendica\Core\Config;
|
|||
*/
|
||||
class Cache extends \Friendica\BaseObject
|
||||
{
|
||||
const MONTH = 0;
|
||||
const WEEK = 1;
|
||||
const DAY = 2;
|
||||
const HOUR = 3;
|
||||
const HALF_HOUR = 4;
|
||||
const QUARTER_HOUR = 5;
|
||||
const FIVE_MINUTES = 6;
|
||||
const MINUTE = 7;
|
||||
const MONTH = 2592000;
|
||||
const WEEK = 604800;
|
||||
const DAY = 86400;
|
||||
const HOUR = 3600;
|
||||
const HALF_HOUR = 1800;
|
||||
const QUARTER_HOUR = 900;
|
||||
const FIVE_MINUTES = 300;
|
||||
const MINUTE = 60;
|
||||
|
||||
/**
|
||||
* @var Cache\ICacheDriver
|
||||
|
@ -45,45 +45,6 @@ class Cache extends \Friendica\BaseObject
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the duration for a given cache level
|
||||
*
|
||||
* @param integer $level Cache level
|
||||
*
|
||||
* @return integer The cache duration in seconds
|
||||
*/
|
||||
public static function duration($level)
|
||||
{
|
||||
switch ($level) {
|
||||
case self::MONTH:
|
||||
$seconds = 2592000;
|
||||
break;
|
||||
case self::WEEK:
|
||||
$seconds = 604800;
|
||||
break;
|
||||
case self::DAY:
|
||||
$seconds = 86400;
|
||||
break;
|
||||
case self::HOUR:
|
||||
$seconds = 3600;
|
||||
break;
|
||||
case self::HALF_HOUR:
|
||||
$seconds = 1800;
|
||||
break;
|
||||
case self::QUARTER_HOUR:
|
||||
$seconds = 900;
|
||||
break;
|
||||
case self::FIVE_MINUTES:
|
||||
$seconds = 300;
|
||||
break;
|
||||
case self::MINUTE:
|
||||
default:
|
||||
$seconds = 60;
|
||||
break;
|
||||
}
|
||||
return $seconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current cache driver
|
||||
*
|
||||
|
@ -138,6 +99,24 @@ class Cache extends \Friendica\BaseObject
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete a value from the cache
|
||||
*
|
||||
* @param string $key The key to the cached data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete($key)
|
||||
{
|
||||
$time = microtime(true);
|
||||
|
||||
$return = self::getDriver()->delete($key);
|
||||
|
||||
self::getApp()->save_timestamp($time, 'cache_write');
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove outdated data from the cache
|
||||
*
|
||||
|
|
|
@ -1,56 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use dba;
|
||||
use Friendica\Core\Cache;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
||||
/**
|
||||
* Database Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class DatabaseCacheDriver implements ICacheDriver
|
||||
{
|
||||
public function get($key)
|
||||
{
|
||||
$cache = dba::selectFirst('cache', ['v'], ['`k` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
|
||||
|
||||
if (DBM::is_result($cache)) {
|
||||
$cached = $cache['v'];
|
||||
$value = @unserialize($cached);
|
||||
|
||||
// Only return a value if the serialized value is valid.
|
||||
// We also check if the db entry is a serialized
|
||||
// boolean 'false' value (which we want to return).
|
||||
if ($cached === serialize(false) || $value !== false) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
$fields = [
|
||||
'v' => serialize($value),
|
||||
'expires' => DateTimeFormat::utc('now + ' . Cache::duration($duration) . ' seconds'),
|
||||
'updated' => DateTimeFormat::utcNow()
|
||||
];
|
||||
|
||||
return dba::update('cache', $fields, ['k' => $key], true);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return dba::delete('cache', ['k' => $key]);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return dba::delete('cache', ['`expires` < NOW()']);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use dba;
|
||||
use Friendica\Core\Cache;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
|
||||
/**
|
||||
* Database Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class DatabaseCacheDriver implements ICacheDriver
|
||||
{
|
||||
public function get($key)
|
||||
{
|
||||
$cache = dba::selectFirst('cache', ['v'], ['`k` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
|
||||
|
||||
if (DBM::is_result($cache)) {
|
||||
$cached = $cache['v'];
|
||||
$value = @unserialize($cached);
|
||||
|
||||
// Only return a value if the serialized value is valid.
|
||||
// We also check if the db entry is a serialized
|
||||
// boolean 'false' value (which we want to return).
|
||||
if ($cached === serialize(false) || $value !== false) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
$fields = [
|
||||
'v' => serialize($value),
|
||||
'expires' => DateTimeFormat::utc('now + ' . $duration . ' seconds'),
|
||||
'updated' => DateTimeFormat::utcNow()
|
||||
];
|
||||
|
||||
return dba::update('cache', $fields, ['k' => $key], true);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return dba::delete('cache', ['k' => $key]);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return dba::delete('cache', ['`expires` < NOW()']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Cache Driver Interface
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
interface ICacheDriver
|
||||
{
|
||||
/**
|
||||
* Fetches cached data according to the key
|
||||
*
|
||||
* @param string $key The key to the cached data
|
||||
*
|
||||
* @return mixed Cached $value or "null" if not found
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Stores data in the cache identified by the key. The input $value can have multiple formats.
|
||||
*
|
||||
* @param string $key The cache key
|
||||
* @param mixed $value The value to store
|
||||
* @param integer $duration The cache lifespan, must be one of the Cache constants
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $duration = Cache::MONTH);
|
||||
|
||||
|
||||
/**
|
||||
* Delete a key from the cache
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key);
|
||||
|
||||
/**
|
||||
* Remove outdated data from the cache
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear();
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Cache Driver Interface
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
interface ICacheDriver
|
||||
{
|
||||
/**
|
||||
* Fetches cached data according to the key
|
||||
*
|
||||
* @param string $key The key to the cached data
|
||||
*
|
||||
* @return mixed Cached $value or "null" if not found
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Stores data in the cache identified by the key. The input $value can have multiple formats.
|
||||
*
|
||||
* @param string $key The cache key
|
||||
* @param mixed $value The value to store
|
||||
* @param integer $duration The cache lifespan, must be one of the Cache constants
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $duration = Cache::MONTH);
|
||||
|
||||
|
||||
/**
|
||||
* Delete a key from the cache
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key);
|
||||
|
||||
/**
|
||||
* Remove outdated data from the cache
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear();
|
||||
}
|
||||
|
|
|
@ -1,77 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Memcache Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class MemcacheCacheDriver extends BaseObject implements ICacheDriver
|
||||
{
|
||||
/**
|
||||
* @var Memcache
|
||||
*/
|
||||
private $memcache;
|
||||
|
||||
public function __construct($memcache_host, $memcache_port)
|
||||
{
|
||||
if (!class_exists('Memcache', false)) {
|
||||
throw new \Exception('Memcache class isn\'t available');
|
||||
}
|
||||
|
||||
$this->memcache = new \Memcache();
|
||||
|
||||
if (!$this->memcache->connect($memcache_host, $memcache_port)) {
|
||||
throw new \Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available');
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
$return = null;
|
||||
|
||||
// We fetch with the hostname as key to avoid problems with other applications
|
||||
$cached = $this->memcache->get(self::getApp()->get_hostname() . ':' . $key);
|
||||
|
||||
// @see http://php.net/manual/en/memcache.get.php#84275
|
||||
if (is_bool($cached) || is_double($cached) || is_long($cached)) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$value = @unserialize($cached);
|
||||
|
||||
// Only return a value if the serialized value is valid.
|
||||
// We also check if the db entry is a serialized
|
||||
// boolean 'false' value (which we want to return).
|
||||
if ($cached === serialize(false) || $value !== false) {
|
||||
$return = $value;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
// We store with the hostname as key to avoid problems with other applications
|
||||
return $this->memcache->set(
|
||||
self::getApp()->get_hostname() . ":" . $key,
|
||||
serialize($value),
|
||||
MEMCACHE_COMPRESSED,
|
||||
Cache::duration($duration)
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->memcache->delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Memcache Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class MemcacheCacheDriver extends BaseObject implements ICacheDriver
|
||||
{
|
||||
/**
|
||||
* @var Memcache
|
||||
*/
|
||||
private $memcache;
|
||||
|
||||
public function __construct($memcache_host, $memcache_port)
|
||||
{
|
||||
if (!class_exists('Memcache', false)) {
|
||||
throw new \Exception('Memcache class isn\'t available');
|
||||
}
|
||||
|
||||
$this->memcache = new \Memcache();
|
||||
|
||||
if (!$this->memcache->connect($memcache_host, $memcache_port)) {
|
||||
throw new \Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available');
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
$return = null;
|
||||
|
||||
// We fetch with the hostname as key to avoid problems with other applications
|
||||
$cached = $this->memcache->get(self::getApp()->get_hostname() . ':' . $key);
|
||||
|
||||
// @see http://php.net/manual/en/memcache.get.php#84275
|
||||
if (is_bool($cached) || is_double($cached) || is_long($cached)) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$value = @unserialize($cached);
|
||||
|
||||
// Only return a value if the serialized value is valid.
|
||||
// We also check if the db entry is a serialized
|
||||
// boolean 'false' value (which we want to return).
|
||||
if ($cached === serialize(false) || $value !== false) {
|
||||
$return = $value;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
// We store with the hostname as key to avoid problems with other applications
|
||||
return $this->memcache->set(
|
||||
self::getApp()->get_hostname() . ":" . $key,
|
||||
serialize($value),
|
||||
MEMCACHE_COMPRESSED,
|
||||
time() + $duration
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->memcache->delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Memcached Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class MemcachedCacheDriver extends BaseObject implements ICacheDriver
|
||||
{
|
||||
/**
|
||||
* @var Memcached
|
||||
*/
|
||||
private $memcached;
|
||||
|
||||
public function __construct(array $memcached_hosts)
|
||||
{
|
||||
if (!class_exists('Memcached', false)) {
|
||||
throw new \Exception('Memcached class isn\'t available');
|
||||
}
|
||||
|
||||
$this->memcached = new \Memcached();
|
||||
|
||||
$this->memcached->addServers($memcached_hosts);
|
||||
|
||||
if (count($this->memcached->getServerList()) == 0) {
|
||||
throw new \Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true));
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
$return = null;
|
||||
|
||||
// We fetch with the hostname as key to avoid problems with other applications
|
||||
$value = $this->memcached->get(self::getApp()->get_hostname() . ':' . $key);
|
||||
|
||||
if ($this->memcached->getResultCode() === \Memcached::RES_SUCCESS) {
|
||||
$return = $value;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
// We store with the hostname as key to avoid problems with other applications
|
||||
return $this->memcached->set(
|
||||
self::getApp()->get_hostname() . ":" . $key,
|
||||
$value,
|
||||
Cache::duration($duration)
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->memcached->delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace Friendica\Core\Cache;
|
||||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Cache;
|
||||
|
||||
/**
|
||||
* Memcached Cache Driver
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class MemcachedCacheDriver extends BaseObject implements ICacheDriver
|
||||
{
|
||||
/**
|
||||
* @var Memcached
|
||||
*/
|
||||
private $memcached;
|
||||
|
||||
public function __construct(array $memcached_hosts)
|
||||
{
|
||||
if (!class_exists('Memcached', false)) {
|
||||
throw new \Exception('Memcached class isn\'t available');
|
||||
}
|
||||
|
||||
$this->memcached = new \Memcached();
|
||||
|
||||
$this->memcached->addServers($memcached_hosts);
|
||||
|
||||
if (count($this->memcached->getServerList()) == 0) {
|
||||
throw new \Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true));
|
||||
}
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
$return = null;
|
||||
|
||||
// We fetch with the hostname as key to avoid problems with other applications
|
||||
$value = $this->memcached->get(self::getApp()->get_hostname() . ':' . $key);
|
||||
|
||||
if ($this->memcached->getResultCode() === \Memcached::RES_SUCCESS) {
|
||||
$return = $value;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($key, $value, $duration = Cache::MONTH)
|
||||
{
|
||||
// We store with the hostname as key to avoid problems with other applications
|
||||
return $this->memcached->set(
|
||||
self::getApp()->get_hostname() . ":" . $key,
|
||||
$value,
|
||||
time() + $duration
|
||||
);
|
||||
}
|
||||
|
||||
public function delete($key)
|
||||
{
|
||||
return $this->memcached->delete($key);
|
||||
}
|
||||
|
||||
public function clear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,26 +8,33 @@
|
|||
*/
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\Database\DBM;
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Config;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* @brief Arbitrary sytem configuration storage
|
||||
* @brief Arbitrary system configuration storage
|
||||
*
|
||||
* Note:
|
||||
* If we ever would decide to return exactly the variable type as entered,
|
||||
* we will have fun with the additional features. :-)
|
||||
*
|
||||
* The config class always returns strings but in the default features
|
||||
* we use a "false" to determine if the config value isn't set.
|
||||
*
|
||||
*/
|
||||
class Config
|
||||
class Config extends BaseObject
|
||||
{
|
||||
private static $cache;
|
||||
private static $in_db;
|
||||
/**
|
||||
* @var Friendica\Core\Config\IConfigAdapter
|
||||
*/
|
||||
private static $adapter = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
if (self::getApp()->getConfigValue('system', 'config_adapter') == 'preload') {
|
||||
self::$adapter = new Config\PreloadConfigAdapter();
|
||||
} else {
|
||||
self::$adapter = new Config\JITConfigAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads all configuration values of family into a cached storage.
|
||||
|
@ -41,26 +48,11 @@ class Config
|
|||
*/
|
||||
public static function load($family = "config")
|
||||
{
|
||||
// We don't preload "system" anymore.
|
||||
// This reduces the number of database reads a lot.
|
||||
if ($family === 'system') {
|
||||
return;
|
||||
if (empty(self::$adapter)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$r = dba::select('config', ['v', 'k'], ['cat' => $family]);
|
||||
while ($rr = dba::fetch($r)) {
|
||||
$k = $rr['k'];
|
||||
if ($family === 'config') {
|
||||
$a->config[$k] = $rr['v'];
|
||||
} else {
|
||||
$a->config[$family][$k] = $rr['v'];
|
||||
self::$cache[$family][$k] = $rr['v'];
|
||||
self::$in_db[$family][$k] = true;
|
||||
}
|
||||
}
|
||||
dba::close($r);
|
||||
self::$adapter->load($family);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,40 +76,11 @@ class Config
|
|||
*/
|
||||
public static function get($family, $key, $default_value = null, $refresh = false)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if (!$refresh) {
|
||||
// Do we have the cached value? Then return it
|
||||
if (isset(self::$cache[$family][$key])) {
|
||||
if (self::$cache[$family][$key] === '!<unset>!') {
|
||||
return $default_value;
|
||||
} else {
|
||||
return self::$cache[$family][$key];
|
||||
}
|
||||
}
|
||||
if (empty(self::$adapter)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
$config = dba::selectFirst('config', ['v'], ['cat' => $family, 'k' => $key]);
|
||||
if (DBM::is_result($config)) {
|
||||
// manage array value
|
||||
$val = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']);
|
||||
|
||||
// Assign the value from the database to the cache
|
||||
self::$cache[$family][$key] = $val;
|
||||
self::$in_db[$family][$key] = true;
|
||||
return $val;
|
||||
} elseif (isset($a->config[$family][$key])) {
|
||||
// Assign the value (mostly) from the .htconfig.php to the cache
|
||||
self::$cache[$family][$key] = $a->config[$family][$key];
|
||||
self::$in_db[$family][$key] = false;
|
||||
|
||||
return $a->config[$family][$key];
|
||||
}
|
||||
|
||||
self::$cache[$family][$key] = '!<unset>!';
|
||||
self::$in_db[$family][$key] = false;
|
||||
|
||||
return $default_value;
|
||||
return self::$adapter->get($family, $key, $default_value, $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,38 +99,11 @@ class Config
|
|||
*/
|
||||
public static function set($family, $key, $value)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
// We store our setting values in a string variable.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$dbvalue = (!is_array($value) ? (string)$value : $value);
|
||||
|
||||
$stored = self::get($family, $key, null, true);
|
||||
|
||||
if (($stored === $dbvalue) && self::$in_db[$family][$key]) {
|
||||
return true;
|
||||
if (empty(self::$adapter)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
if ($family === 'config') {
|
||||
$a->config[$key] = $dbvalue;
|
||||
} elseif ($family != 'system') {
|
||||
$a->config[$family][$key] = $dbvalue;
|
||||
}
|
||||
|
||||
// Assign the just added value to the cache
|
||||
self::$cache[$family][$key] = $dbvalue;
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value) ? serialize($value) : $dbvalue);
|
||||
|
||||
$ret = dba::update('config', ['v' => $dbvalue], ['cat' => $family, 'k' => $key], true);
|
||||
|
||||
if ($ret) {
|
||||
self::$in_db[$family][$key] = true;
|
||||
return $value;
|
||||
}
|
||||
return $ret;
|
||||
return self::$adapter->set($family, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,13 +119,10 @@ class Config
|
|||
*/
|
||||
public static function delete($family, $key)
|
||||
{
|
||||
if (isset(self::$cache[$family][$key])) {
|
||||
unset(self::$cache[$family][$key]);
|
||||
unset(self::$in_db[$family][$key]);
|
||||
if (empty(self::$adapter)) {
|
||||
self::init();
|
||||
}
|
||||
|
||||
$ret = dba::delete('config', ['cat' => $family, 'k' => $key]);
|
||||
|
||||
return $ret;
|
||||
return self::$adapter->delete($family, $key);
|
||||
}
|
||||
}
|
||||
|
|
72
src/Core/Config/IConfigAdapter.php
Normal file
72
src/Core/Config/IConfigAdapter.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
interface IConfigAdapter
|
||||
{
|
||||
/**
|
||||
* @brief Loads all configuration values into a cached storage.
|
||||
*
|
||||
* All configuration values of the system are stored in global cache
|
||||
* which is available under the global variable $a->config
|
||||
*
|
||||
* @param string $cat The category of the configuration values to load
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load($cat = "config");
|
||||
|
||||
/**
|
||||
* @brief Get a particular user's config variable given the category name
|
||||
* ($family) and a key.
|
||||
*
|
||||
* Get a particular config value from the given category ($family)
|
||||
* and the $key from a cached storage in $a->config[$uid].
|
||||
* $instore is only used by the set_config function
|
||||
* to determine if the key already exists in the DB
|
||||
* If a key is found in the DB but doesn't exist in
|
||||
* local config cache, pull it into the cache so we don't have
|
||||
* to hit the DB again for this item.
|
||||
*
|
||||
* @param string $cat The category of the configuration value
|
||||
* @param string $k The configuration key to query
|
||||
* @param mixed $default_value optional, The value to return if key is not set (default: null)
|
||||
* @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false)
|
||||
*
|
||||
* @return mixed Stored value or null if it does not exist
|
||||
*/
|
||||
public function get($cat, $k, $default_value = null, $refresh = false);
|
||||
|
||||
/**
|
||||
* @brief Sets a configuration value for system config
|
||||
*
|
||||
* Stores a config value ($value) in the category ($family) under the key ($key)
|
||||
* for the user_id $uid.
|
||||
*
|
||||
* Note: Please do not store booleans - convert to 0/1 integer values!
|
||||
*
|
||||
* @param string $family The category of the configuration value
|
||||
* @param string $key The configuration key to set
|
||||
* @param mixed $value The value to store
|
||||
*
|
||||
* @return mixed Stored $value or false if the database update failed
|
||||
*/
|
||||
public function set($cat, $k, $value);
|
||||
|
||||
/**
|
||||
* @brief Deletes the given key from the system configuration.
|
||||
*
|
||||
* Removes the configured value from the stored cache in $a->config
|
||||
* and removes it from the database.
|
||||
*
|
||||
* @param string $cat The category of the configuration value
|
||||
* @param string $k The configuration key to delete
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete($cat, $k);
|
||||
}
|
77
src/Core/Config/IPConfigAdapter.php
Normal file
77
src/Core/Config/IPConfigAdapter.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author benlo
|
||||
*/
|
||||
interface IPConfigAdapter
|
||||
{
|
||||
/**
|
||||
* @brief Loads all configuration values of a user's config family into a cached storage.
|
||||
*
|
||||
* All configuration values of the given user are stored in global cache
|
||||
* which is available under the global variable $a->config[$uid].
|
||||
*
|
||||
* @param string $uid The user_id
|
||||
* @param string $cat The category of the configuration value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load($uid, $cat);
|
||||
|
||||
/**
|
||||
* @brief Get a particular user's config variable given the category name
|
||||
* ($family) and a key.
|
||||
*
|
||||
* Get a particular user's config value from the given category ($family)
|
||||
* and the $key from a cached storage in $a->config[$uid].
|
||||
*
|
||||
* @param string $uid The user_id
|
||||
* @param string $cat The category of the configuration value
|
||||
* @param string $k The configuration key to query
|
||||
* @param mixed $default_value optional, The value to return if key is not set (default: null)
|
||||
* @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false)
|
||||
*
|
||||
* @return mixed Stored value or null if it does not exist
|
||||
*/
|
||||
public function get($uid, $cat, $k, $default_value = null, $refresh = false);
|
||||
|
||||
/**
|
||||
* @brief Sets a configuration value for a user
|
||||
*
|
||||
* Stores a config value ($value) in the category ($family) under the key ($key)
|
||||
* for the user_id $uid.
|
||||
*
|
||||
* @note Please do not store booleans - convert to 0/1 integer values!
|
||||
*
|
||||
* @param string $uid The user_id
|
||||
* @param string $cat The category of the configuration value
|
||||
* @param string $k The configuration key to set
|
||||
* @param string $value The value to store
|
||||
*
|
||||
* @return mixed Stored $value or false
|
||||
*/
|
||||
public function set($uid, $cat, $k, $value);
|
||||
|
||||
/**
|
||||
* @brief Deletes the given key from the users's configuration.
|
||||
*
|
||||
* Removes the configured value from the stored cache in $a->config[$uid]
|
||||
* and removes it from the database.
|
||||
*
|
||||
* @param string $uid The user_id
|
||||
* @param string $cat The category of the configuration value
|
||||
* @param string $k The configuration key to delete
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete($uid, $cat, $k);
|
||||
}
|
126
src/Core/Config/JITConfigAdapter.php
Normal file
126
src/Core/Config/JITConfigAdapter.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Database\DBM;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* JustInTime Configuration Adapter
|
||||
*
|
||||
* Default Config Adapter. Provides the best performance for pages loading few configuration variables.
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class JITConfigAdapter extends BaseObject implements IConfigAdapter
|
||||
{
|
||||
private $cache;
|
||||
private $in_db;
|
||||
|
||||
public function load($cat = "config")
|
||||
{
|
||||
// We don't preload "system" anymore.
|
||||
// This reduces the number of database reads a lot.
|
||||
if ($cat === 'system') {
|
||||
return;
|
||||
}
|
||||
|
||||
$configs = dba::select('config', ['v', 'k'], ['cat' => $cat]);
|
||||
while ($config = dba::fetch($configs)) {
|
||||
$k = $config['k'];
|
||||
|
||||
self::getApp()->setConfigValue($cat, $k, $config['v']);
|
||||
|
||||
if ($cat !== 'config') {
|
||||
$this->cache[$cat][$k] = $config['v'];
|
||||
$this->in_db[$cat][$k] = true;
|
||||
}
|
||||
}
|
||||
dba::close($configs);
|
||||
}
|
||||
|
||||
public function get($cat, $k, $default_value = null, $refresh = false)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
if (!$refresh) {
|
||||
// Do we have the cached value? Then return it
|
||||
if (isset($this->cache[$cat][$k])) {
|
||||
if ($this->cache[$cat][$k] === '!<unset>!') {
|
||||
return $default_value;
|
||||
} else {
|
||||
return $this->cache[$cat][$k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config = dba::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]);
|
||||
if (DBM::is_result($config)) {
|
||||
// manage array value
|
||||
$value = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']);
|
||||
|
||||
// Assign the value from the database to the cache
|
||||
$this->cache[$cat][$k] = $value;
|
||||
$this->in_db[$cat][$k] = true;
|
||||
return $value;
|
||||
} elseif (isset($a->config[$cat][$k])) {
|
||||
// Assign the value (mostly) from the .htconfig.php to the cache
|
||||
$this->cache[$cat][$k] = $a->config[$cat][$k];
|
||||
$this->in_db[$cat][$k] = false;
|
||||
|
||||
return $a->config[$cat][$k];
|
||||
}
|
||||
|
||||
$this->cache[$cat][$k] = '!<unset>!';
|
||||
$this->in_db[$cat][$k] = false;
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
public function set($cat, $k, $value)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
// We store our setting values in a string variable.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$dbvalue = (!is_array($value) ? (string)$value : $value);
|
||||
|
||||
$stored = $this->get($cat, $k, null, true);
|
||||
|
||||
if (($stored === $dbvalue) && $this->in_db[$cat][$k]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::getApp()->setConfigValue($cat, $k, $value);
|
||||
|
||||
// Assign the just added value to the cache
|
||||
$this->cache[$cat][$k] = $dbvalue;
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value) ? serialize($value) : $dbvalue);
|
||||
|
||||
$result = dba::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $k], true);
|
||||
|
||||
if ($result) {
|
||||
$this->in_db[$cat][$k] = true;
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function delete($cat, $k)
|
||||
{
|
||||
if (isset($this->cache[$cat][$k])) {
|
||||
unset($this->cache[$cat][$k]);
|
||||
unset($this->in_db[$cat][$k]);
|
||||
}
|
||||
|
||||
$result = dba::delete('config', ['cat' => $cat, 'k' => $k]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
119
src/Core/Config/JITPConfigAdapter.php
Normal file
119
src/Core/Config/JITPConfigAdapter.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Database\DBM;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* JustInTime User Configuration Adapter
|
||||
*
|
||||
* Default PConfig Adapter. Provides the best performance for pages loading few configuration variables.
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
|
||||
{
|
||||
private $in_db;
|
||||
|
||||
public function load($uid, $cat)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$pconfigs = dba::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]);
|
||||
if (DBM::is_result($pconfigs)) {
|
||||
while ($pconfig = dba::fetch($pconfigs)) {
|
||||
$k = $pconfig['k'];
|
||||
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, $pconfig['v']);
|
||||
|
||||
$this->in_db[$uid][$cat][$k] = true;
|
||||
}
|
||||
} else if ($cat != 'config') {
|
||||
// Negative caching
|
||||
$a->config[$uid][$cat] = "!<unset>!";
|
||||
}
|
||||
dba::close($pconfigs);
|
||||
}
|
||||
|
||||
public function get($uid, $cat, $k, $default_value = null, $refresh = false)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
if (!$refresh) {
|
||||
// Looking if the whole family isn't set
|
||||
if (isset($a->config[$uid][$cat])) {
|
||||
if ($a->config[$uid][$cat] === '!<unset>!') {
|
||||
return $default_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($a->config[$uid][$cat][$k])) {
|
||||
if ($a->config[$uid][$cat][$k] === '!<unset>!') {
|
||||
return $default_value;
|
||||
}
|
||||
return $a->config[$uid][$cat][$k];
|
||||
}
|
||||
}
|
||||
|
||||
$pconfig = dba::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
|
||||
if (DBM::is_result($pconfig)) {
|
||||
$val = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']);
|
||||
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, $val);
|
||||
|
||||
$this->in_db[$uid][$cat][$k] = true;
|
||||
|
||||
return $val;
|
||||
} else {
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, '!<unset>!');
|
||||
|
||||
$this->in_db[$uid][$cat][$k] = false;
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
}
|
||||
|
||||
public function set($uid, $cat, $k, $value)
|
||||
{
|
||||
// We store our setting values in a string variable.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$dbvalue = (!is_array($value) ? (string)$value : $value);
|
||||
|
||||
$stored = $this->get($uid, $cat, $k, null, true);
|
||||
|
||||
if (($stored === $dbvalue) && $this->in_db[$uid][$cat][$k]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, $value);
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value) ? serialize($value) : $dbvalue);
|
||||
|
||||
$result = dba::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $k], true);
|
||||
|
||||
if ($result) {
|
||||
$this->in_db[$uid][$cat][$k] = true;
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function delete($uid, $cat, $k)
|
||||
{
|
||||
self::getApp()->deletePConfigValue($uid, $cat, $k);
|
||||
|
||||
if (!empty($this->in_db[$uid][$cat][$k])) {
|
||||
unset($this->in_db[$uid][$cat][$k]);
|
||||
}
|
||||
|
||||
$result = dba::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
90
src/Core/Config/PreloadConfigAdapter.php
Normal file
90
src/Core/Config/PreloadConfigAdapter.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
use dba;
|
||||
use Exception;
|
||||
use Friendica\App;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Database\DBM;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* Preload Configuration Adapter
|
||||
*
|
||||
* Minimizes the number of database queries to retrieve configuration values at the cost of memory.
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class PreloadConfigAdapter extends BaseObject implements IConfigAdapter
|
||||
{
|
||||
private $config_loaded = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->load();
|
||||
}
|
||||
|
||||
public function load($family = 'config')
|
||||
{
|
||||
if ($this->config_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
$configs = dba::select('config', ['cat', 'v', 'k']);
|
||||
while ($config = dba::fetch($configs)) {
|
||||
self::getApp()->setConfigValue($config['cat'], $config['k'], $config['v']);
|
||||
}
|
||||
dba::close($configs);
|
||||
|
||||
$this->config_loaded = true;
|
||||
}
|
||||
|
||||
public function get($cat, $k, $default_value = null, $refresh = false)
|
||||
{
|
||||
if ($refresh) {
|
||||
$config = dba::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]);
|
||||
if (DBM::is_result($config)) {
|
||||
self::getApp()->setConfigValue($cat, $k, $config['v']);
|
||||
}
|
||||
}
|
||||
|
||||
$return = self::getApp()->getConfigValue($cat, $k, $default_value);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($cat, $k, $value)
|
||||
{
|
||||
// We store our setting values as strings.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$compare_value = !is_array($value) ? (string)$value : $value;
|
||||
|
||||
if (self::getApp()->getConfigValue($cat, $k) === $compare_value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::getApp()->setConfigValue($cat, $k, $value);
|
||||
|
||||
// manage array value
|
||||
$dbvalue = is_array($value) ? serialize($value) : $value;
|
||||
|
||||
$result = dba::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $k], true);
|
||||
if (!$result) {
|
||||
throw new Exception('Unable to store config value in [' . $cat . '][' . $k . ']');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($cat, $k)
|
||||
{
|
||||
self::getApp()->deleteConfigValue($cat, $k);
|
||||
|
||||
$result = dba::delete('config', ['cat' => $cat, 'k' => $k]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
107
src/Core/Config/PreloadPConfigAdapter.php
Normal file
107
src/Core/Config/PreloadPConfigAdapter.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Config;
|
||||
|
||||
use dba;
|
||||
use Exception;
|
||||
use Friendica\App;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Database\DBM;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* Preload User Configuration Adapter
|
||||
*
|
||||
* Minimizes the number of database queries to retrieve configuration values at the cost of memory.
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
|
||||
{
|
||||
private $config_loaded = false;
|
||||
|
||||
public function __construct($uid)
|
||||
{
|
||||
$this->load($uid, 'config');
|
||||
}
|
||||
|
||||
public function load($uid, $family)
|
||||
{
|
||||
if ($this->config_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($uid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pconfigs = dba::select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]);
|
||||
while ($pconfig = dba::fetch($pconfigs)) {
|
||||
self::getApp()->setPConfigValue($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']);
|
||||
}
|
||||
dba::close($pconfigs);
|
||||
|
||||
$this->config_loaded = true;
|
||||
}
|
||||
|
||||
public function get($uid, $cat, $k, $default_value = null, $refresh = false)
|
||||
{
|
||||
if (!$this->config_loaded) {
|
||||
$this->load($uid, $cat);
|
||||
}
|
||||
|
||||
if ($refresh) {
|
||||
$config = dba::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
|
||||
if (DBM::is_result($config)) {
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, $config['v']);
|
||||
} else {
|
||||
self::getApp()->deletePConfigValue($uid, $cat, $k);
|
||||
}
|
||||
}
|
||||
|
||||
$return = self::getApp()->getPConfigValue($uid, $cat, $k, $default_value);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function set($uid, $cat, $k, $value)
|
||||
{
|
||||
if (!$this->config_loaded) {
|
||||
$this->load($uid, $cat);
|
||||
}
|
||||
// We store our setting values as strings.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$compare_value = !is_array($value) ? (string)$value : $value;
|
||||
|
||||
if (self::getApp()->getPConfigValue($uid, $cat, $k) === $compare_value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::getApp()->setPConfigValue($uid, $cat, $k, $value);
|
||||
|
||||
// manage array value
|
||||
$dbvalue = is_array($value) ? serialize($value) : $value;
|
||||
|
||||
$result = dba::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $k], true);
|
||||
if (!$result) {
|
||||
throw new Exception('Unable to store config value in [' . $uid . '][' . $cat . '][' . $k . ']');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($uid, $cat, $k)
|
||||
{
|
||||
if (!$this->config_loaded) {
|
||||
$this->load($uid, $cat);
|
||||
}
|
||||
|
||||
self::getApp()->deletePConfigValue($uid, $cat, $k);
|
||||
|
||||
$result = dba::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
125
src/Core/Console.php
Normal file
125
src/Core/Console.php
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core;
|
||||
|
||||
/**
|
||||
* Description of Console
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Console extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
// Disables the default help handling
|
||||
protected $helpOptions = [];
|
||||
protected $customHelpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected $subConsoles = [
|
||||
'config' => __NAMESPACE__ . '\Console\Config',
|
||||
'createdoxygen' => __NAMESPACE__ . '\Console\CreateDoxygen',
|
||||
'docbloxerrorchecker' => __NAMESPACE__ . '\Console\DocBloxErrorChecker',
|
||||
'dbstructure' => __NAMESPACE__ . '\Console\DatabaseStructure',
|
||||
'extract' => __NAMESPACE__ . '\Console\Extract',
|
||||
'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock',
|
||||
'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence',
|
||||
'maintenance' => __NAMESPACE__ . '\Console\Maintenance',
|
||||
'newpassword' => __NAMESPACE__ . '\Console\NewPassword',
|
||||
'php2po' => __NAMESPACE__ . '\Console\PhpToPo',
|
||||
'po2php' => __NAMESPACE__ . '\Console\PoToPhp',
|
||||
'typo' => __NAMESPACE__ . '\Console\Typo',
|
||||
];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
Usage: bin/console [--version] [-h|--help|-?] <command> [<args>] [-v]
|
||||
|
||||
Commands:
|
||||
config Edit site config
|
||||
createdoxygen Generate Doxygen headers
|
||||
dbstructure Do database updates
|
||||
docbloxerrorchecker Check the file tree for DocBlox errors
|
||||
extract Generate translation string file for the Friendica project (deprecated)
|
||||
globalcommunityblock Block remote profile from interacting with this node
|
||||
globalcommunitysilence Silence remote profile from global community page
|
||||
help Show help about a command, e.g (bin/console help config)
|
||||
maintenance Set maintenance mode for this node
|
||||
newpassword Set a new password for a given user
|
||||
php2po Generate a messages.po file from a strings.php file
|
||||
po2php Generate a strings.php file from a messages.po file
|
||||
typo Checks for parse errors in Friendica files
|
||||
|
||||
Options:
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Executable: ' . $this->executable);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
$showHelp = false;
|
||||
$subHelp = false;
|
||||
$command = null;
|
||||
|
||||
if ($this->getOption('version')) {
|
||||
$this->out('Friendica Console version ' . FRIENDICA_VERSION);
|
||||
|
||||
return 0;
|
||||
} elseif ((count($this->options) === 0 || $this->getOption($this->customHelpOptions) === true || $this->getOption($this->customHelpOptions) === 1) && count($this->args) === 0
|
||||
) {
|
||||
$showHelp = true;
|
||||
} elseif (count($this->args) >= 2 && $this->getArgument(0) == 'help') {
|
||||
$command = $this->getArgument(1);
|
||||
$subHelp = true;
|
||||
array_shift($this->args);
|
||||
array_shift($this->args);
|
||||
} elseif (count($this->args) >= 1) {
|
||||
$command = $this->getArgument(0);
|
||||
array_shift($this->args);
|
||||
}
|
||||
|
||||
if (is_null($command)) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
$console = $this->getSubConsole($command);
|
||||
|
||||
if ($subHelp) {
|
||||
$console->setOption($this->customHelpOptions, true);
|
||||
}
|
||||
|
||||
return $console->execute();
|
||||
}
|
||||
|
||||
private function getSubConsole($command)
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Command: ' . $command);
|
||||
}
|
||||
|
||||
if (!isset($this->subConsoles[$command])) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Command ' . $command . ' doesn\'t exist');
|
||||
}
|
||||
|
||||
$subargs = $this->args;
|
||||
array_unshift($subargs, $this->executable);
|
||||
|
||||
$className = $this->subConsoles[$command];
|
||||
|
||||
$subconsole = new $className($subargs);
|
||||
|
||||
foreach ($this->options as $name => $value) {
|
||||
$subconsole->setOption($name, $value);
|
||||
}
|
||||
|
||||
return $subconsole;
|
||||
}
|
||||
|
||||
}
|
137
src/Core/Console/Config.php
Normal file
137
src/Core/Console/Config.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Asika\SimpleConsole\CommandArgsException;
|
||||
use dba;
|
||||
use Friendica\Core;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/text.php';
|
||||
|
||||
/**
|
||||
* @brief tool to access the system config from the CLI
|
||||
*
|
||||
* With this script you can access the system configuration of your node from
|
||||
* the CLI. You can do both, reading current values stored in the database and
|
||||
* set new values to config variables.
|
||||
*
|
||||
* Usage:
|
||||
* If you specify no parameters at the CLI, the script will list all config
|
||||
* variables defined.
|
||||
*
|
||||
* If you specify one parameter, the script will list all config variables
|
||||
* defined in this section of the configuration (e.g. "system").
|
||||
*
|
||||
* If you specify two parameters, the script will show you the current value
|
||||
* of the named configuration setting. (e.g. "system loglevel")
|
||||
*
|
||||
* If you specify three parameters, the named configuration setting will be
|
||||
* set to the value of the last parameter. (e.g. "system loglevel 0" will
|
||||
* disable logging)
|
||||
*
|
||||
* @author Tobias Diekershoff
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Config extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console config - Manage site configuration
|
||||
Synopsis
|
||||
bin/console config [-h|--help|-?] [-v]
|
||||
bin/console config <category> [-h|--help|-?] [-v]
|
||||
bin/console config <category> <key> [-h|--help|-?] [-v]
|
||||
bin/console config <category> <key> <value> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
bin/console config
|
||||
Lists all config values
|
||||
|
||||
bin/console config <category>
|
||||
Lists all config values in the provided category
|
||||
|
||||
bin/console config <category> <key>
|
||||
Shows the value of the provided key in the category
|
||||
|
||||
bin/console config <category> <key> <value>
|
||||
Sets the value of the provided key in the category
|
||||
|
||||
Notes:
|
||||
Setting config entries which are manually set in .htconfig.php may result in
|
||||
conflict between database settings and the manual startup settings.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Executable: ' . $this->executable);
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 3) {
|
||||
throw new CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
if (count($this->args) == 3) {
|
||||
Core\Config::set($this->getArgument(0), $this->getArgument(1), $this->getArgument(2));
|
||||
$this->out("config[{$this->getArgument(0)}][{$this->getArgument(1)}] = " . Core\Config::get($this->getArgument(0),
|
||||
$this->getArgument(1)));
|
||||
}
|
||||
|
||||
if (count($this->args) == 2) {
|
||||
$this->out("config[{$this->getArgument(0)}][{$this->getArgument(1)}] = " . Core\Config::get($this->getArgument(0),
|
||||
$this->getArgument(1)));
|
||||
}
|
||||
|
||||
if (count($this->args) == 1) {
|
||||
Core\Config::load($this->getArgument(0));
|
||||
|
||||
$a = get_app();
|
||||
if (!is_null($a->config[$this->getArgument(0)])) {
|
||||
foreach ($a->config[$this->getArgument(0)] as $k => $x) {
|
||||
$this->out("config[{$this->getArgument(0)}][{$k}] = " . $x);
|
||||
}
|
||||
} else {
|
||||
$this->out('Config section ' . $this->getArgument(0) . ' returned nothing');
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$configs = dba::select('config');
|
||||
foreach ($configs as $config) {
|
||||
$this->out("config[{$config['cat']}][{$config['k']}] = " . $config['v']);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
148
src/Core/Console/CreateDoxygen.php
Normal file
148
src/Core/Console/CreateDoxygen.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Description of CreateDoxygen
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class CreateDoxygen extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console createdoxygen - Generate Doxygen headers
|
||||
Usage
|
||||
bin/console createdoxygen <file> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Outputs the provided file with added Doxygen headers to functions
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$file = $this->getArgument(0);
|
||||
if (!file_exists($file)) {
|
||||
throw new \RuntimeException('Unable to find specified file.');
|
||||
}
|
||||
|
||||
$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, 19) == "protected 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, 24) == "private static function ") {
|
||||
$found = true;
|
||||
}
|
||||
|
||||
if (substr($detect, 0, 10) == "function (") {
|
||||
$found = false;
|
||||
}
|
||||
|
||||
if ($found && ( trim($previous) == "*/")) {
|
||||
$found = false;
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$this->out($this->addDocumentation($line));
|
||||
}
|
||||
}
|
||||
$this->out($line);
|
||||
$previous = $line;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a doxygen header
|
||||
*
|
||||
* @param string $line The current line of the document
|
||||
*
|
||||
* @return string added doxygen header
|
||||
*/
|
||||
private function addDocumentation($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;
|
||||
}
|
||||
|
||||
}
|
112
src/Core/Console/DatabaseStructure.php
Normal file
112
src/Core/Console/DatabaseStructure.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core;
|
||||
use Friendica\Database\DBStructure;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* @brief Does database updates from the command line
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class DatabaseStructure extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console dbstructure - Does database updates
|
||||
Usage
|
||||
bin/console dbstructure <command> [-h|--help|-?] [-v]
|
||||
|
||||
Commands
|
||||
dryrun Show database update schema queries without running them
|
||||
update Update database schema
|
||||
dumpsql Dump database schema
|
||||
toinnodb Convert all tables from MyISAM to InnoDB
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
Core\Config::load();
|
||||
|
||||
switch ($this->getArgument(0)) {
|
||||
case "dryrun":
|
||||
$output = DBStructure::update(true, false);
|
||||
break;
|
||||
case "update":
|
||||
$output = DBStructure::update(true, true);
|
||||
|
||||
$build = Core\Config::get('system', 'build');
|
||||
if (empty($build)) {
|
||||
Core\Config::set('system', 'build', DB_UPDATE_VERSION);
|
||||
$build = DB_UPDATE_VERSION;
|
||||
}
|
||||
|
||||
$stored = intval($build);
|
||||
$current = intval(DB_UPDATE_VERSION);
|
||||
|
||||
// run any left update_nnnn functions in update.php
|
||||
for ($x = $stored; $x < $current; $x ++) {
|
||||
$r = run_update_function($x);
|
||||
if (!$r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Core\Config::set('system', 'build', DB_UPDATE_VERSION);
|
||||
break;
|
||||
case "dumpsql":
|
||||
ob_start();
|
||||
DBStructure::printStructure();
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
case "toinnodb":
|
||||
ob_start();
|
||||
DBStructure::convertToInnoDB();
|
||||
$output = ob_get_clean();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->out($output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
192
src/Core/Console/DocBloxErrorChecker.php
Normal file
192
src/Core/Console/DocBloxErrorChecker.php
Normal file
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* When I installed docblox, I had the experience that it does not generate any output at all.
|
||||
* This script may be used to find that kind of problems with the documentation build process.
|
||||
* If docblox generates output, use another approach for debugging.
|
||||
*
|
||||
* Basically, docblox takes a list of files to build documentation from. This script assumes there is a file or set of files
|
||||
* breaking the build when it is included in that list. It tries to calculate the smallest list containing these files.
|
||||
* Unfortunatly, the original problem is NP-complete, so what the script does is a best guess only.
|
||||
*
|
||||
* So it starts with a list of all files in the project.
|
||||
* If that list can't be build, it cuts it in two parts and tries both parts independently. If only one of them breaks,
|
||||
* it takes that one and tries the same independently. If both break, it assumes this is the smallest set. This assumption
|
||||
* is not necessarily true. Maybe the smallest set consists of two files and both of them were in different parts when
|
||||
* the list was divided, but by now it is my best guess. To make this assumption better, the list is shuffled after every step.
|
||||
*
|
||||
* After that, the script tries to remove a file from the list. It tests if the list breaks and if so, it
|
||||
* assumes that the file it removed belongs to the set of erroneous files.
|
||||
* This is done for all files, so, in the end removing one file leads to a working doc build.
|
||||
*
|
||||
* @author Alexander Kampmann
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class DocBloxErrorChecker extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console docbloxerrorchecker - Checks the file tree for docblox errors
|
||||
Usage
|
||||
bin/console docbloxerrorchecker [-h|--help|-?] [-v]
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 0) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
if (!$this->commandExists('docblox')) {
|
||||
throw new \RuntimeException('DocBlox isn\'t available.');
|
||||
}
|
||||
|
||||
//return from util folder to frindica base dir
|
||||
$dir = get_app()->get_basepath();
|
||||
|
||||
//stack for dirs to search
|
||||
$dirstack = [];
|
||||
//list of source files
|
||||
$filelist = [];
|
||||
|
||||
//loop over all files in $dir
|
||||
while ($dh = opendir($dir)) {
|
||||
while ($file = readdir($dh)) {
|
||||
if (is_dir($dir . "/" . $file)) {
|
||||
//add to directory stack
|
||||
if (strpos($file, '.') !== 0) {
|
||||
array_push($dirstack, $dir . "/" . $file);
|
||||
$this->out('dir ' . $dir . '/' . $file);
|
||||
}
|
||||
} else {
|
||||
//test if it is a source file and add to filelist
|
||||
if (substr($file, strlen($file) - 4) == ".php") {
|
||||
array_push($filelist, $dir . "/" . $file);
|
||||
$this->out($dir . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
//look at the next dir
|
||||
$dir = array_pop($dirstack);
|
||||
}
|
||||
|
||||
//check the entire set
|
||||
if ($this->runs($filelist)) {
|
||||
throw new \RuntimeException("I can not detect a problem.");
|
||||
}
|
||||
|
||||
//check half of the set and discard if that half is okay
|
||||
$res = $filelist;
|
||||
$i = count($res);
|
||||
do {
|
||||
$this->out($i . '/' . count($filelist) . ' elements remaining.');
|
||||
$res = $this->reduce($res, count($res) / 2);
|
||||
shuffle($res);
|
||||
$i = count($res);
|
||||
} while (count($res) < $i);
|
||||
|
||||
//check one file after another
|
||||
$needed = [];
|
||||
|
||||
while (count($res) != 0) {
|
||||
$file = array_pop($res);
|
||||
|
||||
if ($this->runs(array_merge($res, $needed))) {
|
||||
$this->out('needs: ' . $file . ' and file count ' . count($needed));
|
||||
array_push($needed, $file);
|
||||
}
|
||||
}
|
||||
|
||||
$this->out('Smallest Set is: ' . $this->namesList($needed) . ' with ' . count($needed) . ' files. ');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function commandExists($command)
|
||||
{
|
||||
$prefix = strpos(strtolower(PHP_OS),'win') > -1 ? 'where' : 'which';
|
||||
exec("{$prefix} {$command}", $output, $returnVal);
|
||||
return $returnVal === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function generates a comma separated list of file names.
|
||||
*
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset Set of file names
|
||||
*
|
||||
* @return string comma-separated list of the file names
|
||||
*/
|
||||
private function namesList($fileset)
|
||||
{
|
||||
return implode(',', $fileset);
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions runs phpdoc on the provided list of files
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset Set of filenames
|
||||
*
|
||||
* @return bool true, if that set can be built
|
||||
*/
|
||||
private function runs($fileset)
|
||||
{
|
||||
$fsParam = $this->namesList($fileset);
|
||||
$this->exec('docblox -t phpdoc_out -f ' . $fsParam);
|
||||
if (file_exists("phpdoc_out/index.html")) {
|
||||
$this->out('Subset ' . $fsParam . ' is okay.');
|
||||
$this->exec('rm -r phpdoc_out');
|
||||
return true;
|
||||
} else {
|
||||
$this->out('Subset ' . $fsParam . ' failed.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This functions cuts down a fileset by removing files until it finally works.
|
||||
* it was meant to be recursive, but php's maximum stack size is to small. So it just simulates recursion.
|
||||
*
|
||||
* In that version, it does not necessarily generate the smallest set, because it may not alter the elements order enough.
|
||||
*
|
||||
* @package util
|
||||
*
|
||||
* @param array $fileset set of filenames
|
||||
* @param int $ps number of files in subsets
|
||||
*
|
||||
* @return array a part of $fileset, that crashes
|
||||
*/
|
||||
private function reduce($fileset, $ps)
|
||||
{
|
||||
//split array...
|
||||
$parts = array_chunk($fileset, $ps);
|
||||
//filter working subsets...
|
||||
$parts = array_filter($parts, [$this, 'runs']);
|
||||
//melt remaining parts together
|
||||
if (is_array($parts)) {
|
||||
return array_reduce($parts, "array_merge", []);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
140
src/Core/Console/Extract.php
Normal file
140
src/Core/Console/Extract.php
Normal file
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Extracts translation strings from the Friendica project's files to be exported
|
||||
* to Transifex for translation.
|
||||
*
|
||||
* Outputs a PHP file with language strings used by Friendica
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Extract extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console extract - Generate translation string file for the Friendica project (deprecated)
|
||||
Usage
|
||||
bin/console extract [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
This script was used to generate the translation string file to be exported to Transifex,
|
||||
please use bin/run_xgettext.sh instead
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 0) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$s = '<?php' . PHP_EOL;
|
||||
$s .= '
|
||||
function string_plural_select($n){
|
||||
return ($n != 1);
|
||||
}
|
||||
|
||||
';
|
||||
|
||||
$arr = [];
|
||||
|
||||
$files = array_merge(
|
||||
['index.php', 'boot.php'],
|
||||
glob('mod/*'),
|
||||
glob('include/*'),
|
||||
glob('addon/*/*'),
|
||||
$this->globRecursive('src')
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$str = file_get_contents($file);
|
||||
|
||||
$pat = '|L10n::t\(([^\)]*+)[\)]|';
|
||||
$patt = '|L10n::tt\(([^\)]*+)[\)]|';
|
||||
|
||||
$matches = [];
|
||||
$matchestt = [];
|
||||
|
||||
preg_match_all($pat, $str, $matches);
|
||||
preg_match_all($patt, $str, $matchestt);
|
||||
|
||||
if (count($matches) || count($matchestt)) {
|
||||
$s .= '// ' . $file . PHP_EOL;
|
||||
}
|
||||
|
||||
if (!empty($matches[1])) {
|
||||
foreach ($matches[1] as $long_match) {
|
||||
$match_arr = preg_split('/(?<=[\'"])\s*,/', $long_match);
|
||||
$match = $match_arr[0];
|
||||
if (!in_array($match, $arr)) {
|
||||
if (substr($match, 0, 1) == '$') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arr[] = $match;
|
||||
|
||||
$s .= '$a->strings[' . $match . '] = ' . $match . ';' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($matchestt[1])) {
|
||||
foreach ($matchestt[1] as $match) {
|
||||
$matchtkns = preg_split("|[ \t\r\n]*,[ \t\r\n]*|", $match);
|
||||
if (count($matchtkns) == 3 && !in_array($matchtkns[0], $arr)) {
|
||||
if (substr($matchtkns[1], 0, 1) == '$') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arr[] = $matchtkns[0];
|
||||
|
||||
$s .= '$a->strings[' . $matchtkns[0] . "] = array(\n";
|
||||
$s .= "\t0 => " . $matchtkns[0] . ",\n";
|
||||
$s .= "\t1 => " . $matchtkns[1] . ",\n";
|
||||
$s .= ");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$s .= '// Timezones' . PHP_EOL;
|
||||
|
||||
$zones = timezone_identifiers_list();
|
||||
foreach ($zones as $zone) {
|
||||
$s .= '$a->strings[\'' . $zone . '\'] = \'' . $zone . '\';' . "\n";
|
||||
}
|
||||
|
||||
$this->out($s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function globRecursive($path) {
|
||||
$dir_iterator = new \RecursiveDirectoryIterator($path);
|
||||
$iterator = new \RecursiveIteratorIterator($dir_iterator, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
$return = [];
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->getBasename() != '.' && $file->getBasename() != '..') {
|
||||
$return[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
79
src/Core/Console/GlobalCommunityBlock.php
Normal file
79
src/Core/Console/GlobalCommunityBlock.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Model\Contact;
|
||||
|
||||
/**
|
||||
* @brief tool to block an account from the node
|
||||
*
|
||||
* With this tool, you can block an account in such a way, that no postings
|
||||
* or comments this account writes are accepted to the node.
|
||||
*
|
||||
* License: AGPLv3 or later, same as Friendica
|
||||
*
|
||||
* @author Tobias Diekershoff <mrpetovan@gmail.com>
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class GlobalCommunityBlock extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console globalcommunityblock - Block remote profile from interacting with this node
|
||||
Usage
|
||||
bin/console globalcommunityblock <profile_url> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Blocks an account in such a way that no postings or comments this account writes are accepted to this node.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
$contact_id = Contact::getIdForURL($this->getArgument(0));
|
||||
if (!$contact_id) {
|
||||
throw new \RuntimeException(L10n::t('Could not find any contact entry for this URL (%s)', $nurl));
|
||||
}
|
||||
if(Contact::block($contact_id)) {
|
||||
$this->out(L10n::t('The contact has been blocked from the node'));
|
||||
} else {
|
||||
throw new \RuntimeException('The contact block failed.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
96
src/Core/Console/GlobalCommunitySilence.php
Normal file
96
src/Core/Console/GlobalCommunitySilence.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Network\Probe;
|
||||
|
||||
require_once 'include/text.php';
|
||||
|
||||
/**
|
||||
* @brief tool to silence accounts on the global community page
|
||||
*
|
||||
* With this tool, you can silence an account on the global community page.
|
||||
* Postings from silenced accounts will not be displayed on the community
|
||||
* page. This silencing does only affect the display on the community page,
|
||||
* accounts following the silenced accounts will still get their postings.
|
||||
*
|
||||
* License: AGPLv3 or later, same as Friendica
|
||||
*
|
||||
* @author Tobias Diekershoff
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class GlobalCommunitySilence extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console globalcommunitysilence - Silence remote profile from global community page
|
||||
Usage
|
||||
bin/console globalcommunitysilence <profile_url> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
With this tool, you can silence an account on the global community page.
|
||||
Postings from silenced accounts will not be displayed on the community page.
|
||||
This silencing does only affect the display on the community page, accounts
|
||||
following the silenced accounts will still get their postings.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. make nurl from last parameter
|
||||
* 2. check DB (contact) if there is a contact with uid=0 and that nurl, get the ID
|
||||
* 3. set the flag hidden=1 for the contact entry with the found ID
|
||||
* */
|
||||
$net = Probe::uri($this->getArgument(0));
|
||||
if (in_array($net['network'], [Protocol::PHANTOM, Protocol::MAIL])) {
|
||||
throw new \RuntimeException('This account seems not to exist.');
|
||||
}
|
||||
|
||||
$nurl = normalise_link($net['url']);
|
||||
$contact = \dba::selectFirst("contact", ["id"], ["nurl" => $nurl, "uid" => 0]);
|
||||
if (DBM::is_result($contact)) {
|
||||
\dba::update("contact", ["hidden" => true], ["id" => $contact["id"]]);
|
||||
$this->out('NOTICE: The account should be silenced from the global community page');
|
||||
} else {
|
||||
throw new \RuntimeException('NOTICE: Could not find any entry for this URL (' . $nurl . ')');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
107
src/Core/Console/Maintenance.php
Normal file
107
src/Core/Console/Maintenance.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* @brief Sets maintenance mode for this node
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Maintenance extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console maintenance - Sets maintenance mode for this node
|
||||
Usage
|
||||
bin/console maintenance <enable> [<reason>] [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
<enable> cen be either 0 or 1 to disabled or enable the maintenance mode on this node.
|
||||
|
||||
<reason> is a quote-enclosed string with the optional reason for the maintenance mode.
|
||||
|
||||
Examples
|
||||
bin/console maintenance 1
|
||||
Enables the maintenance mode without setting a reason message
|
||||
|
||||
bin/console maintenance 1 "SSL certification update"
|
||||
Enables the maintenance mode with setting a reason message
|
||||
|
||||
bin/console maintenance 0
|
||||
Disables the maintenance mode
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 2) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
Core\Config::load();
|
||||
|
||||
$lang = Core\L10n::getBrowserLanguage();
|
||||
Core\L10n::loadTranslationTable($lang);
|
||||
|
||||
$enabled = intval($this->getArgument(0));
|
||||
|
||||
Core\Config::set('system', 'maintenance', $enabled);
|
||||
|
||||
$reason = $this->getArgument(1);
|
||||
|
||||
if ($enabled && $this->getArgument(1)) {
|
||||
Core\Config::set('system', 'maintenance_reason', $this->getArgument(1));
|
||||
} else {
|
||||
Core\Config::set('system', 'maintenance_reason', '');
|
||||
}
|
||||
|
||||
if ($enabled) {
|
||||
$mode_str = "maintenance mode";
|
||||
} else {
|
||||
$mode_str = "normal mode";
|
||||
}
|
||||
|
||||
$this->out('System set in ' . $mode_str);
|
||||
|
||||
if ($enabled && $reason != '') {
|
||||
$this->out('Maintenance reason: ' . $reason);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
89
src/Core/Console/NewPassword.php
Normal file
89
src/Core/Console/NewPassword.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Database\DBM;
|
||||
use dba;
|
||||
|
||||
/**
|
||||
* @brief tool to set a new password for a user
|
||||
*
|
||||
* With this tool, you can set a new password for a user
|
||||
*
|
||||
* License: AGPLv3 or later, same as Friendica
|
||||
*
|
||||
* @author Michael Vogel <heluecht@pirati.ca>
|
||||
*/
|
||||
class NewPassword extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console newpassword - Creates a new password for a given user
|
||||
Usage
|
||||
bin/console newpassword <nickname> <password> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Creates a new password for a user without using the "forgot password" functionality.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 2) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
require_once '.htconfig.php';
|
||||
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
|
||||
if (!$result) {
|
||||
throw new \RuntimeException('Unable to connect to database');
|
||||
}
|
||||
|
||||
$nick = $this->getArgument(0);
|
||||
$password = $this->getArgument(1);
|
||||
|
||||
$user = dba::selectFirst('user', ['uid'], ['nickname' => $nick]);
|
||||
if (!DBM::is_result($user)) {
|
||||
throw new \RuntimeException(L10n::t('User not found'));
|
||||
}
|
||||
|
||||
if (!Config::get('system', 'disable_password_exposed', false) && User::isPasswordExposed($password)) {
|
||||
throw new \RuntimeException(L10n::t('The new password has been exposed in a public data dump, please choose another.'));
|
||||
}
|
||||
|
||||
if (!User::updatePassword($user['uid'], $password)) {
|
||||
throw new \RuntimeException(L10n::t('Password update failed. Please try again.'));
|
||||
}
|
||||
|
||||
$this->out(L10n::t('Password changed.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
234
src/Core/Console/PhpToPo.php
Normal file
234
src/Core/Console/PhpToPo.php
Normal file
|
@ -0,0 +1,234 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Read a strings.php file and create messages.po in the same directory
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class PhpToPo extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
private $normBaseMsgIds = [];
|
||||
const NORM_REGEXP = "|[\\\]|";
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console php2po - Generate a messages.po file from a strings.php file
|
||||
Usage
|
||||
bin/console php2po [-p <n>] [--base <file>] <path/to/strings.php> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Read a strings.php file and create the according messages.po in the same directory
|
||||
|
||||
Options
|
||||
-p <n> Number of plural forms. Default: 2
|
||||
--base <file> Path to base messages.po file. Default: util/messages.po
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$phpfile = realpath($this->getArgument(0));
|
||||
|
||||
if (!file_exists($phpfile)) {
|
||||
throw new \RuntimeException('Supplied file path doesn\'t exist.');
|
||||
}
|
||||
|
||||
if (!is_writable(dirname($phpfile))) {
|
||||
throw new \RuntimeException('Supplied directory isn\'t writable.');
|
||||
}
|
||||
|
||||
$pofile = dirname($phpfile) . DIRECTORY_SEPARATOR . 'messages.po';
|
||||
|
||||
// start !
|
||||
include_once($phpfile);
|
||||
|
||||
$out = '';
|
||||
$out .= "# FRIENDICA Distributed Social Network\n";
|
||||
$out .= "# Copyright (C) 2010, 2011, 2012, 2013 the Friendica Project\n";
|
||||
$out .= "# This file is distributed under the same license as the Friendica package.\n";
|
||||
$out .= "# \n";
|
||||
$out .= 'msgid ""' . "\n";
|
||||
$out .= 'msgstr ""' . "\n";
|
||||
$out .= '"Project-Id-Version: friendica\n"' . "\n";
|
||||
$out .= '"Report-Msgid-Bugs-To: \n"' . "\n";
|
||||
$out .= '"POT-Creation-Date: ' . date("Y-m-d H:i:sO") . '\n"' . "\n";
|
||||
$out .= '"MIME-Version: 1.0\n"' . "\n";
|
||||
$out .= '"Content-Type: text/plain; charset=UTF-8\n"' . "\n";
|
||||
$out .= '"Content-Transfer-Encoding: 8bit\n"' . "\n";
|
||||
|
||||
// search for plural info
|
||||
$lang = "";
|
||||
$lang_logic = "";
|
||||
$lang_pnum = $this->getOption('p', 2);
|
||||
|
||||
$infile = file($phpfile);
|
||||
foreach ($infile as $l) {
|
||||
$l = trim($l);
|
||||
if ($this->startsWith($l, 'function string_plural_select_')) {
|
||||
$lang = str_replace('function string_plural_select_', '', str_replace('($n){', '', $l));
|
||||
}
|
||||
if ($this->startsWith($l, 'return')) {
|
||||
$lang_logic = str_replace('$', '', trim(str_replace('return ', '', $l), ';'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->out('Language: ' . $lang);
|
||||
$this->out('Plural forms: ' . $lang_pnum);
|
||||
$this->out('Plural forms: ' . $lang_logic);
|
||||
|
||||
$out .= sprintf('"Language: %s\n"', $lang) . "\n";
|
||||
$out .= sprintf('"Plural-Forms: nplurals=%s; plural=%s;\n"', $lang_pnum, $lang_logic) . "\n";
|
||||
$out .= "\n";
|
||||
|
||||
$base_path = $this->getOption('base', 'util' . DIRECTORY_SEPARATOR . 'messages.po');
|
||||
|
||||
// load base messages.po and extract msgids
|
||||
$base_msgids = [];
|
||||
$base_f = file($base_path);
|
||||
if (!$base_f) {
|
||||
throw new \RuntimeException('The base ' . $base_path . ' file is missing or unavailable to read.');
|
||||
}
|
||||
|
||||
$this->out('Loading base file ' . $base_path . '...');
|
||||
|
||||
$_f = 0;
|
||||
$_mid = "";
|
||||
$_mids = [];
|
||||
foreach ($base_f as $l) {
|
||||
$l = trim($l);
|
||||
|
||||
if ($this->startsWith($l, 'msgstr')) {
|
||||
if ($_mid != '""') {
|
||||
$base_msgids[$_mid] = $_mids;
|
||||
$this->normBaseMsgIds[preg_replace(self::NORM_REGEXP, "", $_mid)] = $_mid;
|
||||
}
|
||||
|
||||
$_f = 0;
|
||||
$_mid = "";
|
||||
$_mids = [];
|
||||
}
|
||||
|
||||
if ($this->startsWith($l, '"') && $_f == 2) {
|
||||
$_mids[count($_mids) - 1] .= "\n" . $l;
|
||||
}
|
||||
if ($this->startsWith($l, 'msgid_plural ')) {
|
||||
$_f = 2;
|
||||
$_mids[] = str_replace('msgid_plural ', '', $l);
|
||||
}
|
||||
|
||||
if ($this->startsWith($l, '"') && $_f == 1) {
|
||||
$_mid .= "\n" . $l;
|
||||
$_mids[count($_mids) - 1] .= "\n" . $l;
|
||||
}
|
||||
if ($this->startsWith($l, 'msgid ')) {
|
||||
$_f = 1;
|
||||
$_mid = str_replace('msgid ', '', $l);
|
||||
$_mids = [$_mid];
|
||||
}
|
||||
}
|
||||
|
||||
$this->out('Creating ' . $pofile . '...');
|
||||
|
||||
// create msgid and msgstr
|
||||
$warnings = "";
|
||||
foreach ($a->strings as $key => $str) {
|
||||
$msgid = $this->massageString($key);
|
||||
|
||||
if (preg_match("|%[sd0-9](\$[sn])*|", $msgid)) {
|
||||
$out .= "#, php-format\n";
|
||||
}
|
||||
$msgid = $this->findOriginalMsgId($msgid);
|
||||
$out .= 'msgid ' . $msgid . "\n";
|
||||
|
||||
if (is_array($str)) {
|
||||
if (array_key_exists($msgid, $base_msgids) && isset($base_msgids[$msgid][1])) {
|
||||
$out .= 'msgid_plural ' . $base_msgids[$msgid][1] . "\n";
|
||||
} else {
|
||||
$out .= 'msgid_plural ' . $msgid . "\n";
|
||||
$warnings .= "[W] No source plural form for msgid:\n" . str_replace("\n", "\n\t", $msgid) . "\n\n";
|
||||
}
|
||||
foreach ($str as $n => $msgstr) {
|
||||
$out .= 'msgstr[' . $n . '] ' . $this->massageString($msgstr) . "\n";
|
||||
}
|
||||
} else {
|
||||
$out .= 'msgstr ' . $this->massageString($str) . "\n";
|
||||
}
|
||||
|
||||
$out .= "\n";
|
||||
}
|
||||
|
||||
if (!file_put_contents($pofile, $out)) {
|
||||
throw new \RuntimeException('Unable to write to ' . $pofile);
|
||||
}
|
||||
|
||||
if ($warnings != '') {
|
||||
$this->out($warnings);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function startsWith($haystack, $needle)
|
||||
{
|
||||
// search backwards starting from haystack length characters from the end
|
||||
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string and retun a message.po ready text
|
||||
* - replace " with \"
|
||||
* - replace tab char with \t
|
||||
* - manage multiline strings
|
||||
*/
|
||||
private function massageString($str)
|
||||
{
|
||||
$str = str_replace('\\', '\\\\', $str);
|
||||
$str = str_replace('"', '\"', $str);
|
||||
$str = str_replace("\t", '\t', $str);
|
||||
$str = str_replace("\n", '\n"' . "\n" . '"', $str);
|
||||
if (strpos($str, "\n") !== false && $str[0] !== '"') {
|
||||
$str = '"' . "\n" . $str;
|
||||
}
|
||||
|
||||
$str = preg_replace("|\n([^\"])|", "\n\"$1", $str);
|
||||
return sprintf('"%s"', $str);
|
||||
}
|
||||
|
||||
private function findOriginalMsgId($str)
|
||||
{
|
||||
$norm_str = preg_replace(self::NORM_REGEXP, "", $str);
|
||||
if (array_key_exists($norm_str, $this->normBaseMsgIds)) {
|
||||
return $this->normBaseMsgIds[$norm_str];
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
201
src/Core/Console/PoToPhp.php
Normal file
201
src/Core/Console/PoToPhp.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Read a messages.po file and create strings.php in the same directory
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class PoToPhp extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
const DQ_ESCAPE = "__DQ__";
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console php2po - Generate a strings.php file from a messages.po file
|
||||
Usage
|
||||
bin/console php2po <path/to/messages.po> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Read a messages.po file and create the according strings.php in the same directory
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$pofile = realpath($this->getArgument(0));
|
||||
|
||||
if (!file_exists($pofile)) {
|
||||
throw new \RuntimeException('Supplied file path doesn\'t exist.');
|
||||
}
|
||||
|
||||
if (!is_writable(dirname($pofile))) {
|
||||
throw new \RuntimeException('Supplied directory isn\'t writable.');
|
||||
}
|
||||
|
||||
$outfile = dirname($pofile) . DIRECTORY_SEPARATOR . 'strings.php';
|
||||
|
||||
if (strstr($outfile, 'util')) {
|
||||
$lang = 'en';
|
||||
} else {
|
||||
$lang = str_replace('-', '_', basename(dirname($pofile)));
|
||||
}
|
||||
|
||||
$this->out('Out to ' . $outfile);
|
||||
|
||||
$out = "<?php\n\n";
|
||||
|
||||
$infile = file($pofile);
|
||||
$k = '';
|
||||
$v = '';
|
||||
$arr = false;
|
||||
$ink = false;
|
||||
$inv = false;
|
||||
$escape_s_exp = '|[^\\\\]\$[a-z]|';
|
||||
|
||||
foreach ($infile as $l) {
|
||||
$l = str_replace('\"', self::DQ_ESCAPE, $l);
|
||||
$len = strlen($l);
|
||||
if ($l[0] == "#") {
|
||||
$l = "";
|
||||
}
|
||||
|
||||
if (substr($l, 0, 15) == '"Plural-Forms: ') {
|
||||
$match = [];
|
||||
preg_match("|nplurals=([0-9]*); *plural=(.*)[;\\\\]|", $l, $match);
|
||||
$cond = str_replace('n', '$n', $match[2]);
|
||||
// define plural select function if not already defined
|
||||
$fnname = 'string_plural_select_' . $lang;
|
||||
$out .= 'if(! function_exists("' . $fnname . '")) {' . "\n";
|
||||
$out .= 'function ' . $fnname . '($n){' . "\n";
|
||||
$out .= ' return ' . $cond . ';' . "\n";
|
||||
$out .= '}}' . "\n";
|
||||
}
|
||||
|
||||
if ($k != '' && substr($l, 0, 7) == 'msgstr ') {
|
||||
if ($ink) {
|
||||
$ink = false;
|
||||
$out .= '$a->strings["' . $k . '"] = ';
|
||||
}
|
||||
|
||||
if ($inv) {
|
||||
$inv = false;
|
||||
$out .= '"' . $v . '"';
|
||||
}
|
||||
|
||||
$v = substr($l, 8, $len - 10);
|
||||
$v = preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $v);
|
||||
|
||||
$inv = true;
|
||||
}
|
||||
|
||||
if ($k != "" && substr($l, 0, 7) == 'msgstr[') {
|
||||
if ($ink) {
|
||||
$ink = false;
|
||||
$out .= '$a->strings["' . $k . '"] = ';
|
||||
}
|
||||
if ($inv) {
|
||||
$inv = false;
|
||||
$out .= '"' . $v . '"';
|
||||
}
|
||||
|
||||
if (!$arr) {
|
||||
$arr = true;
|
||||
$out .= "[\n";
|
||||
}
|
||||
|
||||
$match = [];
|
||||
preg_match("|\[([0-9]*)\] (.*)|", $l, $match);
|
||||
$out .= "\t"
|
||||
. preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $match[1])
|
||||
. ' => '
|
||||
. preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $match[2])
|
||||
. ",\n";
|
||||
}
|
||||
|
||||
if (substr($l, 0, 6) == 'msgid_') {
|
||||
$ink = false;
|
||||
$out .= '$a->strings["' . $k . '"] = ';
|
||||
}
|
||||
|
||||
if ($ink) {
|
||||
$k .= trim($l, "\"\r\n");
|
||||
$k = preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $k);
|
||||
}
|
||||
|
||||
if (substr($l, 0, 6) == 'msgid ') {
|
||||
if ($inv) {
|
||||
$inv = false;
|
||||
$out .= '"' . $v . '"';
|
||||
}
|
||||
|
||||
if ($k != "") {
|
||||
$out .= ($arr) ? "];\n" : ";\n";
|
||||
}
|
||||
|
||||
$arr = false;
|
||||
$k = str_replace("msgid ", "", $l);
|
||||
if ($k != '""') {
|
||||
$k = trim($k, "\"\r\n");
|
||||
} else {
|
||||
$k = '';
|
||||
}
|
||||
|
||||
$k = preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $k);
|
||||
$ink = true;
|
||||
}
|
||||
|
||||
if ($inv && substr($l, 0, 6) != "msgstr") {
|
||||
$v .= trim($l, "\"\r\n");
|
||||
$v = preg_replace_callback($escape_s_exp, [$this, 'escapeDollar'], $v);
|
||||
}
|
||||
}
|
||||
|
||||
if ($inv) {
|
||||
$inv = false;
|
||||
$out .= '"' . $v . '"';
|
||||
}
|
||||
|
||||
if ($k != '') {
|
||||
$out .= ($arr ? "];\n" : ";\n");
|
||||
}
|
||||
|
||||
$out = str_replace(self::DQ_ESCAPE, '\"', $out);
|
||||
if (!file_put_contents($outfile, $out)) {
|
||||
throw new \RuntimeException('Unable to write to ' . $outfile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function escapeDollar($match)
|
||||
{
|
||||
return str_replace('$', '\$', $match[0]);
|
||||
}
|
||||
}
|
119
src/Core/Console/Typo.php
Normal file
119
src/Core/Console/Typo.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Core\Console;
|
||||
|
||||
/**
|
||||
* Tired of chasing typos and finding them after a commit.
|
||||
* Run this and quickly see if we've got any parse errors in our application files.
|
||||
*
|
||||
* @author Hypolite Petovan <mrpetovan@gmail.com>
|
||||
*/
|
||||
class Typo extends \Asika\SimpleConsole\Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console typo - Checks for parse errors in Friendica files
|
||||
Usage
|
||||
bin/console typo [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Checks all PHP files in the Friendica file tree for parse errors
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Class: ' . __CLASS__);
|
||||
$this->out('Arguments: ' . var_export($this->args, true));
|
||||
$this->out('Options: ' . var_export($this->options, true));
|
||||
}
|
||||
|
||||
if (count($this->args) > 0) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$php_path = $a->getConfigValue('config', 'php_path', 'php');
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Directory: src');
|
||||
}
|
||||
|
||||
$Iterator = new \RecursiveDirectoryIterator('src');
|
||||
|
||||
foreach (new \RecursiveIteratorIterator($Iterator) as $file) {
|
||||
if (substr($file, -4) === '.php') {
|
||||
$this->checkFile($php_path, $file);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Directory: mod');
|
||||
}
|
||||
|
||||
$files = glob('mod/*.php');
|
||||
$this->checkFiles($php_path, $files);
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Directory: include');
|
||||
}
|
||||
|
||||
$files = glob('include/*.php');
|
||||
$this->checkFiles($php_path, $files);
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Directory: addon');
|
||||
}
|
||||
|
||||
$dirs = glob('addon/*');
|
||||
foreach ($dirs as $dir) {
|
||||
$addon = basename($dir);
|
||||
$files = glob($dir . '/' . $addon . '.php');
|
||||
$this->checkFiles($php_path, $files);
|
||||
}
|
||||
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('String files');
|
||||
}
|
||||
|
||||
$this->checkFile($php_path, 'util/strings.php');
|
||||
|
||||
$files = glob('view/lang/*/strings.php');
|
||||
$this->checkFiles($php_path, $files);
|
||||
|
||||
$this->out('No errors.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function checkFiles($php_path, array $files)
|
||||
{
|
||||
foreach ($files as $file) {
|
||||
$this->checkFile($php_path, $file);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkFile($php_path, $file)
|
||||
{
|
||||
if ($this->getOption('v')) {
|
||||
$this->out('Checking ' . $file);
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$ret = 0;
|
||||
exec("$php_path -l $file", $output, $ret);
|
||||
if ($ret !== 0) {
|
||||
throw new \RuntimeException('Parse error found in ' . $file . ', scan stopped.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ namespace Friendica\Core;
|
|||
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\PConfig;
|
||||
use Friendica\Core\System;
|
||||
|
@ -19,7 +20,6 @@ use Friendica\Util\Temporal;
|
|||
use Friendica\Util\XML;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2plain.php';
|
||||
|
||||
/**
|
||||
* @brief Methods for read and write notifications from/to database
|
||||
|
@ -47,7 +47,7 @@ class NotificationsManager extends BaseObject
|
|||
$n['timestamp'] = strtotime($local_time);
|
||||
$n['date_rel'] = Temporal::getRelativeDate($n['date']);
|
||||
$n['msg_html'] = BBCode::convert($n['msg'], false);
|
||||
$n['msg_plain'] = explode("\n", trim(html2plain($n['msg_html'], 0)))[0];
|
||||
$n['msg_plain'] = explode("\n", trim(HTML::toPlaintext($n['msg_html'], 0)))[0];
|
||||
|
||||
$rets[] = $n;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ class NotificationsManager extends BaseObject
|
|||
public function setSeen($note, $seen = true)
|
||||
{
|
||||
return q(
|
||||
"UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d",
|
||||
"UPDATE `notify` SET `seen` = %d WHERE (`link` = '%s' OR (`parent` != 0 AND `parent` = %d AND `otype` = '%s')) AND `uid` = %d",
|
||||
intval($seen),
|
||||
dbesc($note['link']),
|
||||
intval($note['parent']),
|
||||
|
@ -384,16 +384,18 @@ class NotificationsManager extends BaseObject
|
|||
private function networkTotal($seen = 0)
|
||||
{
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT COUNT(*) AS `total`
|
||||
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
|
||||
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
|
||||
FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` AND `pitem`.`parent` != 0 AND
|
||||
NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
|
||||
$sql_seen",
|
||||
intval(local_user())
|
||||
);
|
||||
|
@ -423,18 +425,20 @@ class NotificationsManager extends BaseObject
|
|||
$total = $this->networkTotal($seen);
|
||||
$notifs = [];
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
|
||||
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
|
||||
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
|
||||
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND
|
||||
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
|
||||
FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` AND `pitem`.`parent` != 0 AND
|
||||
NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
|
||||
$sql_seen
|
||||
ORDER BY `item`.`created` DESC LIMIT %d, %d ",
|
||||
intval(local_user()),
|
||||
|
@ -466,7 +470,7 @@ class NotificationsManager extends BaseObject
|
|||
$sql_seen = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `seen` = 0 ";
|
||||
$sql_seen = " AND NOT `seen` ";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
|
@ -501,7 +505,7 @@ class NotificationsManager extends BaseObject
|
|||
$sql_seen = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `seen` = 0 ";
|
||||
$sql_seen = " AND NOT `seen` ";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
|
@ -536,7 +540,7 @@ class NotificationsManager extends BaseObject
|
|||
$myurl = str_replace(['www.', '.'], ['', '\\.'], $myurl);
|
||||
$diasp_url = str_replace('/profile/', '/u/', $myurl);
|
||||
$sql_extra = sprintf(
|
||||
" AND ( `item`.`author-link` regexp '%s' OR `item`.`tag` regexp '%s' OR `item`.`tag` regexp '%s' ) ",
|
||||
" AND (`item`.`author-link` REGEXP '%s' OR `item`.`tag` REGEXP '%s' OR `item`.`tag` REGEXP '%s') ",
|
||||
dbesc($myurl . '$'),
|
||||
dbesc($myurl . '\\]'),
|
||||
dbesc($diasp_url . '\\]')
|
||||
|
@ -555,19 +559,21 @@ class NotificationsManager extends BaseObject
|
|||
private function personalTotal($seen = 0)
|
||||
{
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
$sql_extra = $this->personalSqlExtra();
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT COUNT(*) AS `total`
|
||||
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` = 1
|
||||
FROM `item` $index_hint
|
||||
WHERE `item`.`visible`
|
||||
$sql_extra
|
||||
$sql_seen
|
||||
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 ",
|
||||
AND NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`",
|
||||
intval(local_user())
|
||||
);
|
||||
if (DBM::is_result($r)) {
|
||||
|
@ -597,20 +603,22 @@ class NotificationsManager extends BaseObject
|
|||
$sql_extra = $this->personalSqlExtra();
|
||||
$notifs = [];
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
|
||||
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
|
||||
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
|
||||
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` = 1
|
||||
FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible`
|
||||
$sql_extra
|
||||
$sql_seen
|
||||
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
|
||||
AND NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
|
||||
ORDER BY `item`.`created` DESC LIMIT %d, %d ",
|
||||
intval(local_user()),
|
||||
intval($start),
|
||||
|
@ -639,13 +647,15 @@ class NotificationsManager extends BaseObject
|
|||
private function homeTotal($seen = 0)
|
||||
{
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT COUNT(*) AS `total` FROM `item`
|
||||
"SELECT COUNT(*) AS `total` FROM `item` $index_hint
|
||||
WHERE `item`.`visible` = 1 AND
|
||||
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
|
||||
$sql_seen",
|
||||
|
@ -677,18 +687,20 @@ class NotificationsManager extends BaseObject
|
|||
$total = $this->homeTotal($seen);
|
||||
$notifs = [];
|
||||
$sql_seen = "";
|
||||
$index_hint = "";
|
||||
|
||||
if ($seen === 0) {
|
||||
$sql_seen = " AND `item`.`unseen` = 1 ";
|
||||
$sql_seen = " AND `item`.`unseen` ";
|
||||
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
|
||||
}
|
||||
|
||||
$r = q(
|
||||
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
|
||||
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
|
||||
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
|
||||
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` = 1 AND
|
||||
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
|
||||
FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
|
||||
WHERE `item`.`visible` AND
|
||||
NOT `item`.`deleted` AND `item`.`uid` = %d AND `item`.`wall`
|
||||
$sql_seen
|
||||
ORDER BY `item`.`created` DESC LIMIT %d, %d ",
|
||||
intval(local_user()),
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* @file src/Core/PConfig.php
|
||||
* User Configuration Class
|
||||
*
|
||||
* @file include/Core/PConfig.php
|
||||
*
|
||||
* @brief Contains the class with methods for user configuration
|
||||
*/
|
||||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\Database\DBM;
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Core\Config;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
|
||||
/**
|
||||
* @file include/Core/PConfig.php
|
||||
* @brief contains the class with methods for the management
|
||||
* of the user configuration
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Management of user configuration storage
|
||||
* Note:
|
||||
|
@ -22,9 +20,23 @@ require_once 'include/dba.php';
|
|||
* The PConfig::get() functions return boolean false for keys that are unset,
|
||||
* and this could lead to subtle bugs.
|
||||
*/
|
||||
class PConfig
|
||||
class PConfig extends BaseObject
|
||||
{
|
||||
private static $in_db;
|
||||
/**
|
||||
* @var Friendica\Core\Config\IPConfigAdapter
|
||||
*/
|
||||
private static $adapter = null;
|
||||
|
||||
public static function init($uid)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
if (isset($a->config['system']['config_adapter']) && $a->config['system']['config_adapter'] == 'preload') {
|
||||
self::$adapter = new Config\PreloadPConfigAdapter($uid);
|
||||
} else {
|
||||
self::$adapter = new Config\JITPConfigAdapter($uid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Loads all configuration values of a user's config family into a cached storage.
|
||||
|
@ -39,20 +51,11 @@ class PConfig
|
|||
*/
|
||||
public static function load($uid, $family)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
$r = dba::select('pconfig', ['v', 'k'], ['cat' => $family, 'uid' => $uid]);
|
||||
if (DBM::is_result($r)) {
|
||||
while ($rr = dba::fetch($r)) {
|
||||
$k = $rr['k'];
|
||||
$a->config[$uid][$family][$k] = $rr['v'];
|
||||
self::$in_db[$uid][$family][$k] = true;
|
||||
}
|
||||
} else if ($family != 'config') {
|
||||
// Negative caching
|
||||
$a->config[$uid][$family] = "!<unset>!";
|
||||
if (empty(self::$adapter)) {
|
||||
self::init($uid);
|
||||
}
|
||||
dba::close($r);
|
||||
|
||||
self::$adapter->load($uid, $family);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,37 +75,11 @@ class PConfig
|
|||
*/
|
||||
public static function get($uid, $family, $key, $default_value = null, $refresh = false)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if (!$refresh) {
|
||||
// Looking if the whole family isn't set
|
||||
if (isset($a->config[$uid][$family])) {
|
||||
if ($a->config[$uid][$family] === '!<unset>!') {
|
||||
return $default_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($a->config[$uid][$family][$key])) {
|
||||
if ($a->config[$uid][$family][$key] === '!<unset>!') {
|
||||
return $default_value;
|
||||
}
|
||||
return $a->config[$uid][$family][$key];
|
||||
}
|
||||
if (empty(self::$adapter)) {
|
||||
self::init($uid);
|
||||
}
|
||||
|
||||
$pconfig = dba::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $family, 'k' => $key]);
|
||||
if (DBM::is_result($pconfig)) {
|
||||
$val = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']);
|
||||
$a->config[$uid][$family][$key] = $val;
|
||||
self::$in_db[$uid][$family][$key] = true;
|
||||
|
||||
return $val;
|
||||
} else {
|
||||
$a->config[$uid][$family][$key] = '!<unset>!';
|
||||
self::$in_db[$uid][$family][$key] = false;
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
return self::$adapter->get($uid, $family, $key, $default_value, $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,31 +99,11 @@ class PConfig
|
|||
*/
|
||||
public static function set($uid, $family, $key, $value)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
// We store our setting values in a string variable.
|
||||
// So we have to do the conversion here so that the compare below works.
|
||||
// The exception are array values.
|
||||
$dbvalue = (!is_array($value) ? (string)$value : $value);
|
||||
|
||||
$stored = self::get($uid, $family, $key, null, true);
|
||||
|
||||
if (($stored === $dbvalue) && self::$in_db[$uid][$family][$key]) {
|
||||
return true;
|
||||
if (empty(self::$adapter)) {
|
||||
self::init($uid);
|
||||
}
|
||||
|
||||
$a->config[$uid][$family][$key] = $dbvalue;
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value) ? serialize($value) : $dbvalue);
|
||||
|
||||
$ret = dba::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $family, 'k' => $key], true);
|
||||
|
||||
if ($ret) {
|
||||
self::$in_db[$uid][$family][$key] = true;
|
||||
return $value;
|
||||
}
|
||||
return $ret;
|
||||
return self::$adapter->set($uid, $family, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,15 +120,10 @@ class PConfig
|
|||
*/
|
||||
public static function delete($uid, $family, $key)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if (x($a->config[$uid][$family], $key)) {
|
||||
unset($a->config[$uid][$family][$key]);
|
||||
unset(self::$in_db[$uid][$family][$key]);
|
||||
if (empty(self::$adapter)) {
|
||||
self::init($uid);
|
||||
}
|
||||
|
||||
$ret = dba::delete('pconfig', ['uid' => $uid, 'cat' => $family, 'k' => $key]);
|
||||
|
||||
return $ret;
|
||||
return self::$adapter->delete($uid, $family, $key);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,6 @@ class Protocol
|
|||
*/
|
||||
public static function formatMention($profile_url, $display_name)
|
||||
{
|
||||
return $display_name . '(' . self::getAddrFromProfileUrl($profile_url) . ')';
|
||||
return $display_name . ' (' . self::getAddrFromProfileUrl($profile_url) . ')';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,12 +60,12 @@ class System extends BaseObject
|
|||
$previous = ['class' => '', 'function' => ''];
|
||||
|
||||
// The ignore list contains all functions that are only wrapper functions
|
||||
$ignore = ['fetchUrl'];
|
||||
$ignore = ['fetchUrl', 'call_user_func_array'];
|
||||
|
||||
while ($func = array_pop($trace)) {
|
||||
if (!empty($func['class'])) {
|
||||
// Don't show multiple calls from the same function (mostly used for "dba" class)
|
||||
if (($previous['class'] != $func['class']) && ($previous['function'] != 'q')) {
|
||||
// Don't show multiple calls from the "dba" class to show the essential parts of the callstack
|
||||
if ((($previous['class'] != $func['class']) || ($func['class'] != 'dba')) && ($previous['function'] != 'q')) {
|
||||
$classparts = explode("\\", $func['class']);
|
||||
$callstack[] = array_pop($classparts).'::'.$func['function'];
|
||||
$previous = $func;
|
||||
|
|
|
@ -201,7 +201,7 @@ class Worker
|
|||
$mypid = getmypid();
|
||||
|
||||
// Quit when in maintenance
|
||||
if (Config::get('system', 'maintenance', true)) {
|
||||
if (Config::get('system', 'maintenance', false, true)) {
|
||||
logger("Maintenance mode - quit process ".$mypid, LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
@ -1003,7 +1003,7 @@ class Worker
|
|||
*/
|
||||
public static function spawnWorker()
|
||||
{
|
||||
$args = ["scripts/worker.php", "no_cron"];
|
||||
$args = ["bin/worker.php", "no_cron"];
|
||||
get_app()->proc_run($args);
|
||||
}
|
||||
|
||||
|
|
|
@ -199,12 +199,13 @@ class DBStructure
|
|||
*
|
||||
* @param bool $verbose
|
||||
* @param bool $action Whether to actually apply the update
|
||||
* @param bool $install Is this the initial update during the installation?
|
||||
* @param array $tables An array of the database tables
|
||||
* @param array $definition An array of the definition tables
|
||||
* @return string Empty string if the update is successful, error messages otherwise
|
||||
*/
|
||||
public static function update($verbose, $action, array $tables = null, array $definition = null) {
|
||||
if ($action) {
|
||||
public static function update($verbose, $action, $install = false, array $tables = null, array $definition = null) {
|
||||
if ($action && !$install) {
|
||||
Config::set('system', 'maintenance', 1);
|
||||
Config::set('system', 'maintenance_reason', L10n::t(': Database update', DBM::date().' '.date('e')));
|
||||
}
|
||||
|
@ -455,7 +456,9 @@ class DBStructure
|
|||
}
|
||||
|
||||
if ($action) {
|
||||
Config::set('system', 'maintenance_reason', L10n::t('%s: updating %s table.', DBM::date().' '.date('e'), $name));
|
||||
if (!$install) {
|
||||
Config::set('system', 'maintenance_reason', L10n::t('%s: updating %s table.', DBM::date().' '.date('e'), $name));
|
||||
}
|
||||
|
||||
// Ensure index conversion to unique removes duplicates
|
||||
if ($is_unique && ($temp_name != $name)) {
|
||||
|
@ -505,15 +508,15 @@ class DBStructure
|
|||
}
|
||||
}
|
||||
|
||||
if ($action) {
|
||||
if ($action && !$install) {
|
||||
Config::set('system', 'maintenance', 0);
|
||||
Config::set('system', 'maintenance_reason', '');
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
Config::set('system', 'dbupdate', DB_UPDATE_FAILED);
|
||||
} else {
|
||||
Config::set('system', 'dbupdate', DB_UPDATE_SUCCESSFUL);
|
||||
if ($errors) {
|
||||
Config::set('system', 'dbupdate', DB_UPDATE_FAILED);
|
||||
} else {
|
||||
Config::set('system', 'dbupdate', DB_UPDATE_SUCCESSFUL);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
@ -785,9 +788,9 @@ class DBStructure
|
|||
"xmpp" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"attag" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"photo" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"thumb" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"micro" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"photo" => ["type" => "varchar(255)", "default" => "", "comment" => ""],
|
||||
"thumb" => ["type" => "varchar(255)", "default" => "", "comment" => ""],
|
||||
"micro" => ["type" => "varchar(255)", "default" => "", "comment" => ""],
|
||||
"site-pubkey" => ["type" => "text", "comment" => ""],
|
||||
"issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
|
@ -1071,6 +1074,8 @@ class DBStructure
|
|||
"noscrape" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"platform" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"relay-subscribe" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Has the server subscribed to the relay system"],
|
||||
"relay-scope" => ["type" => "varchar(10)", "not null" => "1", "default" => "", "comment" => "The scope of messages that the server wants to get"],
|
||||
"created" => ["type" => "datetime", "not null" => "1", "default" => NULL_DATE, "comment" => ""],
|
||||
"last_poco_query" => ["type" => "datetime", "default" => NULL_DATE, "comment" => ""],
|
||||
"last_contact" => ["type" => "datetime", "default" => NULL_DATE, "comment" => ""],
|
||||
|
@ -1081,6 +1086,17 @@ class DBStructure
|
|||
"nurl" => ["UNIQUE", "nurl(190)"],
|
||||
]
|
||||
];
|
||||
$database["gserver-tag"] = [
|
||||
"comment" => "Tags that the server has subscribed",
|
||||
"fields" => [
|
||||
"gserver-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["gserver" => "id"], "primary" => "1", "comment" => "The id of the gserver"],
|
||||
"tag" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "primary" => "1", "comment" => "Tag that the server has subscribed"],
|
||||
],
|
||||
"indexes" => [
|
||||
"PRIMARY" => ["gserver-id", "tag"],
|
||||
"tag" => ["tag"],
|
||||
]
|
||||
];
|
||||
$database["hook"] = [
|
||||
"comment" => "addon hook registry",
|
||||
"fields" => [
|
||||
|
@ -1143,6 +1159,7 @@ class DBStructure
|
|||
"author-link" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"author-avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"title" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"content-warning" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"body" => ["type" => "mediumtext", "comment" => ""],
|
||||
"app" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
"verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => ""],
|
||||
|
|
|
@ -22,6 +22,7 @@ use Friendica\Protocol\PortableContact;
|
|||
use Friendica\Protocol\Salmon;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Object\Image;
|
||||
use dba;
|
||||
|
||||
require_once 'boot.php';
|
||||
|
@ -138,6 +139,95 @@ class Contact extends BaseObject
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the self-contact for the provided user id
|
||||
*
|
||||
* @param int $uid
|
||||
* @param boolean $update_avatar Force the avatar update
|
||||
*/
|
||||
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
||||
{
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'gender', 'avatar',
|
||||
'xmpp', 'contact-type', 'forum', 'prv', 'avatar-date', 'nurl'];
|
||||
$self = dba::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||
if (!DBM::is_result($self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = ['nickname', 'page-flags', 'account-type'];
|
||||
$user = dba::selectFirst('user', $fields, ['uid' => $uid]);
|
||||
if (!DBM::is_result($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region',
|
||||
'country-name', 'gender', 'pub_keywords', 'xmpp'];
|
||||
$profile = dba::selectFirst('profile', $fields, ['uid' => $uid, 'is-default' => true]);
|
||||
if (!DBM::is_result($profile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = ['name' => $profile['name'], 'nick' => $user['nickname'],
|
||||
'avatar-date' => $self['avatar-date'], 'location' => Profile::formatLocation($profile),
|
||||
'about' => $profile['about'], 'keywords' => $profile['pub_keywords'],
|
||||
'gender' => $profile['gender'], 'avatar' => $profile['photo'],
|
||||
'contact-type' => $user['account-type'], 'xmpp' => $profile['xmpp']];
|
||||
|
||||
$avatar = dba::selectFirst('photo', ['resource-id', 'type'], ['uid' => $uid, 'profile' => true]);
|
||||
if (DBM::is_result($avatar)) {
|
||||
if ($update_avatar) {
|
||||
$fields['avatar-date'] = DateTimeFormat::utcNow();
|
||||
}
|
||||
|
||||
// Creating the path to the avatar, beginning with the file suffix
|
||||
$types = Image::supportedTypes();
|
||||
if (isset($types[$avatar['type']])) {
|
||||
$file_suffix = $types[$avatar['type']];
|
||||
} else {
|
||||
$file_suffix = 'jpg';
|
||||
}
|
||||
|
||||
// We are adding a timestamp value so that other systems won't use cached content
|
||||
$timestamp = strtotime($fields['avatar-date']);
|
||||
|
||||
$prefix = System::baseUrl() . '/photo/' .$avatar['resource-id'] . '-';
|
||||
$suffix = '.' . $file_suffix . '?ts=' . $timestamp;
|
||||
|
||||
$fields['photo'] = $prefix . '4' . $suffix;
|
||||
$fields['thumb'] = $prefix . '5' . $suffix;
|
||||
$fields['micro'] = $prefix . '6' . $suffix;
|
||||
} else {
|
||||
// We hadn't found a photo entry, so we use the default avatar
|
||||
$fields['photo'] = System::baseUrl() . '/images/person-175.jpg';
|
||||
$fields['thumb'] = System::baseUrl() . '/images/person-80.jpg';
|
||||
$fields['micro'] = System::baseUrl() . '/images/person-48.jpg';
|
||||
}
|
||||
|
||||
$fields['forum'] = $user['page-flags'] == PAGE_COMMUNITY;
|
||||
$fields['prv'] = $user['page-flags'] == PAGE_PRVGROUP;
|
||||
|
||||
$update = false;
|
||||
|
||||
foreach ($fields as $field => $content) {
|
||||
if ($self[$field] != $content) {
|
||||
$update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
$fields['name-date'] = DateTimeFormat::utcNow();
|
||||
dba::update('contact', $fields, ['id' => $self['id']]);
|
||||
|
||||
// Update the public contact as well
|
||||
dba::update('contact', $fields, ['uid' => 0, 'nurl' => $self['nurl']]);
|
||||
|
||||
// Update the profile
|
||||
$fields = ['photo' => System::baseUrl() . '/photo/profile/' .$uid . '.jpg',
|
||||
'thumb' => System::baseUrl() . '/photo/avatar/' . $uid .'.jpg'];
|
||||
dba::update('profile', $fields, ['uid' => $uid, 'is-default' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Marks a contact for removal
|
||||
*
|
||||
|
@ -173,20 +263,18 @@ class Contact extends BaseObject
|
|||
*/
|
||||
public static function terminateFriendship(array $user, array $contact)
|
||||
{
|
||||
if ($contact['network'] === NETWORK_OSTATUS) {
|
||||
if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DFRN])) {
|
||||
// create an unfollow slap
|
||||
$item = [];
|
||||
$item['verb'] = NAMESPACE_OSTATUS . "/unfollow";
|
||||
$item['follow'] = $contact["url"];
|
||||
$slap = OStatus::salmon($item, $user);
|
||||
|
||||
if ((x($contact, 'notify')) && (strlen($contact['notify']))) {
|
||||
if (!empty($contact['notify'])) {
|
||||
Salmon::slapper($user, $contact['notify'], $slap);
|
||||
}
|
||||
} elseif ($contact['network'] === NETWORK_DIASPORA) {
|
||||
} elseif ($contact['network'] == NETWORK_DIASPORA) {
|
||||
Diaspora::sendUnshare($user, $contact);
|
||||
} elseif ($contact['network'] === NETWORK_DFRN) {
|
||||
DFRN::deliver($user, $contact, 'placeholder', 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +311,9 @@ class Contact extends BaseObject
|
|||
*/
|
||||
|
||||
/// @todo Check for contact vitality via probing
|
||||
$expiry = $contact['term-date'] . ' + 32 days ';
|
||||
$archival_days = Config::get('system', 'archival_days', 32);
|
||||
|
||||
$expiry = $contact['term-date'] . ' + ' . $archival_days . ' days ';
|
||||
if (DateTimeFormat::utcNow() > DateTimeFormat::utc($expiry)) {
|
||||
/* Relationship is really truly dead. archive them rather than
|
||||
* delete, though if the owner tries to unarchive them we'll start
|
||||
|
@ -260,9 +350,14 @@ class Contact extends BaseObject
|
|||
$fields = ['term-date' => NULL_DATE, 'archive' => false];
|
||||
dba::update('contact', $fields, ['id' => $contact['id']]);
|
||||
|
||||
if ($contact['url'] != '') {
|
||||
if (!empty($contact['url'])) {
|
||||
dba::update('contact', $fields, ['nurl' => normalise_link($contact['url'])]);
|
||||
}
|
||||
|
||||
if (!empty($contact['batch'])) {
|
||||
$condition = ['batch' => $contact['batch'], 'contact-type' => ACCOUNT_TYPE_RELAY];
|
||||
dba::update('contact', $fields, $condition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -516,7 +611,7 @@ class Contact extends BaseObject
|
|||
}
|
||||
|
||||
$sparkle = false;
|
||||
if ($contact['network'] === NETWORK_DFRN) {
|
||||
if (($contact['network'] === NETWORK_DFRN) && !$contact['self']) {
|
||||
$sparkle = true;
|
||||
$profile_link = System::baseUrl() . '/redir/' . $contact['id'];
|
||||
} else {
|
||||
|
@ -533,18 +628,21 @@ class Contact extends BaseObject
|
|||
$profile_link = $profile_link . '?url=profile';
|
||||
}
|
||||
|
||||
if (in_array($contact['network'], [NETWORK_DFRN, NETWORK_DIASPORA])) {
|
||||
if (in_array($contact['network'], [NETWORK_DFRN, NETWORK_DIASPORA]) && !$contact['self']) {
|
||||
$pm_url = System::baseUrl() . '/message/new/' . $contact['id'];
|
||||
}
|
||||
|
||||
if ($contact['network'] == NETWORK_DFRN) {
|
||||
if (($contact['network'] == NETWORK_DFRN) && !$contact['self']) {
|
||||
$poke_link = System::baseUrl() . '/poke/?f=&c=' . $contact['id'];
|
||||
}
|
||||
|
||||
$contact_url = System::baseUrl() . '/contacts/' . $contact['id'];
|
||||
|
||||
$posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/posts';
|
||||
$contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
|
||||
|
||||
if (!$contact['self']) {
|
||||
$contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu array:
|
||||
|
@ -1168,7 +1266,26 @@ class Contact extends BaseObject
|
|||
return result;
|
||||
}
|
||||
|
||||
if ($ret['network'] === NETWORK_DFRN) {
|
||||
// check if we already have a contact
|
||||
// the poll url is more reliable than the profile url, as we may have
|
||||
// indirect links or webfinger links
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' AND NOT `pending` LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($ret['poll']),
|
||||
dbesc(normalise_link($ret['poll'])),
|
||||
dbesc($ret['network'])
|
||||
);
|
||||
|
||||
if (!DBM::is_result($r)) {
|
||||
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' AND NOT `pending` LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc(normalise_link($url)),
|
||||
dbesc($ret['network'])
|
||||
);
|
||||
}
|
||||
|
||||
if (($ret['network'] === NETWORK_DFRN) && !DBM::is_result($r)) {
|
||||
if ($interactive) {
|
||||
if (strlen($a->path)) {
|
||||
$myaddr = bin2hex(System::baseUrl() . '/profile/' . $a->user['nickname']);
|
||||
|
@ -1180,7 +1297,7 @@ class Contact extends BaseObject
|
|||
|
||||
// NOTREACHED
|
||||
}
|
||||
} elseif (Config::get('system', 'dfrn_only')) {
|
||||
} elseif (Config::get('system', 'dfrn_only') && ($ret['network'] != NETWORK_DFRN)) {
|
||||
$result['message'] = L10n::t('This site is not configured to allow communications with other networks.') . EOL;
|
||||
$result['message'] != L10n::t('No compatible communication protocols or feeds were discovered.') . EOL;
|
||||
return $result;
|
||||
|
@ -1230,25 +1347,6 @@ class Contact extends BaseObject
|
|||
$writeable = 1;
|
||||
}
|
||||
|
||||
// check if we already have a contact
|
||||
// the poll url is more reliable than the profile url, as we may have
|
||||
// indirect links or webfinger links
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc($ret['poll']),
|
||||
dbesc(normalise_link($ret['poll'])),
|
||||
dbesc($ret['network'])
|
||||
);
|
||||
|
||||
if (!DBM::is_result($r)) {
|
||||
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' LIMIT 1",
|
||||
intval($uid),
|
||||
dbesc(normalise_link($url)),
|
||||
dbesc($ret['network'])
|
||||
);
|
||||
}
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
// update contact
|
||||
$new_relation = (($r[0]['rel'] == CONTACT_IS_FOLLOWER) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING);
|
||||
|
@ -1309,16 +1407,16 @@ class Contact extends BaseObject
|
|||
);
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
if (($contact['network'] == NETWORK_OSTATUS) && (strlen($contact['notify']))) {
|
||||
if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DFRN])) {
|
||||
// create a follow slap
|
||||
$item = [];
|
||||
$item['verb'] = ACTIVITY_FOLLOW;
|
||||
$item['follow'] = $contact["url"];
|
||||
$slap = OStatus::salmon($item, $r[0]);
|
||||
Salmon::slapper($r[0], $contact['notify'], $slap);
|
||||
}
|
||||
|
||||
if ($contact['network'] == NETWORK_DIASPORA) {
|
||||
if (!empty($contact['notify'])) {
|
||||
Salmon::slapper($r[0], $contact['notify'], $slap);
|
||||
}
|
||||
} elseif ($contact['network'] == NETWORK_DIASPORA) {
|
||||
$ret = Diaspora::sendShare($a->user, $contact);
|
||||
logger('share returns: ' . $ret);
|
||||
}
|
||||
|
@ -1377,7 +1475,7 @@ class Contact extends BaseObject
|
|||
}
|
||||
|
||||
if (is_array($contact)) {
|
||||
if (($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING)
|
||||
if (($contact['rel'] == CONTACT_IS_SHARING)
|
||||
|| ($sharing && $contact['rel'] == CONTACT_IS_FOLLOWER)) {
|
||||
dba::update('contact', ['rel' => CONTACT_IS_FRIEND, 'writable' => true],
|
||||
['id' => $contact['id'], 'uid' => $importer['uid']]);
|
||||
|
|
959
src/Model/Event.php
Normal file
959
src/Model/Event.php
Normal file
|
@ -0,0 +1,959 @@
|
|||
<?php
|
||||
/**
|
||||
* @file src/Model/Event.php
|
||||
*/
|
||||
|
||||
namespace Friendica\Model;
|
||||
|
||||
use dba;
|
||||
use Friendica\BaseObject;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\PConfig;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Map;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/items.php';
|
||||
|
||||
/**
|
||||
* @brief functions for interacting with the event database table
|
||||
*/
|
||||
class Event extends BaseObject
|
||||
{
|
||||
|
||||
public static function getHTML(array $event, $simple = false)
|
||||
{
|
||||
if (empty($event)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$bd_format = L10n::t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8 AM.
|
||||
|
||||
$event_start = day_translate(
|
||||
$event['adjust'] ?
|
||||
DateTimeFormat::local($event['start'], $bd_format) : DateTimeFormat::utc($event['start'], $bd_format)
|
||||
);
|
||||
|
||||
$event_end = day_translate(
|
||||
$event['adjust'] ?
|
||||
DateTimeFormat::local($event['finish'], $bd_format) : DateTimeFormat::utc($event['finish'], $bd_format)
|
||||
);
|
||||
|
||||
if ($simple) {
|
||||
$o = "<h3>" . BBCode::convert($event['summary'], false, $simple) . "</h3>";
|
||||
|
||||
$o .= "<div>" . BBCode::convert($event['desc'], false, $simple) . "</div>";
|
||||
|
||||
$o .= "<h4>" . L10n::t('Starts:') . "</h4><p>" . $event_start . "</p>";
|
||||
|
||||
if (!$event['nofinish']) {
|
||||
$o .= "<h4>" . L10n::t('Finishes:') . "</h4><p>" . $event_end . "</p>";
|
||||
}
|
||||
|
||||
if (strlen($event['location'])) {
|
||||
$o .= "<h4>" . L10n::t('Location:') . "</h4><p>" . BBCode::convert($event['location'], false, $simple) . "</p>";
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
$o = '<div class="vevent">' . "\r\n";
|
||||
|
||||
$o .= '<div class="summary event-summary">' . BBCode::convert($event['summary'], false, $simple) . '</div>' . "\r\n";
|
||||
|
||||
$o .= '<div class="event-start"><span class="event-label">' . L10n::t('Starts:') . '</span> <span class="dtstart" title="'
|
||||
. DateTimeFormat::utc($event['start'], (($event['adjust']) ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s'))
|
||||
. '" >' . $event_start
|
||||
. '</span></div>' . "\r\n";
|
||||
|
||||
if (!$event['nofinish']) {
|
||||
$o .= '<div class="event-end" ><span class="event-label">' . L10n::t('Finishes:') . '</span> <span class="dtend" title="'
|
||||
. DateTimeFormat::utc($event['finish'], (($event['adjust']) ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s'))
|
||||
. '" >' . $event_end
|
||||
. '</span></div>' . "\r\n";
|
||||
}
|
||||
|
||||
$o .= '<div class="description event-description">' . BBCode::convert($event['desc'], false, $simple) . '</div>' . "\r\n";
|
||||
|
||||
if (strlen($event['location'])) {
|
||||
$o .= '<div class="event-location"><span class="event-label">' . L10n::t('Location:') . '</span> <span class="location">'
|
||||
. BBCode::convert($event['location'], false, $simple)
|
||||
. '</span></div>' . "\r\n";
|
||||
|
||||
// Include a map of the location if the [map] BBCode is used.
|
||||
if (strpos($event['location'], "[map") !== false) {
|
||||
$map = Map::byLocation($event['location'], $simple);
|
||||
if ($map !== $event['location']) {
|
||||
$o .= $map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o .= '</div>' . "\r\n";
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an array with event data to bbcode.
|
||||
*
|
||||
* @param array $event Array which contains the event data.
|
||||
* @return string The event as a bbcode formatted string.
|
||||
*/
|
||||
private static function getBBCode(array $event)
|
||||
{
|
||||
$o = '';
|
||||
|
||||
if ($event['summary']) {
|
||||
$o .= '[event-summary]' . $event['summary'] . '[/event-summary]';
|
||||
}
|
||||
|
||||
if ($event['desc']) {
|
||||
$o .= '[event-description]' . $event['desc'] . '[/event-description]';
|
||||
}
|
||||
|
||||
if ($event['start']) {
|
||||
$o .= '[event-start]' . $event['start'] . '[/event-start]';
|
||||
}
|
||||
|
||||
if (($event['finish']) && (!$event['nofinish'])) {
|
||||
$o .= '[event-finish]' . $event['finish'] . '[/event-finish]';
|
||||
}
|
||||
|
||||
if ($event['location']) {
|
||||
$o .= '[event-location]' . $event['location'] . '[/event-location]';
|
||||
}
|
||||
|
||||
if ($event['adjust']) {
|
||||
$o .= '[event-adjust]' . $event['adjust'] . '[/event-adjust]';
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract bbcode formatted event data from a string.
|
||||
*
|
||||
* @params: string $s The string which should be parsed for event data.
|
||||
* @return array The array with the event information.
|
||||
*/
|
||||
public static function fromBBCode($text)
|
||||
{
|
||||
$ev = [];
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-summary\](.*?)\[\/event\-summary\]/is", $text, $match)) {
|
||||
$ev['summary'] = $match[1];
|
||||
}
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-description\](.*?)\[\/event\-description\]/is", $text, $match)) {
|
||||
$ev['desc'] = $match[1];
|
||||
}
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-start\](.*?)\[\/event\-start\]/is", $text, $match)) {
|
||||
$ev['start'] = $match[1];
|
||||
}
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-finish\](.*?)\[\/event\-finish\]/is", $text, $match)) {
|
||||
$ev['finish'] = $match[1];
|
||||
}
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-location\](.*?)\[\/event\-location\]/is", $text, $match)) {
|
||||
$ev['location'] = $match[1];
|
||||
}
|
||||
|
||||
$match = '';
|
||||
if (preg_match("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is", $text, $match)) {
|
||||
$ev['adjust'] = $match[1];
|
||||
}
|
||||
|
||||
$ev['nofinish'] = !empty($ev['start']) && empty($ev['finish']) ? 1 : 0;
|
||||
|
||||
return $ev;
|
||||
}
|
||||
|
||||
public static function sortByDate($event_list)
|
||||
{
|
||||
usort($event_list, ['self', 'compareDatesCallback']);
|
||||
return $event_list;
|
||||
}
|
||||
|
||||
private static function compareDatesCallback($event_a, $event_b)
|
||||
{
|
||||
$date_a = (($event_a['adjust']) ? DateTimeFormat::local($event_a['start']) : $event_a['start']);
|
||||
$date_b = (($event_b['adjust']) ? DateTimeFormat::local($event_b['start']) : $event_b['start']);
|
||||
|
||||
if ($date_a === $date_b) {
|
||||
return strcasecmp($event_a['desc'], $event_b['desc']);
|
||||
}
|
||||
|
||||
return strcmp($date_a, $date_b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete an event from the event table.
|
||||
*
|
||||
* Note: This function does only delete the event from the event table not its
|
||||
* related entry in the item table.
|
||||
*
|
||||
* @param int $event_id Event ID.
|
||||
* @return void
|
||||
*/
|
||||
public static function delete($event_id)
|
||||
{
|
||||
if ($event_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dba::delete('event', ['id' => $event_id]);
|
||||
logger("Deleted event ".$event_id, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store the event.
|
||||
*
|
||||
* Store the event in the event table and create an event item in the item table.
|
||||
*
|
||||
* @param array $arr Array with event data.
|
||||
* @return int The new event id.
|
||||
*/
|
||||
public static function store($arr)
|
||||
{
|
||||
$a = self::getApp();
|
||||
|
||||
$event = [];
|
||||
$event['id'] = intval(defaults($arr, 'id' , 0));
|
||||
$event['uid'] = intval(defaults($arr, 'uid' , 0));
|
||||
$event['cid'] = intval(defaults($arr, 'cid' , 0));
|
||||
$event['uri'] = defaults($arr, 'uri' , item_new_uri($a->get_hostname(), $event['uid']));
|
||||
$event['type'] = defaults($arr, 'type' , 'event');
|
||||
$event['summary'] = defaults($arr, 'summary' , '');
|
||||
$event['desc'] = defaults($arr, 'desc' , '');
|
||||
$event['location'] = defaults($arr, 'location' , '');
|
||||
$event['allow_cid'] = defaults($arr, 'allow_cid', '');
|
||||
$event['allow_gid'] = defaults($arr, 'allow_gid', '');
|
||||
$event['deny_cid'] = defaults($arr, 'deny_cid' , '');
|
||||
$event['deny_gid'] = defaults($arr, 'deny_gid' , '');
|
||||
$event['adjust'] = intval(defaults($arr, 'adjust' , 0));
|
||||
$event['nofinish'] = intval(defaults($arr, 'nofinish' , !empty($event['start']) && empty($event['finish'])));
|
||||
|
||||
$event['created'] = DateTimeFormat::utc(defaults($arr, 'created' , 'now'));
|
||||
$event['edited'] = DateTimeFormat::utc(defaults($arr, 'edited' , 'now'));
|
||||
$event['start'] = DateTimeFormat::utc(defaults($arr, 'start' , NULL_DATE));
|
||||
$event['finish'] = DateTimeFormat::utc(defaults($arr, 'finish' , NULL_DATE));
|
||||
if ($event['finish'] < NULL_DATE) {
|
||||
$event['finish'] = NULL_DATE;
|
||||
}
|
||||
$private = intval(defaults($arr, 'private', 0));
|
||||
|
||||
$conditions = ['uid' => $event['uid']];
|
||||
if ($event['cid']) {
|
||||
$conditions['id'] = $event['cid'];
|
||||
} else {
|
||||
$conditions['self'] = true;
|
||||
}
|
||||
|
||||
$contact = dba::selectFirst('contact', [], $conditions);
|
||||
|
||||
// Existing event being modified.
|
||||
if ($event['id']) {
|
||||
// has the event actually changed?
|
||||
$existing_event = dba::selectFirst('event', ['edited'], ['id' => $event['id'], 'uid' => $event['uid']]);
|
||||
if ((! DBM::is_result($existing_event)) || ($existing_event['edited'] === $event['edited'])) {
|
||||
|
||||
$item = dba::selectFirst('item', [], ['event-id' => $event['id'], 'uid' => $event['uid']]);
|
||||
|
||||
return DBM::is_result($item) ? $item['id'] : 0;
|
||||
}
|
||||
|
||||
$updated_fields = [
|
||||
'edited' => $event['edited'],
|
||||
'start' => $event['start'],
|
||||
'finish' => $event['finish'],
|
||||
'summary' => $event['summary'],
|
||||
'desc' => $event['desc'],
|
||||
'location' => $event['location'],
|
||||
'type' => $event['type'],
|
||||
'adjust' => $event['adjust'],
|
||||
'nofinish' => $event['nofinish'],
|
||||
];
|
||||
|
||||
dba::update('event', $updated_fields, ['id' => $event['id'], 'uid' => $event['uid']]);
|
||||
|
||||
$item = dba::selectFirst('item', ['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]);
|
||||
if (DBM::is_result($item)) {
|
||||
$object = '<object><type>' . xmlify(ACTIVITY_OBJ_EVENT) . '</type><title></title><id>' . xmlify($event['uri']) . '</id>';
|
||||
$object .= '<content>' . xmlify(self::getBBCode($event)) . '</content>';
|
||||
$object .= '</object>' . "\n";
|
||||
|
||||
$fields = ['body' => self::getBBCode($event), 'object' => $object, 'edited' => $event['edited']];
|
||||
Item::update($fields, ['id' => $item['id']]);
|
||||
|
||||
$item_id = $item['id'];
|
||||
} else {
|
||||
$item_id = 0;
|
||||
}
|
||||
|
||||
Addon::callHooks('event_updated', $event['id']);
|
||||
} else {
|
||||
$event['guid'] = get_guid(32);
|
||||
|
||||
// New event. Store it.
|
||||
dba::insert('event', $event);
|
||||
|
||||
$event['id'] = dba::lastInsertId();
|
||||
|
||||
$item_arr = [];
|
||||
|
||||
$item_arr['uid'] = $event['uid'];
|
||||
$item_arr['contact-id'] = $event['cid'];
|
||||
$item_arr['uri'] = $event['uri'];
|
||||
$item_arr['parent-uri'] = $event['uri'];
|
||||
$item_arr['guid'] = $event['guid'];
|
||||
$item_arr['type'] = 'activity';
|
||||
$item_arr['wall'] = $event['cid'] ? 0 : 1;
|
||||
$item_arr['contact-id'] = $contact['id'];
|
||||
$item_arr['owner-name'] = $contact['name'];
|
||||
$item_arr['owner-link'] = $contact['url'];
|
||||
$item_arr['owner-avatar'] = $contact['thumb'];
|
||||
$item_arr['author-name'] = $contact['name'];
|
||||
$item_arr['author-link'] = $contact['url'];
|
||||
$item_arr['author-avatar'] = $contact['thumb'];
|
||||
$item_arr['title'] = '';
|
||||
$item_arr['allow_cid'] = $event['allow_cid'];
|
||||
$item_arr['allow_gid'] = $event['allow_gid'];
|
||||
$item_arr['deny_cid'] = $event['deny_cid'];
|
||||
$item_arr['deny_gid'] = $event['deny_gid'];
|
||||
$item_arr['private'] = $private;
|
||||
$item_arr['visible'] = 1;
|
||||
$item_arr['verb'] = ACTIVITY_POST;
|
||||
$item_arr['object-type'] = ACTIVITY_OBJ_EVENT;
|
||||
$item_arr['origin'] = $event['cid'] === 0 ? 1 : 0;
|
||||
$item_arr['body'] = self::getBBCode($event);
|
||||
$item_arr['event-id'] = $event['id'];
|
||||
|
||||
$item_arr['object'] = '<object><type>' . xmlify(ACTIVITY_OBJ_EVENT) . '</type><title></title><id>' . xmlify($event['uri']) . '</id>';
|
||||
$item_arr['object'] .= '<content>' . xmlify(self::getBBCode($event)) . '</content>';
|
||||
$item_arr['object'] .= '</object>' . "\n";
|
||||
|
||||
$item_id = Item::insert($item_arr);
|
||||
|
||||
Addon::callHooks("event_created", $event['id']);
|
||||
}
|
||||
|
||||
return $item_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create an array with translation strings used for events.
|
||||
*
|
||||
* @return array Array with translations strings.
|
||||
*/
|
||||
public static function getStrings()
|
||||
{
|
||||
// First day of the week (0 = Sunday).
|
||||
$firstDay = PConfig::get(local_user(), 'system', 'first_day_of_week', 0);
|
||||
|
||||
$i18n = [
|
||||
"firstDay" => $firstDay,
|
||||
"allday" => L10n::t("all-day"),
|
||||
|
||||
"Sun" => L10n::t("Sun"),
|
||||
"Mon" => L10n::t("Mon"),
|
||||
"Tue" => L10n::t("Tue"),
|
||||
"Wed" => L10n::t("Wed"),
|
||||
"Thu" => L10n::t("Thu"),
|
||||
"Fri" => L10n::t("Fri"),
|
||||
"Sat" => L10n::t("Sat"),
|
||||
|
||||
"Sunday" => L10n::t("Sunday"),
|
||||
"Monday" => L10n::t("Monday"),
|
||||
"Tuesday" => L10n::t("Tuesday"),
|
||||
"Wednesday" => L10n::t("Wednesday"),
|
||||
"Thursday" => L10n::t("Thursday"),
|
||||
"Friday" => L10n::t("Friday"),
|
||||
"Saturday" => L10n::t("Saturday"),
|
||||
|
||||
"Jan" => L10n::t("Jan"),
|
||||
"Feb" => L10n::t("Feb"),
|
||||
"Mar" => L10n::t("Mar"),
|
||||
"Apr" => L10n::t("Apr"),
|
||||
"May" => L10n::t("May"),
|
||||
"Jun" => L10n::t("Jun"),
|
||||
"Jul" => L10n::t("Jul"),
|
||||
"Aug" => L10n::t("Aug"),
|
||||
"Sep" => L10n::t("Sept"),
|
||||
"Oct" => L10n::t("Oct"),
|
||||
"Nov" => L10n::t("Nov"),
|
||||
"Dec" => L10n::t("Dec"),
|
||||
|
||||
"January" => L10n::t("January"),
|
||||
"February" => L10n::t("February"),
|
||||
"March" => L10n::t("March"),
|
||||
"April" => L10n::t("April"),
|
||||
"May" => L10n::t("May"),
|
||||
"June" => L10n::t("June"),
|
||||
"July" => L10n::t("July"),
|
||||
"August" => L10n::t("August"),
|
||||
"September" => L10n::t("September"),
|
||||
"October" => L10n::t("October"),
|
||||
"November" => L10n::t("November"),
|
||||
"December" => L10n::t("December"),
|
||||
|
||||
"today" => L10n::t("today"),
|
||||
"month" => L10n::t("month"),
|
||||
"week" => L10n::t("week"),
|
||||
"day" => L10n::t("day"),
|
||||
|
||||
"noevent" => L10n::t("No events to display"),
|
||||
|
||||
"dtstart_label" => L10n::t("Starts:"),
|
||||
"dtend_label" => L10n::t("Finishes:"),
|
||||
"location_label" => L10n::t("Location:")
|
||||
];
|
||||
|
||||
return $i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes duplicated birthday events.
|
||||
*
|
||||
* @param array $dates Array of possibly duplicated events.
|
||||
* @return array Cleaned events.
|
||||
*
|
||||
* @todo We should replace this with a separate update function if there is some time left.
|
||||
*/
|
||||
private static function removeDuplicates(array $dates)
|
||||
{
|
||||
$dates2 = [];
|
||||
|
||||
foreach ($dates as $date) {
|
||||
if ($date['type'] == 'birthday') {
|
||||
$dates2[$date['uid'] . "-" . $date['cid'] . "-" . $date['start']] = $date;
|
||||
} else {
|
||||
$dates2[] = $date;
|
||||
}
|
||||
}
|
||||
return array_values($dates2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get an event by its event ID.
|
||||
*
|
||||
* @param int $owner_uid The User ID of the owner of the event
|
||||
* @param int $event_id The ID of the event in the event table
|
||||
* @param string $sql_extra
|
||||
* @return array Query result
|
||||
*/
|
||||
public static function getListById($owner_uid, $event_id, $sql_extra = '')
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// Ownly allow events if there is a valid owner_id.
|
||||
if ($owner_uid == 0) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Query for the event by event id
|
||||
$r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
|
||||
`item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
|
||||
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
WHERE `event`.`uid` = %d AND `event`.`id` = %d $sql_extra",
|
||||
intval($owner_uid),
|
||||
intval($event_id)
|
||||
);
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
$return = self::removeDuplicates($r);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get all events in a specific time frame.
|
||||
*
|
||||
* @param int $owner_uid The User ID of the owner of the events.
|
||||
* @param array $event_params An associative array with
|
||||
* int 'ignore' =>
|
||||
* string 'start' => Start time of the timeframe.
|
||||
* string 'finish' => Finish time of the timeframe.
|
||||
* string 'adjust_start' =>
|
||||
* string 'adjust_finish' =>
|
||||
*
|
||||
* @param string $sql_extra Additional sql conditions (e.g. permission request).
|
||||
*
|
||||
* @return array Query results.
|
||||
*/
|
||||
public static function getListByDate($owner_uid, $event_params, $sql_extra = '')
|
||||
{
|
||||
$return = [];
|
||||
|
||||
// Only allow events if there is a valid owner_id.
|
||||
if ($owner_uid == 0) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Query for the event by date.
|
||||
// @todo Slow query (518 seconds to run), to be optimzed
|
||||
$r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
|
||||
`item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
|
||||
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
WHERE `event`.`uid` = %d AND event.ignore = %d
|
||||
AND ((`adjust` = 0 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s')
|
||||
OR (`adjust` = 1 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s'))
|
||||
$sql_extra ",
|
||||
intval($owner_uid),
|
||||
intval($event_params["ignore"]),
|
||||
dbesc($event_params["start"]),
|
||||
dbesc($event_params["start"]),
|
||||
dbesc($event_params["finish"]),
|
||||
dbesc($event_params["adjust_start"]),
|
||||
dbesc($event_params["adjust_start"]),
|
||||
dbesc($event_params["adjust_finish"])
|
||||
);
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
$return = self::removeDuplicates($r);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an array query results in an array which could be used by the events template.
|
||||
*
|
||||
* @param array $event_result Event query array.
|
||||
* @return array Event array for the template.
|
||||
*/
|
||||
public static function prepareListForTemplate(array $event_result)
|
||||
{
|
||||
$event_list = [];
|
||||
|
||||
$last_date = '';
|
||||
$fmt = L10n::t('l, F j');
|
||||
foreach ($event_result as $event) {
|
||||
$start = $event['adjust'] ? DateTimeFormat::local($event['start'], 'c') : DateTimeFormat::utc($event['start'], 'c');
|
||||
$j = $event['adjust'] ? DateTimeFormat::local($event['start'], 'j') : DateTimeFormat::utc($event['start'], 'j');
|
||||
$day = $event['adjust'] ? DateTimeFormat::local($event['start'], $fmt) : DateTimeFormat::utc($event['start'], $fmt);
|
||||
$day = day_translate($day);
|
||||
|
||||
if ($event['nofinish']) {
|
||||
$end = null;
|
||||
} else {
|
||||
$end = $event['adjust'] ? DateTimeFormat::local($event['finish'], 'c') : DateTimeFormat::utc($event['finish'], 'c');
|
||||
}
|
||||
|
||||
$is_first = ($day !== $last_date);
|
||||
|
||||
$last_date = $day;
|
||||
|
||||
// Show edit and drop actions only if the user is the owner of the event and the event
|
||||
// is a real event (no bithdays).
|
||||
$edit = null;
|
||||
$copy = null;
|
||||
$drop = null;
|
||||
if (local_user() && local_user() == $event['uid'] && $event['type'] == 'event') {
|
||||
$edit = !$event['cid'] ? [System::baseUrl() . '/events/event/' . $event['id'], L10n::t('Edit event') , '', ''] : null;
|
||||
$copy = !$event['cid'] ? [System::baseUrl() . '/events/copy/' . $event['id'] , L10n::t('Duplicate event'), '', ''] : null;
|
||||
$drop = [System::baseUrl() . '/events/drop/' . $event['id'] , L10n::t('Delete event') , '', ''];
|
||||
}
|
||||
|
||||
$title = strip_tags(html_entity_decode(BBCode::convert($event['summary']), ENT_QUOTES, 'UTF-8'));
|
||||
if (!$title) {
|
||||
list($title, $_trash) = explode("<br", BBCode::convert($event['desc']), 2);
|
||||
$title = strip_tags(html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
|
||||
$html = self::getHTML($event);
|
||||
$event['desc'] = BBCode::convert($event['desc']);
|
||||
$event['location'] = BBCode::convert($event['location']);
|
||||
$event_list[] = [
|
||||
'id' => $event['id'],
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'allDay' => false,
|
||||
'title' => $title,
|
||||
'j' => $j,
|
||||
'd' => $day,
|
||||
'edit' => $edit,
|
||||
'drop' => $drop,
|
||||
'copy' => $copy,
|
||||
'is_first' => $is_first,
|
||||
'item' => $event,
|
||||
'html' => $html,
|
||||
'plink' => [$event['plink'], L10n::t('link to source'), '', ''],
|
||||
];
|
||||
}
|
||||
|
||||
return $event_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format event to export format (ical/csv).
|
||||
*
|
||||
* @param array $events Query result for events.
|
||||
* @param string $format The output format (ical/csv).
|
||||
* @param string $timezone The timezone of the user (not implemented yet).
|
||||
*
|
||||
* @return string Content according to selected export format.
|
||||
*
|
||||
* @todo Implement timezone support
|
||||
*/
|
||||
private static function formatListForExport(array $events, $format, $timezone)
|
||||
{
|
||||
if (!count($events)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($format) {
|
||||
// Format the exported data as a CSV file.
|
||||
case "csv":
|
||||
header("Content-type: text/csv");
|
||||
$o = '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL;
|
||||
|
||||
foreach ($events as $event) {
|
||||
/// @todo The time / date entries don't include any information about the
|
||||
/// timezone the event is scheduled in :-/
|
||||
$tmp1 = strtotime($event['start']);
|
||||
$tmp2 = strtotime($event['finish']);
|
||||
$time_format = "%H:%M:%S";
|
||||
$date_format = "%Y-%m-%d";
|
||||
|
||||
$o .= '"' . $event['summary'] . '", "' . strftime($date_format, $tmp1) .
|
||||
'", "' . strftime($time_format, $tmp1) . '", "' . $event['desc'] .
|
||||
'", "' . strftime($date_format, $tmp2) .
|
||||
'", "' . strftime($time_format, $tmp2) .
|
||||
'", "' . $event['location'] . '"' . PHP_EOL;
|
||||
}
|
||||
break;
|
||||
|
||||
// Format the exported data as a ics file.
|
||||
case "ical":
|
||||
header("Content-type: text/ics");
|
||||
$o = 'BEGIN:VCALENDAR' . PHP_EOL
|
||||
. 'VERSION:2.0' . PHP_EOL
|
||||
. 'PRODID:-//friendica calendar export//0.1//EN' . PHP_EOL;
|
||||
/// @todo include timezone informations in cases were the time is not in UTC
|
||||
// see http://tools.ietf.org/html/rfc2445#section-4.8.3
|
||||
// . 'BEGIN:VTIMEZONE' . PHP_EOL
|
||||
// . 'TZID:' . $timezone . PHP_EOL
|
||||
// . 'END:VTIMEZONE' . PHP_EOL;
|
||||
// TODO instead of PHP_EOL CRLF should be used for long entries
|
||||
// but test your solution against http://icalvalid.cloudapp.net/
|
||||
// also long lines SHOULD be split at 75 characters length
|
||||
foreach ($events as $event) {
|
||||
if ($event['adjust'] == 1) {
|
||||
$UTC = 'Z';
|
||||
} else {
|
||||
$UTC = '';
|
||||
}
|
||||
$o .= 'BEGIN:VEVENT' . PHP_EOL;
|
||||
|
||||
if ($event['start']) {
|
||||
$tmp = strtotime($event['start']);
|
||||
$dtformat = "%Y%m%dT%H%M%S" . $UTC;
|
||||
$o .= 'DTSTART:' . strftime($dtformat, $tmp) . PHP_EOL;
|
||||
}
|
||||
|
||||
if (!$event['nofinish']) {
|
||||
$tmp = strtotime($event['finish']);
|
||||
$dtformat = "%Y%m%dT%H%M%S" . $UTC;
|
||||
$o .= 'DTEND:' . strftime($dtformat, $tmp) . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($event['summary']) {
|
||||
$tmp = $event['summary'];
|
||||
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
|
||||
$tmp = addcslashes($tmp, ',;');
|
||||
$o .= 'SUMMARY:' . $tmp . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($event['desc']) {
|
||||
$tmp = $event['desc'];
|
||||
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
|
||||
$tmp = addcslashes($tmp, ',;');
|
||||
$o .= 'DESCRIPTION:' . $tmp . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($event['location']) {
|
||||
$tmp = $event['location'];
|
||||
$tmp = str_replace(PHP_EOL, PHP_EOL . ' ', $tmp);
|
||||
$tmp = addcslashes($tmp, ',;');
|
||||
$o .= 'LOCATION:' . $tmp . PHP_EOL;
|
||||
}
|
||||
|
||||
$o .= 'END:VEVENT' . PHP_EOL;
|
||||
$o .= PHP_EOL;
|
||||
}
|
||||
|
||||
$o .= 'END:VCALENDAR' . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get all events for a user ID.
|
||||
*
|
||||
* The query for events is done permission sensitive.
|
||||
* If the user is the owner of the calendar they
|
||||
* will get all of their available events.
|
||||
* If the user is only a visitor only the public events will
|
||||
* be available.
|
||||
*
|
||||
* @param int $uid The user ID.
|
||||
*
|
||||
* @return array Query results.
|
||||
*/
|
||||
private static function getListByUserId($uid = 0)
|
||||
{
|
||||
$return = [];
|
||||
|
||||
if ($uid == 0) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
$fields = ['start', 'finish', 'adjust', 'summary', 'desc', 'location', 'nofinish'];
|
||||
|
||||
$conditions = ['uid' => $uid, 'cid' => 0];
|
||||
|
||||
// Does the user who requests happen to be the owner of the events
|
||||
// requested? then show all of your events, otherwise only those that
|
||||
// don't have limitations set in allow_cid and allow_gid.
|
||||
if (local_user() != $uid) {
|
||||
$conditions += ['allow_cid' => '', 'allow_gid' => ''];
|
||||
}
|
||||
|
||||
$events = dba::select('event', $fields, $conditions);
|
||||
if (DBM::is_result($events)) {
|
||||
$return = dba::inArray($events);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $uid The user ID.
|
||||
* @param string $format Output format (ical/csv).
|
||||
* @return array With the results:
|
||||
* bool 'success' => True if the processing was successful,<br>
|
||||
* string 'format' => The output format,<br>
|
||||
* string 'extension' => The file extension of the output format,<br>
|
||||
* string 'content' => The formatted output content.<br>
|
||||
*
|
||||
* @todo Respect authenticated users with events_by_uid().
|
||||
*/
|
||||
public static function exportListByUserId($uid, $format = 'ical')
|
||||
{
|
||||
$process = false;
|
||||
|
||||
$user = dba::selectFirst('user', ['timezone'], ['uid' => $uid]);
|
||||
if (DBM::is_result($user)) {
|
||||
$timezone = $user['timezone'];
|
||||
}
|
||||
|
||||
// Get all events which are owned by a uid (respects permissions).
|
||||
$events = self::getListByUserId($uid);
|
||||
|
||||
// We have the events that are available for the requestor.
|
||||
// Now format the output according to the requested format.
|
||||
$res = self::formatListForExport($events, $format, $timezone);
|
||||
|
||||
// If there are results the precess was successfull.
|
||||
if (!empty($res)) {
|
||||
$process = true;
|
||||
}
|
||||
|
||||
// Get the file extension for the format.
|
||||
switch ($format) {
|
||||
case "ical":
|
||||
$file_ext = "ics";
|
||||
break;
|
||||
|
||||
case "csv":
|
||||
$file_ext = "csv";
|
||||
break;
|
||||
|
||||
default:
|
||||
$file_ext = "";
|
||||
}
|
||||
|
||||
$return = [
|
||||
'success' => $process,
|
||||
'format' => $format,
|
||||
'extension' => $file_ext,
|
||||
'content' => $res,
|
||||
];
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format an item array with event data to HTML.
|
||||
*
|
||||
* @param arr $item Array with item and event data.
|
||||
* @return string HTML output.
|
||||
*/
|
||||
public static function getItemHTML($item) {
|
||||
$same_date = false;
|
||||
$finish = false;
|
||||
|
||||
// Set the different time formats.
|
||||
$dformat = L10n::t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM.
|
||||
$dformat_short = L10n::t('D g:i A'); // Fri 8:01 AM.
|
||||
$tformat = L10n::t('g:i A'); // 8:01 AM.
|
||||
|
||||
// Convert the time to different formats.
|
||||
$dtstart_dt = day_translate(
|
||||
$item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-start'], $dformat)
|
||||
: DateTimeFormat::utc($item['event-start'], $dformat)
|
||||
);
|
||||
$dtstart_title = DateTimeFormat::utc($item['event-start'], $item['event-adjust'] ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s');
|
||||
// Format: Jan till Dec.
|
||||
$month_short = day_short_translate(
|
||||
$item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-start'], 'M')
|
||||
: DateTimeFormat::utc($item['event-start'], 'M')
|
||||
);
|
||||
// Format: 1 till 31.
|
||||
$date_short = $item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-start'], 'j')
|
||||
: DateTimeFormat::utc($item['event-start'], 'j');
|
||||
$start_time = $item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-start'], $tformat)
|
||||
: DateTimeFormat::utc($item['event-start'], $tformat);
|
||||
$start_short = day_short_translate(
|
||||
$item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-start'], $dformat_short)
|
||||
: DateTimeFormat::utc($item['event-start'], $dformat_short)
|
||||
);
|
||||
|
||||
// If the option 'nofinisch' isn't set, we need to format the finish date/time.
|
||||
if (!$item['event-nofinish']) {
|
||||
$finish = true;
|
||||
$dtend_dt = day_translate(
|
||||
$item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-finish'], $dformat)
|
||||
: DateTimeFormat::utc($item['event-finish'], $dformat)
|
||||
);
|
||||
$dtend_title = DateTimeFormat::utc($item['event-finish'], $item['event-adjust'] ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s');
|
||||
$end_short = day_short_translate(
|
||||
$item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-finish'], $dformat_short)
|
||||
: DateTimeFormat::utc($item['event-finish'], $dformat_short)
|
||||
);
|
||||
$end_time = $item['event-adjust'] ?
|
||||
DateTimeFormat::local($item['event-finish'], $tformat)
|
||||
: DateTimeFormat::utc($item['event-finish'], $tformat);
|
||||
// Check if start and finish time is at the same day.
|
||||
if (substr($dtstart_title, 0, 10) === substr($dtend_title, 0, 10)) {
|
||||
$same_date = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the event location.
|
||||
$location = self::locationToArray($item['event-location']);
|
||||
|
||||
// Construct the profile link (magic-auth).
|
||||
$sp = false;
|
||||
$profile_link = best_link_url($item, $sp);
|
||||
|
||||
if (!$sp) {
|
||||
$profile_link = Profile::zrl($profile_link);
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('event_stream_item.tpl');
|
||||
$return = replace_macros($tpl, [
|
||||
'$id' => $item['event-id'],
|
||||
'$title' => prepare_text($item['event-summary']),
|
||||
'$dtstart_label' => L10n::t('Starts:'),
|
||||
'$dtstart_title' => $dtstart_title,
|
||||
'$dtstart_dt' => $dtstart_dt,
|
||||
'$finish' => $finish,
|
||||
'$dtend_label' => L10n::t('Finishes:'),
|
||||
'$dtend_title' => $dtend_title,
|
||||
'$dtend_dt' => $dtend_dt,
|
||||
'$month_short' => $month_short,
|
||||
'$date_short' => $date_short,
|
||||
'$same_date' => $same_date,
|
||||
'$start_time' => $start_time,
|
||||
'$start_short' => $start_short,
|
||||
'$end_time' => $end_time,
|
||||
'$end_short' => $end_short,
|
||||
'$author_name' => $item['author-name'],
|
||||
'$author_link' => $profile_link,
|
||||
'$author_avatar' => $item['author-avatar'],
|
||||
'$description' => prepare_text($item['event-desc']),
|
||||
'$location_label' => L10n::t('Location:'),
|
||||
'$show_map_label' => L10n::t('Show map'),
|
||||
'$hide_map_label' => L10n::t('Hide map'),
|
||||
'$map_btn_label' => L10n::t('Show map'),
|
||||
'$location' => $location
|
||||
]);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format a string with map bbcode to an array with location data.
|
||||
*
|
||||
* Note: The string must only contain location data. A string with no bbcode will be
|
||||
* handled as location name.
|
||||
*
|
||||
* @param string $s The string with the bbcode formatted location data.
|
||||
*
|
||||
* @return array The array with the location data.
|
||||
* 'name' => The name of the location,<br>
|
||||
* 'address' => The address of the location,<br>
|
||||
* 'coordinates' => Latitude and longitude (e.g. '48.864716,2.349014').<br>
|
||||
*/
|
||||
private static function locationToArray($s = '') {
|
||||
if ($s == '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$location = ['name' => $s];
|
||||
|
||||
// Map tag with location name - e.g. [map]Paris[/map].
|
||||
if (strpos($s, '[/map]') !== false) {
|
||||
$found = preg_match("/\[map\](.*?)\[\/map\]/ism", $s, $match);
|
||||
if (intval($found) > 0 && array_key_exists(1, $match)) {
|
||||
$location['address'] = $match[1];
|
||||
// Remove the map bbcode from the location name.
|
||||
$location['name'] = str_replace($match[0], "", $s);
|
||||
}
|
||||
// Map tag with coordinates - e.g. [map=48.864716,2.349014].
|
||||
} elseif (strpos($s, '[map=') !== false) {
|
||||
$found = preg_match("/\[map=(.*?)\]/ism", $s, $match);
|
||||
if (intval($found) > 0 && array_key_exists(1, $match)) {
|
||||
$location['coordinates'] = $match[1];
|
||||
// Remove the map bbcode from the location name.
|
||||
$location['name'] = str_replace($match[0], "", $s);
|
||||
}
|
||||
}
|
||||
|
||||
$location['name'] = prepare_text($location['name']);
|
||||
|
||||
// Construct the map HTML.
|
||||
if (isset($location['address'])) {
|
||||
$location['map'] = '<div class="map">' . Map::byLocation($location['address']) . '</div>';
|
||||
} elseif (isset($location['coordinates'])) {
|
||||
$location['map'] = '<div class="map">' . Map::byCoordinates(str_replace('/', ' ', $location['coordinates'])) . '</div>';
|
||||
}
|
||||
|
||||
return $location;
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ use dba;
|
|||
use Exception;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2bbcode.php';
|
||||
|
||||
/**
|
||||
* @brief This class handles GlobalContact related functions
|
||||
|
@ -37,56 +36,53 @@ class GContact
|
|||
*/
|
||||
public static function searchByName($search, $mode = '')
|
||||
{
|
||||
if ($search) {
|
||||
// check supported networks
|
||||
if (Config::get('system', 'diaspora_enabled')) {
|
||||
$diaspora = NETWORK_DIASPORA;
|
||||
} else {
|
||||
$diaspora = NETWORK_DFRN;
|
||||
}
|
||||
|
||||
if (!Config::get('system', 'ostatus_disabled')) {
|
||||
$ostatus = NETWORK_OSTATUS;
|
||||
} else {
|
||||
$ostatus = NETWORK_DFRN;
|
||||
}
|
||||
|
||||
// check if we search only communities or every contact
|
||||
if ($mode === "community") {
|
||||
$extra_sql = " AND `community`";
|
||||
} else {
|
||||
$extra_sql = "";
|
||||
}
|
||||
|
||||
$search .= "%";
|
||||
|
||||
$results = q(
|
||||
"SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
|
||||
`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`, `gcontact`.`community`
|
||||
FROM `gcontact`
|
||||
LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl`
|
||||
AND `contact`.`uid` = %d AND NOT `contact`.`blocked`
|
||||
AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s')
|
||||
WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
|
||||
((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR
|
||||
(`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
|
||||
(`gcontact`.`addr` LIKE '%s' OR `gcontact`.`name` LIKE '%s' OR `gcontact`.`nick` LIKE '%s') $extra_sql
|
||||
GROUP BY `gcontact`.`nurl`
|
||||
ORDER BY `gcontact`.`nurl` DESC
|
||||
LIMIT 1000",
|
||||
intval(local_user()),
|
||||
dbesc(CONTACT_IS_SHARING),
|
||||
dbesc(CONTACT_IS_FRIEND),
|
||||
dbesc(NETWORK_DFRN),
|
||||
dbesc($ostatus),
|
||||
dbesc($diaspora),
|
||||
dbesc(escape_tags($search)),
|
||||
dbesc(escape_tags($search)),
|
||||
dbesc(escape_tags($search))
|
||||
);
|
||||
|
||||
return $results;
|
||||
if (empty($search)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// check supported networks
|
||||
if (Config::get('system', 'diaspora_enabled')) {
|
||||
$diaspora = NETWORK_DIASPORA;
|
||||
} else {
|
||||
$diaspora = NETWORK_DFRN;
|
||||
}
|
||||
|
||||
if (!Config::get('system', 'ostatus_disabled')) {
|
||||
$ostatus = NETWORK_OSTATUS;
|
||||
} else {
|
||||
$ostatus = NETWORK_DFRN;
|
||||
}
|
||||
|
||||
// check if we search only communities or every contact
|
||||
if ($mode === "community") {
|
||||
$extra_sql = " AND `community`";
|
||||
} else {
|
||||
$extra_sql = "";
|
||||
}
|
||||
|
||||
$search .= "%";
|
||||
|
||||
$results = dba::p("SELECT `nurl` FROM `gcontact`
|
||||
WHERE NOT `hide` AND `network` IN (?, ?, ?) AND
|
||||
((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND
|
||||
(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
|
||||
GROUP BY `nurl` ORDER BY `nurl` DESC LIMIT 1000",
|
||||
NETWORK_DFRN, $ostatus, $diaspora, $search, $search, $search
|
||||
);
|
||||
|
||||
$gcontacts = [];
|
||||
while ($result = dba::fetch($results)) {
|
||||
$urlparts = parse_url($result["nurl"]);
|
||||
|
||||
// Ignore results that look strange.
|
||||
// For historic reasons the gcontact table does contain some garbage.
|
||||
if (!empty($urlparts['query']) || !empty($urlparts['fragment'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$gcontacts[] = Contact::getDetailsByURL($result["nurl"], local_user());
|
||||
}
|
||||
return $gcontacts;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -877,7 +873,8 @@ class GContact
|
|||
|
||||
// Now update the contact entry with the user id "0" as well.
|
||||
// This is used for the shadow copies of public items.
|
||||
|
||||
/// @todo Check if we really should do this.
|
||||
// The quality of the gcontact table is mostly lower than the public contact
|
||||
$public_contact = dba::selectFirst('contact', ['id'], ['nurl' => normalise_link($contact["url"]), 'uid' => 0]);
|
||||
if (DBM::is_result($public_contact)) {
|
||||
logger("Update public contact ".$public_contact["id"], LOGGER_DEBUG);
|
||||
|
@ -898,6 +895,12 @@ class GContact
|
|||
'contact-type' => $contact['contact-type'], 'url' => $contact['url'],
|
||||
'location' => $contact['location'], 'about' => $contact['about']];
|
||||
|
||||
// Don't update the birthday field if not set or invalid
|
||||
if (empty($contact['birthday']) || ($contact['birthday'] < '0001-01-01')) {
|
||||
unset($fields['bd']);
|
||||
}
|
||||
|
||||
|
||||
dba::update('contact', $fields, ['id' => $public_contact["id"]], $old_contact);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class Group extends BaseObject
|
|||
// was restricted to this group may now be seen by the new group members.
|
||||
$group = dba::selectFirst('group', ['deleted'], ['id' => $gid]);
|
||||
if (DBM::is_result($group) && $group['deleted']) {
|
||||
dba::update('group', ['deleted' => 0], ['gid' => $gid]);
|
||||
dba::update('group', ['deleted' => 0], ['id' => $gid]);
|
||||
notice(L10n::t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.') . EOL);
|
||||
}
|
||||
return true;
|
||||
|
@ -54,6 +54,19 @@ class Group extends BaseObject
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group information.
|
||||
*
|
||||
* @param int $id Group ID
|
||||
* @param string $name Group name
|
||||
*
|
||||
* @return bool Was the update successful?
|
||||
*/
|
||||
public static function update($id, $name)
|
||||
{
|
||||
return dba::update('group', ['name' => $name], ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a list of group ids a contact belongs to
|
||||
*
|
||||
|
@ -138,7 +151,7 @@ class Group extends BaseObject
|
|||
return false;
|
||||
}
|
||||
|
||||
$group = dba::selectFirst('group', ['uid'], ['gid' => $gid]);
|
||||
$group = dba::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (!DBM::is_result($group)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -22,13 +22,13 @@ use Friendica\Object\Image;
|
|||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\XML;
|
||||
use dba;
|
||||
use Text_LanguageDetect;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/items.php';
|
||||
require_once 'include/text.php';
|
||||
require_once 'include/event.php';
|
||||
|
||||
class Item extends BaseObject
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ class Item extends BaseObject
|
|||
* @param integer $item_id Item ID that should be delete
|
||||
* @param integer $priority Priority for the notification
|
||||
*
|
||||
* @return $boolean success
|
||||
* @return boolean success
|
||||
*/
|
||||
public static function deleteById($item_id, $priority = PRIORITY_HIGH)
|
||||
{
|
||||
|
@ -152,7 +152,7 @@ class Item extends BaseObject
|
|||
|
||||
// If item is a link to an event, delete the event.
|
||||
if (intval($item['event-id'])) {
|
||||
event_delete($item['event-id']);
|
||||
Event::delete($item['event-id']);
|
||||
}
|
||||
|
||||
// If item has attachments, drop them
|
||||
|
@ -355,6 +355,10 @@ class Item extends BaseObject
|
|||
}
|
||||
}
|
||||
|
||||
if (!empty($item['thr-parent'])) {
|
||||
$item['parent-uri'] = $item['thr-parent'];
|
||||
}
|
||||
|
||||
if (x($item, 'gravity')) {
|
||||
$item['gravity'] = intval($item['gravity']);
|
||||
} elseif ($item['parent-uri'] === $item['uri']) {
|
||||
|
@ -897,7 +901,11 @@ class Item extends BaseObject
|
|||
$item['uid'] = 0;
|
||||
$item['origin'] = 0;
|
||||
$item['wall'] = 0;
|
||||
$item['contact-id'] = Contact::getIdForURL($item['author-link']);
|
||||
if ($item['uri'] == $item['parent-uri']) {
|
||||
$item['contact-id'] = Contact::getIdForURL($item['owner-link']);
|
||||
} else {
|
||||
$item['contact-id'] = Contact::getIdForURL($item['author-link']);
|
||||
}
|
||||
|
||||
if (in_array($item['type'], ["net-comment", "wall-comment"])) {
|
||||
$item['type'] = 'remote-comment';
|
||||
|
@ -1264,21 +1272,23 @@ class Item extends BaseObject
|
|||
}
|
||||
|
||||
// now change this copy of the post to a forum head message and deliver to all the tgroup members
|
||||
$self = dba::selectFirst('contact', ['name', 'url', 'thumb'], ['uid' => $uid, 'self' => true]);
|
||||
$self = dba::selectFirst('contact', ['id', 'name', 'url', 'thumb'], ['uid' => $uid, 'self' => true]);
|
||||
if (!DBM::is_result($self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$owner_id = Contact::getIdForURL($self['url']);
|
||||
|
||||
// also reset all the privacy bits to the forum default permissions
|
||||
|
||||
$private = ($user['allow_cid'] || $user['allow_gid'] || $user['deny_cid'] || $user['deny_gid']) ? 1 : 0;
|
||||
|
||||
$forum_mode = ($prvgroup ? 2 : 1);
|
||||
|
||||
$fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode,
|
||||
'owner-name' => $self['name'], 'owner-link' => $self['url'], 'owner-avatar' => $self['thumb'],
|
||||
'private' => $private, 'allow_cid' => $user['allow_cid'], 'allow_gid' => $user['allow_gid'],
|
||||
'deny_cid' => $user['deny_cid'], 'deny_gid' => $user['deny_gid']];
|
||||
$fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $self['id'],
|
||||
'owner-id' => $owner_id, 'owner-name' => $self['name'], 'owner-link' => $self['url'],
|
||||
'owner-avatar' => $self['thumb'], 'private' => $private, 'allow_cid' => $user['allow_cid'],
|
||||
'allow_gid' => $user['allow_gid'], 'deny_cid' => $user['deny_cid'], 'deny_gid' => $user['deny_gid']];
|
||||
dba::update('item', $fields, ['id' => $item_id]);
|
||||
|
||||
self::updateThread($item_id);
|
||||
|
|
|
@ -500,6 +500,8 @@ class Profile
|
|||
$p['photo'] = proxy_url($p['photo'], false, PROXY_SIZE_SMALL);
|
||||
}
|
||||
|
||||
$p['url'] = self::magicLink($p['url']);
|
||||
|
||||
$tpl = get_markup_template('profile_vcard.tpl');
|
||||
$o .= replace_macros($tpl, [
|
||||
'$profile' => $p,
|
||||
|
@ -1005,6 +1007,29 @@ class Profile
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a magic link to authenticate remote visitors
|
||||
*
|
||||
* @param string $contact_url The address of the contact profile
|
||||
* @param integer $uid The user id, "local_user" is the default
|
||||
*
|
||||
* @return string with "redir" link
|
||||
*/
|
||||
public static function magicLink($contact_url, $uid = -1)
|
||||
{
|
||||
if ($uid == -1) {
|
||||
$uid = local_user();
|
||||
}
|
||||
$condition = ['pending' => false, 'uid' => $uid,
|
||||
'nurl' => normalise_link($contact_url),
|
||||
'network' => NETWORK_DFRN, 'self' => false];
|
||||
$contact = dba::selectFirst('contact', ['id'], $condition);
|
||||
if (DBM::is_result($contact)) {
|
||||
return System::baseUrl() . '/redir/' . $contact['id'];
|
||||
}
|
||||
return self::zrl($contact_url);
|
||||
}
|
||||
|
||||
public static function zrl($s, $force = false)
|
||||
{
|
||||
if (!strlen($s)) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
namespace Friendica\Model;
|
||||
|
||||
use DivineOmega\PasswordExposed\PasswordStatus;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\L10n;
|
||||
|
@ -22,6 +23,7 @@ use Friendica\Util\Network;
|
|||
use dba;
|
||||
use Exception;
|
||||
use LightOpenID;
|
||||
use function password_exposed;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/dba.php';
|
||||
|
@ -101,7 +103,7 @@ class User
|
|||
* @param string $password
|
||||
* @return int|boolean
|
||||
* @deprecated since version 3.6
|
||||
* @see Friendica\Model\User::getIdFromPasswordAuthentication()
|
||||
* @see User::getIdFromPasswordAuthentication()
|
||||
*/
|
||||
public static function authenticate($user_info, $password)
|
||||
{
|
||||
|
@ -125,13 +127,23 @@ class User
|
|||
{
|
||||
$user = self::getAuthenticationInfo($user_info);
|
||||
|
||||
if ($user['legacy_password']) {
|
||||
if (strpos($user['password'], '$') === false) {
|
||||
//Legacy hash that has not been replaced by a new hash yet
|
||||
if (self::hashPasswordLegacy($password) === $user['password']) {
|
||||
self::updatePassword($user['uid'], $password);
|
||||
|
||||
return $user['uid'];
|
||||
}
|
||||
} elseif (!empty($user['legacy_password'])) {
|
||||
//Legacy hash that has been double-hashed and not replaced by a new hash yet
|
||||
//Warning: `legacy_password` is not necessary in sync with the content of `password`
|
||||
if (password_verify(self::hashPasswordLegacy($password), $user['password'])) {
|
||||
self::updatePassword($user['uid'], $password);
|
||||
|
||||
return $user['uid'];
|
||||
}
|
||||
} elseif (password_verify($password, $user['password'])) {
|
||||
//New password hash
|
||||
if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
|
||||
self::updatePassword($user['uid'], $password);
|
||||
}
|
||||
|
@ -216,6 +228,17 @@ class User
|
|||
return autoname(6) . mt_rand(100, 9999);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided plaintext password has been exposed or not
|
||||
*
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPasswordExposed($password)
|
||||
{
|
||||
return password_exposed($password) === PasswordStatus::EXPOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy hashing function, kept for password migration purposes
|
||||
*
|
||||
|
@ -382,7 +405,7 @@ class User
|
|||
throw new Exception(L10n::t('Not a valid email address.'));
|
||||
}
|
||||
|
||||
if (dba::exists('user', ['email' => $email])) {
|
||||
if (Config::get('system', 'block_extended_register', false) && dba::exists('user', ['email' => $email])) {
|
||||
throw new Exception(L10n::t('Cannot use that email.'));
|
||||
}
|
||||
|
||||
|
@ -601,11 +624,12 @@ class User
|
|||
'));
|
||||
$body = deindent(L10n::t('
|
||||
The login details are as follows:
|
||||
Site Location: %3$s
|
||||
Login Name: %1$s
|
||||
Password: %5$s
|
||||
|
||||
You may change your password from your account Settings page after logging
|
||||
Site Location: %3$s
|
||||
Login Name: %1$s
|
||||
Password: %5$s
|
||||
|
||||
You may change your password from your account "Settings" page after logging
|
||||
in.
|
||||
|
||||
Please take a few moments to review the other account settings on that page.
|
||||
|
@ -614,7 +638,7 @@ class User
|
|||
' . "\x28" . 'on the "Profiles" page' . "\x29" . ' so that other people can easily find you.
|
||||
|
||||
We recommend setting your full name, adding a profile photo,
|
||||
adding some profile keywords ' . "\x28" . 'very useful in making new friends' . "\x29" . ' - and
|
||||
adding some profile "keywords" ' . "\x28" . 'very useful in making new friends' . "\x29" . ' - and
|
||||
perhaps what country you live in; if you do not wish to be more specific
|
||||
than that.
|
||||
|
||||
|
@ -622,6 +646,7 @@ class User
|
|||
If you are new and do not know anybody here, they may help
|
||||
you to make some new and interesting friends.
|
||||
|
||||
If you ever want to delete your account, you can do so at %3$s/removeme
|
||||
|
||||
Thank you and welcome to %2$s.'));
|
||||
|
||||
|
|
40
src/Module/Hashtag.php
Normal file
40
src/Module/Hashtag.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* @file src/Module/Hashtag.php
|
||||
*/
|
||||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\System;
|
||||
use dba;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/text.php';
|
||||
|
||||
/**
|
||||
* Hashtag module.
|
||||
*/
|
||||
class Hashtag extends BaseModule
|
||||
{
|
||||
|
||||
public static function content()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$t = escape_tags($_REQUEST['t']);
|
||||
if (empty($t)) {
|
||||
System::jsonExit($result);
|
||||
}
|
||||
|
||||
$taglist = dba::p("SELECT DISTINCT(`term`) FROM `term` WHERE `term` LIKE ? AND `type` = ? ORDER BY `term`",
|
||||
$t . '%',
|
||||
intval(TERM_HASHTAG)
|
||||
);
|
||||
while ($tag = dba::fetch($taglist)) {
|
||||
$result[] = ['text' => $tag['term']];
|
||||
}
|
||||
dba::close($taglist);
|
||||
|
||||
System::jsonExit($result);
|
||||
}
|
||||
}
|
60
src/Module/Tos.php
Normal file
60
src/Module/Tos.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
/**
|
||||
* @file mod/tos.php
|
||||
*
|
||||
* This module displays the Terms of Service for a node, if the admin
|
||||
* wants them to be displayed.
|
||||
*/
|
||||
|
||||
namespace Friendica\Module;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
|
||||
class Tos extends BaseModule
|
||||
{
|
||||
/**
|
||||
* @brief initialize the TOS module.
|
||||
*
|
||||
* If this is a single user instance, we expect the user to know their
|
||||
* dealings with their own node so a TOS is not necessary.
|
||||
*
|
||||
**/
|
||||
public static function init()
|
||||
{
|
||||
if (strlen(Config::get('system','singleuser'))) {
|
||||
goaway(System::baseUrl()."/profile/" . Config::get('system','singleuser'));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief generate the content of the /tos page
|
||||
*
|
||||
* The content of the /tos page is generated from two parts.
|
||||
* (1) a free form part the admin of the node can set in the admin panel
|
||||
* (2) an optional privacy statement that gives some transparency about
|
||||
* what information are needed by the software to provide the service.
|
||||
* This privacy statement has fixed text, so it can be translated easily.
|
||||
*
|
||||
* @return string
|
||||
**/
|
||||
public static function content() {
|
||||
$tpl = get_markup_template('tos.tpl');
|
||||
if (Config::get('system', 'tosdisplay'))
|
||||
{
|
||||
return replace_macros($tpl, [
|
||||
'$title' => L10n::t("Terms of Service"),
|
||||
'$tostext' => BBCode::convert(Config::get('system', 'tostext')),
|
||||
'$displayprivstatement' => Config::get('system', 'tosprivstatement'),
|
||||
'$privstatementtitle' => L10n::t("Privacy Statement"),
|
||||
'$privoperate' => L10n::t('At the time of registration, and for providing communications between the user account and their contacts, the user has to provide a display name (pen name), an username (nickname) and a working email address. The names will be accessible on the profile page of the account by any visitor of the page, even if other profile details are not displayed. The email address will only be used to send the user notifications about interactions, but wont be visibly displayed. The listing of an account in the node\'s user directory or the global user directory is optional and can be controlled in the user settings, it is not necessary for communication.'),
|
||||
'$privdistribute' => L10n::t('This data is required for communication and is passed on to the nodes of the communication partners. Users can enter additional private data that may be transmitted to the communication partners accounts.'),
|
||||
'$privdelete' => L10n::t('At any point in time a logged in user can export their account data from the <a href="%1$s/settings/uexport">account settings</a>. If the user wants to delete their account they can do so at <a href="%1$s/removeme">%1$s/removeme</a>. The deletion of the account will be permanent.', System::baseurl())
|
||||
]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -771,6 +771,10 @@ class Image
|
|||
{
|
||||
$data = [];
|
||||
|
||||
if (empty($url)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$data = Cache::get($url);
|
||||
|
||||
if (is_null($data) || !$data || !is_array($data)) {
|
||||
|
|
|
@ -8,6 +8,7 @@ use Friendica\BaseObject;
|
|||
use Friendica\Content\ContactSelector;
|
||||
use Friendica\Content\Feature;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\PConfig;
|
||||
use Friendica\Database\DBM;
|
||||
|
@ -315,7 +316,13 @@ class Post extends BaseObject
|
|||
$body_e = $body;
|
||||
$text_e = strip_tags($body);
|
||||
$name_e = $profile_name;
|
||||
$title_e = $item['title'];
|
||||
|
||||
if (!empty($item['content-warning']) && PConfig::get(local_user(), 'system', 'disable_cw', false)) {
|
||||
$title_e = ucfirst($item['content-warning']);
|
||||
} else {
|
||||
$title_e = $item['title'];
|
||||
}
|
||||
|
||||
$location_e = $location;
|
||||
$owner_name_e = $this->getOwnerName();
|
||||
|
||||
|
@ -337,6 +344,7 @@ class Post extends BaseObject
|
|||
$tmp_item = [
|
||||
'template' => $this->getTemplate(),
|
||||
'type' => implode("", array_slice(explode("/", $item['verb']), -1)),
|
||||
'suppress_tags' => Config::get('system', 'suppress_tags'),
|
||||
'tags' => $item['tags'],
|
||||
'hashtags' => $item['hashtags'],
|
||||
'mentions' => $item['mentions'],
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace Friendica\Protocol;
|
|||
use Friendica\App;
|
||||
use Friendica\Content\OEmbed;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\L10n;
|
||||
|
@ -18,11 +19,11 @@ use Friendica\Core\System;
|
|||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBM;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Event;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\Term;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Object\Image;
|
||||
use Friendica\Protocol\OStatus;
|
||||
|
@ -30,6 +31,7 @@ use Friendica\Util\Crypto;
|
|||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\XML;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
use dba;
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
|
@ -40,9 +42,7 @@ require_once 'boot.php';
|
|||
require_once 'include/dba.php';
|
||||
require_once "include/enotify.php";
|
||||
require_once "include/items.php";
|
||||
require_once "include/event.php";
|
||||
require_once "include/text.php";
|
||||
require_once "include/html2bbcode.php";
|
||||
|
||||
/**
|
||||
* @brief This class contain functions to create and send DFRN XML files
|
||||
|
@ -1146,13 +1146,20 @@ class DFRN
|
|||
* @param string $atom Content that will be transmitted
|
||||
* @param bool $dissolve (to be documented)
|
||||
*
|
||||
* @return int Deliver status. -1 means an error.
|
||||
* @return int Deliver status. Negative values mean an error.
|
||||
* @todo Add array type-hint for $owner, $contact
|
||||
*/
|
||||
public static function deliver($owner, $contact, $atom, $dissolve = false)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
// At first try the Diaspora transport layer
|
||||
$ret = self::transmit($owner, $contact, $atom);
|
||||
if ($ret >= 200) {
|
||||
logger('Delivery via Diaspora transport layer was successful with status ' . $ret);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
|
||||
|
||||
if ($contact['duplex'] && $contact['dfrn-id']) {
|
||||
|
@ -1196,13 +1203,13 @@ class DFRN
|
|||
$xml = $ret['body'];
|
||||
|
||||
$curl_stat = $a->get_curl_code();
|
||||
if (!$curl_stat) {
|
||||
if (empty($curl_stat)) {
|
||||
return -3; // timed out
|
||||
}
|
||||
|
||||
logger('dfrn_deliver: ' . $xml, LOGGER_DATA);
|
||||
|
||||
if (! $xml) {
|
||||
if (empty($xml)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
@ -1215,7 +1222,7 @@ class DFRN
|
|||
$res = XML::parseString($xml);
|
||||
|
||||
if ((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) {
|
||||
return (($res->status) ? $res->status : 3);
|
||||
return ($res->status ? $res->status : 3);
|
||||
}
|
||||
|
||||
$postvars = [];
|
||||
|
@ -1338,11 +1345,11 @@ class DFRN
|
|||
logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
|
||||
|
||||
$curl_stat = $a->get_curl_code();
|
||||
if ((!$curl_stat) || (!strlen($xml))) {
|
||||
if (empty($curl_stat) || empty($xml)) {
|
||||
return -9; // timed out
|
||||
}
|
||||
|
||||
if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) {
|
||||
if (($curl_stat == 503) && stristr($a->get_curl_headers(), 'retry-after')) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
|
@ -1358,6 +1365,11 @@ class DFRN
|
|||
return -11;
|
||||
}
|
||||
|
||||
// Possibly old servers had returned an empty value when everything was okay
|
||||
if (empty($res->status)) {
|
||||
$res->status = 200;
|
||||
}
|
||||
|
||||
if (!empty($res->message)) {
|
||||
logger('Delivery returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG);
|
||||
}
|
||||
|
@ -1369,6 +1381,79 @@ class DFRN
|
|||
return intval($res->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmits atom content to the contacts via the Diaspora transport layer
|
||||
*
|
||||
* @param array $owner Owner record
|
||||
* @param array $contact Contact record of the receiver
|
||||
* @param string $atom Content that will be transmitted
|
||||
*
|
||||
* @return int Deliver status. Negative values mean an error.
|
||||
*/
|
||||
public static function transmit($owner, $contact, $atom, $public_batch = false)
|
||||
{
|
||||
$a = get_app();
|
||||
|
||||
if (empty($contact['addr'])) {
|
||||
logger('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.');
|
||||
if (Contact::updateFromProbe($contact['id'])) {
|
||||
$new_contact = dba::selectFirst('contact', ['addr'], ['id' => $contact['id']]);
|
||||
$contact['addr'] = $new_contact['addr'];
|
||||
}
|
||||
|
||||
if (empty($contact['addr'])) {
|
||||
logger('Unable to find contact handle for ' . $contact['id'] . ' - ' . $contact['url']);
|
||||
return -21;
|
||||
}
|
||||
}
|
||||
|
||||
$fcontact = Diaspora::personByHandle($contact['addr']);
|
||||
if (empty($fcontact)) {
|
||||
logger('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']);
|
||||
return -22;
|
||||
}
|
||||
|
||||
$envelope = Diaspora::buildMessage($atom, $owner, $contact, $owner['uprvkey'], $fcontact['pubkey'], $public_batch);
|
||||
|
||||
$dest_url = ($public_batch ? $fcontact["batch"] : $contact["notify"]);
|
||||
|
||||
$content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json");
|
||||
|
||||
$xml = Network::post($dest_url, $envelope, ["Content-Type: ".$content_type]);
|
||||
|
||||
$curl_stat = $a->get_curl_code();
|
||||
if (empty($curl_stat) || empty($xml)) {
|
||||
logger('Empty answer from ' . $contact['id'] . ' - ' . $dest_url);
|
||||
return -9; // timed out
|
||||
}
|
||||
|
||||
if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
if (strpos($xml, '<?xml') === false) {
|
||||
logger('No valid XML returned from ' . $contact['id'] . ' - ' . $dest_url);
|
||||
logger('Returned XML: ' . $xml, LOGGER_DATA);
|
||||
return 3;
|
||||
}
|
||||
|
||||
$res = XML::parseString($xml);
|
||||
|
||||
if (empty($res->status)) {
|
||||
return -23;
|
||||
}
|
||||
|
||||
if (!empty($res->message)) {
|
||||
logger('Transmit to ' . $dest_url . ' returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
if ($res->status == 200) {
|
||||
Contact::unmarkForArchival($contact);
|
||||
}
|
||||
|
||||
return intval($res->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add new birthday event for this person
|
||||
*
|
||||
|
@ -1434,7 +1519,7 @@ class DFRN
|
|||
$contact_old = dba::fetch_first("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `avatar`, `name-date`, `uri-date`, `addr`,
|
||||
`name`, `nick`, `about`, `location`, `keywords`, `xmpp`, `bdyear`, `bd`, `hidden`, `contact-type`
|
||||
FROM `contact` WHERE `uid` = ? AND `nurl` = ? AND `network` != ?",
|
||||
$importer["uid"],
|
||||
$importer["importer_uid"],
|
||||
normalise_link($author["link"]),
|
||||
NETWORK_STATUSNET
|
||||
);
|
||||
|
@ -1444,7 +1529,7 @@ class DFRN
|
|||
$author["network"] = $contact_old["network"];
|
||||
} else {
|
||||
if (!$onlyfetch) {
|
||||
logger("Contact ".$author["link"]." wasn't found for user ".$importer["uid"]." XML: ".$xml, LOGGER_DEBUG);
|
||||
logger("Contact ".$author["link"]." wasn't found for user ".$importer["importer_uid"]." XML: ".$xml, LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
$author["contact-id"] = $importer["id"];
|
||||
|
@ -1640,7 +1725,7 @@ class DFRN
|
|||
|
||||
Contact::updateAvatar(
|
||||
$author['avatar'],
|
||||
$importer['uid'],
|
||||
$importer['importer_uid'],
|
||||
$contact['id'],
|
||||
(strtotime($contact['avatar-date']) > strtotime($contact_old['avatar-date']) || ($author['avatar'] != $contact_old['avatar']))
|
||||
);
|
||||
|
@ -1658,7 +1743,7 @@ class DFRN
|
|||
$poco["contact-type"] = $contact["contact-type"];
|
||||
$gcid = GContact::update($poco);
|
||||
|
||||
GContact::link($gcid, $importer["uid"], $contact["id"]);
|
||||
GContact::link($gcid, $importer["importer_uid"], $contact["id"]);
|
||||
}
|
||||
|
||||
return $author;
|
||||
|
@ -2079,8 +2164,8 @@ class DFRN
|
|||
return false;
|
||||
}
|
||||
|
||||
$fields = ['title' => $item["title"], 'body' => $item["body"],
|
||||
'tag' => $item["tag"], 'changed' => DateTimeFormat::utcNow(),
|
||||
$fields = ['title' => defaults($item, 'title', ''), 'body' => defaults($item, 'body', ''),
|
||||
'tag' => defaults($item, 'tag', ''), 'changed' => DateTimeFormat::utcNow(),
|
||||
'edited' => DateTimeFormat::utc($item["edited"])];
|
||||
|
||||
$condition = ["`uri` = ? AND `uid` IN (0, ?)", $item["uri"], $importer["importer_uid"]];
|
||||
|
@ -2454,7 +2539,7 @@ class DFRN
|
|||
$purifier = new HTMLPurifier($config);
|
||||
$item['body'] = $purifier->purify($item['body']);
|
||||
|
||||
$item['body'] = @html2bbcode($item['body']);
|
||||
$item['body'] = @HTML::toBBCode($item['body']);
|
||||
}
|
||||
|
||||
/// @todo We should check for a repeated post and if we know the repeated author.
|
||||
|
@ -2614,11 +2699,11 @@ class DFRN
|
|||
// Is it an event?
|
||||
if ($item["object-type"] == ACTIVITY_OBJ_EVENT) {
|
||||
logger("Item ".$item["uri"]." seems to contain an event.", LOGGER_DEBUG);
|
||||
$ev = bbtoevent($item["body"]);
|
||||
$ev = Event::fromBBCode($item["body"]);
|
||||
if ((x($ev, "desc") || x($ev, "summary")) && x($ev, "start")) {
|
||||
logger("Event in item ".$item["uri"]." was found.", LOGGER_DEBUG);
|
||||
$ev["cid"] = $importer["id"];
|
||||
$ev["uid"] = $importer["uid"];
|
||||
$ev["uid"] = $importer["importer_uid"];
|
||||
$ev["uri"] = $item["uri"];
|
||||
$ev["edited"] = $item["edited"];
|
||||
$ev["private"] = $item["private"];
|
||||
|
@ -2627,13 +2712,13 @@ class DFRN
|
|||
$r = q(
|
||||
"SELECT `id` FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
|
||||
dbesc($item["uri"]),
|
||||
intval($importer["uid"])
|
||||
intval($importer["importer_uid"])
|
||||
);
|
||||
if (DBM::is_result($r)) {
|
||||
$ev["id"] = $r[0]["id"];
|
||||
}
|
||||
|
||||
$event_id = event_store($ev);
|
||||
$event_id = Event::store($ev);
|
||||
logger("Event ".$event_id." was stored", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
@ -2682,6 +2767,14 @@ class DFRN
|
|||
return true;
|
||||
}
|
||||
} else { // $entrytype == DFRN_TOP_LEVEL
|
||||
if ($importer["readonly"]) {
|
||||
logger('ignoring read-only contact '.$importer["id"]);
|
||||
return;
|
||||
}
|
||||
if ($importer["uid"] == 0) {
|
||||
logger("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
if (!link_compare($item["owner-link"], $importer["url"])) {
|
||||
/*
|
||||
* The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery,
|
||||
|
@ -2737,10 +2830,10 @@ class DFRN
|
|||
return false;
|
||||
}
|
||||
|
||||
$condition = ["`uri` = ? AND `uid` = ? AND NOT `file` LIKE '%[%'", $uri, $importer["uid"]];
|
||||
$condition = ["`uri` = ? AND `uid` = ? AND NOT `file` LIKE '%[%'", $uri, $importer["importer_uid"]];
|
||||
$item = dba::selectFirst('item', ['id', 'parent', 'contact-id'], $condition);
|
||||
if (!DBM::is_result($item)) {
|
||||
logger("Item with uri " . $uri . " for user " . $importer["uid"] . " wasn't found.", LOGGER_DEBUG);
|
||||
logger("Item with uri " . $uri . " for user " . $importer["importer_uid"] . " wasn't found.", LOGGER_DEBUG);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2809,7 +2902,7 @@ class DFRN
|
|||
$xpath->registerNamespace("statusnet", NAMESPACE_STATUSNET);
|
||||
|
||||
$header = [];
|
||||
$header["uid"] = $importer["uid"];
|
||||
$header["uid"] = $importer["importer_uid"];
|
||||
$header["network"] = NETWORK_DFRN;
|
||||
$header["type"] = "remote";
|
||||
$header["wall"] = 0;
|
||||
|
@ -2828,7 +2921,7 @@ class DFRN
|
|||
self::fetchauthor($xpath, $doc->firstChild, $importer, "dfrn:owner", false, $xml);
|
||||
}
|
||||
|
||||
logger("Import DFRN message for user " . $importer["uid"] . " from contact " . $importer["id"], LOGGER_DEBUG);
|
||||
logger("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG);
|
||||
|
||||
// The account type is new since 3.5.1
|
||||
if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) {
|
||||
|
@ -2854,21 +2947,16 @@ class DFRN
|
|||
self::processRelocation($xpath, $relocation, $importer);
|
||||
}
|
||||
|
||||
if ($importer["readonly"]) {
|
||||
// We aren't receiving stuff from this person. But we will quietly ignore them
|
||||
// rather than a blatant "go away" message.
|
||||
logger('ignoring contact '.$importer["id"]);
|
||||
return 403;
|
||||
}
|
||||
if (($importer["uid"] != 0) && !$importer["readonly"]) {
|
||||
$mails = $xpath->query("/atom:feed/dfrn:mail");
|
||||
foreach ($mails as $mail) {
|
||||
self::processMail($xpath, $mail, $importer);
|
||||
}
|
||||
|
||||
$mails = $xpath->query("/atom:feed/dfrn:mail");
|
||||
foreach ($mails as $mail) {
|
||||
self::processMail($xpath, $mail, $importer);
|
||||
}
|
||||
|
||||
$suggestions = $xpath->query("/atom:feed/dfrn:suggest");
|
||||
foreach ($suggestions as $suggestion) {
|
||||
self::processSuggestion($xpath, $suggestion, $importer);
|
||||
$suggestions = $xpath->query("/atom:feed/dfrn:suggest");
|
||||
foreach ($suggestions as $suggestion) {
|
||||
self::processSuggestion($xpath, $suggestion, $importer);
|
||||
}
|
||||
}
|
||||
|
||||
$deletions = $xpath->query("/atom:feed/at:deleted-entry");
|
||||
|
@ -2896,7 +2984,7 @@ class DFRN
|
|||
self::processEntry($header, $xpath, $entry, $importer, $xml);
|
||||
}
|
||||
}
|
||||
logger("Import done for user " . $importer["uid"] . " from contact " . $importer["id"], LOGGER_DEBUG);
|
||||
logger("Import done for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG);
|
||||
return 200;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ use Friendica\Util\Crypto;
|
|||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\XML;
|
||||
use Friendica\Util\Map;
|
||||
use dba;
|
||||
use SimpleXMLElement;
|
||||
|
||||
|
@ -46,60 +47,140 @@ class Diaspora
|
|||
/**
|
||||
* @brief Return a list of relay servers
|
||||
*
|
||||
* This is an experimental Diaspora feature.
|
||||
* The list contains not only the official relays but also servers that we serve directly
|
||||
*
|
||||
* @param integer $item_id The id of the item that is sent
|
||||
* @param array $contacts The previously fetched contacts
|
||||
*
|
||||
* @return array of relay servers
|
||||
*/
|
||||
public static function relayList()
|
||||
public static function relayList($item_id, $contacts = [])
|
||||
{
|
||||
$serverlist = [];
|
||||
|
||||
// Fetching relay servers
|
||||
$serverdata = Config::get("system", "relay_server");
|
||||
if ($serverdata == "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
$relay = [];
|
||||
|
||||
$servers = explode(",", $serverdata);
|
||||
|
||||
foreach ($servers as $server) {
|
||||
$server = trim($server);
|
||||
$addr = "relay@".str_replace("http://", "", normalise_link($server));
|
||||
$batch = $server."/receive/public";
|
||||
|
||||
$relais = q(
|
||||
"SELECT `batch`, `id`, `name`,`network` FROM `contact` WHERE `uid` = 0 AND `batch` = '%s' AND `addr` = '%s' AND `nurl` = '%s' LIMIT 1",
|
||||
dbesc($batch),
|
||||
dbesc($addr),
|
||||
dbesc(normalise_link($server))
|
||||
);
|
||||
|
||||
if (!$relais) {
|
||||
$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')",
|
||||
DateTimeFormat::utcNow(),
|
||||
dbesc($addr),
|
||||
dbesc($addr),
|
||||
dbesc($server),
|
||||
dbesc(normalise_link($server)),
|
||||
dbesc($batch),
|
||||
dbesc(NETWORK_DIASPORA),
|
||||
intval(CONTACT_IS_FOLLOWER),
|
||||
dbesc(DateTimeFormat::utcNow()),
|
||||
dbesc(DateTimeFormat::utcNow()),
|
||||
dbesc(DateTimeFormat::utcNow())
|
||||
);
|
||||
|
||||
$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];
|
||||
if ($serverdata != "") {
|
||||
$servers = explode(",", $serverdata);
|
||||
foreach ($servers as $server) {
|
||||
$serverlist[$server] = trim($server);
|
||||
}
|
||||
}
|
||||
|
||||
return $relay;
|
||||
if (Config::get("system", "relay_directly", false)) {
|
||||
// Servers that want to get all content
|
||||
$servers = dba::select('gserver', ['url'], ['relay-subscribe' => true, 'relay-scope' => 'all']);
|
||||
while ($server = dba::fetch($servers)) {
|
||||
$serverlist[$server['url']] = $server['url'];
|
||||
}
|
||||
|
||||
// All tags of the current post
|
||||
$condition = ['otype' => TERM_OBJ_POST, 'type' => TERM_HASHTAG, 'oid' => $item_id];
|
||||
$tags = dba::select('term', ['term'], $condition);
|
||||
$taglist = [];
|
||||
while ($tag = dba::fetch($tags)) {
|
||||
$taglist[] = $tag['term'];
|
||||
}
|
||||
|
||||
// All servers who wants content with this tag
|
||||
$tagserverlist = [];
|
||||
if (!empty($taglist)) {
|
||||
$tagserver = dba::select('gserver-tag', ['gserver-id'], ['tag' => $taglist]);
|
||||
while ($server = dba::fetch($tagserver)) {
|
||||
$tagserverlist[] = $server['gserver-id'];
|
||||
}
|
||||
}
|
||||
|
||||
// All adresses with the given id
|
||||
if (!empty($tagserverlist)) {
|
||||
$servers = dba::select('gserver', ['url'], ['relay-subscribe' => true, 'relay-scope' => 'tags', 'id' => $tagserverlist]);
|
||||
while ($server = dba::fetch($servers)) {
|
||||
$serverlist[$server['url']] = $server['url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we are collecting all relay contacts
|
||||
foreach ($serverlist as $server_url) {
|
||||
// We don't send messages to ourselves
|
||||
if (link_compare($server_url, System::baseUrl())) {
|
||||
continue;
|
||||
}
|
||||
$contact = self::getRelayContact($server_url);
|
||||
if (is_bool($contact)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$exists = false;
|
||||
foreach ($contacts as $entry) {
|
||||
if ($entry['batch'] == $contact['batch']) {
|
||||
$exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exists) {
|
||||
$contacts[] = $contact;
|
||||
}
|
||||
}
|
||||
|
||||
return $contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a contact for a given server address or creates a dummy entry
|
||||
*
|
||||
* @param string $server_url The url of the server
|
||||
* @return array with the contact
|
||||
*/
|
||||
private static function getRelayContact($server_url)
|
||||
{
|
||||
$batch = $server_url . '/receive/public';
|
||||
|
||||
$fields = ['batch', 'id', 'name', 'network', 'archive', 'blocked'];
|
||||
|
||||
// Fetch the relay contact
|
||||
$condition = ['uid' => 0, 'network' => NETWORK_DIASPORA, 'batch' => $batch,
|
||||
'contact-type' => ACCOUNT_TYPE_RELAY];
|
||||
$contact = dba::selectFirst('contact', $fields, $condition);
|
||||
|
||||
// If there is nothing found, we check if there is some unmarked relay
|
||||
// This code segment can be removed before the release 2018-05
|
||||
if (!DBM::is_result($contact)) {
|
||||
$condition = ['uid' => 0, 'network' => NETWORK_DIASPORA, 'batch' => $batch,
|
||||
'name' => 'relay', 'nick' => 'relay', 'url' => $server_url];
|
||||
$contact = dba::selectFirst('contact', $fields, $condition);
|
||||
|
||||
if (DBM::is_result($contact)) {
|
||||
// Mark the relay account as a relay account
|
||||
$fields = ['contact-type' => ACCOUNT_TYPE_RELAY];
|
||||
dba::update('contact', $fields, ['id' => $contact['id']]);
|
||||
}
|
||||
}
|
||||
if (DBM::is_result($contact)) {
|
||||
if ($contact['archive'] || $contact['blocked']) {
|
||||
return false;
|
||||
}
|
||||
return $contact;
|
||||
} else {
|
||||
$fields = ['uid' => 0, 'created' => DateTimeFormat::utcNow(),
|
||||
'name' => 'relay', 'nick' => 'relay',
|
||||
'url' => $server_url, 'nurl' => normalise_link($server_url),
|
||||
'batch' => $batch, 'network' => NETWORK_DIASPORA,
|
||||
'rel' => CONTACT_IS_FOLLOWER, 'blocked' => false,
|
||||
'contact-type' => ACCOUNT_TYPE_RELAY,
|
||||
'pending' => false, 'writable' => true];
|
||||
dba::insert('contact', $fields);
|
||||
|
||||
$fields = ['batch', 'id', 'name', 'network'];
|
||||
$contact = dba::selectFirst('contact', $fields, $condition);
|
||||
if (DBM::is_result($contact)) {
|
||||
return $contact;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// It should never happen that we arrive here
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,11 +303,20 @@ class Diaspora
|
|||
|
||||
$signable_data = $msg.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
|
||||
|
||||
if ($handle == '') {
|
||||
logger('No author could be decoded. Discarding. Message: ' . $envelope);
|
||||
return false;
|
||||
}
|
||||
|
||||
$key = self::key($handle);
|
||||
if ($key == '') {
|
||||
logger("Couldn't get a key for handle " . $handle . ". Discarding.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$verify = Crypto::rsaVerify($signable_data, $sig, $key);
|
||||
if (!$verify) {
|
||||
logger('Message did not verify. Discarding.');
|
||||
logger('Message from ' . $handle . ' did not verify. Discarding.');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -322,7 +412,16 @@ class Diaspora
|
|||
// Get the senders' public key
|
||||
$key_id = $base->sig[0]->attributes()->key_id[0];
|
||||
$author_addr = base64_decode($key_id);
|
||||
if ($author_addr == '') {
|
||||
logger('No author could be decoded. Discarding. Message: ' . $xml);
|
||||
System::httpExit(400);
|
||||
}
|
||||
|
||||
$key = self::key($author_addr);
|
||||
if ($key == '') {
|
||||
logger("Couldn't get a key for handle " . $author_addr . ". Discarding.");
|
||||
System::httpExit(400);
|
||||
}
|
||||
|
||||
$verify = Crypto::rsaVerify($signed_data, $signature, $key);
|
||||
if (!$verify) {
|
||||
|
@ -2212,7 +2311,10 @@ class Diaspora
|
|||
}
|
||||
|
||||
logger('Received participation for ID: '.$item['id'].' - Contact: '.$contact_id.' - Server: '.$server, LOGGER_DEBUG);
|
||||
dba::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]);
|
||||
|
||||
if (!dba::exists('participation', ['iid' => $item['id'], 'server' => $server])) {
|
||||
dba::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]);
|
||||
}
|
||||
|
||||
// Send all existing comments and likes to the requesting server
|
||||
$comments = dba::p("SELECT `item`.`id`, `item`.`verb`, `contact`.`self`
|
||||
|
@ -3143,7 +3245,7 @@ class Diaspora
|
|||
*
|
||||
* @return string The message that will be transmitted to other servers
|
||||
*/
|
||||
private static function buildMessage($msg, $user, $contact, $prvkey, $pubkey, $public = false)
|
||||
public static function buildMessage($msg, $user, $contact, $prvkey, $pubkey, $public = false)
|
||||
{
|
||||
// The message is put into an envelope with the sender's signature
|
||||
$envelope = self::buildMagicEnvelope($msg, $user);
|
||||
|
@ -3197,13 +3299,16 @@ class Diaspora
|
|||
}
|
||||
|
||||
$logid = random_string(4);
|
||||
|
||||
$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
|
||||
|
||||
// Fetch the fcontact entry when there is missing data
|
||||
// Will possibly happen when data is transmitted to a DFRN contact
|
||||
if (empty($dest_url) && !empty($contact['addr'])) {
|
||||
// We always try to use the data from the fcontact table.
|
||||
// This is important for transmitting data to Friendica servers.
|
||||
if (!empty($contact['addr'])) {
|
||||
$fcontact = self::personByHandle($contact['addr']);
|
||||
$dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]);
|
||||
if (!empty($fcontact)) {
|
||||
$dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$dest_url) {
|
||||
|
@ -3227,10 +3332,10 @@ class Diaspora
|
|||
}
|
||||
}
|
||||
|
||||
logger("transmit: ".$logid."-".$guid." returns: ".$return_code);
|
||||
logger("transmit: ".$logid."-".$guid." to ".$dest_url." returns: ".$return_code);
|
||||
|
||||
if (!$return_code || (($return_code == 503) && (stristr($a->get_curl_headers(), "retry-after")))) {
|
||||
if (!$no_queue) {
|
||||
if (!$no_queue && ($contact['contact-type'] != ACCOUNT_TYPE_RELAY)) {
|
||||
logger("queue message");
|
||||
// queue message for redelivery
|
||||
Queue::add($contact["id"], NETWORK_DIASPORA, $envelope, $public_batch, $guid);
|
||||
|
@ -3588,10 +3693,18 @@ class Diaspora
|
|||
$eventdata['description'] = html_entity_decode(BBCode::toMarkdown($event['desc']));
|
||||
}
|
||||
if ($event['location']) {
|
||||
$event['location'] = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $event['location']);
|
||||
$coord = Map::getCoordinates($event['location']);
|
||||
|
||||
$location = [];
|
||||
$location["address"] = html_entity_decode(BBCode::toMarkdown($event['location']));
|
||||
$location["lat"] = 0;
|
||||
$location["lng"] = 0;
|
||||
if (!empty($coord['lat']) && !empty($coord['lon'])) {
|
||||
$location["lat"] = $coord['lat'];
|
||||
$location["lng"] = $coord['lon'];
|
||||
} else {
|
||||
$location["lat"] = 0;
|
||||
$location["lng"] = 0;
|
||||
}
|
||||
$eventdata['location'] = $location;
|
||||
}
|
||||
|
||||
|
@ -3638,6 +3751,12 @@ class Diaspora
|
|||
$title = $item["title"];
|
||||
$body = $item["body"];
|
||||
|
||||
if ($item['author-link'] != $item['owner-link']) {
|
||||
require_once 'mod/share.php';
|
||||
$body = share_header($item['author-name'], $item['author-link'], $item['author-avatar'],
|
||||
"", $item['created'], $item['plink']) . $body . '[/share]';
|
||||
}
|
||||
|
||||
// convert to markdown
|
||||
$body = html_entity_decode(BBCode::toMarkdown($body));
|
||||
|
||||
|
@ -3685,7 +3804,13 @@ class Diaspora
|
|||
if (count($event)) {
|
||||
$message['event'] = $event;
|
||||
|
||||
/// @todo Once Diaspora supports it, we will remove the body
|
||||
if (!empty($event['location']['address']) &&
|
||||
!empty($event['location']['lat']) &&
|
||||
!empty($event['location']['lng'])) {
|
||||
$message['location'] = $event['location'];
|
||||
}
|
||||
|
||||
/// @todo Once Diaspora supports it, we will remove the body and the location hack above
|
||||
// $message['text'] = '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
namespace Friendica\Protocol;
|
||||
|
||||
require_once 'include/html2plain.php';
|
||||
use Friendica\Content\Text\HTML;
|
||||
|
||||
/**
|
||||
* @brief Email class
|
||||
|
@ -111,7 +111,7 @@ class Email
|
|||
if (trim($ret['body']) == '') {
|
||||
$ret['body'] = self::messageGetPart($mbox, $uid, $struc, 0, 'plain');
|
||||
} else {
|
||||
$ret['body'] = html2bbcode($ret['body']);
|
||||
$ret['body'] = HTML::toBBCode($ret['body']);
|
||||
}
|
||||
} else {
|
||||
$text = '';
|
||||
|
@ -128,7 +128,7 @@ class Email
|
|||
}
|
||||
}
|
||||
if (trim($html) != '') {
|
||||
$ret['body'] = html2bbcode($html);
|
||||
$ret['body'] = HTML::toBBCode($html);
|
||||
} else {
|
||||
$ret['body'] = $text;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ class Email
|
|||
$body .= "Content-Transfer-Encoding: 8bit\n";
|
||||
$body .= "Content-Type: text/plain; charset=utf-8; format=flowed\n\n";
|
||||
|
||||
$body .= html2plain($html)."\n";
|
||||
$body .= HTML::toPlaintext($html)."\n";
|
||||
|
||||
$body .= "--=_".$part."\n";
|
||||
$body .= "Content-Transfer-Encoding: 8bit\n";
|
||||
|
|
|
@ -10,12 +10,13 @@ use Friendica\Database\DBM;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Content\Text\HTML;
|
||||
|
||||
use dba;
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2bbcode.php';
|
||||
require_once 'include/items.php';
|
||||
|
||||
/**
|
||||
|
@ -85,9 +86,9 @@ class Feed {
|
|||
if ($xpath->query('/atom:feed')->length > 0) {
|
||||
$alternate = $xpath->query("atom:link[@rel='alternate']")->item(0)->attributes;
|
||||
if (is_object($alternate)) {
|
||||
foreach ($alternate AS $attributes) {
|
||||
if ($attributes->name == "href") {
|
||||
$author["author-link"] = $attributes->textContent;
|
||||
foreach ($alternate AS $attribute) {
|
||||
if ($attribute->name == "href") {
|
||||
$author["author-link"] = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,9 +99,9 @@ class Feed {
|
|||
if ($author["author-link"] == "") {
|
||||
$self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes;
|
||||
if (is_object($self)) {
|
||||
foreach ($self AS $attributes) {
|
||||
if ($attributes->name == "href") {
|
||||
$author["author-link"] = $attributes->textContent;
|
||||
foreach ($self AS $attribute) {
|
||||
if ($attribute->name == "href") {
|
||||
$author["author-link"] = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,9 +141,9 @@ class Feed {
|
|||
}
|
||||
$avatar = $xpath->evaluate("atom:author/atom:link[@rel='avatar']")->item(0)->attributes;
|
||||
if (is_object($avatar)) {
|
||||
foreach ($avatar AS $attributes) {
|
||||
if ($attributes->name == "href") {
|
||||
$author["author-avatar"] = $attributes->textContent;
|
||||
foreach ($avatar AS $attribute) {
|
||||
if ($attribute->name == "href") {
|
||||
$author["author-avatar"] = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,13 +208,10 @@ class Feed {
|
|||
}
|
||||
|
||||
$items = [];
|
||||
// Importing older entries first
|
||||
for($i = $entries->length - 1; $i >= 0;--$i) {
|
||||
$entry = $entries->item($i);
|
||||
|
||||
$entrylist = [];
|
||||
|
||||
foreach ($entries AS $entry) {
|
||||
$entrylist[] = $entry;
|
||||
}
|
||||
foreach (array_reverse($entrylist) AS $entry) {
|
||||
$item = array_merge($header, $author);
|
||||
|
||||
$alternate = $xpath->query("atom:link[@rel='alternate']", $entry)->item(0)->attributes;
|
||||
|
@ -221,9 +219,9 @@ class Feed {
|
|||
$alternate = $xpath->query("atom:link", $entry)->item(0)->attributes;
|
||||
}
|
||||
if (is_object($alternate)) {
|
||||
foreach ($alternate AS $attributes) {
|
||||
if ($attributes->name == "href") {
|
||||
$item["plink"] = $attributes->textContent;
|
||||
foreach ($alternate AS $attribute) {
|
||||
if ($attribute->name == "href") {
|
||||
$item["plink"] = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -309,20 +307,20 @@ class Feed {
|
|||
|
||||
$attachments = [];
|
||||
|
||||
$enclosures = $xpath->query("enclosure", $entry);
|
||||
$enclosures = $xpath->query("enclosure|atom:link[@rel='enclosure']", $entry);
|
||||
foreach ($enclosures AS $enclosure) {
|
||||
$href = "";
|
||||
$length = "";
|
||||
$type = "";
|
||||
$title = "";
|
||||
|
||||
foreach ($enclosure->attributes AS $attributes) {
|
||||
if ($attributes->name == "url") {
|
||||
$href = $attributes->textContent;
|
||||
} elseif ($attributes->name == "length") {
|
||||
$length = $attributes->textContent;
|
||||
} elseif ($attributes->name == "type") {
|
||||
$type = $attributes->textContent;
|
||||
foreach ($enclosure->attributes AS $attribute) {
|
||||
if (in_array($attribute->name, ["url", "href"])) {
|
||||
$href = $attribute->textContent;
|
||||
} elseif ($attribute->name == "length") {
|
||||
$length = $attribute->textContent;
|
||||
} elseif ($attribute->name == "type") {
|
||||
$type = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
if (strlen($item["attach"])) {
|
||||
|
@ -363,7 +361,7 @@ class Feed {
|
|||
if (self::titleIsBody($item["title"], $body)) {
|
||||
$item["title"] = "";
|
||||
}
|
||||
$item["body"] = html2bbcode($body, $basepath);
|
||||
$item["body"] = HTML::toBBCode($body, $basepath);
|
||||
|
||||
if (($item["body"] == '') && ($item["title"] != '')) {
|
||||
$item["body"] = $item["title"];
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace Friendica\Protocol;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Cache;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\L10n;
|
||||
|
@ -25,7 +26,6 @@ use DOMDocument;
|
|||
use DOMXPath;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2bbcode.php';
|
||||
require_once 'include/items.php';
|
||||
require_once 'mod/share.php';
|
||||
require_once 'include/enotify.php';
|
||||
|
@ -72,8 +72,9 @@ class OStatus
|
|||
|
||||
$contact = null;
|
||||
if ($aliaslink != '') {
|
||||
$condition = ["`uid` = ? AND `alias` = ? AND `network` != ?",
|
||||
$importer["uid"], $aliaslink, NETWORK_STATUSNET];
|
||||
$condition = ["`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
|
||||
$importer["uid"], $aliaslink, NETWORK_STATUSNET,
|
||||
CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
|
||||
$contact = dba::selectFirst('contact', [], $condition);
|
||||
}
|
||||
|
||||
|
@ -82,14 +83,16 @@ class OStatus
|
|||
$aliaslink = $author["author-link"];
|
||||
}
|
||||
|
||||
$condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ?", $importer["uid"],
|
||||
normalise_link($author["author-link"]), normalise_link($aliaslink), NETWORK_STATUSNET];
|
||||
$condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
|
||||
$importer["uid"], normalise_link($author["author-link"]), normalise_link($aliaslink),
|
||||
NETWORK_STATUSNET, CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
|
||||
$contact = dba::selectFirst('contact', [], $condition);
|
||||
}
|
||||
|
||||
if (!DBM::is_result($contact) && ($addr != '')) {
|
||||
$condition = ["`uid` = ? AND `addr` = ? AND `network` != ?",
|
||||
$importer["uid"], $addr, NETWORK_STATUSNET];
|
||||
$condition = ["`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
|
||||
$importer["uid"], $addr, NETWORK_STATUSNET,
|
||||
CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
|
||||
$contact = dba::selectFirst('contact', [], $condition);
|
||||
}
|
||||
|
||||
|
@ -168,7 +171,7 @@ class OStatus
|
|||
|
||||
$value = $xpath->evaluate('atom:author/poco:note/text()', $context)->item(0)->nodeValue;
|
||||
if ($value != "") {
|
||||
$contact["about"] = html2bbcode($value);
|
||||
$contact["about"] = HTML::toBBCode($value);
|
||||
}
|
||||
|
||||
$value = $xpath->evaluate('atom:author/poco:address/poco:formatted/text()', $context)->item(0)->nodeValue;
|
||||
|
@ -243,13 +246,12 @@ class OStatus
|
|||
$xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
|
||||
$xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
|
||||
|
||||
$entries = $xpath->query('/atom:entry');
|
||||
$contact = ["id" => 0];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
// fetch the author
|
||||
$author = self::fetchAuthor($xpath, $entry, $importer, $contact, true);
|
||||
return $author;
|
||||
}
|
||||
// Fetch the first author
|
||||
$authordata = $xpath->query('//author')->item(0);
|
||||
$author = self::fetchAuthor($xpath, $authordata, $importer, $contact, true);
|
||||
return $author;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -557,7 +559,7 @@ class OStatus
|
|||
*/
|
||||
private static function processPost($xpath, $entry, &$item, $importer)
|
||||
{
|
||||
$item["body"] = html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue);
|
||||
$item["body"] = HTML::toBBCode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue);
|
||||
$item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue;
|
||||
if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) || ($item["object-type"] == ACTIVITY_OBJ_EVENT)) {
|
||||
$item["title"] = $xpath->query('atom:title/text()', $entry)->item(0)->nodeValue;
|
||||
|
@ -658,8 +660,9 @@ class OStatus
|
|||
// Mastodon Content Warning
|
||||
if (($item["verb"] == ACTIVITY_POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) {
|
||||
$clear_text = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue;
|
||||
|
||||
$item["body"] = html2bbcode($clear_text) . '[spoiler]' . $item["body"] . '[/spoiler]';
|
||||
if (!empty($clear_text)) {
|
||||
$item['content-warning'] = HTML::toBBCode($clear_text);
|
||||
}
|
||||
}
|
||||
|
||||
if (($self != '') && empty($item['protocol'])) {
|
||||
|
@ -670,9 +673,11 @@ class OStatus
|
|||
self::fetchConversation($item['conversation-href'], $item['conversation-uri']);
|
||||
}
|
||||
|
||||
if (isset($item["parent-uri"]) && ($related != '')) {
|
||||
if (isset($item["parent-uri"])) {
|
||||
if (!dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item['parent-uri']])) {
|
||||
self::fetchRelated($related, $item["parent-uri"], $importer);
|
||||
if ($related != '') {
|
||||
self::fetchRelated($related, $item["parent-uri"], $importer);
|
||||
}
|
||||
} else {
|
||||
logger('Reply with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.', LOGGER_DEBUG);
|
||||
}
|
||||
|
@ -1014,7 +1019,7 @@ class OStatus
|
|||
$item["author-link"] = $orig_author["author-link"];
|
||||
$item["author-avatar"] = $orig_author["author-avatar"];
|
||||
|
||||
$item["body"] = html2bbcode($orig_body);
|
||||
$item["body"] = HTML::toBBCode($orig_body);
|
||||
$item["created"] = $orig_created;
|
||||
$item["edited"] = $orig_edited;
|
||||
|
||||
|
@ -1283,6 +1288,13 @@ class OStatus
|
|||
"rel" => "self", "type" => "application/atom+xml"];
|
||||
XML::addElement($doc, $root, "link", "", $attributes);
|
||||
|
||||
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
|
||||
$condition = ['uid' => $owner['uid'], 'self' => false, 'pending' => false,
|
||||
'archive' => false, 'hidden' => false, 'blocked' => false];
|
||||
$members = dba::count('contact', $condition);
|
||||
XML::addElement($doc, $root, "statusnet:group_info", "", ["member_count" => $members]);
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
|
@ -1301,7 +1313,7 @@ class OStatus
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Adds attachement data to the XML document
|
||||
* @brief Adds attachment data to the XML document
|
||||
*
|
||||
* @param object $doc XML document
|
||||
* @param object $root XML root element where the hub links are added
|
||||
|
@ -1316,11 +1328,13 @@ class OStatus
|
|||
switch ($siteinfo["type"]) {
|
||||
case 'photo':
|
||||
$imgdata = Image::getInfoFromURL($siteinfo["image"]);
|
||||
$attributes = ["rel" => "enclosure",
|
||||
"href" => $siteinfo["image"],
|
||||
"type" => $imgdata["mime"],
|
||||
"length" => intval($imgdata["size"])];
|
||||
XML::addElement($doc, $root, "link", "", $attributes);
|
||||
if ($imgdata) {
|
||||
$attributes = ["rel" => "enclosure",
|
||||
"href" => $siteinfo["image"],
|
||||
"type" => $imgdata["mime"],
|
||||
"length" => intval($imgdata["size"])];
|
||||
XML::addElement($doc, $root, "link", "", $attributes);
|
||||
}
|
||||
break;
|
||||
case 'video':
|
||||
$attributes = ["rel" => "enclosure",
|
||||
|
@ -1336,12 +1350,14 @@ class OStatus
|
|||
|
||||
if (!Config::get('system', 'ostatus_not_attach_preview') && ($siteinfo["type"] != "photo") && isset($siteinfo["image"])) {
|
||||
$imgdata = Image::getInfoFromURL($siteinfo["image"]);
|
||||
$attributes = ["rel" => "enclosure",
|
||||
"href" => $siteinfo["image"],
|
||||
"type" => $imgdata["mime"],
|
||||
"length" => intval($imgdata["size"])];
|
||||
if ($imgdata) {
|
||||
$attributes = ["rel" => "enclosure",
|
||||
"href" => $siteinfo["image"],
|
||||
"type" => $imgdata["mime"],
|
||||
"length" => intval($imgdata["size"])];
|
||||
|
||||
XML::addElement($doc, $root, "link", "", $attributes);
|
||||
XML::addElement($doc, $root, "link", "", $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
$arr = explode('[/attach],', $item['attach']);
|
||||
|
@ -1374,16 +1390,22 @@ class OStatus
|
|||
*
|
||||
* @return object author element
|
||||
*/
|
||||
private static function addAuthor($doc, $owner)
|
||||
private static function addAuthor($doc, $owner, $show_profile = true)
|
||||
{
|
||||
$profile = dba::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid'], 'is-default' => true]);
|
||||
$author = $doc->createElement("author");
|
||||
XML::addElement($doc, $author, "id", $owner["url"]);
|
||||
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON);
|
||||
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
|
||||
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_GROUP);
|
||||
} else {
|
||||
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON);
|
||||
}
|
||||
XML::addElement($doc, $author, "uri", $owner["url"]);
|
||||
XML::addElement($doc, $author, "name", $owner["nick"]);
|
||||
XML::addElement($doc, $author, "email", $owner["addr"]);
|
||||
XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, 7));
|
||||
if ($show_profile) {
|
||||
XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, 7));
|
||||
}
|
||||
|
||||
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]];
|
||||
XML::addElement($doc, $author, "link", "", $attributes);
|
||||
|
@ -1408,15 +1430,17 @@ class OStatus
|
|||
|
||||
XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]);
|
||||
XML::addElement($doc, $author, "poco:displayName", $owner["name"]);
|
||||
XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, 7));
|
||||
if ($show_profile) {
|
||||
XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, 7));
|
||||
|
||||
if (trim($owner["location"]) != "") {
|
||||
$element = $doc->createElement("poco:address");
|
||||
XML::addElement($doc, $element, "poco:formatted", $owner["location"]);
|
||||
$author->appendChild($element);
|
||||
if (trim($owner["location"]) != "") {
|
||||
$element = $doc->createElement("poco:address");
|
||||
XML::addElement($doc, $element, "poco:formatted", $owner["location"]);
|
||||
$author->appendChild($element);
|
||||
}
|
||||
}
|
||||
|
||||
if (DBM::is_result($profile)) {
|
||||
if (DBM::is_result($profile) && !$show_profile) {
|
||||
if (trim($profile["homepage"]) != "") {
|
||||
$urls = $doc->createElement("poco:urls");
|
||||
XML::addElement($doc, $urls, "poco:type", "homepage");
|
||||
|
@ -1427,11 +1451,12 @@ class OStatus
|
|||
|
||||
XML::addElement($doc, $author, "followers", "", ["url" => System::baseUrl()."/viewcontacts/".$owner["nick"]]);
|
||||
XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]);
|
||||
|
||||
if ($profile["publish"]) {
|
||||
XML::addElement($doc, $author, "mastodon:scope", "public");
|
||||
}
|
||||
}
|
||||
|
||||
if ($profile["publish"]) {
|
||||
XML::addElement($doc, $author, "mastodon:scope", "public");
|
||||
}
|
||||
return $author;
|
||||
}
|
||||
|
||||
|
@ -1593,7 +1618,7 @@ class OStatus
|
|||
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
$title = self::entryHeader($doc, $entry, $owner, $toplevel);
|
||||
$title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
|
||||
|
||||
$r = q(
|
||||
"SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1",
|
||||
|
@ -1622,7 +1647,7 @@ class OStatus
|
|||
|
||||
self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false);
|
||||
|
||||
$author = self::addAuthor($doc, $contact);
|
||||
$author = self::addAuthor($doc, $contact, false);
|
||||
$as_object->appendChild($author);
|
||||
|
||||
$as_object2 = $doc->createElement("activity:object");
|
||||
|
@ -1664,7 +1689,7 @@ class OStatus
|
|||
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
$title = self::entryHeader($doc, $entry, $owner, $toplevel);
|
||||
$title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
|
||||
|
||||
$verb = NAMESPACE_ACTIVITY_SCHEMA."favorite";
|
||||
self::entryContent($doc, $entry, $item, $owner, "Favorite", $verb, false);
|
||||
|
@ -1787,7 +1812,7 @@ class OStatus
|
|||
|
||||
$item["body"] = sprintf($message, $owner["nick"], $contact["nick"]);
|
||||
|
||||
self::entryHeader($doc, $entry, $owner, $toplevel);
|
||||
self::entryHeader($doc, $entry, $owner, $item, $toplevel);
|
||||
|
||||
self::entryContent($doc, $entry, $item, $owner, $title);
|
||||
|
||||
|
@ -1815,7 +1840,7 @@ class OStatus
|
|||
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
|
||||
}
|
||||
|
||||
$title = self::entryHeader($doc, $entry, $owner, $toplevel);
|
||||
$title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
|
||||
|
||||
XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
|
||||
|
||||
|
@ -1836,12 +1861,18 @@ class OStatus
|
|||
*
|
||||
* @return string The title for the element
|
||||
*/
|
||||
private static function entryHeader($doc, &$entry, $owner, $toplevel)
|
||||
private static function entryHeader($doc, &$entry, $owner, $item, $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"]);
|
||||
|
||||
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
|
||||
$contact = self::contactEntry($item['author-link'], $owner);
|
||||
$author = self::addAuthor($doc, $contact, false);
|
||||
$entry->appendChild($author);
|
||||
}
|
||||
} else {
|
||||
$entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry");
|
||||
|
||||
|
@ -1996,12 +2027,10 @@ class OStatus
|
|||
$mentioned = $newmentions;
|
||||
|
||||
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"] || $r[0]["prv"]) {
|
||||
$condition = ['uid' => $owner['uid'], 'nurl' => normalise_link($mention)];
|
||||
$contact = dba::selectFirst('contact', ['forum', 'prv', 'self', 'contact-type'], $condition);
|
||||
if ($contact["forum"] || $contact["prv"] || ($owner['contact-type'] == ACCOUNT_TYPE_COMMUNITY) ||
|
||||
($contact['self'] && ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY))) {
|
||||
XML::addElement($doc, $entry, "link", "",
|
||||
[
|
||||
"rel" => "mentioned",
|
||||
|
@ -2018,6 +2047,12 @@ class OStatus
|
|||
}
|
||||
}
|
||||
|
||||
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
|
||||
XML::addElement($doc, $entry, "link", "", ["rel" => "mentioned",
|
||||
"ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group",
|
||||
"href" => $owner['url']]);
|
||||
}
|
||||
|
||||
if (!$item["private"]) {
|
||||
XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention",
|
||||
"href" => "http://activityschema.org/collection/public"]);
|
||||
|
@ -2094,7 +2129,7 @@ class OStatus
|
|||
}
|
||||
|
||||
$owner = dba::fetch_first(
|
||||
"SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
|
||||
"SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`, `user`.`account-type`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`self` AND `user`.`nickname` = ? LIMIT 1",
|
||||
$owner_nick
|
||||
|
@ -2119,22 +2154,23 @@ class OStatus
|
|||
$sql_extra .= sprintf(" AND `item`.`object-type` = '%s' ", dbesc(ACTIVITY_OBJ_COMMENT));
|
||||
}
|
||||
|
||||
if ($owner['account-type'] != ACCOUNT_TYPE_COMMUNITY) {
|
||||
$sql_extra .= sprintf(" AND `item`.`contact-id` = %d AND `item`.`author-id` = %d ", intval($owner["id"]), intval($authorid));
|
||||
}
|
||||
|
||||
$items = q(
|
||||
"SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`)
|
||||
STRAIGHT_JOIN `thread` ON `thread`.`iid` = `item`.`parent`
|
||||
WHERE `item`.`uid` = %d
|
||||
AND `item`.`contact-id` = %d
|
||||
AND `item`.`author-id` = %d
|
||||
AND `item`.`created` > '%s'
|
||||
AND NOT `item`.`deleted`
|
||||
AND NOT `item`.`private`
|
||||
AND `item`.`visible`
|
||||
AND `item`.`wall`
|
||||
AND `thread`.`network` IN ('%s', '%s')
|
||||
$sql_extra
|
||||
ORDER BY `item`.`created` DESC LIMIT %d",
|
||||
intval($owner["uid"]),
|
||||
intval($owner["id"]),
|
||||
intval($authorid),
|
||||
dbesc($check_date),
|
||||
dbesc(NETWORK_OSTATUS),
|
||||
dbesc(NETWORK_DFRN),
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
namespace Friendica\Protocol;
|
||||
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBM;
|
||||
|
@ -23,7 +24,6 @@ use DOMXPath;
|
|||
use Exception;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2bbcode.php';
|
||||
|
||||
class PortableContact
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ class PortableContact
|
|||
}
|
||||
|
||||
if (isset($entry->aboutMe)) {
|
||||
$about = html2bbcode($entry->aboutMe);
|
||||
$about = HTML::toBBCode($entry->aboutMe);
|
||||
}
|
||||
|
||||
if (isset($entry->gender)) {
|
||||
|
@ -945,6 +945,15 @@ class PortableContact
|
|||
$register_policy = $gserver["register_policy"];
|
||||
$registered_users = $gserver["registered-users"];
|
||||
|
||||
// See discussion under https://forum.friendi.ca/display/0b6b25a8135aabc37a5a0f5684081633
|
||||
// It can happen that a zero date is in the database, but storing it again is forbidden.
|
||||
if ($last_contact < NULL_DATE) {
|
||||
$last_contact = NULL_DATE;
|
||||
}
|
||||
if ($last_failure < NULL_DATE) {
|
||||
$last_failure = NULL_DATE;
|
||||
}
|
||||
|
||||
if (!$force && !self::updateNeeded($gserver["created"], "", $last_failure, $last_contact)) {
|
||||
logger("Use cached data for server ".$server_url, LOGGER_DEBUG);
|
||||
return ($last_contact >= $last_failure);
|
||||
|
@ -1302,7 +1311,7 @@ class PortableContact
|
|||
if (isset($data->version)) {
|
||||
$network = NETWORK_DFRN;
|
||||
|
||||
$noscrape = $data->no_scrape_url;
|
||||
$noscrape = defaults($data->no_scrape_url, '');
|
||||
$version = $data->version;
|
||||
$site_name = $data->site_name;
|
||||
$info = $data->info;
|
||||
|
@ -1368,11 +1377,60 @@ class PortableContact
|
|||
$fields['created'] = DateTimeFormat::utcNow();
|
||||
dba::insert('gserver', $fields);
|
||||
}
|
||||
|
||||
if (!$failure && in_array($fields['network'], [NETWORK_DFRN, NETWORK_DIASPORA])) {
|
||||
self::discoverRelay($server_url);
|
||||
}
|
||||
|
||||
logger("End discovery for server " . $server_url, LOGGER_DEBUG);
|
||||
|
||||
return !$failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch relay data from a given server url
|
||||
*
|
||||
* @param string $server_url address of the server
|
||||
*/
|
||||
private static function discoverRelay($server_url)
|
||||
{
|
||||
logger("Discover relay data for server " . $server_url, LOGGER_DEBUG);
|
||||
|
||||
$serverret = Network::curl($server_url."/.well-known/x-social-relay");
|
||||
if (!$serverret["success"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = json_decode($serverret['body']);
|
||||
if (!is_object($data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gserver = dba::selectFirst('gserver', ['id', 'relay-subscribe', 'relay-scope'], ['nurl' => normalise_link($server_url)]);
|
||||
if (!DBM::is_result($gserver)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (($gserver['relay-subscribe'] != $data->subscribe) || ($gserver['relay-scope'] != $data->scope)) {
|
||||
$fields = ['relay-subscribe' => $data->subscribe, 'relay-scope' => $data->scope];
|
||||
dba::update('gserver', $fields, ['id' => $gserver['id']]);
|
||||
}
|
||||
|
||||
dba::delete('gserver-tag', ['gserver-id' => $gserver['id']]);
|
||||
if ($data->scope == 'tags') {
|
||||
// Avoid duplicates
|
||||
$tags = [];
|
||||
foreach ($data->tags as $tag) {
|
||||
$tag = strtolower($tag);
|
||||
$tags[$tag] = $tag;
|
||||
}
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
dba::insert('gserver-tag', ['gserver-id' => $gserver['id'], 'tag' => $tag]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a list of all known servers
|
||||
* @return array List of server urls
|
||||
|
@ -1454,8 +1512,8 @@ class PortableContact
|
|||
$header = ['Authorization: Bearer '.$accesstoken];
|
||||
$serverdata = Network::curl($api, false, $redirects, ['headers' => $header]);
|
||||
if ($serverdata['success']) {
|
||||
$servers = json_decode($serverdata['body']);
|
||||
foreach ($servers->instances as $server) {
|
||||
$servers = json_decode($serverdata['body']);
|
||||
foreach ($servers->instances as $server) {
|
||||
$url = (is_null($server->https_score) ? 'http' : 'https').'://'.$server->name;
|
||||
Worker::add(PRIORITY_LOW, "DiscoverPoCo", "server", $url);
|
||||
}
|
||||
|
@ -1669,7 +1727,7 @@ class PortableContact
|
|||
}
|
||||
|
||||
if (isset($entry->aboutMe)) {
|
||||
$about = html2bbcode($entry->aboutMe);
|
||||
$about = HTML::toBBCode($entry->aboutMe);
|
||||
}
|
||||
|
||||
if (isset($entry->gender)) {
|
||||
|
|
|
@ -109,6 +109,9 @@ class DateTimeFormat
|
|||
* months and days always start with 1.
|
||||
*/
|
||||
if (substr($s, 0, 10) <= '0001-01-01') {
|
||||
if ($s < '0000-00-00') {
|
||||
$s = '0000-00-00';
|
||||
}
|
||||
$d = new DateTime($s . ' + 32 days', new DateTimeZone('UTC'));
|
||||
return str_replace('1', '0', $d->format($format));
|
||||
}
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
* Installation:
|
||||
*
|
||||
* - Change it's owner to whichever user is running the server, ie. ejabberd
|
||||
* $ chown ejabberd:ejabberd /path/to/friendica/scripts/auth_ejabberd.php
|
||||
* $ chown ejabberd:ejabberd /path/to/friendica/bin/auth_ejabberd.php
|
||||
*
|
||||
* - Change the access mode so it is readable only to the user ejabberd and has exec
|
||||
* $ chmod 700 /path/to/friendica/scripts/auth_ejabberd.php
|
||||
* $ chmod 700 /path/to/friendica/bin/auth_ejabberd.php
|
||||
*
|
||||
* - Edit your ejabberd.cfg file, comment out your auth_method and add:
|
||||
* {auth_method, external}.
|
||||
* {extauth_program, "/path/to/friendica/script/auth_ejabberd.php"}.
|
||||
* {extauth_program, "/path/to/friendica/bin/auth_ejabberd.php"}.
|
||||
*
|
||||
* - Restart your ejabberd service, you should be able to login with your friendica auth info
|
||||
*
|
||||
|
|
|
@ -10,17 +10,23 @@ use Friendica\Core\Addon;
|
|||
* Leaflet Map related functions
|
||||
*/
|
||||
class Map {
|
||||
public static function byCoordinates($coord) {
|
||||
public static function byCoordinates($coord, $html_mode = 0) {
|
||||
$coord = trim($coord);
|
||||
$coord = str_replace([',','/',' '],[' ',' ',' '],$coord);
|
||||
$arr = ['lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'html' => ''];
|
||||
$arr = ['lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'mode' => $html_mode, 'html' => ''];
|
||||
Addon::callHooks('generate_map',$arr);
|
||||
return ($arr['html']) ? $arr['html'] : $coord;
|
||||
}
|
||||
|
||||
public static function byLocation($location) {
|
||||
$arr = ['location' => $location, 'html' => ''];
|
||||
public static function byLocation($location, $html_mode = 0) {
|
||||
$arr = ['location' => $location, 'mode' => $html_mode, 'html' => ''];
|
||||
Addon::callHooks('generate_named_map',$arr);
|
||||
return ($arr['html']) ? $arr['html'] : $location;
|
||||
}
|
||||
|
||||
public static function getCoordinates($location) {
|
||||
$arr = ['location' => $location, 'lat' => false, 'lon' => false];
|
||||
Addon::callHooks('Map::getCoordinates', $arr);
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,18 @@ class Network
|
|||
|
||||
$a = get_app();
|
||||
|
||||
$parts = parse_url($url);
|
||||
$path_parts = explode('/', $parts['path']);
|
||||
foreach ($path_parts as $part) {
|
||||
if (strlen($part) <> mb_strlen($part)) {
|
||||
$parts2[] = rawurlencode($part);
|
||||
} else {
|
||||
$parts2[] = $part;
|
||||
}
|
||||
}
|
||||
$parts['path'] = implode('/', $parts2);
|
||||
$url = self::unparseURL($parts);
|
||||
|
||||
if (self::isUrlBlocked($url)) {
|
||||
logger('domain of ' . $url . ' is blocked', LOGGER_DATA);
|
||||
return $ret;
|
||||
|
@ -217,7 +229,7 @@ class Network
|
|||
|
||||
$newurl = $curl_info['redirect_url'];
|
||||
|
||||
if (($new_location_info['path'] == '') && ( $new_location_info['host'] != '')) {
|
||||
if (($new_location_info['path'] == '') && ($new_location_info['host'] != '')) {
|
||||
$newurl = $new_location_info['scheme'] . '://' . $new_location_info['host'] . $old_location_info['path'];
|
||||
}
|
||||
|
||||
|
@ -229,6 +241,11 @@ class Network
|
|||
if (strpos($newurl, '/') === 0) {
|
||||
$newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl;
|
||||
}
|
||||
$old_location_query = @parse_url($url, PHP_URL_QUERY);
|
||||
|
||||
if ($old_location_query != '') {
|
||||
$newurl .= '?' . $old_location_query;
|
||||
}
|
||||
|
||||
if (filter_var($newurl, FILTER_VALIDATE_URL)) {
|
||||
$redirects++;
|
||||
|
@ -429,7 +446,7 @@ class Network
|
|||
/// @TODO Really suppress function outcomes? Why not find them + debug them?
|
||||
$h = @parse_url($url);
|
||||
|
||||
if ((is_array($h)) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
|
||||
if ((is_array($h)) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME) || filter_var($h['host'], FILTER_VALIDATE_IP) )) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
@ -454,7 +471,7 @@ class Network
|
|||
|
||||
$h = substr($addr, strpos($addr, '@') + 1);
|
||||
|
||||
if (($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
|
||||
if (($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -17,7 +17,6 @@ use Friendica\Protocol\Diaspora;
|
|||
use Friendica\Protocol\Email;
|
||||
use dba;
|
||||
|
||||
require_once 'include/html2plain.php';
|
||||
require_once 'include/items.php';
|
||||
|
||||
/// @todo This is some ugly code that needs to be split into several methods
|
||||
|
@ -209,7 +208,7 @@ class Delivery {
|
|||
$atom = DFRN::mail($item, $owner);
|
||||
} elseif ($fsuggest) {
|
||||
$atom = DFRN::fsuggest($item, $owner);
|
||||
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
|
||||
dba::delete('fsuggest', ['id' => $item['id']]);
|
||||
} elseif ($relocate) {
|
||||
$atom = DFRN::relocate($owner, $uid);
|
||||
} elseif ($followup) {
|
||||
|
@ -291,9 +290,7 @@ class Delivery {
|
|||
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",
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
dba::update('contact', ['writable' => true], ['id' => $x[0]['id']]);
|
||||
$x[0]['writable'] = 1;
|
||||
}
|
||||
|
||||
|
@ -314,7 +311,7 @@ class Delivery {
|
|||
if (!Queue::wasDelayed($contact['id'])) {
|
||||
$deliver_status = DFRN::deliver($owner, $contact, $atom);
|
||||
} else {
|
||||
$deliver_status = (-1);
|
||||
$deliver_status = -1;
|
||||
}
|
||||
|
||||
logger('notifier: dfrn_delivery to '.$contact["url"].' with guid '.$target_item["guid"].' returns '.$deliver_status);
|
||||
|
@ -322,12 +319,14 @@ class Delivery {
|
|||
if ($deliver_status < 0) {
|
||||
logger('notifier: delivery failed: queuing message');
|
||||
Queue::add($contact['id'], NETWORK_DFRN, $atom, false, $target_item['guid']);
|
||||
}
|
||||
|
||||
// The message could not be delivered. We mark the contact as "dead"
|
||||
Contact::markForArchival($contact);
|
||||
} else {
|
||||
if (($deliver_status >= 200) && ($deliver_status <= 299)) {
|
||||
// We successfully delivered a message, the contact is alive
|
||||
Contact::unmarkForArchival($contact);
|
||||
} else {
|
||||
// The message could not be delivered. We mark the contact as "dead"
|
||||
Contact::markForArchival($contact);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -400,7 +399,7 @@ class Delivery {
|
|||
$headers = 'From: '.Email::encodeHeader($local_user[0]['username'],'UTF-8').' <'.$local_user[0]['email'].'>'."\n";
|
||||
}
|
||||
} else {
|
||||
$headers = 'From: '. Email::encodeHeader($local_user[0]['username'],'UTF-8') .' <'. L10n::t('noreply') .'@'.$a->get_hostname() .'>'. "\n";
|
||||
$headers = 'From: '. Email::encodeHeader($local_user[0]['username'], 'UTF-8') . ' <noreply@' . $a->get_hostname() . '>' . "\n";
|
||||
}
|
||||
|
||||
//if ($reply_to)
|
||||
|
|
|
@ -13,6 +13,7 @@ use Friendica\Network\Probe;
|
|||
use Friendica\Protocol\PortableContact;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Network;
|
||||
use dba;
|
||||
|
||||
class DiscoverPoCo {
|
||||
/// @todo Clean up this mess of a parameter hell and split it in several classes
|
||||
|
@ -158,8 +159,8 @@ class DiscoverPoCo {
|
|||
|
||||
$urlparts = parse_url($user["url"]);
|
||||
if (!isset($urlparts["scheme"])) {
|
||||
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc(NETWORK_PHANTOM), dbesc(normalise_link($user["url"])));
|
||||
dba::update('gcontact', ['network' => NETWORK_PHANTOM],
|
||||
['nurl' => normalise_link($user["url"])]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -171,8 +172,8 @@ class DiscoverPoCo {
|
|||
"identi.ca" => NETWORK_PUMPIO,
|
||||
"alpha.app.net" => NETWORK_APPNET];
|
||||
|
||||
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc($networks[$urlparts["host"]]), dbesc(normalise_link($user["url"])));
|
||||
dba::update('gcontact', ['network' => $networks[$urlparts["host"]]],
|
||||
['nurl' => normalise_link($user["url"])]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -194,8 +195,8 @@ class DiscoverPoCo {
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
|
||||
dbesc(DateTimeFormat::utcNow()), dbesc(normalise_link($user["url"])));
|
||||
dba::update('gcontact', ['last_failure' => DateTimeFormat::utcNow()],
|
||||
['nurl' => normalise_link($user["url"])]);
|
||||
}
|
||||
|
||||
// Quit the loop after 3 minutes
|
||||
|
|
|
@ -18,7 +18,6 @@ use Friendica\Protocol\Salmon;
|
|||
use dba;
|
||||
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/html2plain.php';
|
||||
require_once 'include/items.php';
|
||||
|
||||
/*
|
||||
|
@ -480,15 +479,9 @@ class Notifier {
|
|||
|
||||
|
||||
if ($public_message) {
|
||||
|
||||
$r0 = [];
|
||||
$r1 = [];
|
||||
|
||||
if ($diaspora_delivery) {
|
||||
if (!$followup) {
|
||||
$r0 = Diaspora::relayList();
|
||||
}
|
||||
|
||||
$r1 = q("SELECT `batch`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`name`) AS `name`, ANY_VALUE(`network`) AS `network`
|
||||
FROM `contact` WHERE `network` = '%s' AND `batch` != ''
|
||||
AND `uid` = %d AND `rel` != %d AND NOT `blocked` AND NOT `pending` AND NOT `archive` GROUP BY `batch`",
|
||||
|
@ -501,17 +494,17 @@ class Notifier {
|
|||
// The function will ensure that there are no duplicates
|
||||
$r1 = Diaspora::participantsForThread($item_id, $r1);
|
||||
|
||||
// Add the relay to the list, avoid duplicates
|
||||
if (!$followup) {
|
||||
$r1 = Diaspora::relayList($item_id, $r1);
|
||||
}
|
||||
}
|
||||
|
||||
$r2 = q("SELECT `id`, `name`,`network` FROM `contact`
|
||||
WHERE `network` in ('%s') AND `uid` = %d AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `rel` != %d",
|
||||
dbesc(NETWORK_DFRN),
|
||||
intval($owner['uid']),
|
||||
intval(CONTACT_IS_SHARING)
|
||||
);
|
||||
$condition = ['network' => NETWORK_DFRN, 'uid' => $owner['uid'], 'blocked' => false,
|
||||
'pending' => false, 'archive' => false, 'rel' => [CONTACT_IS_FOLLOWER, CONTACT_IS_FRIEND]];
|
||||
$r2 = dba::inArray(dba::select('contact', ['id', 'name', 'network'], $condition));
|
||||
|
||||
|
||||
$r = array_merge($r2, $r1, $r0);
|
||||
$r = array_merge($r2, $r1);
|
||||
|
||||
if (DBM::is_result($r)) {
|
||||
logger('pubdeliver '.$target_item["guid"].': '.print_r($r,true), LOGGER_DEBUG);
|
||||
|
@ -542,8 +535,8 @@ class Notifier {
|
|||
if ($push_notify) {
|
||||
// Set push flag for PuSH subscribers to this topic,
|
||||
// they will be notified in queue.php
|
||||
q("UPDATE `push_subscriber` SET `push` = 1 ".
|
||||
"WHERE `nickname` = '%s' AND `push` = 0", dbesc($owner['nickname']));
|
||||
$condition = ['push' => false, 'nickname' => $owner['nickname']];
|
||||
dba::update('push_subscriber', ['push' => true], $condition);
|
||||
|
||||
logger('Activating internal PuSH for item '.$item_id, LOGGER_DEBUG);
|
||||
|
||||
|
|
|
@ -582,11 +582,12 @@ class OnePoll
|
|||
|
||||
logger("Consume feed of contact ".$contact['id']);
|
||||
|
||||
consume_feed($xml, $importer, $contact, $hub, 1, 1);
|
||||
consume_feed($xml, $importer, $contact, $hub);
|
||||
|
||||
// do it twice. Ensures that children of parents which may be later in the stream aren't tossed
|
||||
|
||||
consume_feed($xml, $importer, $contact, $hub, 1, 2);
|
||||
// do it a second time for DFRN so that any children find their parents.
|
||||
if ($contact['network'] === NETWORK_DFRN) {
|
||||
consume_feed($xml, $importer, $contact, $hub);
|
||||
}
|
||||
|
||||
$hubmode = 'subscribe';
|
||||
if ($contact['network'] === NETWORK_DFRN || $contact['blocked'] || $contact['readonly']) {
|
||||
|
|
|
@ -12,6 +12,7 @@ use Friendica\Core\Worker;
|
|||
use Friendica\Database\DBM;
|
||||
use Friendica\Protocol\OStatus;
|
||||
use Friendica\Util\Network;
|
||||
use dba;
|
||||
|
||||
require_once 'include/items.php';
|
||||
|
||||
|
@ -76,9 +77,8 @@ class PubSubPublish {
|
|||
logger('successfully pushed to '.$rr['callback_url']);
|
||||
|
||||
// set last_update to the "created" date of the last item, and reset push=0
|
||||
q("UPDATE `push_subscriber` SET `push` = 0, last_update = '%s' WHERE id = %d",
|
||||
dbesc($last_update),
|
||||
intval($rr['id']));
|
||||
$fields = ['push' => 0, 'last_update' => $last_update];
|
||||
dba::update('push_subscriber', $fields, ['id' => $rr['id']]);
|
||||
|
||||
} else {
|
||||
logger('error when pushing to '.$rr['callback_url'].' HTTP: '.$ret);
|
||||
|
@ -90,9 +90,7 @@ class PubSubPublish {
|
|||
if ($new_push > 30) // OK, let's give up
|
||||
$new_push = 0;
|
||||
|
||||
q("UPDATE `push_subscriber` SET `push` = %d WHERE id = %d",
|
||||
$new_push,
|
||||
intval($rr['id']));
|
||||
dba::update('push_subscriber', ['push' => $new_push], ['id' => $rr['id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class Queue
|
|||
// Handling the pubsubhubbub requests
|
||||
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'PubSubPublish');
|
||||
|
||||
$r = dba::inArray(dba::p("SELECT `id` FROM `queue` WHERE `next` < UTC_TIMESTAMP()"));
|
||||
$r = dba::inArray(dba::p("SELECT `id` FROM `queue` WHERE `next` < UTC_TIMESTAMP() ORDER BY `batch`, `cid`"));
|
||||
|
||||
Addon::callHooks('queue_predeliver', $r);
|
||||
|
||||
|
@ -63,6 +63,11 @@ class Queue
|
|||
return;
|
||||
}
|
||||
|
||||
if (empty($contact['notify'])) {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
return;
|
||||
}
|
||||
|
||||
$dead = Cache::get($cachekey_deadguy . $contact['notify']);
|
||||
|
||||
if (!is_null($dead) && $dead && !$no_dead_check) {
|
||||
|
@ -81,7 +86,7 @@ class Queue
|
|||
logger("Check server " . $server . " (" . $contact["network"] . ")");
|
||||
|
||||
$vital = PortableContact::checkServer($server, $contact["network"], true);
|
||||
Cache::set($cachekey_server . $server, $vital, CACHE_QUARTER_HOUR);
|
||||
Cache::set($cachekey_server . $server, $vital, CACHE_MINUTE);
|
||||
}
|
||||
|
||||
if (!is_null($vital) && !$vital) {
|
||||
|
@ -109,37 +114,34 @@ class Queue
|
|||
logger('queue: dfrndelivery: item ' . $q_item['id'] . ' for ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
$deliver_status = DFRN::deliver($owner, $contact, $data);
|
||||
|
||||
if ($deliver_status == (-1)) {
|
||||
if (($deliver_status >= 200) && ($deliver_status <= 299)) {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
} else {
|
||||
QueueModel::updateTime($q_item['id']);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_QUARTER_HOUR);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_MINUTE);
|
||||
}
|
||||
break;
|
||||
case NETWORK_OSTATUS:
|
||||
logger('queue: slapdelivery: item ' . $q_item['id'] . ' for ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
$deliver_status = Salmon::slapper($owner, $contact['notify'], $data);
|
||||
|
||||
if ($deliver_status == -1) {
|
||||
QueueModel::updateTime($q_item['id']);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_MINUTE);
|
||||
} else {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
}
|
||||
break;
|
||||
case NETWORK_OSTATUS:
|
||||
if ($contact['notify']) {
|
||||
logger('queue: slapdelivery: item ' . $q_item['id'] . ' for ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
$deliver_status = Salmon::slapper($owner, $contact['notify'], $data);
|
||||
|
||||
if ($deliver_status == (-1)) {
|
||||
QueueModel::updateTime($q_item['id']);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_QUARTER_HOUR);
|
||||
} else {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
}
|
||||
}
|
||||
break;
|
||||
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, 'Queue:' . $q_item['id'], true);
|
||||
logger('queue: diaspora_delivery: item ' . $q_item['id'] . ' for ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
$deliver_status = Diaspora::transmit($owner, $contact, $data, $public, true, 'Queue:' . $q_item['id'], true);
|
||||
|
||||
if ($deliver_status == (-1)) {
|
||||
QueueModel::updateTime($q_item['id']);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_QUARTER_HOUR);
|
||||
} else {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
}
|
||||
if ((($deliver_status >= 200) && ($deliver_status <= 299)) ||
|
||||
($contact['contact-type'] == ACCOUNT_TYPE_RELAY)) {
|
||||
QueueModel::removeItem($q_item['id']);
|
||||
} else {
|
||||
QueueModel::updateTime($q_item['id']);
|
||||
Cache::set($cachekey_deadguy . $contact['notify'], true, CACHE_MINUTE);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -154,7 +156,7 @@ class Queue
|
|||
}
|
||||
break;
|
||||
}
|
||||
logger('Deliver status ' . (int) $deliver_status . ' for item ' . $q_item['id'] . ' to ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
logger('Deliver status ' . (int)$deliver_status . ' for item ' . $q_item['id'] . ' to ' . $contact['name'] . ' <' . $contact['url'] . '>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Friendica\Worker;
|
||||
|
||||
use dba;
|
||||
|
||||
class TagUpdate
|
||||
{
|
||||
public static function execute()
|
||||
|
@ -13,18 +15,14 @@ class TagUpdate
|
|||
if ($message['uid'] == 0) {
|
||||
$global = true;
|
||||
|
||||
q("UPDATE `term` SET `global` = 1 WHERE `otype` = %d AND `guid` = '%s'",
|
||||
intval(TERM_OBJ_POST), dbesc($message['guid']));
|
||||
dba::update('term', ['global' => true], ['otype' => TERM_OBJ_POST, 'guid' => $message['guid']]);
|
||||
} else {
|
||||
$isglobal = q("SELECT `global` FROM `term` WHERE `uid` = 0 AND `otype` = %d AND `guid` = '%s'",
|
||||
intval(TERM_OBJ_POST), dbesc($message['guid']));
|
||||
|
||||
$global = (count($isglobal) > 0);
|
||||
$global = (dba::count('term', ['uid' => 0, 'otype' => TERM_OBJ_POST, 'guid' => $message['guid']]) > 0);
|
||||
}
|
||||
|
||||
q("UPDATE `term` SET `guid` = '%s', `created` = '%s', `received` = '%s', `global` = %d WHERE `otype` = %d AND `oid` = %d",
|
||||
dbesc($message['guid']), dbesc($message['created']), dbesc($message['received']),
|
||||
intval($global), intval(TERM_OBJ_POST), intval($message['oid']));
|
||||
$fields = ['guid' => $message['guid'], 'created' => $message['created'],
|
||||
'received' => $message['received'], 'global' => $global];
|
||||
dba::update('term', $fields, ['otype' => TERM_OBJ_POST, 'oid' => $message['oid']]);
|
||||
}
|
||||
|
||||
dba::close($messages);
|
||||
|
@ -33,7 +31,7 @@ class TagUpdate
|
|||
|
||||
logger('fetched messages: ' . dba::num_rows($messages));
|
||||
while ($message = dba::fetch(messages)) {
|
||||
q("UPDATE `item` SET `global` = 1 WHERE `guid` = '%s'", dbesc($message['guid']));
|
||||
dba::update('item', ['global' => true], ['guid' => $message['guid']]);
|
||||
}
|
||||
|
||||
dba::close($messages);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue