use Friendica\BaseObject;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
-use Friendica\Core\Addon;
use Friendica\Core\Config;
+use Friendica\Core\Hook;
use Friendica\Core\Lock;
use Friendica\Core\Logger;
use Friendica\Core\L10n;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
-use Friendica\Model\Contact;
-use Friendica\Model\Event;
-use Friendica\Model\FileTag;
-use Friendica\Model\PermissionSet;
-use Friendica\Model\Term;
-use Friendica\Model\ItemURI;
-use Friendica\Object\Image;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Text_LanguageDetect;
-require_once 'boot.php';
-require_once 'include/items.php';
-require_once 'include/text.php';
-
class Item extends BaseObject
{
// Posting types, inspired by https://www.w3.org/TR/activitystreams-vocabulary/#object-types
const PT_PERSONAL_NOTE = 128;
// Field list that is used to display the items
- const DISPLAY_FIELDLIST = ['uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid', 'network',
- 'commented', 'created', 'edited', 'received', 'verb', 'object-type', 'postopts', 'plink',
- 'wall', 'private', 'starred', 'origin', 'title', 'body', 'file', 'attach', 'language',
- 'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
- 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'item_id',
- 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
- 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network',
- 'contact-id', 'contact-link', 'contact-name', 'contact-avatar',
- 'writable', 'self', 'cid', 'alias',
- 'event-id', 'event-created', 'event-edited', 'event-start', 'event-finish',
- 'event-summary', 'event-desc', 'event-location', 'event-type',
- 'event-nofinish', 'event-adjust', 'event-ignore', 'event-id'];
+ const DISPLAY_FIELDLIST = [
+ 'uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid', 'network',
+ 'commented', 'created', 'edited', 'received', 'verb', 'object-type', 'postopts', 'plink',
+ 'wall', 'private', 'starred', 'origin', 'title', 'body', 'file', 'attach', 'language',
+ 'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
+ 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'item_id',
+ 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
+ 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network',
+ 'contact-id', 'contact-link', 'contact-name', 'contact-avatar',
+ 'writable', 'self', 'cid', 'alias',
+ 'event-id', 'event-created', 'event-edited', 'event-start', 'event-finish',
+ 'event-summary', 'event-desc', 'event-location', 'event-type',
+ 'event-nofinish', 'event-adjust', 'event-ignore', 'event-id',
+ 'delivery_queue_count', 'delivery_queue_done'
+ ];
// Field list that is used to deliver items via the protocols
const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri', 'thr-parent', 'parent-uri', 'guid',
// Field list for "item-content" table that is not present in the "item" table
const CONTENT_FIELDLIST = ['language'];
- // Field list for additional delivery data
- const DELIVERY_DATA_FIELDLIST = ['postopts', 'inform'];
-
// All fields in the item table
const ITEM_FIELDLIST = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent', 'guid',
'contact-id', 'type', 'wall', 'gravity', 'extid', 'icid', 'iaid', 'psid',
// Fetch data from the item-content table whenever there is content there
if (self::isLegacyMode()) {
- $legacy_fields = array_merge(self::DELIVERY_DATA_FIELDLIST, self::MIXED_CONTENT_FIELDLIST);
+ $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST);
foreach ($legacy_fields as $field) {
if (empty($row[$field]) && !empty($row['internal-item-' . $field])) {
$row[$field] = $row['internal-item-' . $field];
* @brief Fills an array with data from an item query
*
* @param object $stmt statement object
+ * @param bool $do_close
* @return array Data array
*/
public static function inArray($stmt, $do_close = true) {
* @param array $condition array of fields for condition
*
* @return boolean Are there rows for that condition?
+ * @throws \Exception
*/
public static function exists($condition) {
$stmt = self::select(['id'], $condition, ['limit' => 1]);
*
* @brief Retrieve a single record from a table
* @param integer $uid User ID
- * @param array $fields
- * @param array $condition
- * @param array $params
+ * @param array $selected
+ * @param array $condition
+ * @param array $params
* @return bool|array
- * @see DBA::select
+ * @throws \Exception
+ * @see DBA::select
*/
public static function selectFirstForUser($uid, array $selected = [], array $condition = [], $params = [])
{
/**
* @brief Select rows from the item table for a given user
*
- * @param integer $uid User ID
- * @param array $selected Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param integer $uid User ID
+ * @param array $selected Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return boolean|object
+ * @throws \Exception
*/
public static function selectForUser($uid, array $selected = [], array $condition = [], $params = [])
{
* Retrieve a single record from the item table and returns it in an associative array
*
* @brief Retrieve a single record from a table
- * @param array $fields
- * @param array $condition
- * @param array $params
+ * @param array $fields
+ * @param array $condition
+ * @param array $params
* @return bool|array
- * @see DBA::select
+ * @throws \Exception
+ * @see DBA::select
*/
public static function selectFirst(array $fields = [], array $condition = [], $params = [])
{
/**
* @brief Select rows from the item table
*
- * @param array $selected Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param array $selected Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return boolean|object
+ * @throws \Exception
*/
public static function select(array $selected = [], array $condition = [], $params = [])
{
/**
* @brief Select rows from the starting post in the item table
*
- * @param integer $uid User ID
- * @param array $fields Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param integer $uid User ID
+ * @param array $selected
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return boolean|object
+ * @throws \Exception
*/
public static function selectThreadForUser($uid, array $selected = [], array $condition = [], $params = [])
{
*
* @brief Retrieve a single record from a table
* @param integer $uid User ID
- * @param array $selected
- * @param array $condition
- * @param array $params
+ * @param array $selected
+ * @param array $condition
+ * @param array $params
* @return bool|array
- * @see DBA::select
+ * @throws \Exception
+ * @see DBA::select
*/
public static function selectFirstThreadForUser($uid, array $selected = [], array $condition = [], $params = [])
{
* Retrieve a single record from the starting post in the item table and returns it in an associative array
*
* @brief Retrieve a single record from a table
- * @param array $fields
- * @param array $condition
- * @param array $params
+ * @param array $fields
+ * @param array $condition
+ * @param array $params
* @return bool|array
- * @see DBA::select
+ * @throws \Exception
+ * @see DBA::select
*/
public static function selectFirstThread(array $fields = [], array $condition = [], $params = [])
{
/**
* @brief Select rows from the starting post in the item table
*
- * @param array $selected Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param array $selected Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return boolean|object
+ * @throws \Exception
*/
public static function selectThread(array $selected = [], array $condition = [], $params = [])
{
/**
* @brief Returns a list of fields that are associated with the item table
*
+ * @param $usermode
* @return array field list
*/
private static function fieldlist($usermode)
$fields['item-content'] = array_merge(self::CONTENT_FIELDLIST, self::MIXED_CONTENT_FIELDLIST);
- $fields['item-delivery-data'] = self::DELIVERY_DATA_FIELDLIST;
+ $fields['item-delivery-data'] = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, ItemDeliveryData::FIELD_LIST);
$fields['permissionset'] = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'];
- $fields['author'] = ['url' => 'author-link', 'name' => 'author-name',
+ $fields['author'] = ['url' => 'author-link', 'name' => 'author-name', 'addr' => 'author-addr',
'thumb' => 'author-avatar', 'nick' => 'author-nick', 'network' => 'author-network'];
- $fields['owner'] = ['url' => 'owner-link', 'name' => 'owner-name',
+ $fields['owner'] = ['url' => 'owner-link', 'name' => 'owner-name', 'addr' => 'owner-addr',
'thumb' => 'owner-avatar', 'nick' => 'owner-nick', 'network' => 'owner-network'];
$fields['contact'] = ['url' => 'contact-link', 'name' => 'contact-name', 'thumb' => 'contact-avatar',
/**
* @brief Returns all needed "JOIN" commands for the "select" functions
*
- * @param integer $uid User ID
- * @param string $sql_commands The parts of the built SQL commands in the "select" functions
- * @param boolean $thread_mode Called for the items (false) or for the threads (true)
+ * @param integer $uid User ID
+ * @param string $sql_commands The parts of the built SQL commands in the "select" functions
+ * @param boolean $thread_mode Called for the items (false) or for the threads (true)
*
+ * @param $user_mode
* @return string The SQL joins for the "select" functions
*/
private static function constructJoins($uid, $sql_commands, $thread_mode, $user_mode)
$selected[] = 'interaction';
}
+ $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST);
+
$selection = [];
foreach ($fields as $table => $table_fields) {
foreach ($table_fields as $field => $select) {
if (empty($selected) || in_array($select, $selected)) {
- $legacy_fields = array_merge(self::DELIVERY_DATA_FIELDLIST, self::MIXED_CONTENT_FIELDLIST);
if (self::isLegacyMode() && in_array($select, $legacy_fields)) {
$selection[] = "`item`.`".$select."` AS `internal-item-" . $select . "`";
}
/**
* @brief Update existing item entries
*
- * @param array $fields The fields that are to be changed
+ * @param array $fields The fields that are to be changed
* @param array $condition The condition for finding the item entries
*
* In the future we may have to change permissions as well.
* A return value of "0" doesn't mean an error - but that 0 rows had been changed.
*
* @return integer|boolean number of affected rows - or "false" if there was an error
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function update(array $fields, array $condition)
{
}
}
- $clear_fields = ['bookmark', 'type', 'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link'];
+ $delivery_data = ItemDeliveryData::extractFields($fields);
+
+ $clear_fields = ['bookmark', 'type', 'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link', 'postopts', 'inform'];
foreach ($clear_fields as $field) {
if (array_key_exists($field, $fields)) {
$fields[$field] = null;
$files = $fields['file'];
$fields['file'] = null;
} else {
- $files = '';
+ $files = null;
}
- $delivery_data = ['postopts' => defaults($fields, 'postopts', ''),
- 'inform' => defaults($fields, 'inform', '')];
-
- $fields['postopts'] = null;
- $fields['inform'] = null;
-
if (!empty($fields)) {
$success = DBA::update('item', $fields, $condition);
}
}
- if (!empty($files)) {
+ if (!is_null($files)) {
Term::insertFromFileFieldByItemId($item['id'], $files);
if (!empty($item['file'])) {
DBA::update('item', ['file' => ''], ['id' => $item['id']]);
}
}
- self::updateDeliveryData($item['id'], $delivery_data);
+ ItemDeliveryData::update($item['id'], $delivery_data);
self::updateThread($item['id']);
/**
* @brief Delete an item and notify others about it - if it was ours
*
- * @param array $condition The condition for finding the item entries
- * @param integer $priority Priority for the notification
+ * @param array $condition The condition for finding the item entries
+ * @param integer $priority Priority for the notification
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function delete($condition, $priority = PRIORITY_HIGH)
{
/**
* @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
+ * @param array $condition The condition for finding the item entries
+ * @param integer $uid User who wants to delete this item
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function deleteForUser($condition, $uid)
{
/**
* @brief Delete an item and notify others about it - if it was ours
*
- * @param integer $item_id Item ID that should be delete
+ * @param integer $item_id Item ID that should be delete
* @param integer $priority Priority for the notification
*
* @return boolean success
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function deleteById($item_id, $priority = PRIORITY_HIGH)
{
$matches = false;
$cnt = preg_match_all('/<(.*?)>/', $item['file'], $matches, PREG_SET_ORDER);
- if ($cnt)
- {
- foreach ($matches as $mtch)
- {
+ if ($cnt) {
+ foreach ($matches as $mtch) {
FileTag::unsaveFile($item['uid'], $item['id'], $mtch[1],true);
}
}
$cnt = preg_match_all('/\[(.*?)\]/', $item['file'], $matches, PREG_SET_ORDER);
- if ($cnt)
- {
- foreach ($matches as $mtch)
- {
+ if ($cnt) {
+ foreach ($matches as $mtch) {
FileTag::unsaveFile($item['uid'], $item['id'], $mtch[1],false);
}
}
* This only applies to photos uploaded from the photos page. Photos inserted into a post do not
* generate a resource-id and therefore aren't intimately linked to the item.
*/
+ /// @TODO: this should first check if photo is used elsewhere
if (strlen($item['resource-id'])) {
- DBA::delete('photo', ['resource-id' => $item['resource-id'], 'uid' => $item['uid']]);
+ Photo::delete(['resource-id' => $item['resource-id'], 'uid' => $item['uid']]);
}
// If item is a link to an event, delete the event.
}
// If item has attachments, drop them
- foreach (explode(", ", $item['attach']) as $attach) {
+ /// @TODO: this should first check if attachment is used elsewhere
+ foreach (explode(",", $item['attach']) as $attach) {
preg_match("|attach/(\d+)|", $attach, $matches);
if (is_array($matches) && count($matches) > 1) {
- DBA::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]);
+ Attach::delete(['id' => $matches[1], 'uid' => $item['uid']]);
}
}
self::delete(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority);
}
- DBA::delete('item-delivery-data', ['iid' => $item['id']]);
+ ItemDeliveryData::delete($item['id']);
// We don't delete the item-activity here, since we need some of the data for ActivityPub
private static function guid($item, $notify)
{
if (!empty($item['guid'])) {
- return Strings::removeTags(trim($item['guid']));
+ return Strings::escapeTags(trim($item['guid']));
}
if ($notify) {
// We have to avoid duplicates. So we create the GUID in form of a hash of the plink or uri.
// We add the hash of our own host because our host is the original creator of the post.
- $prefix_host = get_app()->getHostName();
+ $prefix_host = \get_app()->getHostName();
} else {
$prefix_host = '';
// This function will finally cover most of the preparation functionality in mod/item.php
public static function prepare(&$item)
{
+ /*
+ * @TODO: Unused code triggering inspection errors
+ *
$data = BBCode::getAttachmentData($item['body']);
if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $item['body'], $match, PREG_SET_ORDER) || isset($data["type"]))
&& ($posttype != Item::PT_PERSONAL_NOTE)) {
$posttype = Item::PT_PAGE;
$objecttype = ACTIVITY_OBJ_BOOKMARK;
}
+ */
}
public static function insert($item, $force_parent = false, $notify = false, $dontcache = false)
{
- $a = get_app();
+ $orig_item = $item;
+
+ $priority = PRIORITY_HIGH;
// If it is a posting where users should get notifications, then define it as wall posting
if ($notify) {
if (is_int($notify)) {
$priority = $notify;
- } else {
- $priority = PRIORITY_HIGH;
}
} else {
$item['network'] = trim(defaults($item, 'network', Protocol::PHANTOM));
}
$item['guid'] = self::guid($item, $notify);
- $item['uri'] = Strings::removeTags(trim(defaults($item, 'uri', self::newURI($item['uid'], $item['guid']))));
+ $item['uri'] = Strings::escapeTags(trim(defaults($item, 'uri', self::newURI($item['uid'], $item['guid']))));
// Store URI data
$item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
$item['owner-name'] = trim(defaults($item, 'owner-name', ''));
$item['owner-link'] = trim(defaults($item, 'owner-link', ''));
$item['owner-avatar'] = trim(defaults($item, 'owner-avatar', ''));
- $item['received'] = ((x($item, 'received') !== false) ? DateTimeFormat::utc($item['received']) : DateTimeFormat::utcNow());
- $item['created'] = ((x($item, 'created') !== false) ? DateTimeFormat::utc($item['created']) : $item['received']);
- $item['edited'] = ((x($item, 'edited') !== false) ? DateTimeFormat::utc($item['edited']) : $item['created']);
- $item['changed'] = ((x($item, 'changed') !== false) ? DateTimeFormat::utc($item['changed']) : $item['created']);
- $item['commented'] = ((x($item, 'commented') !== false) ? DateTimeFormat::utc($item['commented']) : $item['created']);
+ $item['received'] = (isset($item['received']) ? DateTimeFormat::utc($item['received']) : DateTimeFormat::utcNow());
+ $item['created'] = (isset($item['created']) ? DateTimeFormat::utc($item['created']) : $item['received']);
+ $item['edited'] = (isset($item['edited']) ? DateTimeFormat::utc($item['edited']) : $item['created']);
+ $item['changed'] = (isset($item['changed']) ? DateTimeFormat::utc($item['changed']) : $item['created']);
+ $item['commented'] = (isset($item['commented']) ? DateTimeFormat::utc($item['commented']) : $item['created']);
$item['title'] = trim(defaults($item, 'title', ''));
$item['location'] = trim(defaults($item, 'location', ''));
$item['coord'] = trim(defaults($item, 'coord', ''));
- $item['visible'] = ((x($item, 'visible') !== false) ? intval($item['visible']) : 1);
+ $item['visible'] = (isset($item['visible']) ? intval($item['visible']) : 1);
$item['deleted'] = 0;
$item['parent-uri'] = trim(defaults($item, 'parent-uri', $item['uri']));
$item['post-type'] = defaults($item, 'post-type', self::PT_ARTICLE);
if ($notify) {
$item['edit'] = false;
$item['parent'] = $parent_id;
- Addon::callHooks('post_local', $item);
+ Hook::callAll('post_local', $item);
unset($item['edit']);
unset($item['parent']);
} else {
- Addon::callHooks('post_remote', $item);
+ Hook::callAll('post_remote', $item);
}
// This array field is used to trigger some automatic reactions
// It is mainly used in the "post_local" hook.
unset($item['api_source']);
- if (x($item, 'cancel')) {
+ if (!empty($item['cancel'])) {
Logger::log('post cancelled by addon.');
return 0;
}
self::insertContent($item);
}
- $delivery_data = ['postopts' => defaults($item, 'postopts', ''),
- 'inform' => defaults($item, 'inform', '')];
+ $delivery_data = ItemDeliveryData::extractFields($item);
unset($item['postopts']);
unset($item['inform']);
if ($spoolpath != "") {
$spool = $spoolpath.'/'.$file;
- // Ensure to have the removed data from above again in the item array
- $item = array_merge($item, $delivery_data);
-
- file_put_contents($spool, json_encode($item));
+ file_put_contents($spool, json_encode($orig_item));
Logger::log("Item wasn't stored - Item was spooled into file ".$file, Logger::DEBUG);
}
return 0;
DBA::insert('diaspora-interaction', ['uri-id' => $item['uri-id'], 'interaction' => $diaspora_signed_text], true);
}
- $deleted = self::tagDeliver($item['uid'], $current_post);
+ self::tagDeliver($item['uid'], $current_post);
/*
* current post can be deleted if is for a community page and no mention are
* in it.
*/
- if (!$deleted && !$dontcache) {
+ if (!$dontcache) {
$posted_item = self::selectFirst(self::ITEM_FIELDLIST, ['id' => $current_post]);
if (DBA::isResult($posted_item)) {
if ($notify) {
- Addon::callHooks('post_local_end', $posted_item);
+ Hook::callAll('post_local_end', $posted_item);
} else {
- Addon::callHooks('post_remote_end', $posted_item);
+ Hook::callAll('post_remote_end', $posted_item);
}
} else {
Logger::log('new item not found in DB, id ' . $current_post);
self::updateThread($parent_id);
}
- $delivery_data['iid'] = $current_post;
-
- self::insertDeliveryData($delivery_data);
+ ItemDeliveryData::insert($current_post, $delivery_data);
DBA::commit();
$cmd = 'wall-new';
}
- Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', $cmd, $current_post);
+ Worker::add(['priority' => $priority, 'dont_fork' => true], 'Notifier', $cmd, $current_post);
}
return $current_post;
}
- /**
- * @brief Insert a new item delivery data entry
- *
- * @param array $item The item fields that are to be inserted
- */
- private static function insertDeliveryData($delivery_data)
- {
- if (empty($delivery_data['iid']) || (empty($delivery_data['postopts']) && empty($delivery_data['inform']))) {
- return;
- }
-
- DBA::insert('item-delivery-data', $delivery_data);
- }
-
- /**
- * @brief Update an existing item delivery data entry
- *
- * @param integer $id The item id that is to be updated
- * @param array $item The item fields that are to be inserted
- */
- private static function updateDeliveryData($id, $delivery_data)
- {
- if (empty($id) || (empty($delivery_data['postopts']) && empty($delivery_data['inform']))) {
- return;
- }
-
- DBA::update('item-delivery-data', $delivery_data, ['iid' => $id], true);
- }
-
/**
* @brief Insert a new item content entry
*
* @param array $item The item fields that are to be inserted
+ * @return bool
+ * @throws \Exception
*/
private static function insertActivity(&$item)
{
* @brief Insert a new item content entry
*
* @param array $item The item fields that are to be inserted
+ * @throws \Exception
*/
private static function insertContent(&$item)
{
/**
* @brief Update existing item content entries
*
- * @param array $item The item fields that are to be changed
+ * @param array $item The item fields that are to be changed
* @param array $condition The condition for finding the item content entries
+ * @return bool
+ * @throws \Exception
*/
private static function updateActivity($item, $condition)
{
/**
* @brief Update existing item content entries
*
- * @param array $item The item fields that are to be changed
+ * @param array $item The item fields that are to be changed
* @param array $condition The condition for finding the item content entries
+ * @throws \Exception
*/
private static function updateContent($item, $condition)
{
*
* @param integer $itemid Item ID that should be added
* @param string $signed_text Original text (for Diaspora signatures), JSON encoded.
+ * @throws \Exception
*/
public static function distribute($itemid, $signed_text = '')
{
$condition = ['id' => $itemid, 'uid' => 0,
'network' => [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS, ""],
'visible' => true, 'deleted' => false, 'moderated' => false, 'private' => false];
- $item = self::selectFirst(self::ITEM_FIELDLIST, ['id' => $itemid]);
+ $item = self::selectFirst(self::ITEM_FIELDLIST, $condition);
if (!DBA::isResult($item)) {
return;
}
* @param integer $itemid Item ID that should be added
* @param array $item The item entry that will be stored
* @param integer $uid The user that will receive the item entry
+ * @throws \Exception
*/
private static function storeForUser($itemid, $item, $uid)
{
* It is planned that in the future we will store public item entries only once.
*
* @param integer $itemid Item ID that should be added
+ * @throws \Exception
*/
public static function addShadow($itemid)
{
* This function does the same like the function above - but for comments
*
* @param integer $itemid Item ID that should be added
+ * @throws \Exception
*/
public static function addShadowPost($itemid)
{
}
}
- /**
+ /**
* Adds a language specification in a "language" element of given $arr.
* Expects "body" element to exist in $arr.
+ *
+ * @param $item
+ * @throws \Text_LanguageDetect_Exception
*/
private static function addLanguageToItemArray(&$item)
{
/**
* generate an unique URI
*
- * @param integer $uid User id
- * @param string $guid An existing GUID (Otherwise it will be generated)
+ * @param integer $uid User id
+ * @param string $guid An existing GUID (Otherwise it will be generated)
*
* @return string
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function newURI($uid, $guid = "")
{
* Don't set this value if it isn't from the owner (could be an author that we don't know)
*
* @param array $arr Contains the just posted item record
+ * @throws \Exception
*/
private static function updateContact($arr)
{
$update = (!$arr['private'] && ((defaults($arr, 'author-link', '') === defaults($arr, 'owner-link', '')) || ($arr["parent-uri"] === $arr["uri"])));
// Is it a forum? Then we don't care about the rules from above
- if (!$update && ($arr["network"] == Protocol::DFRN) && ($arr["parent-uri"] === $arr["uri"])) {
+ if (!$update && in_array($arr["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN]) && ($arr["parent-uri"] === $arr["uri"])) {
if (DBA::exists('contact', ['id' => $arr['contact-id'], 'forum' => true])) {
$update = true;
}
public static function setHashtags(&$item)
{
-
$tags = BBCode::getTags($item["body"]);
// No hashtags?
return false;
}
+ // What happens in [code], stays in [code]!
+ // escape the # and the [
+ // hint: we will also get in trouble with #tags, when we want markdown in posts -> ### Headline 3
+ $item["body"] = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism",
+ function ($match) {
+ // we truly ESCape all # and [ to prevent gettin weird tags in [code] blocks
+ $find = ['#', '['];
+ $replace = [chr(27).'sharp', chr(27).'leftsquarebracket'];
+ return ("[code" . $match[1] . "]" . str_replace($find, $replace, $match[2]) . "[/code]");
+ }, $item["body"]);
+
// This sorting is important when there are hashtags that are part of other hashtags
// Otherwise there could be problems with hashtags like #test and #test2
rsort($tags);
"#$2", $item["body"]);
foreach ($tags as $tag) {
- if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=')) {
+ if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || $tag[1] == '#') {
continue;
}
$basetag = str_replace('_',' ',substr($tag,1));
-
- $newtag = '#[url=' . System::baseUrl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
+ $newtag = '#[url=' . System::baseUrl() . '/search?tag=' . $basetag . ']' . $basetag . '[/url]';
$item["body"] = str_replace($tag, $newtag, $item["body"]);
if (!stristr($item["tag"], "/search?tag=" . $basetag . "]" . $basetag . "[/url]")) {
if (strlen($item["tag"])) {
- $item["tag"] = ','.$item["tag"];
+ $item["tag"] = ',' . $item["tag"];
}
- $item["tag"] = $newtag.$item["tag"];
+ $item["tag"] = $newtag . $item["tag"];
}
}
// Convert back the masked hashtags
$item["body"] = str_replace("#", "#", $item["body"]);
+
+ // Remember! What happens in [code], stays in [code]
+ // roleback the # and [
+ $item["body"] = preg_replace_callback("/\[code(.*?)\](.*?)\[\/code\]/ism",
+ function ($match) {
+ // we truly unESCape all sharp and leftsquarebracket
+ $find = [chr(27).'sharp', chr(27).'leftsquarebracket'];
+ $replace = ['#', '['];
+ return ("[code" . $match[1] . "]" . str_replace($find, $replace, $match[2]) . "[/code]");
+ }, $item["body"]);
}
public static function getGuidById($id)
/**
* This function is only used for the old Friendica app on Android that doesn't like paths with guid
+ *
* @param string $guid item guid
* @param int $uid user id
* @return array with id and nick of the item with the given guid
+ * @throws \Exception
*/
public static function getIdAndNickByGuid($guid, $uid = 0)
{
$id = 0;
if ($uid == 0) {
- $uid == local_user();
+ $uid = local_user();
}
// Does the given user have this item?
/**
* look for mention tags and setup a second delivery chain for forum/community posts if appropriate
+ *
* @param int $uid
* @param int $item_id
- * @return bool true if item was deleted, else false
+ * @return void true if item was deleted, else false
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
*/
private static function tagDeliver($uid, $item_id)
{
return;
}
- $community_page = (($user['page-flags'] == Contact::PAGE_COMMUNITY) ? true : false);
- $prvgroup = (($user['page-flags'] == Contact::PAGE_PRVGROUP) ? true : false);
+ $community_page = (($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY) ? true : false);
+ $prvgroup = (($user['page-flags'] == User::PAGE_FLAGS_PRVGROUP) ? true : false);
$item = self::selectFirst(self::ITEM_FIELDLIST, ['id' => $item_id]);
if (!DBA::isResult($item)) {
$arr = ['item' => $item, 'user' => $user];
- Addon::callHooks('tagged', $arr);
+ Hook::callAll('tagged', $arr);
if (!$community_page && !$prvgroup) {
return;
public static function isRemoteSelf($contact, &$datarray)
{
- $a = get_app();
+ $a = \get_app();
if (!$contact['remote_self']) {
return false;
* @param array $item
* @param int $cid
* @return string
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
*/
public static function fixPrivatePhotos($s, $uid, $item = null, $cid = 0)
{
if ($x) {
$res = substr($i, $x + 1);
$i = substr($i, 0, $x);
- $fields = ['data', 'type', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'];
- $photo = DBA::selectFirst('photo', $fields, ['resource-id' => $i, 'scale' => $res, 'uid' => $uid]);
+ $photo = Photo::getPhotoForUser($uid, $i, $res);
if (DBA::isResult($photo)) {
/*
* Check to see if we should replace this photo link with an embedded image
}
}
if ($replace) {
- $data = $photo['data'];
- $type = $photo['type'];
-
+ $photo_img = Photo::getImageForPhoto($photo);
// If a custom width and height were specified, apply before embedding
if (preg_match("/\[img\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) {
Logger::log('scaling photo', Logger::DEBUG);
$width = intval($match[1]);
$height = intval($match[2]);
- $Image = new Image($data, $type);
- if ($Image->isValid()) {
- $Image->scaleDown(max($width, $height));
- $data = $Image->asString();
- $type = $Image->getType();
- }
+ $photo_img->scaleDown(max($width, $height));
}
+ $data = $photo_img->asString();
+ $type = $photo_img->getType();
+
Logger::log('replacing photo', Logger::DEBUG);
$image = 'data:' . $type . ';base64,' . base64_encode($data);
Logger::log('replaced: ' . $image, Logger::DATA);
*
* @param string $item_id
* @param string $verb
- * Activity verb. One of
- * like, unlike, dislike, undislike, attendyes, unattendyes,
- * attendno, unattendno, attendmaybe, unattendmaybe
- * @hook 'post_local_end'
- * array $arr
- * 'post_id' => ID of posted item
+ * Activity verb. One of
+ * like, unlike, dislike, undislike, attendyes, unattendyes,
+ * attendno, unattendno, attendmaybe, unattendmaybe
+ * @return bool
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
+ * @hook 'post_local_end'
+ * array $arr
+ * 'post_id' => ID of posted item
*/
public static function performLike($item_id, $verb)
{
// Contact-id is the uid-dependant author contact
if (local_user() == $uid) {
$item_contact_id = $owner_self_contact['id'];
- $item_contact = $owner_self_contact;
} else {
$item_contact_id = Contact::getIdForURL($author_contact['url'], $uid, true);
$item_contact = DBA::selectFirst('contact', [], ['id' => $item_contact_id]);
$new_item['id'] = $new_item_id;
- Addon::callHooks('post_local_end', $new_item);
+ Hook::callAll('post_local_end', $new_item);
return true;
}
$item["mention"] = 1;
}
- $sql = "";
-
$fields = [];
foreach ($item as $field => $data) {
}
}
- public static function getPermissionsSQLByUserId($owner_id, $remote_verified = false, $groups = null)
+ public static function getPermissionsSQLByUserId($owner_id, $remote_verified = false, $groups = null, $remote_cid = null)
{
$local_user = local_user();
$remote_user = remote_user();
* If pre-verified, the caller is expected to have already
* done this and passed the groups into this function.
*/
- $set = PermissionSet::get($owner_id, $remote_user, $groups);
+ $set = PermissionSet::get($owner_id, $remote_cid, $groups);
if (!empty($set)) {
$sql_set = " OR (`item`.`private` IN (1,2) AND `item`.`wall` AND `item`.`psid` IN (" . implode(',', $set) . "))";
/**
* get translated item type
*
- * @param array $itme
+ * @param $item
* @return string
*/
public static function postType($item)
* @param array $item
* @param bool $update
*
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Remove reference, simply return "rendered-html" and "rendered-hash"
*/
public static function putInCache(&$item, $update = false)
$item["rendered-hash"] = hash("md5", $item["body"]);
$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
- Addon::callHooks('put_item_in_cache', $hook_data);
+ Hook::callAll('put_item_in_cache', $hook_data);
$item['rendered-html'] = $hook_data['rendered-html'];
$item['rendered-hash'] = $hook_data['rendered-hash'];
unset($hook_data);
* @param boolean $attach
* @param boolean $is_preview
* @return string item body html
- * @hook prepare_body_init item array before any work
- * @hook prepare_body_content_filter ('item'=>item array, 'filter_reasons'=>string array) before first bbcode to html
- * @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
- * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
+ * @hook prepare_body_init item array before any work
+ * @hook prepare_body_content_filter ('item'=>item array, 'filter_reasons'=>string array) before first bbcode to html
+ * @hook prepare_body ('item'=>item array, 'html'=>body string, 'is_preview'=>boolean, 'filter_reasons'=>string array) after first bbcode to html
+ * @hook prepare_body_final ('item'=>item array, 'html'=>body string) after attach icons and blockquote special case handling (spoiler, author)
*/
public static function prepareBody(array &$item, $attach = false, $is_preview = false)
{
$a = self::getApp();
- Addon::callHooks('prepare_body_init', $item);
+ Hook::callAll('prepare_body_init', $item);
// In order to provide theme developers more possibilities, event items
// are treated differently.
'item' => $item,
'filter_reasons' => $filter_reasons
];
- Addon::callHooks('prepare_body_content_filter', $hook_data);
+ Hook::callAll('prepare_body_content_filter', $hook_data);
$filter_reasons = $hook_data['filter_reasons'];
unset($hook_data);
}
'preview' => $is_preview,
'filter_reasons' => $filter_reasons
];
- Addon::callHooks('prepare_body', $hook_data);
+ Hook::callAll('prepare_body', $hook_data);
$s = $hook_data['html'];
unset($hook_data);
$filesubtype = 'unkn';
}
- $title = Strings::escapeTags(trim(!empty($mtch[4]) ? $mtch[4] : $mtch[1]));
+ $title = Strings::escapeHtml(trim(defaults($mtch, 4, $mtch[1])));
$title .= ' ' . $mtch[2] . ' ' . L10n::t('bytes');
$icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
}
// Map.
- if (strpos($s, '<div class="map">') !== false && x($item, 'coord')) {
+ if (strpos($s, '<div class="map">') !== false && !empty($item['coord'])) {
$x = Map::byCoordinates(trim($item['coord']));
if ($x) {
$s = preg_replace('/\<div class\=\"map\"\>/', '$0' . $x, $s);
$s = HTML::applyContentFilter($s, $filter_reasons);
$hook_data = ['item' => $item, 'html' => $s];
- Addon::callHooks('prepare_body_final', $hook_data);
+ Hook::callAll('prepare_body_final', $hook_data);
return $hook_data['html'];
}
/**
* get private link for item
+ *
* @param array $item
* @return boolean|array False if item has not plink, otherwise array('href'=>plink url, 'title'=>translated title)
+ * @throws \Exception
*/
public static function getPlink($item)
{