From: Hypolite Petovan Date: Sun, 19 Nov 2017 21:50:49 +0000 (-0500) Subject: Move Item and Conversation from Core to Object X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=f43aaf5227af34b870bae3715f2cb1de293aa272;p=friendica.git Move Item and Conversation from Core to Object - Move BaseObject from Core\ to Friendica\ --- diff --git a/include/conversation.php b/include/conversation.php index 218d7405b5..3eae184a82 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -4,11 +4,11 @@ */ use Friendica\App; use Friendica\Core\Config; -use Friendica\Core\Conversation; -use Friendica\Core\Item; use Friendica\Core\PConfig; use Friendica\Core\System; use Friendica\Database\DBM; +use Friendica\Object\Conversation; +use Friendica\Object\Item; require_once "include/bbcode.php"; require_once "include/acl_selectors.php"; diff --git a/index.php b/index.php index 5e8860afd3..0290a38be7 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,7 @@ */ use Friendica\App; -use Friendica\Core\BaseObject; +use Friendica\BaseObject; use Friendica\Core\System; use Friendica\Core\Config; use Friendica\Core\Worker; diff --git a/src/BaseObject.php b/src/BaseObject.php new file mode 100644 index 0000000000..01957164c1 --- /dev/null +++ b/src/BaseObject.php @@ -0,0 +1,47 @@ +setMode($mode); - $this->preview = $preview; - } - - /** - * Set the mode we'll be displayed on - * - * @param string $mode The mode to set - * - * @return void - */ - private function setMode($mode) - { - if ($this->getMode() == $mode) { - return; - } - - $a = $this->getApp(); - - switch ($mode) { - case 'network': - case 'notes': - $this->profile_owner = local_user(); - $this->writable = true; - break; - case 'profile': - $this->profile_owner = $a->profile['profile_uid']; - $this->writable = can_write_wall($a, $this->profile_owner); - break; - case 'display': - $this->profile_owner = $a->profile['uid']; - $this->writable = can_write_wall($a, $this->profile_owner); - break; - default: - logger('[ERROR] Conversation::setMode : Unhandled mode ('. $mode .').', LOGGER_DEBUG); - return false; - break; - } - $this->mode = $mode; - } - - /** - * Get mode - * - * @return string - */ - public function getMode() - { - return $this->mode; - } - - /** - * Check if page is writable - * - * @return boolean - */ - public function isWritable() - { - return $this->writable; - } - - /** - * Check if page is a preview - * - * @return boolean - */ - public function isPreview() - { - return $this->preview; - } - - /** - * Get profile owner - * - * @return integer - */ - public function getProfileOwner() - { - return $this->profile_owner; - } - - /** - * Add a thread to the conversation - * - * @param object $item The item to insert - * - * @return mixed The inserted item on success - * false on failure - */ - public function addThread($item) - { - $item_id = $item->getId(); - - if (!$item_id) { - logger('[ERROR] Conversation::addThread : Item has no ID!!', LOGGER_DEBUG); - return false; - } - - if ($this->getThread($item->getId())) { - logger('[WARN] Conversation::addThread : Thread already exists ('. $item->getId() .').', LOGGER_DEBUG); - return false; - } - - /* - * Only add will be displayed - */ - if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { - logger('[WARN] Conversation::addThread : Thread is a mail ('. $item->getId() .').', LOGGER_DEBUG); - return false; - } - - if ($item->getDataValue('verb') === ACTIVITY_LIKE || $item->getDataValue('verb') === ACTIVITY_DISLIKE) { - logger('[WARN] Conversation::addThread : Thread is a (dis)like ('. $item->getId() .').', LOGGER_DEBUG); - return false; - } - - $item->setConversation($this); - $this->threads[] = $item; - - return end($this->threads); - } - - /** - * Get data in a form usable by a conversation template - * - * We should find a way to avoid using those arguments (at least most of them) - * - * @param object $conv_responses data - * - * @return mixed The data requested on success - * false on failure - */ - public function getTemplateData($conv_responses) - { - $a = get_app(); - $result = array(); - $i = 0; - - foreach ($this->threads as $item) { - if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { - continue; - } - - $item_data = $item->getTemplateData($conv_responses); - - if (!$item_data) { - logger('[ERROR] Conversation::getTemplateData : Failed to get item template data ('. $item->getId() .').', LOGGER_DEBUG); - return false; - } - $result[] = $item_data; - } - - return $result; - } - - /** - * Get a thread based on its item id - * - * @param integer $id Item id - * - * @return mixed The found item on success - * false on failure - */ - private function getThread($id) - { - foreach ($this->threads as $item) { - if ($item->getId() == $id) { - return $item; - } - } - - return false; - } -} diff --git a/src/Core/Item.php b/src/Core/Item.php deleted file mode 100644 index 24e524d2ea..0000000000 --- a/src/Core/Item.php +++ /dev/null @@ -1,925 +0,0 @@ - 'wall_thread.tpl', - 'wall2wall' => 'wallwall_thread.tpl' - ); - private $comment_box_template = 'comment_item.tpl'; - private $toplevel = false; - private $writable = false; - private $children = array(); - private $parent = null; - private $conversation = null; - private $redirect_url = null; - private $owner_url = ''; - private $owner_photo = ''; - private $owner_name = ''; - private $wall_to_wall = false; - private $threaded = false; - private $visiting = false; - - /** - * Constructor - * - * @param array $data data array - */ - public function __construct($data) - { - $a = $this->getApp(); - - $this->data = $data; - $this->setTemplate('wall'); - $this->toplevel = ($this->getId() == $this->getDataValue('parent')); - - if (is_array($_SESSION['remote'])) { - foreach ($_SESSION['remote'] as $visitor) { - if ($visitor['cid'] == $this->getDataValue('contact-id')) { - $this->visiting = true; - break; - } - } - } - - $this->writable = ($this->getDataValue('writable') || $this->getDataValue('self')); - - $ssl_state = ((local_user()) ? true : false); - $this->redirect_url = 'redir/' . $this->getDataValue('cid'); - - if (Config::get('system', 'thread_allow') && $a->theme_thread_allow && !$this->isToplevel()) { - $this->threaded = true; - } - - // Prepare the children - if (count($data['children'])) { - foreach ($data['children'] as $item) { - /* - * Only add will be displayed - */ - if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) { - continue; - } elseif (! visible_activity($item)) { - continue; - } - - // You can always comment on Diaspora items - if (($item['network'] == NETWORK_DIASPORA) && (local_user() == $item['uid'])) { - $item['writable'] = true; - } - - $item['pagedrop'] = $data['pagedrop']; - $child = new Item($item); - $this->addChild($child); - } - } - } - - /** - * Get data in a form usable by a conversation template - * - * @param object $conv_responses conversation responses - * @param integer $thread_level default = 1 - * - * @return mixed The data requested on success - * false on failure - */ - public function getTemplateData($conv_responses, $thread_level = 1) - { - require_once "mod/proxy.php"; - - $result = array(); - - $a = $this->getApp(); - - $item = $this->getData(); - $edited = false; - // If the time between "created" and "edited" differs we add - // a notice that the post was edited. - // Note: In some networks reshared items seem to have (sometimes) a difference - // between creation time and edit time of a second. Thats why we add the notice - // only if the difference is more than 1 second. - if (strtotime($item['edited']) - strtotime($item['created']) > 1) { - $edited = array( - 'label' => t('This entry was edited'), - 'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r'), - 'relative' => relative_date($item['edited']) - ); - } - $commentww = ''; - $sparkle = ''; - $buttons = ''; - $dropping = false; - $star = false; - $ignore = false; - $isstarred = "unstarred"; - $indent = ''; - $shiny = ''; - $osparkle = ''; - $total_children = $this->countDescendants(); - - $conv = $this->getConversation(); - - $lock = ((($item['private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) - || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) - ? t('Private Message') - : false); - $shareable = ((($conv->getProfileOwner() == local_user()) && ($item['private'] != 1)) ? true : false); - if (local_user() && link_compare($a->contact['url'], $item['author-link'])) { - if ($item["event-id"] != 0) { - $edpost = array("events/event/".$item['event-id'], t("Edit")); - } else { - $edpost = array("editpost/".$item['id'], t("Edit")); - } - } else { - $edpost = false; - } - - if (($this->getDataValue('uid') == local_user()) || $this->isVisiting()) { - $dropping = true; - } - - $drop = array( - 'dropping' => $dropping, - 'pagedrop' => ((feature_enabled($conv->getProfileOwner(), 'multi_delete')) ? $item['pagedrop'] : ''), - 'select' => t('Select'), - 'delete' => t('Delete'), - ); - - $filer = (($conv->getProfileOwner() == local_user()) ? t("save to folder") : false); - - $diff_author = ((link_compare($item['url'], $item['author-link'])) ? false : true); - $profile_name = htmlentities(((strlen($item['author-name'])) && $diff_author) ? $item['author-name'] : $item['name']); - if ($item['author-link'] && (! $item['author-name'])) { - $profile_name = $item['author-link']; - } - - $sp = false; - $profile_link = best_link_url($item, $sp); - if ($profile_link === 'mailbox') { - $profile_link = ''; - } - - if ($sp) { - $sparkle = ' sparkle'; - } else { - $profile_link = zrl($profile_link); - } - - if (!isset($item['author-thumb']) || ($item['author-thumb'] == "")) { - $author_contact = get_contact_details_by_url($item['author-link'], $conv->getProfileOwner()); - if ($author_contact["thumb"]) { - $item['author-thumb'] = $author_contact["thumb"]; - } else { - $item['author-thumb'] = $item['author-avatar']; - } - } - - if (!isset($item['owner-thumb']) || ($item['owner-thumb'] == "")) { - $owner_contact = get_contact_details_by_url($item['owner-link'], $conv->getProfileOwner()); - if ($owner_contact["thumb"]) { - $item['owner-thumb'] = $owner_contact["thumb"]; - } else { - $item['owner-thumb'] = $item['owner-avatar']; - } - } - - $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); - call_hooks('render_location', $locate); - $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate)); - - $tags=array(); - $hashtags = array(); - $mentions = array(); - - /*foreach(explode(',',$item['tag']) as $tag){ - $tag = trim($tag); - if ($tag!="") { - $t = bbcode($tag); - $tags[] = $t; - if($t[0] == '#') - $hashtags[] = $t; - elseif($t[0] == '@') - $mentions[] = $t; - } - }*/ - - // process action responses - e.g. like/dislike/attend/agree/whatever - $response_verbs = array('like', 'dislike'); - - if ($item['object-type'] === ACTIVITY_OBJ_EVENT) { - $response_verbs[] = 'attendyes'; - $response_verbs[] = 'attendno'; - $response_verbs[] = 'attendmaybe'; - if ($conv->isWritable()) { - $isevent = true; - $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); - } - } - - $responses = get_responses($conv_responses, $response_verbs, $this, $item); - - foreach ($response_verbs as $value => $verbs) { - $responses[$verbs]['output'] = ((x($conv_responses[$verbs], $item['uri'])) ? format_like($conv_responses[$verbs][$item['uri']], $conv_responses[$verbs][$item['uri'] . '-l'], $verbs, $item['uri']) : ''); - } - - /* - * We should avoid doing this all the time, but it depends on the conversation mode - * And the conv mode may change when we change the conv, or it changes its mode - * Maybe we should establish a way to be notified about conversation changes - */ - $this->checkWallToWall(); - - if ($this->isWallToWall() && ($this->getOwnerUrl() == $this->getRedirectUrl())) { - $osparkle = ' sparkle'; - } - - if ($this->isToplevel()) { - if ($conv->getProfileOwner() == local_user()) { - $isstarred = (($item['starred']) ? "starred" : "unstarred"); - - $star = array( - 'do' => t("add star"), - 'undo' => t("remove star"), - 'toggle' => t("toggle star status"), - 'classdo' => (($item['starred']) ? "hidden" : ""), - 'classundo' => (($item['starred']) ? "" : "hidden"), - 'starred' => t('starred'), - ); - $r = dba::select('thread', array('ignored'), array('uid' => $item['uid'], 'iid' => $item['id']), array('limit' => 1)); - if (DBM::is_result($r)) { - $ignore = array( - 'do' => t("ignore thread"), - 'undo' => t("unignore thread"), - 'toggle' => t("toggle ignore status"), - 'classdo' => (($r['ignored']) ? "hidden" : ""), - 'classundo' => (($r['ignored']) ? "" : "hidden"), - 'ignored' => t('ignored'), - ); - } - - $tagger = ''; - if (feature_enabled($conv->getProfileOwner(), 'commtag')) { - $tagger = array( - 'add' => t("add tag"), - 'class' => "", - ); - } - } - } else { - $indent = 'comment'; - } - - if ($conv->isWritable()) { - $buttons = array( - 'like' => array( t("I like this \x28toggle\x29"), t("like")), - 'dislike' => ((feature_enabled($conv->getProfileOwner(), 'dislike')) ? array( t("I don't like this \x28toggle\x29"), t("dislike")) : ''), - ); - if ($shareable) { - $buttons['share'] = array( t('Share this'), t('share')); - } - } - - $comment = $this->getCommentBox($indent); - - if (strcmp(datetime_convert('UTC', 'UTC', $item['created']), datetime_convert('UTC', 'UTC', 'now - 12 hours')) > 0) { - $shiny = 'shiny'; - } - - localize_item($item); - - $body = prepare_body($item, true); - - list($categories, $folders) = get_cats_and_terms($item); - - if ($a->theme['template_engine'] === 'internal') { - $body_e = template_escape($body); - $text_e = strip_tags(template_escape($body)); - $name_e = template_escape($profile_name); - $title_e = template_escape($item['title']); - $location_e = template_escape($location); - $owner_name_e = template_escape($this->getOwnerName()); - } else { - $body_e = $body; - $text_e = strip_tags($body); - $name_e = $profile_name; - $title_e = $item['title']; - $location_e = $location; - $owner_name_e = $this->getOwnerName(); - } - - // Disable features that aren't available in several networks - - /// @todo Add NETWORK_DIASPORA when it will pass this information - if (!in_array($item["item_network"], array(NETWORK_DFRN)) && isset($buttons["dislike"])) { - unset($buttons["dislike"], $isevent); - $tagger = ''; - } - - if (($item["item_network"] == NETWORK_FEED) && isset($buttons["like"])) { - unset($buttons["like"]); - } - - if (($item["item_network"] == NETWORK_MAIL) && isset($buttons["like"])) { - unset($buttons["like"]); - } - - $tmp_item = array( - 'template' => $this->getTemplate(), - 'type' => implode("", array_slice(explode("/", $item['verb']), -1)), - 'tags' => $item['tags'], - 'hashtags' => $item['hashtags'], - 'mentions' => $item['mentions'], - 'txt_cats' => t('Categories:'), - 'txt_folders' => t('Filed under:'), - 'has_cats' => ((count($categories)) ? 'true' : ''), - 'has_folders' => ((count($folders)) ? 'true' : ''), - 'categories' => $categories, - 'folders' => $folders, - 'body' => $body_e, - 'text' => $text_e, - 'id' => $this->getId(), - 'guid' => urlencode($item['guid']), - 'isevent' => $isevent, - 'attend' => $attend, - 'linktitle' => sprintf(t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])), - 'olinktitle' => sprintf(t('View %s\'s profile @ %s'), htmlentities($this->getOwnerName()), ((strlen($item['owner-link'])) ? $item['owner-link'] : $item['url'])), - 'to' => t('to'), - 'via' => t('via'), - 'wall' => t('Wall-to-Wall'), - 'vwall' => t('via Wall-To-Wall:'), - 'profile_url' => $profile_link, - 'item_photo_menu' => item_photo_menu($item), - 'name' => $name_e, - 'thumb' => $a->remove_baseurl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)), - 'osparkle' => $osparkle, - 'sparkle' => $sparkle, - 'title' => $title_e, - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'ago' => (($item['app']) ? sprintf(t('%s from %s'), relative_date($item['created']), $item['app']) : relative_date($item['created'])), - 'app' => $item['app'], - 'created' => relative_date($item['created']), - 'lock' => $lock, - 'location' => $location_e, - 'indent' => $indent, - 'shiny' => $shiny, - 'owner_url' => $this->getOwnerUrl(), - 'owner_photo' => $a->remove_baseurl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)), - 'owner_name' => htmlentities($owner_name_e), - 'plink' => get_plink($item), - 'edpost' => ((feature_enabled($conv->getProfileOwner(), 'edit_posts')) ? $edpost : ''), - 'isstarred' => $isstarred, - 'star' => ((feature_enabled($conv->getProfileOwner(), 'star_posts')) ? $star : ''), - 'ignore' => ((feature_enabled($conv->getProfileOwner(), 'ignore_posts')) ? $ignore : ''), - 'tagger' => $tagger, - 'filer' => ((feature_enabled($conv->getProfileOwner(), 'filing')) ? $filer : ''), - 'drop' => $drop, - 'vote' => $buttons, - 'like' => $responses['like']['output'], - 'dislike' => $responses['dislike']['output'], - 'responses' => $responses, - 'switchcomment' => t('Comment'), - 'comment' => $comment, - 'previewing' => ($conv->isPreview() ? ' preview ' : ''), - 'wait' => t('Please wait'), - 'thread_level' => $thread_level, - 'edited' => $edited, - 'network' => $item["item_network"], - 'network_name' => network_to_name($item['item_network'], $profile_link), - 'received' => $item['received'], - 'commented' => $item['commented'], - 'created_date' => $item['created'], - ); - - $arr = array('item' => $item, 'output' => $tmp_item); - call_hooks('display_item', $arr); - - $result = $arr['output']; - - $result['children'] = array(); - $children = $this->getChildren(); - $nb_children = count($children); - if ($nb_children > 0) { - foreach ($children as $child) { - $result['children'][] = $child->getTemplateData($conv_responses, $thread_level + 1); - } - // Collapse - if (($nb_children > 2) || ($thread_level > 1)) { - $result['children'][0]['comment_firstcollapsed'] = true; - $result['children'][0]['num_comments'] = sprintf(tt('%d comment', '%d comments', $total_children), $total_children); - $result['children'][0]['hidden_comments_num'] = $total_children; - $result['children'][0]['hidden_comments_text'] = tt('comment', 'comments', $total_children); - $result['children'][0]['hide_text'] = t('show more'); - if ($thread_level > 1) { - $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true; - } else { - $result['children'][$nb_children - 3]['comment_lastcollapsed'] = true; - } - } - } - - if ($this->isToplevel()) { - $result['total_comments_num'] = "$total_children"; - $result['total_comments_text'] = tt('comment', 'comments', $total_children); - } - - $result['private'] = $item['private']; - $result['toplevel'] = ($this->isToplevel() ? 'toplevel_item' : ''); - - if ($this->isThreaded()) { - $result['flatten'] = false; - $result['threaded'] = true; - } else { - $result['flatten'] = true; - $result['threaded'] = false; - } - - return $result; - } - - /** - * @return integer - */ - public function getId() - { - return $this->getDataValue('id'); - } - - /** - * @return boolean - */ - public function isThreaded() - { - return $this->threaded; - } - - /** - * Add a child item - * - * @param object $item The child item to add - * - * @return mixed - */ - public function addChild(Item $item) - { - $item_id = $item->getId(); - if (!$item_id) { - logger('[ERROR] Item::addChild : Item has no ID!!', LOGGER_DEBUG); - return false; - } elseif ($this->getChild($item->getId())) { - logger('[WARN] Item::addChild : Item already exists ('. $item->getId() .').', LOGGER_DEBUG); - return false; - } - /* - * Only add what will be displayed - */ - if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { - return false; - } elseif (activity_match($item->getDataValue('verb'), ACTIVITY_LIKE) || activity_match($item->getDataValue('verb'), ACTIVITY_DISLIKE)) { - return false; - } - - $item->setParent($this); - $this->children[] = $item; - - return end($this->children); - } - - /** - * Get a child by its ID - * - * @param integer $id The child id - * - * @return mixed - */ - public function getChild($id) - { - foreach ($this->getChildren() as $child) { - if ($child->getId() == $id) { - return $child; - } - } - - return null; - } - - /** - * Get all our children - * - * @return object - */ - public function getChildren() - { - return $this->children; - } - - /** - * Set our parent - * - * @param object $item The item to set as parent - * - * @return void - */ - protected function setParent($item) - { - $parent = $this->getParent(); - if ($parent) { - $parent->removeChild($this); - } - - $this->parent = $item; - $this->setConversation($item->getConversation()); - } - - /** - * Remove our parent - * - * @return void - */ - protected function removeParent() - { - $this->parent = null; - $this->conversation = null; - } - - /** - * Remove a child - * - * @param object $item The child to be removed - * - * @return boolean Success or failure - */ - public function removeChild($item) - { - $id = $item->getId(); - foreach ($this->getChildren() as $key => $child) { - if ($child->getId() == $id) { - $child->removeParent(); - unset($this->children[$key]); - // Reindex the array, in order to make sure there won't be any trouble on loops using count() - $this->children = array_values($this->children); - return true; - } - } - logger('[WARN] Item::removeChild : Item is not a child ('. $id .').', LOGGER_DEBUG); - return false; - } - - /** - * Get parent item - * - * @return object - */ - protected function getParent() - { - return $this->parent; - } - - /** - * Set conversation - * - * @param object $conv The conversation - * - * @return void - */ - public function setConversation($conv) - { - $previous_mode = ($this->conversation ? $this->conversation->getMode() : ''); - - $this->conversation = $conv; - - // Set it on our children too - foreach ($this->getChildren() as $child) { - $child->setConversation($conv); - } - } - - /** - * Get conversation - * - * @return object - */ - public function getConversation() - { - return $this->conversation; - } - - /** - * Get raw data - * - * We shouldn't need this - * - * @return array - */ - public function getData() - { - return $this->data; - } - - /** - * Get a data value - * - * @param object $name key - * - * @return mixed value on success - * false on failure - */ - public function getDataValue($name) - { - if (!isset($this->data[$name])) { - // logger('[ERROR] Item::getDataValue : Item has no value name "'. $name .'".', LOGGER_DEBUG); - return false; - } - - return $this->data[$name]; - } - - /** - * Set template - * - * @param object $name template name - * - * @return void - */ - private function setTemplate($name) - { - if (!x($this->available_templates, $name)) { - logger('[ERROR] Item::setTemplate : Template not available ("'. $name .'").', LOGGER_DEBUG); - return false; - } - - $this->template = $this->available_templates[$name]; - } - - /** - * Get template - * - * @return object - */ - private function getTemplate() - { - return $this->template; - } - - /** - * Check if this is a toplevel post - * - * @return boolean - */ - private function isToplevel() - { - return $this->toplevel; - } - - /** - * Check if this is writable - * - * @return boolean - */ - private function isWritable() - { - $conv = $this->getConversation(); - - if ($conv) { - // This will allow us to comment on wall-to-wall items owned by our friends - // and community forums even if somebody else wrote the post. - - // bug #517 - this fixes for conversation owner - if ($conv->getMode() == 'profile' && $conv->getProfileOwner() == local_user()) { - return true; - } - - // this fixes for visitors - return ($this->writable || ($this->isVisiting() && $conv->getMode() == 'profile')); - } - return $this->writable; - } - - /** - * Count the total of our descendants - * - * @return integer - */ - private function countDescendants() - { - $children = $this->getChildren(); - $total = count($children); - if ($total > 0) { - foreach ($children as $child) { - $total += $child->countDescendants(); - } - } - - return $total; - } - - /** - * Get the template for the comment box - * - * @return string - */ - private function getCommentBoxTemplate() - { - return $this->comment_box_template; - } - - /** - * Get the comment box - * - * @param string $indent Indent value - * - * @return mixed The comment box string (empty if no comment box) - * false on failure - */ - private function getCommentBox($indent) - { - $a = $this->getApp(); - if (!$this->isToplevel() && !(Config::get('system', 'thread_allow') && $a->theme_thread_allow)) { - return ''; - } - - $comment_box = ''; - $conv = $this->getConversation(); - $template = get_markup_template($this->getCommentBoxTemplate()); - $ww = ''; - if (($conv->getMode() === 'network') && $this->isWallToWall()) { - $ww = 'ww'; - } - - if ($conv->isWritable() && $this->isWritable()) { - $qc = $qcomment = null; - - /* - * Hmmm, code depending on the presence of a particular plugin? - * This should be better if done by a hook - */ - if (in_array('qcomment', $a->plugins)) { - $qc = ((local_user()) ? PConfig::get(local_user(), 'qcomment', 'words') : null); - $qcomment = (($qc) ? explode("\n", $qc) : null); - } - - $comment_box = replace_macros( - $template, - array( - '$return_path' => $a->query_string, - '$threaded' => $this->isThreaded(), - // '$jsreload' => (($conv->getMode() === 'display') ? $_SESSION['return_url'] : ''), - '$jsreload' => '', - '$type' => (($conv->getMode() === 'profile') ? 'wall-comment' : 'net-comment'), - '$id' => $this->getId(), - '$parent' => $this->getId(), - '$qcomment' => $qcomment, - '$profile_uid' => $conv->getProfileOwner(), - '$mylink' => $a->remove_baseurl($a->contact['url']), - '$mytitle' => t('This is you'), - '$myphoto' => $a->remove_baseurl($a->contact['thumb']), - '$comment' => t('Comment'), - '$submit' => t('Submit'), - '$edbold' => t('Bold'), - '$editalic' => t('Italic'), - '$eduline' => t('Underline'), - '$edquote' => t('Quote'), - '$edcode' => t('Code'), - '$edimg' => t('Image'), - '$edurl' => t('Link'), - '$edvideo' => t('Video'), - '$preview' => ((feature_enabled($conv->getProfileOwner(), 'preview')) ? t('Preview') : ''), - '$indent' => $indent, - '$sourceapp' => t($a->sourcename), - '$ww' => (($conv->getMode() === 'network') ? $ww : ''), - '$rand_num' => random_digits(12)) - ); - } - - return $comment_box; - } - - /** - * @return string - */ - private function getRedirectUrl() - { - return $this->redirect_url; - } - - /** - * Check if we are a wall to wall item and set the relevant properties - * - * @return void - */ - protected function checkWallToWall() - { - $a = $this->getApp(); - $conv = $this->getConversation(); - $this->wall_to_wall = false; - - if ($this->isToplevel()) { - if ($conv->getMode() !== 'profile') { - if ($this->getDataValue('wall') && !$this->getDataValue('self')) { - // On the network page, I am the owner. On the display page it will be the profile owner. - // This will have been stored in $a->page_contact by our calling page. - // Put this person as the wall owner of the wall-to-wall notice. - - $this->owner_url = zrl($a->page_contact['url']); - $this->owner_photo = $a->page_contact['thumb']; - $this->owner_name = $a->page_contact['name']; - $this->wall_to_wall = true; - } elseif ($this->getDataValue('owner-link')) { - $owner_linkmatch = (($this->getDataValue('owner-link')) && link_compare($this->getDataValue('owner-link'), $this->getDataValue('author-link'))); - $alias_linkmatch = (($this->getDataValue('alias')) && link_compare($this->getDataValue('alias'), $this->getDataValue('author-link'))); - $owner_namematch = (($this->getDataValue('owner-name')) && $this->getDataValue('owner-name') == $this->getDataValue('author-name')); - - if ((! $owner_linkmatch) && (! $alias_linkmatch) && (! $owner_namematch)) { - // The author url doesn't match the owner (typically the contact) - // and also doesn't match the contact alias. - // The name match is a hack to catch several weird cases where URLs are - // all over the park. It can be tricked, but this prevents you from - // seeing "Bob Smith to Bob Smith via Wall-to-wall" and you know darn - // well that it's the same Bob Smith. - - // But it could be somebody else with the same name. It just isn't highly likely. - - - $this->owner_photo = $this->getDataValue('owner-avatar'); - $this->owner_name = $this->getDataValue('owner-name'); - $this->wall_to_wall = true; - // If it is our contact, use a friendly redirect link - if ((link_compare($this->getDataValue('owner-link'), $this->getDataValue('url'))) - && ($this->getDataValue('network') === NETWORK_DFRN) - ) { - $this->owner_url = $this->getRedirectUrl(); - } else { - $this->owner_url = zrl($this->getDataValue('owner-link')); - } - } - } - } - } - - if (!$this->wall_to_wall) { - $this->setTemplate('wall'); - $this->owner_url = ''; - $this->owner_photo = ''; - $this->owner_name = ''; - } - } - - /** - * @return boolean - */ - private function isWallToWall() - { - return $this->wall_to_wall; - } - - /** - * @return string - */ - private function getOwnerUrl() - { - return $this->owner_url; - } - - /** - * @return string - */ - private function getOwnerPhoto() - { - return $this->owner_photo; - } - - /** - * @return string - */ - private function getOwnerName() - { - return $this->owner_name; - } - - /** - * @return boolean - */ - private function isVisiting() - { - return $this->visiting; - } -} diff --git a/src/Object/Conversation.php b/src/Object/Conversation.php new file mode 100644 index 0000000000..3721086f96 --- /dev/null +++ b/src/Object/Conversation.php @@ -0,0 +1,207 @@ +setMode($mode); + $this->preview = $preview; + } + + /** + * Set the mode we'll be displayed on + * + * @param string $mode The mode to set + * + * @return void + */ + private function setMode($mode) + { + if ($this->getMode() == $mode) { + return; + } + + $a = self::getApp(); + + switch ($mode) { + case 'network': + case 'notes': + $this->profile_owner = local_user(); + $this->writable = true; + break; + case 'profile': + $this->profile_owner = $a->profile['profile_uid']; + $this->writable = can_write_wall($a, $this->profile_owner); + break; + case 'display': + $this->profile_owner = $a->profile['uid']; + $this->writable = can_write_wall($a, $this->profile_owner); + break; + default: + logger('[ERROR] Conversation::setMode : Unhandled mode ('. $mode .').', LOGGER_DEBUG); + return false; + break; + } + $this->mode = $mode; + } + + /** + * Get mode + * + * @return string + */ + public function getMode() + { + return $this->mode; + } + + /** + * Check if page is writable + * + * @return boolean + */ + public function isWritable() + { + return $this->writable; + } + + /** + * Check if page is a preview + * + * @return boolean + */ + public function isPreview() + { + return $this->preview; + } + + /** + * Get profile owner + * + * @return integer + */ + public function getProfileOwner() + { + return $this->profile_owner; + } + + /** + * Add a thread to the conversation + * + * @param object $item The item to insert + * + * @return mixed The inserted item on success + * false on failure + */ + public function addThread($item) + { + $item_id = $item->getId(); + + if (!$item_id) { + logger('[ERROR] Conversation::addThread : Item has no ID!!', LOGGER_DEBUG); + return false; + } + + if ($this->getThread($item->getId())) { + logger('[WARN] Conversation::addThread : Thread already exists ('. $item->getId() .').', LOGGER_DEBUG); + return false; + } + + /* + * Only add will be displayed + */ + if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { + logger('[WARN] Conversation::addThread : Thread is a mail ('. $item->getId() .').', LOGGER_DEBUG); + return false; + } + + if ($item->getDataValue('verb') === ACTIVITY_LIKE || $item->getDataValue('verb') === ACTIVITY_DISLIKE) { + logger('[WARN] Conversation::addThread : Thread is a (dis)like ('. $item->getId() .').', LOGGER_DEBUG); + return false; + } + + $item->setConversation($this); + $this->threads[] = $item; + + return end($this->threads); + } + + /** + * Get data in a form usable by a conversation template + * + * We should find a way to avoid using those arguments (at least most of them) + * + * @param object $conv_responses data + * + * @return mixed The data requested on success + * false on failure + */ + public function getTemplateData($conv_responses) + { + $a = self::getApp(); + $result = array(); + $i = 0; + + foreach ($this->threads as $item) { + if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { + continue; + } + + $item_data = $item->getTemplateData($conv_responses); + + if (!$item_data) { + logger('[ERROR] Conversation::getTemplateData : Failed to get item template data ('. $item->getId() .').', LOGGER_DEBUG); + return false; + } + $result[] = $item_data; + } + + return $result; + } + + /** + * Get a thread based on its item id + * + * @param integer $id Item id + * + * @return mixed The found item on success + * false on failure + */ + private function getThread($id) + { + foreach ($this->threads as $item) { + if ($item->getId() == $id) { + return $item; + } + } + + return false; + } +} diff --git a/src/Object/Item.php b/src/Object/Item.php new file mode 100644 index 0000000000..082ecae03a --- /dev/null +++ b/src/Object/Item.php @@ -0,0 +1,925 @@ + 'wall_thread.tpl', + 'wall2wall' => 'wallwall_thread.tpl' + ); + private $comment_box_template = 'comment_item.tpl'; + private $toplevel = false; + private $writable = false; + private $children = array(); + private $parent = null; + private $conversation = null; + private $redirect_url = null; + private $owner_url = ''; + private $owner_photo = ''; + private $owner_name = ''; + private $wall_to_wall = false; + private $threaded = false; + private $visiting = false; + + /** + * Constructor + * + * @param array $data data array + */ + public function __construct($data) + { + $a = self::getApp(); + + $this->data = $data; + $this->setTemplate('wall'); + $this->toplevel = ($this->getId() == $this->getDataValue('parent')); + + if (is_array($_SESSION['remote'])) { + foreach ($_SESSION['remote'] as $visitor) { + if ($visitor['cid'] == $this->getDataValue('contact-id')) { + $this->visiting = true; + break; + } + } + } + + $this->writable = ($this->getDataValue('writable') || $this->getDataValue('self')); + + $ssl_state = ((local_user()) ? true : false); + $this->redirect_url = 'redir/' . $this->getDataValue('cid'); + + if (Config::get('system', 'thread_allow') && $a->theme_thread_allow && !$this->isToplevel()) { + $this->threaded = true; + } + + // Prepare the children + if (count($data['children'])) { + foreach ($data['children'] as $item) { + /* + * Only add will be displayed + */ + if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) { + continue; + } elseif (! visible_activity($item)) { + continue; + } + + // You can always comment on Diaspora items + if (($item['network'] == NETWORK_DIASPORA) && (local_user() == $item['uid'])) { + $item['writable'] = true; + } + + $item['pagedrop'] = $data['pagedrop']; + $child = new Item($item); + $this->addChild($child); + } + } + } + + /** + * Get data in a form usable by a conversation template + * + * @param object $conv_responses conversation responses + * @param integer $thread_level default = 1 + * + * @return mixed The data requested on success + * false on failure + */ + public function getTemplateData($conv_responses, $thread_level = 1) + { + require_once "mod/proxy.php"; + + $result = array(); + + $a = self::getApp(); + + $item = $this->getData(); + $edited = false; + // If the time between "created" and "edited" differs we add + // a notice that the post was edited. + // Note: In some networks reshared items seem to have (sometimes) a difference + // between creation time and edit time of a second. Thats why we add the notice + // only if the difference is more than 1 second. + if (strtotime($item['edited']) - strtotime($item['created']) > 1) { + $edited = array( + 'label' => t('This entry was edited'), + 'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r'), + 'relative' => relative_date($item['edited']) + ); + } + $commentww = ''; + $sparkle = ''; + $buttons = ''; + $dropping = false; + $star = false; + $ignore = false; + $isstarred = "unstarred"; + $indent = ''; + $shiny = ''; + $osparkle = ''; + $total_children = $this->countDescendants(); + + $conv = $this->getConversation(); + + $lock = ((($item['private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) + || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) + ? t('Private Message') + : false); + $shareable = ((($conv->getProfileOwner() == local_user()) && ($item['private'] != 1)) ? true : false); + if (local_user() && link_compare($a->contact['url'], $item['author-link'])) { + if ($item["event-id"] != 0) { + $edpost = array("events/event/".$item['event-id'], t("Edit")); + } else { + $edpost = array("editpost/".$item['id'], t("Edit")); + } + } else { + $edpost = false; + } + + if (($this->getDataValue('uid') == local_user()) || $this->isVisiting()) { + $dropping = true; + } + + $drop = array( + 'dropping' => $dropping, + 'pagedrop' => ((feature_enabled($conv->getProfileOwner(), 'multi_delete')) ? $item['pagedrop'] : ''), + 'select' => t('Select'), + 'delete' => t('Delete'), + ); + + $filer = (($conv->getProfileOwner() == local_user()) ? t("save to folder") : false); + + $diff_author = ((link_compare($item['url'], $item['author-link'])) ? false : true); + $profile_name = htmlentities(((strlen($item['author-name'])) && $diff_author) ? $item['author-name'] : $item['name']); + if ($item['author-link'] && (! $item['author-name'])) { + $profile_name = $item['author-link']; + } + + $sp = false; + $profile_link = best_link_url($item, $sp); + if ($profile_link === 'mailbox') { + $profile_link = ''; + } + + if ($sp) { + $sparkle = ' sparkle'; + } else { + $profile_link = zrl($profile_link); + } + + if (!isset($item['author-thumb']) || ($item['author-thumb'] == "")) { + $author_contact = Contact::getDetailsByURL($item['author-link'], $conv->getProfileOwner()); + if ($author_contact["thumb"]) { + $item['author-thumb'] = $author_contact["thumb"]; + } else { + $item['author-thumb'] = $item['author-avatar']; + } + } + + if (!isset($item['owner-thumb']) || ($item['owner-thumb'] == "")) { + $owner_contact = Contact::getDetailsByURL($item['owner-link'], $conv->getProfileOwner()); + if ($owner_contact["thumb"]) { + $item['owner-thumb'] = $owner_contact["thumb"]; + } else { + $item['owner-thumb'] = $item['owner-avatar']; + } + } + + $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); + call_hooks('render_location', $locate); + $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate)); + + $tags=array(); + $hashtags = array(); + $mentions = array(); + + /*foreach(explode(',',$item['tag']) as $tag){ + $tag = trim($tag); + if ($tag!="") { + $t = bbcode($tag); + $tags[] = $t; + if($t[0] == '#') + $hashtags[] = $t; + elseif($t[0] == '@') + $mentions[] = $t; + } + }*/ + + // process action responses - e.g. like/dislike/attend/agree/whatever + $response_verbs = array('like', 'dislike'); + + if ($item['object-type'] === ACTIVITY_OBJ_EVENT) { + $response_verbs[] = 'attendyes'; + $response_verbs[] = 'attendno'; + $response_verbs[] = 'attendmaybe'; + if ($conv->isWritable()) { + $isevent = true; + $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); + } + } + + $responses = get_responses($conv_responses, $response_verbs, $this, $item); + + foreach ($response_verbs as $value => $verbs) { + $responses[$verbs]['output'] = ((x($conv_responses[$verbs], $item['uri'])) ? format_like($conv_responses[$verbs][$item['uri']], $conv_responses[$verbs][$item['uri'] . '-l'], $verbs, $item['uri']) : ''); + } + + /* + * We should avoid doing this all the time, but it depends on the conversation mode + * And the conv mode may change when we change the conv, or it changes its mode + * Maybe we should establish a way to be notified about conversation changes + */ + $this->checkWallToWall(); + + if ($this->isWallToWall() && ($this->getOwnerUrl() == $this->getRedirectUrl())) { + $osparkle = ' sparkle'; + } + + if ($this->isToplevel()) { + if ($conv->getProfileOwner() == local_user()) { + $isstarred = (($item['starred']) ? "starred" : "unstarred"); + + $star = array( + 'do' => t("add star"), + 'undo' => t("remove star"), + 'toggle' => t("toggle star status"), + 'classdo' => (($item['starred']) ? "hidden" : ""), + 'classundo' => (($item['starred']) ? "" : "hidden"), + 'starred' => t('starred'), + ); + $r = dba::select('thread', array('ignored'), array('uid' => $item['uid'], 'iid' => $item['id']), array('limit' => 1)); + if (DBM::is_result($r)) { + $ignore = array( + 'do' => t("ignore thread"), + 'undo' => t("unignore thread"), + 'toggle' => t("toggle ignore status"), + 'classdo' => (($r['ignored']) ? "hidden" : ""), + 'classundo' => (($r['ignored']) ? "" : "hidden"), + 'ignored' => t('ignored'), + ); + } + + $tagger = ''; + if (feature_enabled($conv->getProfileOwner(), 'commtag')) { + $tagger = array( + 'add' => t("add tag"), + 'class' => "", + ); + } + } + } else { + $indent = 'comment'; + } + + if ($conv->isWritable()) { + $buttons = array( + 'like' => array( t("I like this \x28toggle\x29"), t("like")), + 'dislike' => ((feature_enabled($conv->getProfileOwner(), 'dislike')) ? array( t("I don't like this \x28toggle\x29"), t("dislike")) : ''), + ); + if ($shareable) { + $buttons['share'] = array( t('Share this'), t('share')); + } + } + + $comment = $this->getCommentBox($indent); + + if (strcmp(datetime_convert('UTC', 'UTC', $item['created']), datetime_convert('UTC', 'UTC', 'now - 12 hours')) > 0) { + $shiny = 'shiny'; + } + + localize_item($item); + + $body = prepare_body($item, true); + + list($categories, $folders) = get_cats_and_terms($item); + + if ($a->theme['template_engine'] === 'internal') { + $body_e = template_escape($body); + $text_e = strip_tags(template_escape($body)); + $name_e = template_escape($profile_name); + $title_e = template_escape($item['title']); + $location_e = template_escape($location); + $owner_name_e = template_escape($this->getOwnerName()); + } else { + $body_e = $body; + $text_e = strip_tags($body); + $name_e = $profile_name; + $title_e = $item['title']; + $location_e = $location; + $owner_name_e = $this->getOwnerName(); + } + + // Disable features that aren't available in several networks + + /// @todo Add NETWORK_DIASPORA when it will pass this information + if (!in_array($item["item_network"], array(NETWORK_DFRN)) && isset($buttons["dislike"])) { + unset($buttons["dislike"], $isevent); + $tagger = ''; + } + + if (($item["item_network"] == NETWORK_FEED) && isset($buttons["like"])) { + unset($buttons["like"]); + } + + if (($item["item_network"] == NETWORK_MAIL) && isset($buttons["like"])) { + unset($buttons["like"]); + } + + $tmp_item = array( + 'template' => $this->getTemplate(), + 'type' => implode("", array_slice(explode("/", $item['verb']), -1)), + 'tags' => $item['tags'], + 'hashtags' => $item['hashtags'], + 'mentions' => $item['mentions'], + 'txt_cats' => t('Categories:'), + 'txt_folders' => t('Filed under:'), + 'has_cats' => ((count($categories)) ? 'true' : ''), + 'has_folders' => ((count($folders)) ? 'true' : ''), + 'categories' => $categories, + 'folders' => $folders, + 'body' => $body_e, + 'text' => $text_e, + 'id' => $this->getId(), + 'guid' => urlencode($item['guid']), + 'isevent' => $isevent, + 'attend' => $attend, + 'linktitle' => sprintf(t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])), + 'olinktitle' => sprintf(t('View %s\'s profile @ %s'), htmlentities($this->getOwnerName()), ((strlen($item['owner-link'])) ? $item['owner-link'] : $item['url'])), + 'to' => t('to'), + 'via' => t('via'), + 'wall' => t('Wall-to-Wall'), + 'vwall' => t('via Wall-To-Wall:'), + 'profile_url' => $profile_link, + 'item_photo_menu' => item_photo_menu($item), + 'name' => $name_e, + 'thumb' => $a->remove_baseurl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)), + 'osparkle' => $osparkle, + 'sparkle' => $sparkle, + 'title' => $title_e, + 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), + 'ago' => (($item['app']) ? sprintf(t('%s from %s'), relative_date($item['created']), $item['app']) : relative_date($item['created'])), + 'app' => $item['app'], + 'created' => relative_date($item['created']), + 'lock' => $lock, + 'location' => $location_e, + 'indent' => $indent, + 'shiny' => $shiny, + 'owner_url' => $this->getOwnerUrl(), + 'owner_photo' => $a->remove_baseurl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)), + 'owner_name' => htmlentities($owner_name_e), + 'plink' => get_plink($item), + 'edpost' => ((feature_enabled($conv->getProfileOwner(), 'edit_posts')) ? $edpost : ''), + 'isstarred' => $isstarred, + 'star' => ((feature_enabled($conv->getProfileOwner(), 'star_posts')) ? $star : ''), + 'ignore' => ((feature_enabled($conv->getProfileOwner(), 'ignore_posts')) ? $ignore : ''), + 'tagger' => $tagger, + 'filer' => ((feature_enabled($conv->getProfileOwner(), 'filing')) ? $filer : ''), + 'drop' => $drop, + 'vote' => $buttons, + 'like' => $responses['like']['output'], + 'dislike' => $responses['dislike']['output'], + 'responses' => $responses, + 'switchcomment' => t('Comment'), + 'comment' => $comment, + 'previewing' => ($conv->isPreview() ? ' preview ' : ''), + 'wait' => t('Please wait'), + 'thread_level' => $thread_level, + 'edited' => $edited, + 'network' => $item["item_network"], + 'network_name' => network_to_name($item['item_network'], $profile_link), + 'received' => $item['received'], + 'commented' => $item['commented'], + 'created_date' => $item['created'], + ); + + $arr = array('item' => $item, 'output' => $tmp_item); + call_hooks('display_item', $arr); + + $result = $arr['output']; + + $result['children'] = array(); + $children = $this->getChildren(); + $nb_children = count($children); + if ($nb_children > 0) { + foreach ($children as $child) { + $result['children'][] = $child->getTemplateData($conv_responses, $thread_level + 1); + } + // Collapse + if (($nb_children > 2) || ($thread_level > 1)) { + $result['children'][0]['comment_firstcollapsed'] = true; + $result['children'][0]['num_comments'] = sprintf(tt('%d comment', '%d comments', $total_children), $total_children); + $result['children'][0]['hidden_comments_num'] = $total_children; + $result['children'][0]['hidden_comments_text'] = tt('comment', 'comments', $total_children); + $result['children'][0]['hide_text'] = t('show more'); + if ($thread_level > 1) { + $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true; + } else { + $result['children'][$nb_children - 3]['comment_lastcollapsed'] = true; + } + } + } + + if ($this->isToplevel()) { + $result['total_comments_num'] = "$total_children"; + $result['total_comments_text'] = tt('comment', 'comments', $total_children); + } + + $result['private'] = $item['private']; + $result['toplevel'] = ($this->isToplevel() ? 'toplevel_item' : ''); + + if ($this->isThreaded()) { + $result['flatten'] = false; + $result['threaded'] = true; + } else { + $result['flatten'] = true; + $result['threaded'] = false; + } + + return $result; + } + + /** + * @return integer + */ + public function getId() + { + return $this->getDataValue('id'); + } + + /** + * @return boolean + */ + public function isThreaded() + { + return $this->threaded; + } + + /** + * Add a child item + * + * @param object $item The child item to add + * + * @return mixed + */ + public function addChild(Item $item) + { + $item_id = $item->getId(); + if (!$item_id) { + logger('[ERROR] Item::addChild : Item has no ID!!', LOGGER_DEBUG); + return false; + } elseif ($this->getChild($item->getId())) { + logger('[WARN] Item::addChild : Item already exists ('. $item->getId() .').', LOGGER_DEBUG); + return false; + } + /* + * Only add what will be displayed + */ + if ($item->getDataValue('network') === NETWORK_MAIL && local_user() != $item->getDataValue('uid')) { + return false; + } elseif (activity_match($item->getDataValue('verb'), ACTIVITY_LIKE) || activity_match($item->getDataValue('verb'), ACTIVITY_DISLIKE)) { + return false; + } + + $item->setParent($this); + $this->children[] = $item; + + return end($this->children); + } + + /** + * Get a child by its ID + * + * @param integer $id The child id + * + * @return mixed + */ + public function getChild($id) + { + foreach ($this->getChildren() as $child) { + if ($child->getId() == $id) { + return $child; + } + } + + return null; + } + + /** + * Get all our children + * + * @return object + */ + public function getChildren() + { + return $this->children; + } + + /** + * Set our parent + * + * @param object $item The item to set as parent + * + * @return void + */ + protected function setParent($item) + { + $parent = $this->getParent(); + if ($parent) { + $parent->removeChild($this); + } + + $this->parent = $item; + $this->setConversation($item->getConversation()); + } + + /** + * Remove our parent + * + * @return void + */ + protected function removeParent() + { + $this->parent = null; + $this->conversation = null; + } + + /** + * Remove a child + * + * @param object $item The child to be removed + * + * @return boolean Success or failure + */ + public function removeChild($item) + { + $id = $item->getId(); + foreach ($this->getChildren() as $key => $child) { + if ($child->getId() == $id) { + $child->removeParent(); + unset($this->children[$key]); + // Reindex the array, in order to make sure there won't be any trouble on loops using count() + $this->children = array_values($this->children); + return true; + } + } + logger('[WARN] Item::removeChild : Item is not a child ('. $id .').', LOGGER_DEBUG); + return false; + } + + /** + * Get parent item + * + * @return object + */ + protected function getParent() + { + return $this->parent; + } + + /** + * Set conversation + * + * @param object $conv The conversation + * + * @return void + */ + public function setConversation($conv) + { + $previous_mode = ($this->conversation ? $this->conversation->getMode() : ''); + + $this->conversation = $conv; + + // Set it on our children too + foreach ($this->getChildren() as $child) { + $child->setConversation($conv); + } + } + + /** + * Get conversation + * + * @return object + */ + public function getConversation() + { + return $this->conversation; + } + + /** + * Get raw data + * + * We shouldn't need this + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Get a data value + * + * @param object $name key + * + * @return mixed value on success + * false on failure + */ + public function getDataValue($name) + { + if (!isset($this->data[$name])) { + // logger('[ERROR] Item::getDataValue : Item has no value name "'. $name .'".', LOGGER_DEBUG); + return false; + } + + return $this->data[$name]; + } + + /** + * Set template + * + * @param object $name template name + * + * @return void + */ + private function setTemplate($name) + { + if (!x($this->available_templates, $name)) { + logger('[ERROR] Item::setTemplate : Template not available ("'. $name .'").', LOGGER_DEBUG); + return false; + } + + $this->template = $this->available_templates[$name]; + } + + /** + * Get template + * + * @return object + */ + private function getTemplate() + { + return $this->template; + } + + /** + * Check if this is a toplevel post + * + * @return boolean + */ + private function isToplevel() + { + return $this->toplevel; + } + + /** + * Check if this is writable + * + * @return boolean + */ + private function isWritable() + { + $conv = $this->getConversation(); + + if ($conv) { + // This will allow us to comment on wall-to-wall items owned by our friends + // and community forums even if somebody else wrote the post. + + // bug #517 - this fixes for conversation owner + if ($conv->getMode() == 'profile' && $conv->getProfileOwner() == local_user()) { + return true; + } + + // this fixes for visitors + return ($this->writable || ($this->isVisiting() && $conv->getMode() == 'profile')); + } + return $this->writable; + } + + /** + * Count the total of our descendants + * + * @return integer + */ + private function countDescendants() + { + $children = $this->getChildren(); + $total = count($children); + if ($total > 0) { + foreach ($children as $child) { + $total += $child->countDescendants(); + } + } + + return $total; + } + + /** + * Get the template for the comment box + * + * @return string + */ + private function getCommentBoxTemplate() + { + return $this->comment_box_template; + } + + /** + * Get the comment box + * + * @param string $indent Indent value + * + * @return mixed The comment box string (empty if no comment box) + * false on failure + */ + private function getCommentBox($indent) + { + $a = self::getApp(); + if (!$this->isToplevel() && !(Config::get('system', 'thread_allow') && $a->theme_thread_allow)) { + return ''; + } + + $comment_box = ''; + $conv = $this->getConversation(); + $template = get_markup_template($this->getCommentBoxTemplate()); + $ww = ''; + if (($conv->getMode() === 'network') && $this->isWallToWall()) { + $ww = 'ww'; + } + + if ($conv->isWritable() && $this->isWritable()) { + $qc = $qcomment = null; + + /* + * Hmmm, code depending on the presence of a particular plugin? + * This should be better if done by a hook + */ + if (in_array('qcomment', $a->plugins)) { + $qc = ((local_user()) ? PConfig::get(local_user(), 'qcomment', 'words') : null); + $qcomment = (($qc) ? explode("\n", $qc) : null); + } + + $comment_box = replace_macros( + $template, + array( + '$return_path' => $a->query_string, + '$threaded' => $this->isThreaded(), + // '$jsreload' => (($conv->getMode() === 'display') ? $_SESSION['return_url'] : ''), + '$jsreload' => '', + '$type' => (($conv->getMode() === 'profile') ? 'wall-comment' : 'net-comment'), + '$id' => $this->getId(), + '$parent' => $this->getId(), + '$qcomment' => $qcomment, + '$profile_uid' => $conv->getProfileOwner(), + '$mylink' => $a->remove_baseurl($a->contact['url']), + '$mytitle' => t('This is you'), + '$myphoto' => $a->remove_baseurl($a->contact['thumb']), + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$edbold' => t('Bold'), + '$editalic' => t('Italic'), + '$eduline' => t('Underline'), + '$edquote' => t('Quote'), + '$edcode' => t('Code'), + '$edimg' => t('Image'), + '$edurl' => t('Link'), + '$edvideo' => t('Video'), + '$preview' => ((feature_enabled($conv->getProfileOwner(), 'preview')) ? t('Preview') : ''), + '$indent' => $indent, + '$sourceapp' => t($a->sourcename), + '$ww' => (($conv->getMode() === 'network') ? $ww : ''), + '$rand_num' => random_digits(12)) + ); + } + + return $comment_box; + } + + /** + * @return string + */ + private function getRedirectUrl() + { + return $this->redirect_url; + } + + /** + * Check if we are a wall to wall item and set the relevant properties + * + * @return void + */ + protected function checkWallToWall() + { + $a = self::getApp(); + $conv = $this->getConversation(); + $this->wall_to_wall = false; + + if ($this->isToplevel()) { + if ($conv->getMode() !== 'profile') { + if ($this->getDataValue('wall') && !$this->getDataValue('self')) { + // On the network page, I am the owner. On the display page it will be the profile owner. + // This will have been stored in $a->page_contact by our calling page. + // Put this person as the wall owner of the wall-to-wall notice. + + $this->owner_url = zrl($a->page_contact['url']); + $this->owner_photo = $a->page_contact['thumb']; + $this->owner_name = $a->page_contact['name']; + $this->wall_to_wall = true; + } elseif ($this->getDataValue('owner-link')) { + $owner_linkmatch = (($this->getDataValue('owner-link')) && link_compare($this->getDataValue('owner-link'), $this->getDataValue('author-link'))); + $alias_linkmatch = (($this->getDataValue('alias')) && link_compare($this->getDataValue('alias'), $this->getDataValue('author-link'))); + $owner_namematch = (($this->getDataValue('owner-name')) && $this->getDataValue('owner-name') == $this->getDataValue('author-name')); + + if ((! $owner_linkmatch) && (! $alias_linkmatch) && (! $owner_namematch)) { + // The author url doesn't match the owner (typically the contact) + // and also doesn't match the contact alias. + // The name match is a hack to catch several weird cases where URLs are + // all over the park. It can be tricked, but this prevents you from + // seeing "Bob Smith to Bob Smith via Wall-to-wall" and you know darn + // well that it's the same Bob Smith. + + // But it could be somebody else with the same name. It just isn't highly likely. + + + $this->owner_photo = $this->getDataValue('owner-avatar'); + $this->owner_name = $this->getDataValue('owner-name'); + $this->wall_to_wall = true; + // If it is our contact, use a friendly redirect link + if ((link_compare($this->getDataValue('owner-link'), $this->getDataValue('url'))) + && ($this->getDataValue('network') === NETWORK_DFRN) + ) { + $this->owner_url = $this->getRedirectUrl(); + } else { + $this->owner_url = zrl($this->getDataValue('owner-link')); + } + } + } + } + } + + if (!$this->wall_to_wall) { + $this->setTemplate('wall'); + $this->owner_url = ''; + $this->owner_photo = ''; + $this->owner_name = ''; + } + } + + /** + * @return boolean + */ + private function isWallToWall() + { + return $this->wall_to_wall; + } + + /** + * @return string + */ + private function getOwnerUrl() + { + return $this->owner_url; + } + + /** + * @return string + */ + private function getOwnerPhoto() + { + return $this->owner_photo; + } + + /** + * @return string + */ + private function getOwnerName() + { + return $this->owner_name; + } + + /** + * @return boolean + */ + private function isVisiting() + { + return $this->visiting; + } +}