Merge branch 'master' into develop

- Updated new develop version label
- Incremented database build number
This commit is contained in:
Hypolite Petovan 2018-06-01 07:30:04 -04:00
commit 93daf7883e
474 changed files with 75160 additions and 91301 deletions

View file

@ -206,6 +206,16 @@ class Contact extends BaseObject
$fields['forum'] = $user['page-flags'] == PAGE_COMMUNITY;
$fields['prv'] = $user['page-flags'] == PAGE_PRVGROUP;
// it seems as if ported accounts can have wrong values, so we make sure that now everything is fine.
$fields['url'] = System::baseUrl() . '/profile/' . $user['nickname'];
$fields['nurl'] = normalise_link($fields['url']);
$fields['addr'] = $user['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3);
$fields['request'] = System::baseUrl() . '/dfrn_request/' . $user['nickname'];
$fields['notify'] = System::baseUrl() . '/dfrn_notify/' . $user['nickname'];
$fields['poll'] = System::baseUrl() . '/dfrn_poll/' . $user['nickname'];
$fields['confirm'] = System::baseUrl() . '/dfrn_confirm/' . $user['nickname'];
$fields['poco'] = System::baseUrl() . '/poco/' . $user['nickname'];
$update = false;
foreach ($fields as $field => $content) {
@ -752,10 +762,11 @@ class Contact extends BaseObject
* @param string $url Contact URL
* @param integer $uid The user id for the contact (0 = public contact)
* @param boolean $no_update Don't update the contact
* @param array $default Default value for creating the contact when every else fails
*
* @return integer Contact ID
*/
public static function getIdForURL($url, $uid = 0, $no_update = false)
public static function getIdForURL($url, $uid = 0, $no_update = false, $default = [])
{
logger("Get contact data for url " . $url . " and user " . $uid . " - " . System::callstack(), LOGGER_DEBUG);
@ -804,18 +815,48 @@ class Contact extends BaseObject
$data = Probe::uri($url, "", $uid);
// Last try in gcontact for unsupported networks
if (!in_array($data["network"], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_PUMPIO, NETWORK_MAIL])) {
if (!in_array($data["network"], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_PUMPIO, NETWORK_MAIL, NETWORK_FEED])) {
if ($uid != 0) {
return 0;
}
// Get data from the gcontact table
$gcontact = dba::selectFirst('gcontact', ['name', 'nick', 'url', 'photo', 'addr', 'alias', 'network'], ['nurl' => normalise_link($url)]);
if (!DBM::is_result($gcontact)) {
return 0;
$fields = ['name', 'nick', 'url', 'photo', 'addr', 'alias', 'network'];
$contact = dba::selectFirst('gcontact', $fields, ['nurl' => normalise_link($url)]);
if (!DBM::is_result($contact)) {
$contact = dba::selectFirst('contact', $fields, ['nurl' => normalise_link($url)]);
}
$data = array_merge($data, $gcontact);
if (!DBM::is_result($contact)) {
$fields = ['url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick',
'photo', 'keywords', 'location', 'about', 'network',
'priority', 'batch', 'request', 'confirm', 'poco'];
$contact = dba::selectFirst('contact', $fields, ['addr' => $url]);
}
if (!DBM::is_result($contact)) {
// The link could be provided as http although we stored it as https
$ssl_url = str_replace('http://', 'https://', $url);
$condition = ['alias' => [$url, normalise_link($url), $ssl_url]];
$contact = dba::selectFirst('contact', $fields, $condition);
}
if (!DBM::is_result($contact)) {
$fields = ['url', 'addr', 'alias', 'notify', 'poll', 'name', 'nick',
'photo', 'network', 'priority', 'batch', 'request', 'confirm'];
$condition = ['url' => [$url, normalise_link($url), $ssl_url]];
$contact = dba::selectFirst('fcontact', $fields, $condition);
}
if (!empty($default)) {
$contact = $default;
}
if (!DBM::is_result($contact)) {
return 0;
} else {
$data = array_merge($data, $contact);
}
}
if (!$contact_id && ($data["alias"] != '') && ($data["alias"] != $url)) {
@ -1008,7 +1049,7 @@ class Contact extends BaseObject
$contact = ($r[0]["contact-type"] == ACCOUNT_TYPE_COMMUNITY ? 'owner-id' : 'author-id');
$r = q(item_query() . " AND `item`.`" . $contact . "` = %d AND " . $sql .
$r = q(item_query(local_user()) . " AND `item`.`" . $contact . "` = %d AND " . $sql .
" AND `item`.`verb` = '%s' ORDER BY `item`.`created` DESC LIMIT %d, %d",
intval($author_id), intval(local_user()), dbesc(ACTIVITY_POST),
intval($a->pager['start']), intval($a->pager['itemspage'])
@ -1263,7 +1304,7 @@ class Contact extends BaseObject
if (($network != '') && ($ret['network'] != $network)) {
logger('Expected network ' . $network . ' does not match actual network ' . $ret['network']);
return result;
return $result;
}
// check if we already have a contact

View file

@ -239,7 +239,7 @@ class GContact
if ($alternate && ($gcontact['network'] == NETWORK_OSTATUS)) {
// Delete the old entry - if it exists
if (dba::exists('item', ['nurl' => normalise_link($orig_profile)])) {
if (dba::exists('gcontact', ['nurl' => normalise_link($orig_profile)])) {
dba::delete('gcontact', ['nurl' => normalise_link($orig_profile)]);
}
}

View file

@ -52,28 +52,38 @@ class Item extends BaseObject
return false;
}
// To ensure the data integrity we do it in an transaction
dba::transaction();
// We cannot simply expand the condition to check for origin entries
// The condition needn't to be a simple array but could be a complex condition.
// And we have to execute this query before the update to ensure to fetch the same data.
$items = dba::select('item', ['id', 'origin'], $condition);
$success = dba::update('item', $fields, $condition);
if (!$success) {
dba::close($items);
dba::rollback();
return false;
}
$rows = dba::affected_rows();
// We cannot simply expand the condition to check for origin entries
// The condition needn't to be a simple array but could be a complex condition.
$items = dba::select('item', ['id', 'origin'], $condition);
while ($item = dba::fetch($items)) {
Term::insertFromTagFieldByItemId($item['id']);
Term::insertFromFileFieldByItemId($item['id']);
self::updateThread($item['id']);
// We only need to notfiy others when it is an original entry from us
if ($item['origin']) {
// We only need to notfiy others when it is an original entry from us.
// Only call the notifier when the item has some content relevant change.
if ($item['origin'] && in_array('edited', array_keys($fields))) {
Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
}
}
dba::close($items);
dba::commit();
return $rows;
}
@ -92,6 +102,32 @@ class Item extends BaseObject
dba::close($items);
}
/**
* @brief Delete an item for an user and notify others about it - if it was ours
*
* @param array $condition The condition for finding the item entries
* @param integer $uid User who wants to delete this item
*/
public static function deleteForUser($condition, $uid)
{
if ($uid == 0) {
return;
}
$items = dba::select('item', ['id', 'uid'], $condition);
while ($item = dba::fetch($items)) {
// "Deleting" global items just means hiding them
if ($item['uid'] == 0) {
dba::update('user-item', ['hidden' => true], ['iid' => $item['id'], 'uid' => $uid], true);
} elseif ($item['uid'] == $uid) {
self::deleteById($item['id'], PRIORITY_HIGH);
} else {
logger('Wrong ownership. Not deleting item ' . $item['id']);
}
}
dba::close($items);
}
/**
* @brief Delete an item and notify others about it - if it was ours
*
@ -100,18 +136,20 @@ class Item extends BaseObject
*
* @return boolean success
*/
public static function deleteById($item_id, $priority = PRIORITY_HIGH)
private static function deleteById($item_id, $priority = PRIORITY_HIGH)
{
// locate item to be deleted
$fields = ['id', 'uid', 'parent', 'parent-uri', 'origin', 'deleted',
'file', 'resource-id', 'event-id', 'attach',
$fields = ['id', 'uri', 'uid', 'parent', 'parent-uri', 'origin',
'deleted', 'file', 'resource-id', 'event-id', 'attach',
'verb', 'object-type', 'object', 'target', 'contact-id'];
$item = dba::selectFirst('item', $fields, ['id' => $item_id]);
if (!DBM::is_result($item)) {
logger('Item with ID ' . $item_id . " hasn't been found.", LOGGER_DEBUG);
return false;
}
if ($item['deleted']) {
logger('Item with ID ' . $item_id . ' has already been deleted.', LOGGER_DEBUG);
return false;
}
@ -120,8 +158,6 @@ class Item extends BaseObject
$parent = ['origin' => false];
}
logger('delete item: ' . $item['id'], LOGGER_DEBUG);
// clean up categories and tags so they don't end up as orphans
$matches = false;
@ -174,16 +210,34 @@ class Item extends BaseObject
Term::insertFromFileFieldByItemId($item['id']);
self::deleteThread($item['id'], $item['parent-uri']);
// If it's the parent of a comment thread, kill all the kids
if ($item['id'] == $item['parent']) {
self::delete(['parent' => $item['parent']], $priority);
if (!dba::exists('item', ["`uri` = ? AND `uid` != 0 AND NOT `deleted`", $item['uri']])) {
self::delete(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority);
}
// send the notification upstream/downstream
if ($item['origin'] || $parent['origin']) {
Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
// If it's the parent of a comment thread, kill all the kids
if ($item['id'] == $item['parent']) {
self::delete(['parent' => $item['parent'], 'deleted' => false], $priority);
}
// Is it our comment and/or our thread?
if ($item['origin'] || $parent['origin']) {
// When we delete the original post we will delete all existing copies on the server as well
self::delete(['uri' => $item['uri'], 'deleted' => false], $priority);
// send the notification upstream/downstream
Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
} elseif ($item['uid'] != 0) {
// When we delete just our local user copy of an item, we have to set a marker to hide it
$global_item = dba::selectFirst('item', ['id'], ['uri' => $item['uri'], 'uid' => 0, 'deleted' => false]);
if (DBM::is_result($global_item)) {
dba::update('user-item', ['hidden' => true], ['iid' => $global_item['id'], 'uid' => $item['uid']], true);
}
}
logger('Item with ID ' . $item_id . " has been deleted.", LOGGER_DEBUG);
return true;
}
@ -324,6 +378,12 @@ class Item extends BaseObject
$item['origin'] = 1;
$item['network'] = NETWORK_DFRN;
$item['protocol'] = PROTOCOL_DFRN;
if (is_int($notify)) {
$priority = $notify;
} else {
$priority = PRIORITY_HIGH;
}
} else {
$item['network'] = trim(defaults($item, 'network', NETWORK_PHANTOM));
}
@ -346,6 +406,13 @@ class Item extends BaseObject
unset($item['dsprsig']);
}
if (!empty($item['diaspora_signed_text'])) {
$diaspora_signed_text = $item['diaspora_signed_text'];
unset($item['diaspora_signed_text']);
} else {
$diaspora_signed_text = '';
}
// Converting the plink
/// @TODO Check if this is really still needed
if ($item['network'] == NETWORK_OSTATUS) {
@ -479,14 +546,20 @@ class Item extends BaseObject
// The contact-id should be set before "self::insert" was called - but there seems to be issues sometimes
$item["contact-id"] = self::contactId($item);
$item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"]));
$default = ['url' => $item['author-link'], 'name' => $item['author-name'],
'photo' => $item['author-avatar'], 'network' => $item['network']];
$item['author-id'] = defaults($item, 'author-id', Contact::getIdForURL($item["author-link"], 0, false, $default));
if (Contact::isBlocked($item["author-id"])) {
logger('Contact '.$item["author-id"].' is blocked, item '.$item["uri"].' will not be stored');
return 0;
}
$item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"]));
$default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
'photo' => $item['owner-avatar'], 'network' => $item['network']];
$item['owner-id'] = defaults($item, 'owner-id', Contact::getIdForURL($item["owner-link"], 0, false, $default));
if (Contact::isBlocked($item["owner-id"])) {
logger('Contact '.$item["owner-id"].' is blocked, item '.$item["uri"].' will not be stored');
@ -538,7 +611,7 @@ class Item extends BaseObject
$fields = ['uri', 'parent-uri', 'id', 'deleted',
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
'wall', 'private', 'forum_mode'];
'wall', 'private', 'forum_mode', 'origin'];
$condition = ['uri' => $item['parent-uri'], 'uid' => $item['uid']];
$params = ['order' => ['id' => false]];
$parent = dba::selectFirst('item', $fields, $condition, $params);
@ -790,6 +863,12 @@ class Item extends BaseObject
'signature' => $dsprsig->signature, 'signer' => $dsprsig->signer]);
}
if (!empty($diaspora_signed_text)) {
// Formerly we stored the signed text, the signature and the author in different fields.
// We now store the raw data so that we are more flexible.
dba::insert('sign', ['iid' => $current_post, 'signed_text' => $diaspora_signed_text]);
}
$deleted = self::tagDeliver($item['uid'], $current_post);
/*
@ -833,7 +912,9 @@ class Item extends BaseObject
check_user_notification($current_post);
if ($notify) {
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", $notify_type, $current_post);
} elseif (!empty($parent) && $parent['origin']) {
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], "Notifier", "comment-import", $current_post);
}
return $current_post;
@ -842,9 +923,10 @@ class Item extends BaseObject
/**
* @brief Distributes public items to the receivers
*
* @param integer $itemid Item ID that should be added
* @param integer $itemid Item ID that should be added
* @param string $signed_text Original text (for Diaspora signatures), JSON encoded.
*/
public static function distribute($itemid)
public static function distribute($itemid, $signed_text = '')
{
$condition = ["`id` IN (SELECT `parent` FROM `item` WHERE `id` = ?)", $itemid];
$parent = dba::selectFirst('item', ['owner-id'], $condition);
@ -879,14 +961,22 @@ class Item extends BaseObject
$users[$contact['uid']] = $contact['uid'];
}
$origin_uid = 0;
if ($item['uri'] != $item['parent-uri']) {
$parents = dba::select('item', ['uid'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
$parents = dba::select('item', ['uid', 'origin'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
while ($parent = dba::fetch($parents)) {
$users[$parent['uid']] = $parent['uid'];
if ($parent['origin'] && !$item['origin']) {
$origin_uid = $parent['uid'];
}
}
}
foreach ($users as $uid) {
if ($origin_uid == $uid) {
$item['diaspora_signed_text'] = $signed_text;
}
self::storeForUser($itemid, $item, $uid);
}
}
@ -1192,7 +1282,7 @@ class Item extends BaseObject
}
}
private static function setHashtags(&$item)
public static function setHashtags(&$item)
{
$tags = get_tags($item["body"]);
@ -2007,7 +2097,7 @@ EOT;
{
$fields = ['uid', 'guid', 'title', 'body', 'created', 'edited', 'commented', 'received', 'changed',
'wall', 'private', 'pubmail', 'moderated', 'visible', 'spam', 'starred', 'bookmark', 'contact-id',
'deleted', 'origin', 'forum_mode', 'network', 'rendered-html', 'rendered-hash'];
'deleted', 'origin', 'forum_mode', 'network', 'author-id', 'owner-id', 'rendered-html', 'rendered-hash'];
$condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid];
$item = dba::selectFirst('item', $fields, $condition);
@ -2031,7 +2121,7 @@ EOT;
$result = dba::update('thread', $fields, ['iid' => $itemid]);
logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result." ".print_r($item, true), LOGGER_DEBUG);
logger("Update thread for item ".$itemid." - guid ".$item["guid"]." - ".(int)$result, LOGGER_DEBUG);
// Updating a shadow item entry
$items = dba::selectFirst('item', ['id'], ['guid' => $item['guid'], 'uid' => 0]);

View file

@ -90,7 +90,7 @@ class Profile
*/
public static function load(App $a, $nickname, $profile = 0, $profiledata = [], $show_connect = true)
{
$user = dba::selectFirst('user', ['uid'], ['nickname' => $nickname]);
$user = dba::selectFirst('user', ['uid'], ['nickname' => $nickname, 'account_removed' => false]);
if (!DBM::is_result($user) && empty($profiledata)) {
logger('profile error: ' . $a->query_string, LOGGER_DEBUG);

View file

@ -0,0 +1,154 @@
<?php
/**
* @file src/Model/PushSubscriber.php
*/
namespace Friendica\Model;
use Friendica\Core\Worker;
use Friendica\Util\DateTimeFormat;
use Friendica\Database\DBM;
use dba;
require_once 'include/dba.php';
class PushSubscriber
{
/**
* @brief Send subscription notifications for the given user
*
* @param integer $uid User ID
* @param string $priority Priority for push workers
*/
public static function publishFeed($uid, $default_priority = PRIORITY_HIGH)
{
$condition = ['push' => 0, 'uid' => $uid];
dba::update('push_subscriber', ['push' => 1, 'next_try' => NULL_DATE], $condition);
self::requeue($default_priority);
}
/**
* @brief start workers to transmit the feed data
*
* @param string $priority Priority for push workers
*/
public static function requeue($default_priority = PRIORITY_HIGH)
{
// We'll push to each subscriber that has push > 0,
// i.e. there has been an update (set in notifier.php).
$subscribers = dba::select('push_subscriber', ['id', 'push', 'callback_url', 'nickname'], ["`push` > 0 AND `next_try` < UTC_TIMESTAMP()"]);
while ($subscriber = dba::fetch($subscribers)) {
// We always handle retries with low priority
if ($subscriber['push'] > 1) {
$priority = PRIORITY_LOW;
} else {
$priority = $default_priority;
}
logger('Publish feed to ' . $subscriber['callback_url'] . ' for ' . $subscriber['nickname'] . ' with priority ' . $priority, LOGGER_DEBUG);
Worker::add($priority, 'PubSubPublish', (int)$subscriber['id']);
}
dba::close($subscribers);
}
/**
* @brief Renew the feed subscription
*
* @param integer $uid User ID
* @param string $nick Priority for push workers
* @param integer $subscribe Subscribe (Unsubscribe = false)
* @param string $hub_callback Callback address
* @param string $hub_topic Feed topic
* @param string $hub_secret Subscription secret
*/
public static function renew($uid, $nick, $subscribe, $hub_callback, $hub_topic, $hub_secret)
{
// fetch the old subscription if it exists
$subscriber = dba::selectFirst('push_subscriber', ['last_update', 'push'], ['callback_url' => $hub_callback]);
// delete old subscription if it exists
dba::delete('push_subscriber', ['callback_url' => $hub_callback]);
if ($subscribe) {
// if we are just updating an old subscription, keep the
// old values for last_update but reset the push
if (DBM::is_result($subscriber)) {
$last_update = $subscriber['last_update'];
$push_flag = min($subscriber['push'], 1);
} else {
$last_update = DateTimeFormat::utcNow();
$push_flag = 0;
}
// subscribe means adding the row to the table
$fields = ['uid' => $uid, 'callback_url' => $hub_callback,
'topic' => $hub_topic, 'nickname' => $nick, 'push' => $push_flag,
'last_update' => $last_update, 'renewed' => DateTimeFormat::utcNow(),
'secret' => $hub_secret];
dba::insert('push_subscriber', $fields);
logger("Successfully subscribed [$hub_callback] for $nick");
} else {
logger("Successfully unsubscribed [$hub_callback] for $nick");
// we do nothing here, since the row was already deleted
}
}
/**
* @brief Delay the push subscriber
*
* @param integer $id Subscriber ID
*/
public static function delay($id)
{
$subscriber = dba::selectFirst('push_subscriber', ['push', 'callback_url', 'renewed', 'nickname'], ['id' => $id]);
if (!DBM::is_result($subscriber)) {
return;
}
$retrial = $subscriber['push'];
if ($retrial > 14) {
// End subscriptions if they weren't renewed for more than two months
$days = round((time() - strtotime($subscriber['renewed'])) / (60 * 60 * 24));
if ($days > 60) {
dba::update('push_subscriber', ['push' => -1, 'next_try' => NULL_DATE], ['id' => $id]);
logger('Delivery error: Subscription ' . $subscriber['callback_url'] . ' for ' . $subscriber['nickname'] . ' is marked as ended.', LOGGER_DEBUG);
} else {
dba::update('push_subscriber', ['push' => 0, 'next_try' => NULL_DATE], ['id' => $id]);
logger('Delivery error: Giving up ' . $subscriber['callback_url'] . ' for ' . $subscriber['nickname'] . ' for now.', LOGGER_DEBUG);
}
} else {
// Calculate the delay until the next trial
$delay = (($retrial + 3) ** 4) + (rand(1, 30) * ($retrial + 1));
$next = DateTimeFormat::utc('now + ' . $delay . ' seconds');
$retrial = $retrial + 1;
dba::update('push_subscriber', ['push' => $retrial, 'next_try' => $next], ['id' => $id]);
logger('Delivery error: Next try (' . $retrial . ') ' . $subscriber['callback_url'] . ' for ' . $subscriber['nickname'] . ' at ' . $next, LOGGER_DEBUG);
}
}
/**
* @brief Reset the push subscriber
*
* @param integer $id Subscriber ID
* @param date $last_update Date of last transmitted item
*/
public static function reset($id, $last_update)
{
$subscriber = dba::selectFirst('push_subscriber', ['callback_url', 'nickname'], ['id' => $id]);
if (!DBM::is_result($subscriber)) {
return;
}
// set last_update to the 'created' date of the last item, and reset push=0
$fields = ['push' => 0, 'next_try' => NULL_DATE, 'last_update' => $last_update];
dba::update('push_subscriber', $fields, ['id' => $id]);
logger('Subscriber ' . $subscriber['callback_url'] . ' for ' . $subscriber['nickname'] . ' is marked as vital', LOGGER_DEBUG);
}
}

View file

@ -331,6 +331,7 @@ class User
$confirm = x($data, 'confirm') ? trim($data['confirm']) : '';
$blocked = x($data, 'blocked') ? intval($data['blocked']) : 0;
$verified = x($data, 'verified') ? intval($data['verified']) : 0;
$language = x($data, 'language') ? notags(trim($data['language'])) : 'en';
$publish = x($data, 'profile_publish_reg') && intval($data['profile_publish_reg']) ? 1 : 0;
$netpublish = strlen(Config::get('system', 'directory')) ? $publish : 0;
@ -359,7 +360,7 @@ class User
$_SESSION['register'] = 1;
$_SESSION['openid'] = $openid_url;
$openid = new LightOpenID;
$openid = new LightOpenID($a->get_hostname());
$openid->identity = $openid_url;
$openid->returnUrl = System::baseUrl() . '/openid';
$openid->required = ['namePerson/friendly', 'contact/email', 'namePerson'];
@ -466,6 +467,7 @@ class User
'sprvkey' => $sprvkey,
'verified' => $verified,
'blocked' => $blocked,
'language' => $language,
'timezone' => 'UTC',
'register_date' => DateTimeFormat::utcNow(),
'default-location' => ''