Beispiel #1
0
 /**
  * Add a thread to the conversation
  *
  * Returns:
  *      _ The inserted item on success
  *      _ false on failure
  */
 public function add_thread($item)
 {
     $item_id = $item->get_id();
     if (!$item_id) {
         logger('[ERROR] Conversation::add_thread : Item has no ID!!', LOGGER_DEBUG);
         return false;
     }
     if ($this->get_thread($item->get_id())) {
         logger('[WARN] Conversation::add_thread : Thread already exists (' . $item->get_id() . ').', LOGGER_DEBUG);
         return false;
     }
     /*
      * Only add things that will be displayed
      */
     if ($item->get_data_value('id') != $item->get_data_value('parent') && (activity_match($item->get_data_value('verb'), ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'), ACTIVITY_DISLIKE))) {
         return false;
     }
     $item->set_commentable(false);
     $ob_hash = $this->observer ? $this->observer['xchan_hash'] : '';
     if (!comments_are_now_closed($item->get_data())) {
         if ($item->get_data_value('author_xchan') === $ob_hash || $item->get_data_value('owner_xchan') === $ob_hash) {
             $item->set_commentable(true);
         }
         if ($item->get_data_value('item_flags') & ITEM_NOCOMMENT) {
             $item->set_commentable(false);
         } elseif ($this->observer && !$item->is_commentable()) {
             if (array_key_exists('owner', $item->data) && $item->data['owner']['abook_flags'] & ABOOK_FLAG_SELF) {
                 $item->set_commentable(perm_is_allowed($this->profile_owner, $this->observer['xchan_hash'], 'post_comments'));
             } else {
                 $item->set_commentable(can_comment_on_post($this->observer['xchan_hash'], $item->data));
             }
         }
     }
     require_once 'include/identity.php';
     //		$sys = get_sys_channel();
     //		if($sys && $item->get_data_value('uid') == $sys['channel_id']) {
     //			$item->set_commentable(false);
     //		}
     $item->set_conversation($this);
     $this->threads[] = $item;
     return end($this->threads);
 }
Beispiel #2
0
/**
 * @brief
 *
 * @param array $arr
 * @param boolean $allow_exec (optional) default false
 * @return array
 *   * \e boolean \b success
 *   * \e int \b item_id
 */
function item_store($arr, $allow_exec = false)
{
    $d = array('item' => $arr, 'allow_exec' => $allow_exec);
    call_hooks('item_store', $d);
    $arr = $d['item'];
    $allow_exec = $d['allow_exec'];
    $ret = array('success' => false, 'item_id' => 0);
    if (!$arr['uid']) {
        logger('item_store: no uid');
        $ret['message'] = 'No uid.';
        return $ret;
    }
    //$uplinked_comment = false;
    // If a page layout is provided, ensure it exists and belongs to us.
    if (array_key_exists('layout_mid', $arr) && $arr['layout_mid']) {
        $l = q("select item_restrict from item where mid = '%s' and uid = %d limit 1", dbesc($arr['layout_mid']), intval($arr['uid']));
        if (!$l || !($l[0]['item_restrict'] & ITEM_PDL)) {
            unset($arr['layout_mid']);
        }
    }
    // Don't let anybody set these, either intentionally or accidentally
    if (array_key_exists('id', $arr)) {
        unset($arr['id']);
    }
    if (array_key_exists('parent', $arr)) {
        unset($arr['parent']);
    }
    $arr['mimetype'] = x($arr, 'mimetype') ? notags(trim($arr['mimetype'])) : 'text/bbcode';
    if ($arr['mimetype'] == 'application/x-php' && !$allow_exec) {
        logger('item_store: php mimetype but allow_exec is denied.');
        $ret['message'] = 'exec denied.';
        return $ret;
    }
    $arr['title'] = array_key_exists('title', $arr) && strlen($arr['title']) ? trim($arr['title']) : '';
    $arr['body'] = array_key_exists('body', $arr) && strlen($arr['body']) ? trim($arr['body']) : '';
    $arr['diaspora_meta'] = x($arr, 'diaspora_meta') ? $arr['diaspora_meta'] : '';
    $arr['allow_cid'] = x($arr, 'allow_cid') ? trim($arr['allow_cid']) : '';
    $arr['allow_gid'] = x($arr, 'allow_gid') ? trim($arr['allow_gid']) : '';
    $arr['deny_cid'] = x($arr, 'deny_cid') ? trim($arr['deny_cid']) : '';
    $arr['deny_gid'] = x($arr, 'deny_gid') ? trim($arr['deny_gid']) : '';
    $arr['item_private'] = x($arr, 'item_private') ? intval($arr['item_private']) : 0;
    $arr['item_flags'] = x($arr, 'item_flags') ? intval($arr['item_flags']) : 0;
    // only detect language if we have text content, and if the post is private but not yet
    // obscured, make it so.
    if (!($arr['item_flags'] & ITEM_OBSCURED)) {
        $arr['lang'] = detect_language($arr['body']);
        // apply the input filter here - if it is obscured it has been filtered already
        $arr['body'] = trim(z_input_filter($arr['uid'], $arr['body'], $arr['mimetype']));
        if (local_channel() && !$arr['sig']) {
            $channel = get_app()->get_channel();
            if ($channel['channel_hash'] === $arr['author_xchan']) {
                $arr['sig'] = base64url_encode(rsa_sign($arr['body'], $channel['channel_prvkey']));
                $arr['item_flags'] |= ITEM_VERIFIED;
            }
        }
        $allowed_languages = get_pconfig($arr['uid'], 'system', 'allowed_languages');
        if (is_array($allowed_languages) && $arr['lang'] && !array_key_exists($arr['lang'], $allowed_languages)) {
            $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false);
            call_hooks('item_translate', $translate);
            if (!$translate['translated'] && intval(get_pconfig($arr['uid'], 'system', 'reject_disallowed_languages'))) {
                logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']);
                $ret['message'] = 'language not accepted';
                return $ret;
            }
            $arr = $translate['item'];
        }
        if ($arr['item_private']) {
            $key = get_config('system', 'pubkey');
            $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED;
            if ($arr['title']) {
                $arr['title'] = json_encode(crypto_encapsulate($arr['title'], $key));
            }
            if ($arr['body']) {
                $arr['body'] = json_encode(crypto_encapsulate($arr['body'], $key));
            }
        }
    }
    if (x($arr, 'object') && is_array($arr['object'])) {
        activity_sanitise($arr['object']);
        $arr['object'] = json_encode($arr['object']);
    }
    if (x($arr, 'target') && is_array($arr['target'])) {
        activity_sanitise($arr['target']);
        $arr['target'] = json_encode($arr['target']);
    }
    if (x($arr, 'attach') && is_array($arr['attach'])) {
        activity_sanitise($arr['attach']);
        $arr['attach'] = json_encode($arr['attach']);
    }
    $arr['aid'] = x($arr, 'aid') ? intval($arr['aid']) : 0;
    $arr['mid'] = x($arr, 'mid') ? notags(trim($arr['mid'])) : random_string();
    $arr['author_xchan'] = x($arr, 'author_xchan') ? notags(trim($arr['author_xchan'])) : '';
    $arr['owner_xchan'] = x($arr, 'owner_xchan') ? notags(trim($arr['owner_xchan'])) : '';
    $arr['created'] = x($arr, 'created') !== false ? datetime_convert('UTC', 'UTC', $arr['created']) : datetime_convert();
    $arr['edited'] = x($arr, 'edited') !== false ? datetime_convert('UTC', 'UTC', $arr['edited']) : datetime_convert();
    $arr['expires'] = x($arr, 'expires') !== false ? datetime_convert('UTC', 'UTC', $arr['expires']) : NULL_DATE;
    $arr['commented'] = x($arr, 'commented') !== false ? datetime_convert('UTC', 'UTC', $arr['commented']) : datetime_convert();
    $arr['comments_closed'] = x($arr, 'comments_closed') !== false ? datetime_convert('UTC', 'UTC', $arr['comments_closed']) : NULL_DATE;
    $arr['received'] = datetime_convert();
    $arr['changed'] = datetime_convert();
    $arr['location'] = x($arr, 'location') ? notags(trim($arr['location'])) : '';
    $arr['coord'] = x($arr, 'coord') ? notags(trim($arr['coord'])) : '';
    $arr['parent_mid'] = x($arr, 'parent_mid') ? notags(trim($arr['parent_mid'])) : '';
    $arr['thr_parent'] = x($arr, 'thr_parent') ? notags(trim($arr['thr_parent'])) : $arr['parent_mid'];
    $arr['verb'] = x($arr, 'verb') ? notags(trim($arr['verb'])) : ACTIVITY_POST;
    $arr['obj_type'] = x($arr, 'obj_type') ? notags(trim($arr['obj_type'])) : ACTIVITY_OBJ_NOTE;
    $arr['object'] = x($arr, 'object') ? trim($arr['object']) : '';
    $arr['tgt_type'] = x($arr, 'tgt_type') ? notags(trim($arr['tgt_type'])) : '';
    $arr['target'] = x($arr, 'target') ? trim($arr['target']) : '';
    $arr['plink'] = x($arr, 'plink') ? notags(trim($arr['plink'])) : '';
    $arr['attach'] = x($arr, 'attach') ? notags(trim($arr['attach'])) : '';
    $arr['app'] = x($arr, 'app') ? notags(trim($arr['app'])) : '';
    $arr['item_restrict'] = x($arr, 'item_restrict') ? intval($arr['item_restrict']) : 0;
    $arr['public_policy'] = x($arr, 'public_policy') ? notags(trim($arr['public_policy'])) : '';
    $arr['comment_policy'] = x($arr, 'comment_policy') ? notags(trim($arr['comment_policy'])) : 'contacts';
    $arr['item_unseen'] = array_key_exists('item_unseen', $arr) ? intval($arr['item_unseen']) : 1;
    if ($arr['comment_policy'] == 'none') {
        $arr['item_flags'] = $arr['item_flags'] | ITEM_NOCOMMENT;
    }
    // handle time travelers
    // Allow a bit of fudge in case somebody just has a slightly slow/fast clock
    $d1 = new DateTime('now +10 minutes', new DateTimeZone('UTC'));
    $d2 = new DateTime($arr['created'] . '+00:00');
    if ($d2 > $d1) {
        $arr['item_restrict'] = $arr['item_restrict'] | ITEM_DELAYED_PUBLISH;
    }
    $arr['llink'] = z_root() . '/display/' . $arr['mid'];
    if (!$arr['plink']) {
        $arr['plink'] = $arr['llink'];
    }
    if ($arr['parent_mid'] === $arr['mid']) {
        $parent_id = 0;
        $parent_deleted = 0;
        $allow_cid = $arr['allow_cid'];
        $allow_gid = $arr['allow_gid'];
        $deny_cid = $arr['deny_cid'];
        $deny_gid = $arr['deny_gid'];
        $public_policy = $arr['public_policy'];
        $comments_closed = $arr['comments_closed'];
        $arr['item_flags'] = $arr['item_flags'] | ITEM_THREAD_TOP;
    } else {
        // find the parent and snarf the item id and ACL's
        // and anything else we need to inherit
        $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", dbesc($arr['parent_mid']), intval($arr['uid']));
        if ($r) {
            // in case item_store was killed before the parent's parent attribute got set,
            // set it now. This happens with some regularity on Dreamhost. This will keep
            // us from getting notifications for threads that exist but which we can't see.
            if ($r[0]['mid'] === $r[0]['parent_mid'] && !intval($r[0]['parent'])) {
                q("update item set parent = id where id = %d", intval($r[0]['id']));
            }
            if (comments_are_now_closed($r[0])) {
                logger('item_store: comments closed');
                $ret['message'] = 'Comments closed.';
                return $ret;
            }
            if ($arr['obj_type'] == ACTIVITY_OBJ_NOTE) {
                $arr['obj_type'] = ACTIVITY_OBJ_COMMENT;
            }
            // is the new message multi-level threaded?
            // even though we don't support it now, preserve the info
            // and re-attach to the conversation parent.
            if ($r[0]['mid'] != $r[0]['parent_mid']) {
                $arr['parent_mid'] = $r[0]['parent_mid'];
                $z = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `parent_mid` = '%s' AND `uid` = %d\n\t\t\t\t\tORDER BY `id` ASC LIMIT 1", dbesc($r[0]['parent_mid']), dbesc($r[0]['parent_mid']), intval($arr['uid']));
                if ($z && count($z)) {
                    $r = $z;
                }
            }
            $parent_id = $r[0]['id'];
            $parent_deleted = $r[0]['item_restrict'] & ITEM_DELETED;
            $allow_cid = $r[0]['allow_cid'];
            $allow_gid = $r[0]['allow_gid'];
            $deny_cid = $r[0]['deny_cid'];
            $deny_gid = $r[0]['deny_gid'];
            $public_policy = $r[0]['public_policy'];
            $comments_closed = $r[0]['comments_closed'];
            if ($r[0]['item_flags'] & ITEM_WALL) {
                $arr['item_flags'] = $arr['item_flags'] | ITEM_WALL;
            }
            // An uplinked comment might arrive with a downstream owner.
            // Fix it.
            if ($r[0]['owner_xchan'] !== $arr['owner_xchan']) {
                $arr['owner_xchan'] = $r[0]['owner_xchan'];
                //				$uplinked_comment = true;
            }
            // if the parent is private, force privacy for the entire conversation
            if ($r[0]['item_private']) {
                $arr['item_private'] = $r[0]['item_private'];
            }
            // Edge case. We host a public forum that was originally posted to privately.
            // The original author commented, but as this is a comment, the permissions
            // weren't fixed up so it will still show the comment as private unless we fix it here.
            if (intval($r[0]['item_flags']) & ITEM_UPLINK && !$r[0]['item_private']) {
                $arr['item_private'] = 0;
            }
        } else {
            logger('item_store: item parent was not found - ignoring item');
            $ret['message'] = 'parent not found.';
            return $ret;
        }
    }
    if ($parent_deleted) {
        $arr['item_restrict'] = $arr['item_restrict'] | ITEM_DELETED;
    }
    $r = q("SELECT `id` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($arr['mid']), intval($arr['uid']));
    if ($r) {
        logger('item_store: duplicate item ignored. ' . print_r($arr, true));
        $ret['message'] = 'duplicate post.';
        return $ret;
    }
    call_hooks('item_store', $arr);
    // This hook remains for backward compatibility.
    call_hooks('post_remote', $arr);
    if (x($arr, 'cancel')) {
        logger('item_store: post cancelled by plugin.');
        $ret['message'] = 'cancelled.';
        return $ret;
    }
    // pull out all the taxonomy stuff for separate storage
    $terms = null;
    if (array_key_exists('term', $arr)) {
        $terms = $arr['term'];
        unset($arr['term']);
    }
    if (strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid) || strlen($public_policy)) {
        $private = 1;
    } else {
        $private = $arr['item_private'];
    }
    $arr['parent'] = $parent_id;
    $arr['allow_cid'] = $allow_cid;
    $arr['allow_gid'] = $allow_gid;
    $arr['deny_cid'] = $deny_cid;
    $arr['deny_gid'] = $deny_gid;
    $arr['public_policy'] = $public_policy;
    $arr['item_private'] = $private;
    $arr['comments_closed'] = $comments_closed;
    logger('item_store: ' . print_r($arr, true), LOGGER_DATA);
    dbesc_array($arr);
    $r = dbq("INSERT INTO `item` (`" . implode("`, `", array_keys($arr)) . "`) VALUES ('" . implode("', '", array_values($arr)) . "')");
    // find the item we just created
    $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC ", $arr['mid'], intval($arr['uid']));
    if ($r && count($r)) {
        $current_post = $r[0]['id'];
        $arr = $r[0];
        // This will gives us a fresh copy of what's now in the DB and undo the db escaping, which really messes up the notifications
        logger('item_store: created item ' . $current_post, LOGGER_DEBUG);
    } else {
        logger('item_store: could not locate stored item');
        $ret['message'] = 'unable to retrieve.';
        return $ret;
    }
    if (count($r) > 1) {
        logger('item_store: duplicated post occurred. Removing duplicates.');
        q("DELETE FROM `item` WHERE `mid` = '%s' AND `uid` = %d AND `id` != %d ", $arr['mid'], intval($arr['uid']), intval($current_post));
    }
    $arr['id'] = $current_post;
    if (!intval($r[0]['parent'])) {
        $x = q("update item set parent = id where id = %d", intval($r[0]['id']));
    }
    // Store taxonomy
    if ($terms && is_array($terms)) {
        foreach ($terms as $t) {
            q("insert into term (uid,oid,otype,type,term,url)\n\t\t\t\tvalues(%d,%d,%d,%d,'%s','%s') ", intval($arr['uid']), intval($current_post), intval(TERM_OBJ_POST), intval($t['type']), dbesc($t['term']), dbesc($t['url']));
        }
        $arr['term'] = $terms;
    }
    call_hooks('post_remote_end', $arr);
    // update the commented timestamp on the parent
    $z = q("select max(created) as commented from item where parent_mid = '%s' and uid = %d and not ( item_restrict & %d )>0 ", dbesc($arr['parent_mid']), intval($arr['uid']), intval(ITEM_DELAYED_PUBLISH));
    q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d", dbesc($z ? $z[0]['commented'] : datetime_convert()), dbesc(datetime_convert()), intval($parent_id));
    // If _creating_ a deleted item, don't propagate it further or send out notifications.
    // We need to store the item details just in case the delete came in before the original post,
    // so that we have an item in the DB that's marked deleted and won't store a fresh post
    // that isn't aware that we were already told to delete it.
    if (!($arr['item_restrict'] & ITEM_DELETED)) {
        send_status_notifications($current_post, $arr);
        tag_deliver($arr['uid'], $current_post);
    }
    $ret['success'] = true;
    $ret['item_id'] = $current_post;
    return $ret;
}
 /**
  * Add a thread to the conversation
  *
  * Returns:
  *      _ The inserted item on success
  *      _ false on failure
  */
 public function add_thread($item)
 {
     $item_id = $item->get_id();
     if (!$item_id) {
         logger('Item has no ID!!', LOGGER_DEBUG, LOG_ERR);
         return false;
     }
     if ($this->get_thread($item->get_id())) {
         logger('Thread already exists (' . $item->get_id() . ').', LOGGER_DEBUG, LOG_WARNING);
         return false;
     }
     /*
      * Only add things that will be displayed
      */
     if ($item->get_data_value('id') != $item->get_data_value('parent') && (activity_match($item->get_data_value('verb'), ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'), ACTIVITY_DISLIKE))) {
         return false;
     }
     $item->set_commentable(false);
     $ob_hash = $this->observer ? $this->observer['xchan_hash'] : '';
     if (!comments_are_now_closed($item->get_data())) {
         if ($item->get_data_value('author_xchan') === $ob_hash || $item->get_data_value('owner_xchan') === $ob_hash) {
             $item->set_commentable(true);
         }
         if (intval($item->get_data_value('item_nocomment'))) {
             $item->set_commentable(false);
         } elseif ($this->observer && !$item->is_commentable()) {
             if (array_key_exists('owner', $item->data) && intval($item->data['owner']['abook_self'])) {
                 $item->set_commentable(perm_is_allowed($this->profile_owner, $this->observer['xchan_hash'], 'post_comments'));
             } else {
                 $item->set_commentable(can_comment_on_post($this->observer['xchan_hash'], $item->data));
             }
         }
     }
     require_once 'include/identity.php';
     $item->set_conversation($this);
     $this->threads[] = $item;
     return end($this->threads);
 }