diff options
Diffstat (limited to 'include')
87 files changed, 5649 insertions, 10136 deletions
diff --git a/include/BaseObject.php b/include/BaseObject.php deleted file mode 100644 index a88978a83..000000000 --- a/include/BaseObject.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php /** @file */ - -if(class_exists('BaseObject')) - return; - -require_once('boot.php'); - -/** - * Basic object - * - * Contains what is usefull to any object - */ -class BaseObject { - private static $app = null; - - /** - * Get the app - * - * Same as get_app from boot.php - */ - public function get_app() { - if(self::$app) - return self::$app; - - global $a; - self::$app = $a; - - return self::$app; - } - - /** - * Set the app - */ - public static function set_app($app) { - self::$app = $app; - } -} - diff --git a/include/ConversationObject.php b/include/ConversationObject.php deleted file mode 100644 index 82f381b0c..000000000 --- a/include/ConversationObject.php +++ /dev/null @@ -1,225 +0,0 @@ -<?php /** @file */ - -if(class_exists('Conversation')) - return; - -require_once('boot.php'); -require_once('include/BaseObject.php'); -require_once('include/ItemObject.php'); -require_once('include/text.php'); -require_once('include/items.php'); - -/** - * A list of threads - * - */ - -class Conversation extends BaseObject { - - private $threads = array(); - private $mode = null; - private $observer = null; - private $writable = false; - private $commentable = false; - private $profile_owner = 0; - private $preview = false; - private $prepared_item = ''; - private $cipher = 'aes256'; - - // $prepared_item is for use by alternate conversation structures such as photos - // wherein we've already prepared a top level item which doesn't look anything like - // a normal "post" item - - public function __construct($mode, $preview, $prepared_item = '') { - $this->set_mode($mode); - $this->preview = $preview; - $this->prepared_item = $prepared_item; - $c = ((local_channel()) ? get_pconfig(local_channel(),'system','default_cipher') : ''); - if($c) - $this->cipher = $c; - } - - /** - * Set the mode we'll be displayed on - */ - private function set_mode($mode) { - if($this->get_mode() == $mode) - return; - - $a = $this->get_app(); - - $this->observer = App::get_observer(); - $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); - - switch($mode) { - case 'network': - $this->profile_owner = local_channel(); - $this->writable = true; - break; - case 'channel': - $this->profile_owner = App::$profile['profile_uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - break; - case 'display': - // in this mode we set profile_owner after initialisation (from conversation()) and then - // pull some trickery which allows us to re-invoke this function afterward - // it's an ugly hack so FIXME - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - break; - case 'page': - $this->profile_owner = App::$profile['uid']; - $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); - break; - default: - logger('[ERROR] Conversation::set_mode : Unhandled mode ('. $mode .').', LOGGER_DEBUG); - return false; - break; - } - $this->mode = $mode; - } - - /** - * Get mode - */ - public function get_mode() { - return $this->mode; - } - - /** - * Check if page is writable - */ - public function is_writable() { - return $this->writable; - } - - public function is_commentable() { - return $this->commentable; - } - - /** - * Check if page is a preview - */ - public function is_preview() { - return $this->preview; - } - - /** - * Get profile owner - */ - public function get_profile_owner() { - return $this->profile_owner; - } - - public function set_profile_owner($uid) { - $this->profile_owner = $uid; - $mode = $this->get_mode(); - $this->mode = null; - $this->set_mode($mode); - } - - public function get_observer() { - return $this->observer; - } - - public function get_cipher() { - return $this->cipher; - } - - - /** - * 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); - } - - /** - * 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) - * - * Returns: - * _ The data requested on success - * _ false on failure - */ - public function get_template_data($conv_responses) { - $result = array(); - - foreach($this->threads as $item) { - - if(($item->get_data_value('id') == $item->get_data_value('parent')) && $this->prepared_item) { - $item_data = $this->prepared_item; - } - else { - $item_data = $item->get_template_data($conv_responses); - } - if(!$item_data) { - logger('Failed to get item template data ('. $item->get_id() .').', LOGGER_DEBUG, LOG_ERR); - return false; - } - $result[] = $item_data; - } - - return $result; - } - - /** - * Get a thread based on its item id - * - * Returns: - * _ The found item on success - * _ false on failure - */ - private function get_thread($id) { - foreach($this->threads as $item) { - if($item->get_id() == $id) - return $item; - } - - return false; - } -} diff --git a/include/ITemplateEngine.php b/include/ITemplateEngine.php deleted file mode 100755 index 7bd559a63..000000000 --- a/include/ITemplateEngine.php +++ /dev/null @@ -1,10 +0,0 @@ -<?php -require_once 'boot.php'; - -/** - * @brief Interface for template engines. - */ -interface ITemplateEngine { - public function replace_macros($s, $v); - public function get_markup_template($file, $root=''); -} diff --git a/include/Import/import_diaspora.php b/include/Import/import_diaspora.php index a94e73395..b664badf1 100644 --- a/include/Import/import_diaspora.php +++ b/include/Import/import_diaspora.php @@ -6,7 +6,6 @@ require_once('include/follow.php'); require_once('include/photo/photo_driver.php'); function import_diaspora($data) { - $a = get_app(); $account = App::get_account(); if(! $account) diff --git a/include/ItemObject.php b/include/ItemObject.php deleted file mode 100644 index 9d5acd95f..000000000 --- a/include/ItemObject.php +++ /dev/null @@ -1,773 +0,0 @@ -<?php /** @file */ - -if(class_exists('Item')) - return; - -require_once('include/BaseObject.php'); -require_once('include/text.php'); -require_once('boot.php'); - -/** - * An item - */ - -class Item extends BaseObject { - public $data = array(); - private $template = 'conv_item.tpl'; - private $comment_box_template = 'comment_item.tpl'; - private $commentable = false; - private $toplevel = 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; - private $channel = null; - private $display_mode = 'normal'; - - - public function __construct($data) { - $a = $this->get_app(); - - $this->data = $data; - $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); - - // Prepare the children - if(count($data['children'])) { - foreach($data['children'] as $item) { - - /* - * Only add those that will be displayed - */ - - if((! visible_activity($item)) || array_key_exists('author_blocked',$item)) { - continue; - } - - $child = new Item($item); - $this->add_child($child); - } - } - } - - /** - * Get data in a form usable by a conversation template - * - * Returns: - * _ The data requested on success - * _ false on failure - */ - - public function get_template_data($conv_responses, $thread_level=1) { - - $result = array(); - - $a = $this->get_app(); - $item = $this->get_data(); - - $commentww = ''; - $sparkle = ''; - $buttons = ''; - $dropping = false; - $star = false; - $isstarred = "unstarred icon-star-empty"; - $indent = ''; - $osparkle = ''; - $total_children = $this->count_descendants(); - $unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants()); - - $conv = $this->get_conversation(); - $observer = $conv->get_observer(); - - $lock = ((($item['item_private'] == 1) || (($item['uid'] == local_channel()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) - || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) - ? t('Private Message') - : false); - $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); - - // allow an exemption for sharing stuff from your private feeds - if($item['author']['xchan_network'] === 'rss') - $shareable = true; - - $mode = $conv->get_mode(); - - if(local_channel() && $observer['xchan_hash'] === $item['author_xchan']) - $edpost = array(z_root()."/editpost/".$item['id'], t("Edit")); - else - $edpost = false; - - - if($observer['xchan_hash'] == $this->get_data_value('author_xchan') - || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') - || $this->get_data_value('uid') == local_channel()) - $dropping = true; - - - if(array_key_exists('real_uid',$item)) { - $edpost = false; - $dropping = false; - } - - - if($dropping) { - $drop = array( - 'dropping' => $dropping, - 'delete' => t('Delete'), - ); - } -// FIXME - if($observer_is_pageowner) { - $multidrop = array( - 'select' => t('Select'), - ); - } - - $filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false); - - $profile_avatar = $item['author']['xchan_photo_m']; - $profile_link = chanlink_url($item['author']['xchan_url']); - $profile_name = $item['author']['xchan_name']; - - $location = format_location($item); - $isevent = false; - $attend = null; - $canvote = false; - - // process action responses - e.g. like/dislike/attend/agree/whatever - $response_verbs = array('like'); - if(feature_enabled($conv->get_profile_owner(),'dislike')) - $response_verbs[] = 'dislike'; - if($item['obj_type'] === ACTIVITY_OBJ_EVENT) { - $response_verbs[] = 'attendyes'; - $response_verbs[] = 'attendno'; - $response_verbs[] = 'attendmaybe'; - if($this->is_commentable()) { - $isevent = true; - $attend = array( t('I will attend'), t('I will not attend'), t('I might attend')); - } - } - - $consensus = (intval($item['item_consensus']) ? true : false); - if($consensus) { - $response_verbs[] = 'agree'; - $response_verbs[] = 'disagree'; - $response_verbs[] = 'abstain'; - if($this->is_commentable()) { - $conlabels = array( t('I agree'), t('I disagree'), t('I abstain')); - $canvote = true; - } - } - - if(! feature_enabled($conv->get_profile_owner(),'dislike')) - unset($conv_responses['dislike']); - - $responses = get_responses($conv_responses,$response_verbs,$this,$item); - - $like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); - $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); - if (count($like_list) > MAX_LIKERS) { - $like_list_part = array_slice($like_list, 0, MAX_LIKERS); - array_push($like_list_part, '<a href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); - } else { - $like_list_part = ''; - } - $like_button_label = tt('Like','Likes',$like_count,'noun'); - - if (feature_enabled($conv->get_profile_owner(),'dislike')) { - $dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); - $dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); - $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - if (count($dislike_list) > MAX_LIKERS) { - $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); - array_push($dislike_list_part, '<a href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); - } else { - $dislike_list_part = ''; - } - } - - $showlike = ((x($conv_responses['like'],$item['mid'])) ? format_like($conv_responses['like'][$item['mid']],$conv_responses['like'][$item['mid'] . '-l'],'like',$item['mid']) : ''); - $showdislike = ((x($conv_responses['dislike'],$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike')) - ? format_like($conv_responses['dislike'][$item['mid']],$conv_responses['dislike'][$item['mid'] . '-l'],'dislike',$item['mid']) : ''); - - /* - * 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->check_wall_to_wall(); - - if($this->is_toplevel()) { - // FIXME check this permission - if(($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) { - -// FIXME we don't need all this stuff, some can be done in the template - - $star = array( - 'do' => t("Add Star"), - 'undo' => t("Remove Star"), - 'toggle' => t("Toggle Star Status"), - 'classdo' => (intval($item['item_starred']) ? "hidden" : ""), - 'classundo' => (intval($item['item_starred']) ? "" : "hidden"), - 'isstarred' => (intval($item['item_starred']) ? "starred icon-star" : "unstarred icon-star-empty"), - 'starred' => t('starred'), - ); - - } - } else { - $indent = 'comment'; - } - - - $verified = (intval($item['item_verified']) ? t('Message signature validated') : ''); - $forged = ((($item['sig']) && (! intval($item['item_verified']))) ? t('Message signature incorrect') : ''); - $unverified = '' ; // (($this->is_wall_to_wall() && (! intval($item['item_verified']))) ? t('Message cannot be verified') : ''); - - - - // FIXME - check this permission - if($conv->get_profile_owner() == local_channel()) { - $tagger = array( - 'tagit' => t("Add Tag"), - 'classtagger' => "", - ); - } - - $has_bookmarks = false; - if(is_array($item['term'])) { - foreach($item['term'] as $t) { - if(!UNO && $t['type'] == TERM_BOOKMARK) - $has_bookmarks = true; - } - } - - $has_event = false; - if(($item['obj_type'] === ACTIVITY_OBJ_EVENT) && $conv->get_profile_owner() == local_channel()) - $has_event = true; - - if($this->is_commentable()) { - $like = array( t("I like this \x28toggle\x29"), t("like")); - $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike")); - } - - if ($shareable) - $share = array( t('Share This'), t('share')); - - $dreport = ''; - - $keep_reports = intval(get_config('system','expire_delivery_reports')); - if($keep_reports === 0) - $keep_reports = 30; - - if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) - $dreport = t('Delivery Report'); - - if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) - $indent .= ' shiny'; - - - localize_item($item); - - $body = prepare_body($item,true); - - // $viewthread (below) is only valid in list mode. If this is a channel page, build the thread viewing link - // since we can't depend on llink or plink pointing to the right local location. - - $owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@')); - $viewthread = $item['llink']; - if($conv->get_mode() === 'channel') - $viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . $item['mid']; - - $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); - $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : ''); - - - - - - $children = $this->get_children(); - - $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); - - $tmp_item = array( - 'template' => $this->get_template(), - 'mode' => $mode, - 'type' => implode("",array_slice(explode("/",$item['verb']),-1)), - 'body' => $body['html'], - 'tags' => $body['tags'], - 'categories' => $body['categories'], - 'mentions' => $body['mentions'], - 'attachments' => $body['attachments'], - 'folders' => $body['folders'], - 'text' => strip_tags($body['html']), - 'id' => $this->get_id(), - 'mid' => $item['mid'], - 'isevent' => $isevent, - 'attend' => $attend, - 'consensus' => $consensus, - 'conlabels' => $conlabels, - 'canvote' => $canvote, - 'linktitle' => sprintf( t('View %s\'s profile - %s'), $profile_name, $item['author']['xchan_addr']), - 'olinktitle' => sprintf( t('View %s\'s profile - %s'), $this->get_owner_name(), $item['owner']['xchan_addr']), - 'llink' => $item['llink'], - 'viewthread' => $viewthread, - '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), - 'dreport' => $dreport, - 'name' => $profile_name, - 'thumb' => $profile_avatar, - 'osparkle' => $osparkle, - 'sparkle' => $sparkle, - 'title' => $item['title'], - 'title_tosource' => get_pconfig($conv->get_profile_owner(),'system','title_tosource'), - 'ago' => relative_date($item['created']), - 'app' => $item['app'], - 'str_app' => sprintf( t('from %s'), $item['app']), - 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'editedtime' => (($item['edited'] != $item['created']) ? sprintf( t('last edited: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r')) : ''), - 'expiretime' => (($item['expires'] !== NULL_DATE) ? sprintf( t('Expires: %s'), datetime_convert('UTC', date_default_timezone_get(), $item['expires'], 'r')):''), - 'lock' => $lock, - 'verified' => $verified, - 'unverified' => $unverified, - 'forged' => $forged, - 'location' => $location, - 'indent' => $indent, - 'owner_url' => $this->get_owner_url(), - 'owner_photo' => $this->get_owner_photo(), - 'owner_name' => $this->get_owner_name(), - 'photo' => $body['photo'], - 'event' => $body['event'], - 'has_tags' => $has_tags, - -// Item toolbar buttons - 'like' => $like, - 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''), - 'share' => $share, - 'rawmid' => $item['mid'], - 'plink' => get_plink($item), - 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), - 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''), - 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), - 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), - 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), - 'addtocal' => (($has_event) ? t('Add to Calendar') : ''), - 'drop' => $drop, - 'multidrop' => ((feature_enabled($conv->get_profile_owner(),'multi_delete')) ? $multidrop : ''), -// end toolbar buttons - - 'unseen_comments' => $unseen_comments, - 'comment_count' => $total_children, - 'comment_count_txt' => $comment_count_txt, - 'list_unseen_txt' => $list_unseen_txt, - 'markseen' => t('Mark all seen'), - 'responses' => $responses, - 'like_count' => $like_count, - 'like_list' => $like_list, - 'like_list_part' => $like_list_part, - 'like_button_label' => $like_button_label, - 'like_modal_title' => t('Likes','noun'), - 'dislike_modal_title' => t('Dislikes','noun'), - 'dislike_count' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_count : ''), - 'dislike_list' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list : ''), - 'dislike_list_part' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_list_part : ''), - 'dislike_button_label' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike_button_label : ''), - 'modal_dismiss' => t('Close'), - 'showlike' => $showlike, - 'showdislike' => $showdislike, - 'comment' => $this->get_comment_box($indent), - 'previewing' => ($conv->is_preview() ? ' preview ' : ''), - 'wait' => t('Please wait'), - 'thread_level' => $thread_level - ); - - $arr = array('item' => $item, 'output' => $tmp_item); - call_hooks('display_item', $arr); - - $result = $arr['output']; - - $result['children'] = array(); - $nb_children = count($children); - - $visible_comments = get_config('system','expanded_comments'); - if($visible_comments === false) - $visible_comments = 3; - - if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { - foreach($children as $child) { - $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1); - } - // Collapse - if(($nb_children > $visible_comments) || ($thread_level > 1)) { - $result['children'][0]['comment_firstcollapsed'] = true; - $result['children'][0]['num_comments'] = $comment_count_txt; - $result['children'][0]['hide_text'] = t('[+] show all'); - if($thread_level > 1) { - $result['children'][$nb_children - 1]['comment_lastcollapsed'] = true; - } - else { - $result['children'][$nb_children - ($visible_comments + 1)]['comment_lastcollapsed'] = true; - } - } - } - - $result['private'] = $item['item_private']; - $result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : ''); - - if($this->is_threaded()) { - $result['flatten'] = false; - $result['threaded'] = true; - } - else { - $result['flatten'] = true; - $result['threaded'] = false; - } - - return $result; - } - - public function get_id() { - return $this->get_data_value('id'); - } - - public function get_display_mode() { - return $this->display_mode; - } - - public function set_display_mode($mode) { - $this->display_mode = $mode; - } - - public function is_threaded() { - return $this->threaded; - } - - public function set_commentable($val) { - $this->commentable = $val; - foreach($this->get_children() as $child) - $child->set_commentable($val); - } - - public function is_commentable() { - return $this->commentable; - } - - /** - * Add a child item - */ - public function add_child($item) { - $item_id = $item->get_id(); - if(!$item_id) { - logger('[ERROR] Item::add_child : Item has no ID!!', LOGGER_DEBUG); - return false; - } - if($this->get_child($item->get_id())) { - logger('[WARN] Item::add_child : Item already exists ('. $item->get_id() .').', LOGGER_DEBUG); - return false; - } - /* - * Only add what will be displayed - */ - - if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) { - return false; - } - - $item->set_parent($this); - $this->children[] = $item; - return end($this->children); - } - - /** - * Get a child by its ID - */ - public function get_child($id) { - foreach($this->get_children() as $child) { - if($child->get_id() == $id) - return $child; - } - return null; - } - - /** - * Get all our children - */ - public function get_children() { - return $this->children; - } - - /** - * Set our parent - */ - protected function set_parent($item) { - $parent = $this->get_parent(); - if($parent) { - $parent->remove_child($this); - } - $this->parent = $item; - $this->set_conversation($item->get_conversation()); - } - - /** - * Remove our parent - */ - protected function remove_parent() { - $this->parent = null; - $this->conversation = null; - } - - /** - * Remove a child - */ - public function remove_child($item) { - $id = $item->get_id(); - foreach($this->get_children() as $key => $child) { - if($child->get_id() == $id) { - $child->remove_parent(); - 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::remove_child : Item is not a child ('. $id .').', LOGGER_DEBUG); - return false; - } - - /** - * Get parent item - */ - protected function get_parent() { - return $this->parent; - } - - /** - * set conversation - */ - public function set_conversation($conv) { - $previous_mode = ($this->conversation ? $this->conversation->get_mode() : ''); - - $this->conversation = $conv; - - // Set it on our children too - foreach($this->get_children() as $child) - $child->set_conversation($conv); - } - - /** - * get conversation - */ - public function get_conversation() { - return $this->conversation; - } - - /** - * Get raw data - * - * We shouldn't need this - */ - public function get_data() { - return $this->data; - } - - /** - * Get a data value - * - * Returns: - * _ value on success - * _ false on failure - */ - public function get_data_value($name) { - if(!isset($this->data[$name])) { -// logger('[ERROR] Item::get_data_value : Item has no value name "'. $name .'".', LOGGER_DEBUG); - return false; - } - - return $this->data[$name]; - } - - /** - * Get template - */ - public function get_template() { - return $this->template; - } - - - public function set_template($t) { - $this->template = $t; - } - - /** - * Check if this is a toplevel post - */ - private function is_toplevel() { - return $this->toplevel; - } - - /** - * Count the total of our descendants - */ - private function count_descendants() { - $children = $this->get_children(); - $total = count($children); - if($total > 0) { - foreach($children as $child) { - $total += $child->count_descendants(); - } - } - return $total; - } - - private function count_unseen_descendants() { - $children = $this->get_children(); - $total = count($children); - if($total > 0) { - $total = 0; - foreach($children as $child) { - if((! visible_activity($child->data)) || array_key_exists('author_blocked',$child->data)) { - continue; - } - if(intval($child->data['item_unseen'])) - $total ++; - } - } - return $total; - } - - - /** - * Get the template for the comment box - */ - private function get_comment_box_template() { - return $this->comment_box_template; - } - - /** - * Get the comment box - * - * Returns: - * _ The comment box string (empty if no comment box) - * _ false on failure - */ - private function get_comment_box($indent) { - - if(!$this->is_toplevel() && !get_config('system','thread_allow')) { - return ''; - } - - $comment_box = ''; - $conv = $this->get_conversation(); - -// logger('Commentable conv: ' . $conv->is_commentable()); - - if(! $this->is_commentable()) - return; - - $template = get_markup_template($this->get_comment_box_template()); - - $a = $this->get_app(); - $observer = $conv->get_observer(); - - $qc = ((local_channel()) ? get_pconfig(local_channel(),'system','qcomment') : null); - $qcomment = (($qc) ? explode("\n",$qc) : null); - - $arr = array('comment_buttons' => '','id' => $this->get_id()); - call_hooks('comment_buttons',$arr); - $comment_buttons = $arr['comment_buttons']; - - - $comment_box = replace_macros($template,array( - '$return_path' => '', - '$threaded' => $this->is_threaded(), - '$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), - '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), - '$id' => $this->get_id(), - '$parent' => $this->get_id(), - '$qcomment' => $qcomment, - '$comment_buttons' => $comment_buttons, - '$profile_uid' => $conv->get_profile_owner(), - '$mylink' => $observer['xchan_url'], - '$mytitle' => t('This is you'), - '$myphoto' => $observer['xchan_photo_s'], - '$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('Insert Link'), - '$edvideo' => t('Video'), - '$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''), - '$indent' => $indent, - '$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false), - '$encrypt' => t('Encrypt text'), - '$cipher' => $conv->get_cipher(), - '$sourceapp' => App::$sourcename - - )); - - return $comment_box; - } - - private function get_redirect_url() { - return $this->redirect_url; - } - - /** - * Check if we are a wall to wall item and set the relevant properties - */ - protected function check_wall_to_wall() { - $conv = $this->get_conversation(); - $this->wall_to_wall = false; - $this->owner_url = ''; - $this->owner_photo = ''; - $this->owner_name = ''; - - if($conv->get_mode() === 'channel') - return; - - if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) { - $this->owner_url = chanlink_url($this->data['owner']['xchan_url']); - $this->owner_photo = $this->data['owner']['xchan_photo_m']; - $this->owner_name = $this->data['owner']['xchan_name']; - $this->wall_to_wall = true; - } - } - - private function is_wall_to_wall() { - return $this->wall_to_wall; - } - - private function get_owner_url() { - return $this->owner_url; - } - - private function get_owner_photo() { - return $this->owner_photo; - } - - private function get_owner_name() { - return $this->owner_name; - } - - private function is_visiting() { - return $this->visiting; - } - - - - -} - diff --git a/include/ProtoDriver.php b/include/ProtoDriver.php deleted file mode 100644 index 7585a0135..000000000 --- a/include/ProtoDriver.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php /** @file */ - -/* - * Abstraction class for dealing with alternate networks (which of course do not exist, hence the abstraction) - */ - - -abstract class ProtoDriver { - abstract protected function discover($channel,$location); - abstract protected function deliver($item,$channel,$recipients); - abstract protected function collect($channel,$connection); - abstract protected function change_permissions($permissions,$channel,$recipient); - abstract protected function acknowledge_permissions($permissions,$channel,$recipient); - abstract protected function deliver_private($item,$channel,$recipients); - abstract protected function collect_private($channel,$connection); - -} - -class ZotDriver extends ProtoDriver { - - protected function discover($channel,$location) { - - } - protected function deliver($item,$channel,$recipients) { - - } - protected function collect($channel,$connection) { - - } - protected function change_permissions($permissions,$channel,$recipient) { - - } - protected function acknowledge_permissions($permissions,$channel,$recipient) { - - } - protected function deliver_private($item,$channel,$recipients) { - - } - protected function collect_private($channel,$connection) { - - } - -} diff --git a/include/account.php b/include/account.php index c64197b49..142ad1bea 100644 --- a/include/account.php +++ b/include/account.php @@ -11,7 +11,7 @@ require_once('include/text.php'); require_once('include/language.php'); require_once('include/datetime.php'); require_once('include/crypto.php'); -require_once('include/identity.php'); +require_once('include/channel.php'); function check_account_email($email) { @@ -229,7 +229,7 @@ function verify_email_address($arr) { $hash = random_string(); - $r = q("INSERT INTO register ( hash, created, uid, password, language ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", dbesc($hash), dbesc(datetime_convert()), intval($arr['account']['account_id']), @@ -283,7 +283,7 @@ function send_reg_approval_email($arr) { $hash = random_string(); - $r = q("INSERT INTO register ( hash, created, uid, password, language ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", + $r = q("INSERT INTO register ( hash, created, uid, password, lang ) VALUES ( '%s', '%s', %d, '%s', '%s' ) ", dbesc($hash), dbesc(datetime_convert()), intval($arr['account']['account_id']), @@ -387,7 +387,7 @@ function account_allow($hash) { intval($register[0]['uid']) ); - push_lang($register[0]['language']); + push_lang($register[0]['lang']); $email_tpl = get_intltext_template("register_open_eml.tpl"); $email_tpl = replace_macros($email_tpl, array( @@ -499,11 +499,27 @@ function account_approve($hash) { intval($register[0]['uid']) ); + // get a fresh copy after we've modified it. + + $account = q("SELECT * FROM account WHERE account_id = %d LIMIT 1", + intval($register[0]['uid']) + ); + + if(! $account) + return $ret; + + + if(get_config('system','auto_channel_create') || UNO) auto_channel_create($register[0]['uid']); + else { + $_SESSION['login_return_url'] = 'new_channel'; + authenticate_success($account[0],null,true,true,false,true); + } + - info( t('Account verified. Please login.') . EOL ); + // info( t('Account verified. Please login.') . EOL ); return true; } @@ -591,6 +607,7 @@ function service_class_allows($uid, $property, $usage = false) { if($limit === false) return true; // No service class set => everything is allowed + $limit = engr_units_to_bytes($limit); if($usage === false) { // We use negative values for not allowed properties in a subscriber plan return ((x($limit)) ? (bool) $limit : true); @@ -627,6 +644,8 @@ function account_service_class_allows($aid, $property, $usage = false) { if($limit === false) return true; // No service class is set => everything is allowed + $limit = engr_units_to_bytes($limit); + if($usage === false) { // We use negative values for not allowed properties in a subscriber plan return ((x($limit)) ? (bool) $limit : true); @@ -653,7 +672,8 @@ function account_service_class_allows($aid, $property, $usage = false) { * @todo Should we merge this with account_service_class_fetch()? */ function service_class_fetch($uid, $property) { - $a = get_app(); + + if($uid == local_channel()) { $service_class = App::$account['account_service_class']; } diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 92f9436a2..148c67a6c 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -6,15 +6,14 @@ /** * @package acl_selectors */ -function group_select($selname,$selclass,$preselected = false,$size = 4) { - $a = get_app(); +function group_select($selname,$selclass,$preselected = false,$size = 4) { $o = ''; $o .= "<select name=\"{$selname}[]\" id=\"$selclass\" class=\"$selclass\" multiple=\"multiple\" size=\"$size\" >\r\n"; - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `name` ASC", + $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", intval(local_channel()) ); @@ -31,7 +30,7 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { $selected = " selected=\"selected\" "; else $selected = ''; - $trimmed = mb_substr($rr['name'],0,12); + $trimmed = mb_substr($rr['gname'],0,12); $o .= "<option value=\"{$rr['id']}\" $selected title=\"{$rr['name']}\" >$trimmed</option>\r\n"; } @@ -48,7 +47,6 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { /* MicMee 20130114 function contact_selector no longer in use, sql table contact does no longer exist function contact_selector($selname, $selclass, $preselected = false, $options) { - $a = get_app(); $mutual = false; $networks = null; @@ -154,7 +152,6 @@ function contact_selector($selname, $selclass, $preselected = false, $options) { function contact_select($selname, $selclass, $preselected = false, $size = 4, $privmail = false, $celeb = false, $privatenet = false, $tabindex = null) { - $a = get_app(); $o = ''; @@ -210,12 +207,38 @@ function fixacl(&$item) { $item = str_replace(array('<','>'),array('',''),$item); } -function populate_acl($defaults = null,$show_jotnets = true, $showall = '') { +/** +* Builds a modal dialog for editing permissions, using acl_selector.tpl as the template. +* +* @param array $default Optional access control list for the initial state of the dialog. +* @param boolean $show_jotnets Whether plugins for federated networks should be included in the permissions dialog +* @param PermissionDescription $emptyACL_description - An optional description for the permission implied by selecting an empty ACL. Preferably an instance of PermissionDescription. +* @param string $dialog_description Optional message to include at the top of the dialog. E.g. "Warning: Post permissions cannot be changed once sent". +* @param string $context_help Allows the dialog to present a help icon. E.g. "acl_dialog_post" +* @param boolean $readonly Not implemented yet. When implemented, the dialog will use acl_readonly.tpl instead, so that permissions may be viewed for posts that can no longer have their permissions changed. +* +* @return string html modal dialog built from acl_selector.tpl +*/ +function populate_acl($defaults = null,$show_jotnets = true, $emptyACL_description = '', $dialog_description = '', $context_help = '', $readonly = false) { $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; + $showall_origin = ''; + $showall_icon = 'fa-globe'; + $role = get_pconfig(local_channel(),'system','permissions_role'); + + if(! $emptyACL_description) { + $showall_caption = t('Visible to your default audience'); + + } else if (is_a($emptyACL_description, '\\Zotlabs\\Lib\\PermissionDescription')) { + $showall_caption = $emptyACL_description->get_permission_description(); + $showall_origin = (($role === 'custom') ? $emptyACL_description->get_permission_origin_description() : ''); + $showall_icon = $emptyACL_description->get_permission_icon(); + + } else { + // For backwards compatibility we still accept a string... for now! + $showall_caption = $emptyACL_description; + } - if(! $showall) - $showall = t('Visible to your default audience'); if(is_array($defaults)) { $allow_cid = ((strlen($defaults['allow_cid'])) @@ -239,9 +262,16 @@ function populate_acl($defaults = null,$show_jotnets = true, $showall = '') { $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( - '$showall' => $showall, + '$showall' => $showall_caption, + '$onlyme' => t('Only me'), + '$showallOrigin' => $showall_origin, + '$showallIcon' => $showall_icon, + '$select_label' => t('Who can see this?'), + '$showlimited' => t('Custom selection'), + '$showlimitedDesc' => t('Select "Show" to allow viewing. "Don\'t show" lets you override and limit the scope of "Show".'), '$show' => t("Show"), '$hide' => t("Don't show"), + '$search' => t("Search"), '$allowcid' => json_encode($allow_cid), '$allowgid' => json_encode($allow_gid), '$denycid' => json_encode($deny_cid), @@ -249,10 +279,39 @@ function populate_acl($defaults = null,$show_jotnets = true, $showall = '') { '$jnetModalTitle' => t('Other networks and post services'), '$jotnets' => $jotnets, '$aclModalTitle' => t('Permissions'), - '$aclModalDismiss' => t('Close') + '$aclModalDesc' => $dialog_description, + '$aclModalDismiss' => t('Close'), + '$helpUrl' => (($context_help == '') ? '' : (z_root() . '/help/' . $context_help)) )); return $o; } +/** +* Returns a string that's suitable for passing as the $dialog_description argument to a +* populate_acl() call for wall posts or network posts. +* +* This string is needed in 3 different files, and our .po translation system currently +* cannot be used as a string table (because the value is always the key in english) so +* I've centralized the value here (making this function name the "key") until we have a +* better way. +* +* @return string Description to present to user in modal permissions dialog +*/ +function get_post_aclDialogDescription() { + + // I'm trying to make two points in this description text - warn about finality of wall + // post permissions, and try to clear up confusion that these permissions set who is + // *shown* the post, istead of who is able to see the post, i.e. make it clear that clicking + // the "Show" button on a group does not post it to the feed of people in that group, it + // mearly allows those people to view the post if they are viewing/following this channel. + $description = t('Post permissions %s cannot be changed %s after a post is shared.</br />These permissions set who is allowed to view the post.'); + + // Lets keep the emphasis styling seperate from the translation. It may change. + $emphasisOpen = '<b><a href="' . z_root() . '/help/acl_dialog_post" target="hubzilla-help">'; + $emphasisClose = '</a></b>'; + + return sprintf($description, $emphasisOpen, $emphasisClose); +} + diff --git a/include/activities.php b/include/activities.php index 9ba191391..3271db993 100644 --- a/include/activities.php +++ b/include/activities.php @@ -1,7 +1,6 @@ <?php /** @file */ function profile_activity($changed, $value) { - $a = get_app(); if(! local_channel() || ! is_array($changed) || ! count($changed)) return; @@ -89,7 +88,7 @@ function profile_activity($changed, $value) { if($i) { // FIXME - limit delivery in notifier.php to those specificed in the perms argument - proc_run('php',"include/notifier.php","activity","$i", 'PERMS_R_PROFILE'); + Zotlabs\Daemon\Master::Summon(array('Notifier','activity', $i, 'PERMS_R_PROFILE')); } } diff --git a/include/api.php b/include/api.php index fd644947c..8d475c5fa 100644 --- a/include/api.php +++ b/include/api.php @@ -282,7 +282,8 @@ require_once('include/api_auth.php'); intval($uinfo[0]['xchan_hash']) ); $countitms = $r[0]['count']; - $following = (($uinfo[0]['abook_myperms'] & PERMS_R_STREAM) ? true : false ); + + $following = ((get_abconfig($uinfo[0]['abook_channel'],$uinfo[0]['abook_xchan'],'my_perms','view_stream')) ? true : false ); } @@ -368,7 +369,7 @@ require_once('include/api_auth.php'); else $redirect = trim($_REQUEST['redirect_uris']); $icon = trim($_REQUEST['logo_uri']); - $r = q("INSERT INTO clients (client_id, pw, name, redirect_uri, icon, uid) + $r = q("INSERT INTO clients (client_id, pw, clname, redirect_uri, icon, uid) VALUES ('%s','%s','%s','%s','%s',%d)", dbesc($key), dbesc($secret), @@ -451,8 +452,6 @@ require_once('include/api_auth.php'); */ function api_apply_template($templatename, $type, $data){ - $a = get_app(); - switch($type){ case "atom": case "rss": @@ -486,7 +485,7 @@ require_once('include/api_auth.php'); function api_account_logout(&$a, $type){ require_once('include/auth.php'); - \Zotlabs\Web\Session::nuke(); + App::$session->nuke(); return api_apply_template("user", $type, array('$user' => null)); } @@ -514,7 +513,7 @@ require_once('include/api_auth.php'); return false; } - require_once('include/identity.php'); + require_once('include/channel.php'); json_return_and_die(identity_basic_export(api_user(),(($_REQUEST['posts']) ? intval($_REQUEST['posts']) : 0 ))); } @@ -556,7 +555,7 @@ require_once('include/api_auth.php'); dbesc($_REQUEST['file_id']) ); if($r) { - unset($r[0]['data']); + unset($r[0]['content']); $ret = array('attach' => $r[0]); json_return_and_die($ret); } @@ -582,21 +581,21 @@ require_once('include/api_auth.php'); $length = intval($ptr['filesize']); if($ptr['is_dir']) - $ptr['data'] = ''; + $ptr['content'] = ''; elseif(! intval($r[0]['os_storage'])) { $ptr['start'] = $start; - $x = substr(dbunescbin($ptr['data'],$start,$length)); + $x = substr(dbunescbin($ptr['content'],$start,$length)); $ptr['length'] = strlen($x); - $ptr['data'] = base64_encode($x); + $ptr['content'] = base64_encode($x); } else { - $fp = fopen(dbunescbin($ptr['data']),'r'); + $fp = fopen(dbunescbin($ptr['content']),'r'); if($fp) { $seek = fseek($fp,$start,SEEK_SET); $x = fread($fp,$length); $ptr['start'] = $start; $ptr['length'] = strlen($x); - $ptr['data'] = base64_encode($x); + $ptr['content'] = base64_encode($x); } } @@ -619,11 +618,11 @@ require_once('include/api_auth.php'); ); if($r) { if($r[0]['is_dir']) - $r[0]['data'] = ''; + $r[0]['content'] = ''; elseif(intval($r[0]['os_storage'])) - $r[0]['data'] = base64_encode(file_get_contents(dbunescbin($r[0]['data']))); + $r[0]['content'] = base64_encode(file_get_contents(dbunescbin($r[0]['content']))); else - $r[0]['data'] = base64_encode(dbunescbin($r[0]['data'])); + $r[0]['content'] = base64_encode(dbunescbin($r[0]['content'])); $ret = array('attach' => $r[0]); json_return_and_die($ret); @@ -649,16 +648,16 @@ require_once('include/api_auth.php'); if (api_user()===false) return false; if(! $_REQUEST['photo_id']) return false; $scale = ((array_key_exists('scale',$_REQUEST)) ? intval($_REQUEST['scale']) : 0); - $r = q("select * from photo where uid = %d and resource_id = '%s' and scale = %d limit 1", + $r = q("select * from photo where uid = %d and resource_id = '%s' and imgscale = %d limit 1", intval(local_channel()), dbesc($_REQUEST['photo_id']), intval($scale) ); if($r) { - $data = dbunescbin($r[0]['data']); + $data = dbunescbin($r[0]['content']); if(array_key_exists('os_storage',$r[0]) && intval($r[0]['os_storage'])) $data = file_get_contents($data); - $r[0]['data'] = base64_encode($data); + $r[0]['content'] = base64_encode($data); $ret = array('photo' => $r[0]); $i = q("select id from item where uid = %d and resource_type = 'photo' and resource_id = '%s' limit 1", intval(local_channel()), @@ -772,13 +771,15 @@ require_once('include/api_auth.php'); $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo $_FILES['userfile'] = $_FILES['media']; - require_once('mod/wall_attach.php'); - $posted = wall_attach_post($a); - - //now that we have the img url in bbcode we can add it to the status and insert the wall item. + + $mod = new Zotlabs\Module\Wall_attach(); + $mod->post(); + + $_REQUEST['body']=$txt."\n\n".$posted; - require_once('mod/item.php'); - item_post($a); + + $mod = new Zotlabs\Module\Item(); + $mod->post(); // this should output the last post (the one we just posted). return api_status_show($a,$type); @@ -839,7 +840,7 @@ require_once('include/api_auth.php'); $_REQUEST['parent_mid'] = $parent; if($_REQUEST['namespace'] && $parent) { - $x = q("select iid from item_id where service = '%s' and sid = '%s' limit 1", + $x = q("select iid from iconfig where cat = 'system' and k = '%s' and v = '%s' limit 1", dbesc($_REQUEST['namespace']), dbesc($parent) ); @@ -871,9 +872,9 @@ require_once('include/api_auth.php'); // upload each image if we have any $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - require_once('mod/wall_attach.php'); + $mod = new Zotlabs\Module\Wall_attach(); App::$data['api_info'] = $user_info; - $media = wall_attach_post($a); + $media = $mod->post(); if(strlen($media)>0) $_REQUEST['body'] .= "\n\n" . $media; @@ -884,9 +885,9 @@ require_once('include/api_auth.php'); $_FILES['userfile'] = $_FILES['media']; // upload each image if we have any $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - require_once('mod/wall_attach.php'); + $mod = new Zotlabs\Module\Wall_attach(); App::$data['api_info'] = $user_info; - $media = wall_attach_post($a); + $media = $mod->post(); if(strlen($media)>0) $_REQUEST['body'] .= "\n\n" . $media; @@ -896,8 +897,8 @@ require_once('include/api_auth.php'); // call out normal post function - require_once('mod/item.php'); - item_post($a); + $mod = new Zotlabs\Module\Item(); + $mod->post(); // this should output the last post (the one we just posted). return api_status_show($a,$type); @@ -926,14 +927,14 @@ require_once('include/api_auth.php'); $_FILES['userfile'] = $_FILES['media']; // upload the image if we have one $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo - require_once('mod/wall_upload.php'); - $media = wall_upload_post($a); + $mod = new Zotlabs\Module\Wall_upload(); + $media = $mod->post(); if(strlen($media)>0) $_REQUEST['body'] .= "\n\n".$media; } - require_once('mod/item.php'); - $x = item_post($a); + $mod = new Zotlabs\Module\Item(); + $x = $mod->post(); json_return_and_die($x); } @@ -967,20 +968,10 @@ require_once('include/api_auth.php'); $ret = array(); $tmp = array(); - $str = ''; foreach($i as $ii) { $tmp[] = encode_item($ii,true); - if($str) - $str .= ','; - $str .= $ii['id']; } $ret['item'] = $tmp; - if($str) { - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item.id in ( $str ) "); - - if($r) - $ret['item_id'] = $r; - } json_return_and_die($ret); } @@ -1423,9 +1414,8 @@ require_once('include/api_auth.php'); $_REQUEST['profile_uid'] = api_user(); $_REQUEST['type'] = 'wall'; $_REQUEST['api_source'] = true; - - require_once('mod/item.php'); - item_post($a); + $mod = new Zotlabs\Module\Item(); + $mod->post(); } } else @@ -1463,7 +1453,8 @@ require_once('include/api_auth.php'); } else { if($_REQUEST['namespace'] && $_REQUEST['remote_id']) { - $r = q("select * from item_id where service = '%s' and sid = '%s' and uid = %d limit 1", + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where cat = 'system' and k = '%s' and v = '%s' and item.uid = %d limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['remote_id']), intval($user_info['uid']) @@ -1473,7 +1464,7 @@ require_once('include/api_auth.php'); $id = $r[0]['iid']; } if($_REQUEST['namespace'] && $_REQUEST['comment_id']) { - $r = q("select * from item_id left join item on item.id = item_id.iid where service = '%s' and sid = '%s' and uid = %d and item.id != item.parent limit 1", + $r = q("select * from iconfig left join item on item.id = iconfig.iid where cat = 'system' and k = '%s' and v = '%s' and uid = %d and item.id != item.parent limit 1", dbesc($_REQUEST['namespace']), dbesc($_REQUEST['comment_id']), intval($user_info['uid']) @@ -1903,13 +1894,17 @@ require_once('include/api_auth.php'); //logger('api_format_items: ' . print_r($user_info,true)); - $a = get_app(); $ret = array(); + $x = array('items' => $r,'api_user' => api_user(),'user_info' => $user_info); + call_hooks('api_format_items',$x); + $r = $x['items']; + if(! $r) return $ret; foreach($r as $item) { + localize_item($item); $status_user = (($item['author_xchan']==$user_info['guid'])?$user_info: api_item_get_user($a,$item)); @@ -2106,10 +2101,10 @@ require_once('include/api_auth.php'); 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, 'shorturllength' => '30', 'hubzilla' => array( - 'PLATFORM_NAME' => Zotlabs\Project\System::get_platform_name(), - 'RED_VERSION' => Zotlabs\Project\System::get_project_version(), + 'PLATFORM_NAME' => Zotlabs\Lib\System::get_platform_name(), + 'STD_VERSION' => Zotlabs\Lib\System::get_project_version(), 'ZOT_REVISION' => ZOT_REVISION, - 'DB_UPDATE_VERSION' => Zotlabs\Project\System::get_update_version() + 'DB_UPDATE_VERSION' => Zotlabs\Lib\System::get_update_version() ) )); @@ -2142,12 +2137,12 @@ require_once('include/api_auth.php'); if($type === 'xml') { header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . Zotlabs\Project\System::get_project_version() . '</version>' . "\r\n"; + echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . Zotlabs\Lib\System::get_project_version() . '</version>' . "\r\n"; killme(); } elseif($type === 'json') { header("Content-type: application/json"); - echo '"' . Zotlabs\Project\System::get_project_version() . '"'; + echo '"' . Zotlabs\Lib\System::get_project_version() . '"'; killme(); } } diff --git a/include/api_auth.php b/include/api_auth.php index dc8492b20..7a71bad73 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -59,20 +59,12 @@ function api_login(&$a){ if(isset($_SERVER['PHP_AUTH_USER'])) { $channel_login = 0; $record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); - if(! $record) { - $r = q("select * from channel left join account on account.account_id = channel.channel_account_id - where channel.channel_address = '%s' limit 1", - dbesc($_SERVER['PHP_AUTH_USER']) - ); - if ($r) { - $record = account_verify_password($r[0]['account_email'],$_SERVER['PHP_AUTH_PW']); - if($record) - $channel_login = $r[0]['channel_id']; - } + if($record && $record['channel']) { + $channel_login = $record['channel']['channel_id']; } } - if($record) { + if($record['account']) { authenticate_success($record); if($channel_login) diff --git a/include/apps.php b/include/apps.php deleted file mode 100644 index fac58b850..000000000 --- a/include/apps.php +++ /dev/null @@ -1,511 +0,0 @@ -<?php /** @file */ - -/** - * apps - * - */ - -require_once('include/plugin.php'); -require_once('include/identity.php'); - -function get_system_apps() { - - $ret = array(); - if(is_dir('apps')) - $files = glob('apps/*.apd'); - else - $files = glob('app/*.apd'); - if($files) { - foreach($files as $f) { - $x = parse_app_description($f); - if($x) { - $ret[] = $x; - } - } - } - $files = glob('addon/*/*.apd'); - if($files) { - foreach($files as $f) { - $n = basename($f,'.apd'); - if(plugin_is_installed($n)) { - $x = parse_app_description($f); - if($x) { - $ret[] = $x; - } - } - } - } - - return $ret; - -} - -function app_name_compare($a,$b) { - return strcmp($a['name'],$b['name']); -} - -function parse_app_description($f) { - $ret = array(); - - $baseurl = z_root(); - $channel = App::get_channel(); - $address = (($channel) ? $channel['channel_address'] : ''); - - //future expansion - - $observer = App::get_observer(); - - - $lines = @file($f); - if($lines) { - foreach($lines as $x) { - if(preg_match('/^([a-zA-Z].*?):(.*?)$/ism',$x,$matches)) { - $ret[$matches[1]] = trim(str_replace(array('$baseurl','$nick'),array($baseurl,$address),$matches[2])); - } - } - } - - - if(! $ret['photo']) - $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); - - $ret['type'] = 'system'; - - foreach($ret as $k => $v) { - if(strpos($v,'http') === 0) - $ret[$k] = zid($v); - } - - if(array_key_exists('desc',$ret)) - $ret['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['desc']); - - if(array_key_exists('target',$ret)) - $ret['target'] = str_replace(array('\'','"'),array(''','&dquot;'),$ret['target']); - - if(array_key_exists('requires',$ret)) { - $requires = explode(',',$ret['requires']); - foreach($requires as $require) { - $require = trim(strtolower($require)); - switch($require) { - case 'nologin': - if(local_channel()) - unset($ret); - break; - case 'admin': - if(! is_site_admin()) - unset($ret); - break; - case 'local_channel': - if(! local_channel()) - unset($ret); - break; - case 'public_profile': - if(! is_public_profile()) - unset($ret); - break; - case 'observer': - if(! $observer) - unset($ret); - break; - default: - if(! (local_channel() && feature_enabled(local_channel(),$require))) - unset($ret); - break; - - } - } - } - if($ret) { - translate_system_apps($ret); - return $ret; - } - return false; -} - - -function translate_system_apps(&$arr) { - $apps = array( - 'Site Admin' => t('Site Admin'), - 'Bookmarks' => t('Bookmarks'), - 'Address Book' => t('Address Book'), - 'Login' => t('Login'), - 'Channel Manager' => t('Channel Manager'), - 'Grid' => t('Grid'), - 'Settings' => t('Settings'), - 'Files' => t('Files'), - 'Webpages' => t('Webpages'), - 'Channel Home' => t('Channel Home'), - 'Profile' => t('Profile'), - 'Photos' => t('Photos'), - 'Events' => t('Events'), - 'Directory' => t('Directory'), - 'Help' => t('Help'), - 'Mail' => t('Mail'), - 'Mood' => t('Mood'), - 'Poke' => t('Poke'), - 'Chat' => t('Chat'), - 'Search' => t('Search'), - 'Probe' => t('Probe'), - 'Suggest' => t('Suggest'), - 'Random Channel' => t('Random Channel'), - 'Invite' => t('Invite'), - 'Features' => t('Features'), - 'Language' => t('Language'), - 'Post' => t('Post'), - 'Profile Photo' => t('Profile Photo') - ); - - if(array_key_exists($arr['name'],$apps)) - $arr['name'] = $apps[$arr['name']]; - -} - - -// papp is a portable app - -function app_render($papp,$mode = 'view') { - - /** - * modes: - * view: normal mode for viewing an app via bbcode from a conversation or page - * provides install/update button if you're logged in locally - * list: normal mode for viewing an app on the app page - * no buttons are shown - * edit: viewing the app page in editing mode provides a delete button - */ - - $installed = false; - - if(! $papp) - return; - - if(! $papp['photo']) - $papp['photo'] = z_root() . '/' . get_default_profile_photo(80); - - - - $papp['papp'] = papp_encode($papp); - - if(! strstr($papp['url'],'://')) - $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; - - foreach($papp as $k => $v) { - if(strpos($v,'http') === 0 && $k != 'papp') - $papp[$k] = zid($v); - if($k === 'desc') - $papp['desc'] = str_replace(array('\'','"'),array(''','&dquot;'),$papp['desc']); - - if($k === 'requires') { - $requires = explode(',',$v); - foreach($requires as $require) { - $require = trim(strtolower($require)); - switch($require) { - case 'nologin': - if(local_channel()) - return ''; - break; - case 'admin': - if(! is_site_admin()) - return ''; - break; - case 'local_channel': - if(! local_channel()) - return ''; - break; - case 'public_profile': - if(! is_public_profile()) - return ''; - break; - case 'observer': - $observer = App::get_observer(); - if(! $observer) - return ''; - break; - default: - if(! (local_channel() && feature_enabled(local_channel(),$require))) - return ''; - break; - - } - } - - } - } - - $hosturl = ''; - - if(local_channel()) { - $installed = app_installed(local_channel(),$papp); - $hosturl = z_root() . '/'; - } - elseif(remote_channel()) { - $observer = App::get_observer(); - if($observer && $observer['xchan_network'] === 'zot') { - // some folks might have xchan_url redirected offsite, use the connurl - $x = parse_url($observer['xchan_connurl']); - if($x) { - $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; - } - } - } - - $install_action = (($installed) ? t('Update') : t('Install')); - - return replace_macros(get_markup_template('app.tpl'),array( - '$app' => $papp, - '$hosturl' => $hosturl, - '$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''), - '$install' => (($hosturl && $mode == 'view') ? $install_action : ''), - '$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''), - '$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : '') - )); -} - - -function app_install($uid,$app) { - $app['uid'] = $uid; - if(app_installed($uid,$app)) - $x = app_update($app); - else - $x = app_store($app); - - if($x['success']) { - $r = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($x['app_id']), - intval($uid) - ); - if($r) - build_sync_packet($uid,array('app' => $r[0])); - - return $x['app_id']; - } - return false; -} - -function app_destroy($uid,$app) { - - - if($uid && $app['guid']) { - - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($app['guid']), - intval($uid) - ); - $x[0]['app_deleted'] = 1; - - - $r = q("delete from app where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) - ); - - build_sync_packet($uid,array('app' => $x)); - } -} - - -function app_installed($uid,$app) { - - $r = q("select id from app where app_id = '%s' and app_version = '%s' and app_channel = %d limit 1", - dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''), - dbesc((array_key_exists('version',$app)) ? $app['version'] : ''), - intval($uid) - ); - return(($r) ? true : false); - -} - - -function app_list($uid) { - $r = q("select * from app where app_channel = %d order by app_name asc", - intval($uid) - ); - if($r) { - for($x = 0; $x < count($r); $x ++) { - $r[$x]['type'] = 'personal'; - } - } - return($r); -} - - -function app_decode($s) { - $x = base64_decode(str_replace(array('<br />',"\r","\n",' '),array('','','',''),$s)); - return json_decode($x,true); -} - - -function app_store($arr) { - - // logger('app_store: ' . print_r($arr,true)); - - $darray = array(); - $ret = array('success' => false); - - $darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : ''); - $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); - - if((! $darray['app_url']) || (! $darray['app_channel'])) - return $ret; - - if($arr['photo'] && ! strstr($arr['photo'],z_root())) { - $x = import_xchan_photo($arr['photo'],get_observer_hash(),true); - $arr['photo'] = $x[1]; - } - - - $darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : random_string(). '.' . App::get_hostname()); - $darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); - $darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash()); - $darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown')); - $darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : ''); - $darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); - $darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : ''); - $darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : ''); - $darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : ''); - $darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : ''); - $darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : ''); - - $created = datetime_convert(); - - $r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", - dbesc($darray['app_id']), - dbesc($darray['app_sig']), - dbesc($darray['app_author']), - dbesc($darray['app_name']), - dbesc($darray['app_desc']), - dbesc($darray['app_url']), - dbesc($darray['app_photo']), - dbesc($darray['app_version']), - intval($darray['app_channel']), - dbesc($darray['app_addr']), - dbesc($darray['app_price']), - dbesc($darray['app_page']), - dbesc($darray['app_requires']), - dbesc($created), - dbesc($created) - ); - if($r) { - $ret['success'] = true; - $ret['app_id'] = $darray['app_id']; - } - return $ret; -} - - -function app_update($arr) { - - $darray = array(); - $ret = array('success' => false); - - $darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : ''); - $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); - $darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : 0); - - if((! $darray['app_url']) || (! $darray['app_channel']) || (! $darray['app_id'])) - return $ret; - - if($arr['photo'] && ! strstr($arr['photo'],z_root())) { - $x = import_xchan_photo($arr['photo'],get_observer_hash(),true); - $arr['photo'] = $x[1]; - } - - $darray['app_sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); - $darray['app_author'] = ((x($arr,'author')) ? $arr['author'] : get_observer_hash()); - $darray['app_name'] = ((x($arr,'name')) ? escape_tags($arr['name']) : t('Unknown')); - $darray['app_desc'] = ((x($arr,'desc')) ? escape_tags($arr['desc']) : ''); - $darray['app_photo'] = ((x($arr,'photo')) ? $arr['photo'] : z_root() . '/' . get_default_profile_photo(80)); - $darray['app_version'] = ((x($arr,'version')) ? escape_tags($arr['version']) : ''); - $darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : ''); - $darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : ''); - $darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : ''); - $darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : ''); - - $edited = datetime_convert(); - - $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s' where app_id = '%s' and app_channel = %d", - dbesc($darray['app_sig']), - dbesc($darray['app_author']), - dbesc($darray['app_name']), - dbesc($darray['app_desc']), - dbesc($darray['app_url']), - dbesc($darray['app_photo']), - dbesc($darray['app_version']), - dbesc($darray['app_addr']), - dbesc($darray['app_price']), - dbesc($darray['app_page']), - dbesc($darray['app_requires']), - dbesc($edited), - dbesc($darray['app_id']), - intval($darray['app_channel']) - ); - if($r) { - $ret['success'] = true; - $ret['app_id'] = $darray['app_id']; - } - - return $ret; - -} - - -function app_encode($app,$embed = false) { - - $ret = array(); - - $ret['type'] = 'personal'; - - if($app['app_id']) - $ret['guid'] = $app['app_id']; - - if($app['app_id']) - $ret['guid'] = $app['app_id']; - - if($app['app_sig']) - $ret['sig'] = $app['app_sig']; - - if($app['app_author']) - $ret['author'] = $app['app_author']; - - if($app['app_name']) - $ret['name'] = $app['app_name']; - - if($app['app_desc']) - $ret['desc'] = $app['app_desc']; - - if($app['app_url']) - $ret['url'] = $app['app_url']; - - if($app['app_photo']) - $ret['photo'] = $app['app_photo']; - - if($app['app_version']) - $ret['version'] = $app['app_version']; - - if($app['app_addr']) - $ret['addr'] = $app['app_addr']; - - if($app['app_price']) - $ret['price'] = $app['app_price']; - - if($app['app_page']) - $ret['page'] = $app['app_page']; - - if($app['app_requires']) - $ret['requires'] = $app['app_requires']; - - if(! $embed) - return $ret; - - $j = json_encode($ret); - return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; - -} - - -function papp_encode($papp) { - return chunk_split(base64_encode(json_encode($papp)),72,"\n"); - -} - - diff --git a/include/attach.php b/include/attach.php index ae4681994..b3ddfee88 100644 --- a/include/attach.php +++ b/include/attach.php @@ -423,6 +423,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $observer = array(); + $dosync = ((array_key_exists('nosync',$arr) && $arr['nosync']) ? 0 : 1); + if($observer_hash) { $x = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($observer_hash) @@ -616,7 +618,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { ); if($r) { $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files'); - if($overwrite) { + if(($overwrite) || ($options === 'import')) { $options = 'replace'; $existing_id = $x[0]['id']; $existing_size = intval($x[0]['filesize']); @@ -720,7 +722,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { $edited = $created; if($options === 'replace') { - $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, data = '%s', edited = '%s' where id = %d and uid = %d", + $r = q("update attach set filename = '%s', filetype = '%s', folder = '%s', filesize = %d, os_storage = %d, is_photo = %d, content = '%s', edited = '%s' where id = %d and uid = %d", dbesc($filename), dbesc($mimetype), dbesc($folder_hash), @@ -734,7 +736,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { ); } elseif($options === 'revise') { - $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) + $r = q("insert into attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($x[0]['aid']), intval($channel_id), @@ -775,7 +777,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { } else { - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, data, created, edited, allow_cid, allow_gid,deny_cid, deny_gid ) + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, folder, filesize, revision, os_storage, is_photo, content, created, edited, allow_cid, allow_gid,deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($channel['channel_account_id']), intval($channel_id), @@ -800,7 +802,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($is_photo) { - $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => basename($pathname), 'os_path' => $os_basepath . $os_relpath, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct); + $args = array( 'source' => $source, 'visible' => $visible, 'resource_id' => $hash, 'album' => basename($pathname), 'os_path' => $os_basepath . $os_relpath, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct, 'options' => $options ); if($arr['contact_allow']) $args['contact_allow'] = $arr['contact_allow']; if($arr['group_allow']) @@ -829,6 +831,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { if($arr['description']) $args['description'] = $arr['description']; + $args['deliver'] = $dosync; + $p = photo_upload($channel,$observer,$args); if($p['success']) { $ret['body'] = $p['body']; @@ -865,10 +869,12 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) { call_hooks('photo_upload_end',$ret); } - $sync = attach_export_data($channel,$hash); + if($dosync) { + $sync = attach_export_data($channel,$hash); - if($sync) - build_sync_packet($channel['channel_id'],array('file' => array($sync))); + if($sync) + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + } return $ret; } @@ -1032,7 +1038,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) { $created = datetime_convert(); - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, os_storage, is_dir, content, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($channel['channel_account_id']), intval($channel_id), @@ -1275,16 +1281,16 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { // delete a file from filesystem if(intval($r[0]['os_storage'])) { - $y = q("SELECT data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + $y = q("SELECT content FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", dbesc($resource), intval($channel_id) ); if($y) { - if(strpos($y[0]['data'],'store') === false) - $f = 'store/' . $channel_address . '/' . $y[0]['data']; + if(strpos($y[0]['content'],'store') === false) + $f = 'store/' . $channel_address . '/' . $y[0]['content']; else - $f = $y[0]['data']; + $f = $y[0]['content']; if(is_dir($f)) @rmdir($f); @@ -1462,7 +1468,7 @@ function find_filename_by_hash($channel_id, $attachHash) { function pipe_streams($in, $out) { $size = 0; while (!feof($in)) - $size += fwrite($out, fread($in, 8192)); + $size += fwrite($out, fread($in, 16384)); return $size; } @@ -1585,13 +1591,13 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $arr['deny_gid'] = perms2str($u_arr_deny_gid); $arr['item_private'] = $private; $arr['verb'] = ACTIVITY_UPDATE; - $arr['object'] = $u_jsonobject; + $arr['obj'] = $u_jsonobject; $arr['body'] = ''; $post = item_store($arr); $item_id = $post['item_id']; if($item_id) { - proc_run('php',"include/notifier.php","activity",$item_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$item_id)); } call_hooks('post_local_end', $arr); @@ -1620,14 +1626,14 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $arr['deny_gid'] = perms2str($arr_deny_gid); $arr['item_private'] = $private; $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); - $arr['object'] = (($update) ? $u_jsonobject : $jsonobject); + $arr['obj'] = (($update) ? $u_jsonobject : $jsonobject); $arr['body'] = ''; $post = item_store($arr); $item_id = $post['item_id']; if($item_id) { - proc_run('php',"include/notifier.php","activity",$item_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$item_id)); } call_hooks('post_local_end', $arr); @@ -1854,21 +1860,19 @@ function attach_export_data($channel, $resource_id, $deleted = false) { } while($hash_ptr); - - $paths = array_reverse($paths); $ret['attach'] = $paths; if($attach_ptr['is_photo']) { - $r = q("select * from photo where resource_id = '%s' and uid = %d order by scale asc", + $r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", dbesc($resource_id), intval($channel['channel_id']) ); if($r) { for($x = 0; $x < count($r); $x ++) { - $r[$x]['data'] = base64_encode($r[$x]['data']); + $r[$x]['content'] = base64_encode($r[$x]['content']); } $ret['photo'] = $r; } @@ -1905,4 +1909,4 @@ function get_attach_binname($s) { $p = substr($p,strpos($p,'/')+1); } return $p; -}
\ No newline at end of file +} diff --git a/include/auth.php b/include/auth.php index 9643da8eb..f3592cee3 100644 --- a/include/auth.php +++ b/include/auth.php @@ -16,55 +16,97 @@ require_once('include/security.php'); /** * @brief Verify login credentials. * - * If system <i>authlog</i> is set a log entry will be added for failed login + * If system.authlog is set a log entry will be added for failed login * attempts. * - * @param string $email - * The email address to verify. + * @param string $login + * The login to verify (channel address, account email or guest login token). * @param string $pass * The provided password to verify. * @return array|null * Returns account record on success, null on failure. + * The return array is dependent on the login mechanism. + * $ret['account'] will be set if either an email or channel address validation was successful (local login). + * $ret['channel'] will be set if a channel address validation was successful. + * $ret['xchan'] will be set if a guest access token validation was successful. + * Keys will exist for invalid return arrays but will be set to null. + * This function does not perform a login. It merely validates systems passwords and tokens. + * */ -function account_verify_password($email, $pass) { + +function account_verify_password($login, $pass) { + + $ret = [ 'account' => null, 'channel' => null, 'xchan' => null ]; $email_verify = get_config('system', 'verify_email'); $register_policy = get_config('system', 'register_policy'); + if(! $login) + return null; + + $account = null; + $channel = null; + $xchan = null; + + if(! strpos($login,'@')) { + $channel = channelx_by_nick($login); + if(! $channel) { + $x = q("select * from atoken where atoken_name = '%s' and atoken_token = '%s' limit 1", + dbesc($login), + dbesc($pass) + ); + if($x) { + $ret['xchan'] = atoken_xchan($x[0]); + return $ret; + } + } + } + if($channel) { + $where = " where account_id = " . intval($channel['channel_account_id']) . " "; + } + else { + $where = " where account_email = '" . dbesc($login) . "' "; + } + + $a = q("select * from account $where"); + if(! $a) { + return null; + } + + $account = $a[0]; + // Currently we only verify email address if there is an open registration policy. // This isn't because of any policy - it's because the workflow gets too complicated if // you have to verify the email and then go through the account approval workflow before // letting them login. - if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($record['account_flags'] & ACCOUNT_UNVERIFIED)) - return null; - - $r = q("select * from account where account_email = '%s'", - dbesc($email) - ); - if(! ($r && count($r))) + if(($email_verify) && ($register_policy == REGISTER_OPEN) && ($account['account_flags'] & ACCOUNT_UNVERIFIED)) { + logger('email verification required for ' . $login); return null; + } - foreach($r as $record) { - if(($record['account_flags'] == ACCOUNT_OK) - && (hash('whirlpool', $record['account_salt'] . $pass) === $record['account_password'])) { - logger('password verified for ' . $email); - return $record; - } + if(($account['account_flags'] == ACCOUNT_OK) + && (hash('whirlpool',$account['account_salt'] . $pass) === $account['account_password'])) { + logger('password verified for ' . $login); + $ret['account'] = $account; + if($channel) + $ret['channel'] = $channel; + return $ret; } - $error = 'password failed for ' . $email; + + $error = 'password failed for ' . $login; logger($error); - if($record['account_flags'] & ACCOUNT_UNVERIFIED) - logger('Account is unverified. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_BLOCKED) - logger('Account is blocked. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_EXPIRED) - logger('Account is expired. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_REMOVED) - logger('Account is removed. account_flags = ' . $record['account_flags']); - if($record['account_flags'] & ACCOUNT_PENDING) - logger('Account is pending. account_flags = ' . $record['account_flags']); + if($account['account_flags'] & ACCOUNT_UNVERIFIED) + logger('Account is unverified. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_BLOCKED) + logger('Account is blocked. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_EXPIRED) + logger('Account is expired. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_REMOVED) + logger('Account is removed. account_flags = ' . $account['account_flags']); + if($account['account_flags'] & ACCOUNT_PENDING) + logger('Account is pending. account_flags = ' . $account['account_flags']); log_failed_login($error); @@ -101,7 +143,7 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && // process logout request $args = array('channel_id' => local_channel()); call_hooks('logging_out', $args); - \Zotlabs\Web\Session::nuke(); + App::$session->nuke(); info( t('Logged out.') . EOL); goaway(z_root()); } @@ -117,16 +159,24 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && intval(ACCOUNT_ROLE_ADMIN) ); if($x) { - \Zotlabs\Web\Session::new_cookie(60 * 60 * 24); // one day + App::$session->new_cookie(60 * 60 * 24); // one day $_SESSION['last_login_date'] = datetime_convert(); unset($_SESSION['visitor_id']); // no longer a visitor - authenticate_success($x[0], true, true); + authenticate_success($x[0], null, true, true); } } - - $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1", - dbesc($_SESSION['visitor_id']) - ); + if(array_key_exists('atoken',$_SESSION)) { + $y = q("select * from atoken where atoken_id = %d limit 1", + intval($_SESSION['atoken']) + ); + if($y) + $r = array(atoken_xchan($y[0])); + } + else { + $r = q("select * from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_hash = '%s' limit 1", + dbesc($_SESSION['visitor_id']) + ); + } if($r) { App::set_observer($r[0]); } @@ -141,7 +191,7 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && if(x($_SESSION, 'uid') || x($_SESSION, 'account_id')) { - Zotlabs\Web\Session::return_check(); + App::$session->return_check(); $r = q("select * from account where account_id = %d limit 1", intval($_SESSION['account_id']) @@ -155,14 +205,15 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && } if(strcmp(datetime_convert('UTC','UTC','now - 12 hours'), $_SESSION['last_login_date']) > 0 ) { $_SESSION['last_login_date'] = datetime_convert(); - Zotlabs\Web\Session::extend_cookie(); + App::$session->extend_cookie(); $login_refresh = true; } - authenticate_success($r[0], false, false, false, $login_refresh); + $ch = (($_SESSION['uid']) ? channelx_by_n($_SESSION['uid']) : null); + authenticate_success($r[0], null, $ch, false, false, $login_refresh); } else { $_SESSION['account_id'] = 0; - \Zotlabs\Web\Session::nuke(); + App::$session->nuke(); goaway(z_root()); } } // end logged in user returning @@ -170,7 +221,7 @@ if((isset($_SESSION)) && (x($_SESSION, 'authenticated')) && else { if(isset($_SESSION)) { - \Zotlabs\Web\Session::nuke(); + App::$session->nuke(); } // handle a fresh login request @@ -199,30 +250,38 @@ else { call_hooks('authenticate', $addon_auth); + $atoken = null; + $account = null; + if(($addon_auth['authenticated']) && (count($addon_auth['user_record']))) { - $record = $addon_auth['user_record']; + $account = $addon_auth['user_record']; } else { - $record = App::$account = account_verify_password($_POST['username'], $_POST['password']); + $verify = account_verify_password($_POST['username'], $_POST['password']); + if($verify) { + $atoken = $verify['xchan']; + $channel = $verify['channel']; + $account = App::$account = $verify['account']; + } if(App::$account) { $_SESSION['account_id'] = App::$account['account_id']; } + elseif($atoken) { + atoken_login($atoken); + } else { notice( t('Failed authentication') . EOL); } - - logger('authenticate: ' . print_r(App::$account, true), LOGGER_ALL); } - if((! $record) || (! count($record))) { + if(! ($account || $atoken)) { $error = 'authenticate: failed login attempt: ' . notags(trim($_POST['username'])) . ' from IP ' . $_SERVER['REMOTE_ADDR']; logger($error); // Also log failed logins to a separate auth log to reduce overhead for server side intrusion prevention $authlog = get_config('system', 'authlog'); if ($authlog) @file_put_contents($authlog, datetime_convert() . ':' . session_id() . ' ' . $error . "\n", FILE_APPEND); - notice( t('Login failed.') . EOL ); goaway(z_root() . '/login'); } @@ -242,17 +301,18 @@ else { if($_POST['remember_me']) { $_SESSION['remember_me'] = 1; - \Zotlabs\Web\Session::new_cookie(31449600); // one year + App::$session->new_cookie(31449600); // one year } else { $_SESSION['remember_me'] = 0; - \Zotlabs\Web\Session::new_cookie(0); // 0 means delete on browser exit + App::$session->new_cookie(0); // 0 means delete on browser exit } // if we haven't failed up this point, log them in. $_SESSION['last_login_date'] = datetime_convert(); - authenticate_success($record, true, true); + if(! $atoken) + authenticate_success($account,$channel,true, true); } } @@ -270,6 +330,7 @@ else { * @return int|bool * Return channel_id from pconfig or false. */ + function match_openid($authid) { // Query the uid/channel_id from pconfig for a given value. $r = q("SELECT uid FROM pconfig WHERE cat = 'system' AND k = 'openid' AND v = '%s' LIMIT 1", diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index 1ed57bfd4..16f67dc4a 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -270,7 +270,14 @@ function bb2dmention_callback($match) { function bb2diaspora_itemwallwall(&$item) { + // We will provide wallwall (embedded author on the Diaspora side) if + // 1. It is a wall-to-wall post + // 2. A comment arrived which has no Diaspora signature info + + + $wallwall = false; $author_exists = true; + if(! array_key_exists('author',$item)) { $author_exists = false; logger('bb2diaspora_itemwallwall: no author'); @@ -281,15 +288,25 @@ function bb2diaspora_itemwallwall(&$item) { $item['author'] = $r[0]; } - if(($item['mid'] == $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (is_array($item['author']))) { - logger('bb2diaspora_itemwallwall: author: ' . print_r($item['author'],true), LOGGER_DATA); + $has_meta = false; + if($item['diaspora_meta'] || get_iconfig($item,'diaspora','fields')) + $has_meta = true; + + if($item['author_xchan'] != $item['owner_xchan']) { + if($item['mid'] == $item['parent_mid']) + $wallwall = true; + else { + if(! $has_meta) { + $wallwall = true; + } + } } - if(($item['mid'] == $item['parent_mid']) && ($item['author_xchan'] != $item['owner_xchan']) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_m']) { + if(($wallwall) && (is_array($item['author'])) && $item['author']['xchan_url'] && $item['author']['xchan_name'] && $item['author']['xchan_photo_s']) { logger('bb2diaspora_itemwallwall: wall to wall post',LOGGER_DEBUG); // post will come across with the owner's identity. Throw a preamble onto the post to indicate the true author. $item['body'] = "\n\n" - . '[img]' . $item['author']['xchan_photo_m'] . '[/img]' + . '[img]' . $item['author']['xchan_photo_s'] . '[/img]' . '[url=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/url]' . "\n\n" . $item['body']; } @@ -301,7 +318,12 @@ function bb2diaspora_itemwallwall(&$item) { } -function bb2diaspora_itembody($item, $force_update = false) { +function bb2diaspora_itembody($item, $force_update = false, $have_channel = false) { + + + if(! get_iconfig($item,'diaspora','fields')) { + $force_update = true; + } $matches = array(); @@ -339,8 +361,8 @@ function bb2diaspora_itembody($item, $force_update = false) { } } - - bb2diaspora_itemwallwall($newitem); + if(! $have_channel) + bb2diaspora_itemwallwall($newitem); $title = $newitem['title']; $body = preg_replace('/\#\^http/i', 'http', $newitem['body']); @@ -457,8 +479,6 @@ function unescape_underscores_in_links($m) { function format_event_diaspora($ev) { - $a = get_app(); - if(! ((is_array($ev)) && count($ev))) return ''; diff --git a/include/bbcode.php b/include/bbcode.php index 78a2759c1..7f7be4300 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -165,11 +165,10 @@ function bb_parse_crypt($match) { } function bb_parse_app($match) { - require_once('include/apps.php'); - $app = app_decode($match[1]); + $app = Zotlabs\Lib\Apps::app_decode($match[1]); if ($app) - return app_render($app); + return Zotlabs\Lib\Apps::app_render($app); } function bb_parse_element($match) { @@ -243,6 +242,13 @@ function bb_ShareAttributes($match) { if ($matches[1] != "") $message_id = $matches[1]; + if(! $message_id) { + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $message_id = $matches[1]; + } + + $reldate = '<span class="autotime" title="' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'c') . '" >' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'r') . '</span>'; $headline = '<div class="shared_container"> <div class="shared_header">'; @@ -276,7 +282,6 @@ function bb_location($match) { * @return string HTML iframe with content of $match[1] */ function bb_iframe($match) { - $a = get_app(); $sandbox = ((strpos($match[1], App::get_hostname())) ? ' sandbox="allow-scripts" ' : ''); @@ -330,8 +335,61 @@ function bb_map_location($match) { } function bb_opentag($match) { + $openclose = (($match[2]) ? '<span class="bb-open" title="' . t('Click to open/close') . '">' . $match[1] . '</span>' : t('Click to open/close')); + $text = (($match[2]) ? $match[2] : $match[1]); + $rnd = mt_rand(); + + return '<div onclick="openClose(\'opendiv-' . $rnd . '\'); return false;" class="fakelink">' . $openclose . '</div><div id="opendiv-' . $rnd . '" style="display: none;">' . $text . '</div>'; +} + +function bb_spoilertag($match) { + $openclose = (($match[2]) ? '<span class="bb-spoiler" title="' . t('Click to open/close') . '">' . $match[1] . ' ' . t('spoiler') . '</span>' : t('Click to open/close')); + $text = (($match[2]) ? $match[2] : $match[1]); $rnd = mt_rand(); - return "<br /><div onclick=\"openClose('opendiv-" . $rnd . "');return false;\" class=\"fakelink\">" . $match[1] . "</div><div id=\"opendiv-" . $rnd . "\" style=\"display: none;\">" . $match[2] . "</div>"; + + return '<div onclick="openClose(\'opendiv-' . $rnd . '\'); return false;" class="fakelink">' . $openclose . '</div><blockquote id="opendiv-' . $rnd . '" style="display: none;">' . $text . '</blockquote>'; +} + +function bb_definitionList($match) { + // $match[1] is the markup styles for the "terms" in the definition list. + // $match[2] is the content between the [dl]...[/dl] tags + + $classes = ''; + if (stripos($match[1], "b") !== false) $classes .= 'dl-terms-bold '; + if (stripos($match[1], "i") !== false) $classes .= 'dl-terms-italic '; + if (stripos($match[1], "u") !== false) $classes .= 'dl-terms-underline '; + if (stripos($match[1], "l") !== false) $classes .= 'dl-terms-large '; + if (stripos($match[1], "m") !== false) $classes .= 'dl-terms-monospace '; + if (stripos($match[1], "h") !== false) $classes .= 'dl-horizontal '; // dl-horizontal is already provided by bootstrap + if (strlen($classes) === 0) $classes = "dl-terms-plain"; + + // The bbcode transformation will be: + // [*=term-text] description-text => </dd> <dt>term-text<dt><dd> description-text + // then after all replacements have been made, the extra </dd> at the start of the + // first line can be removed. HTML5 allows the tag to be missing from the end of the last line. + // Using '(?<!\\\)' to allow backslash-escaped closing braces to appear in the term-text. + $closeDescriptionTag = "</dd>\n"; + $eatLeadingSpaces = '(?: |[ \t])*'; // prevent spaces infront of [*= from adding another line to the previous element + $listElements = preg_replace('/^(\n|<br \/>)/', '', $match[2]); // ltrim the first newline + $listElements = preg_replace( + '/' . $eatLeadingSpaces . '\[\*=([[:print:]]*?)(?<!\\\)\]/ism', + $closeDescriptionTag . '<dt>$1</dt><dd>', + $listElements + ); + // Unescape any \] inside the <dt> tags + $listElements = preg_replace_callback('/<dt>(.*?)<\/dt>/ism', 'bb_definitionList_unescapeBraces', $listElements); + + // Remove the extra </dd> at the start of the string, if there is one. + $firstOpenTag = strpos($listElements, '<dd>'); + $firstCloseTag = strpos($listElements, $closeDescriptionTag); + if ($firstCloseTag !== false && ($firstOpenTag === false || ($firstCloseTag < $firstOpenTag))) { + $listElements = preg_replace( '/<\/dd>/ism', '', $listElements, 1); + } + + return '<dl class="bb-dl ' . rtrim($classes) . '">' . $listElements . '</dl>';; +} +function bb_definitionList_unescapeBraces($match) { + return '<dt>' . str_replace('\]', ']', $match[1]) . '</dt>'; } /** @@ -397,8 +455,6 @@ function bb_sanitize_style($input) { function bb_observer($Text) { - $a = get_app(); - $observer = App::get_observer(); if ((strpos($Text,'[/observer]') !== false) || (strpos($Text,'[/rpost]') !== false)) { @@ -428,9 +484,30 @@ function bb_observer($Text) { return $Text; } +function bb_code($match) { + if(strpos($match[0], "<br />")) + return '<code>' . trim($match[1]) . '</code>'; + else + return '<code class="inline-code">' . trim($match[1]) . '</code>'; +} +function bb_highlight($match) { + if(in_array(strtolower($match[1]),['php','css','mysql','sql','abap','diff','html','perl','ruby', + 'vbscript','avrc','dtd','java','xml','cpp','python','javascript','js','json','sh'])) + return text_highlight($match[2],strtolower($match[1])); + return $match[0]; +} +function bb_fixtable_lf($match) { + // remove extraneous whitespace between table element tags since newlines will all + // be converted to '<br />' and turn your neatly crafted tables into a whole lot of + // empty space. + + $x = preg_replace("/\]\s+\[/",'][',$match[1]); + return '[table]' . $x . '[/table]'; + +} @@ -439,18 +516,6 @@ function bb_observer($Text) { function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) { - $a = get_app(); - - // Move all spaces out of the tags - // ....Uhm why? - // This is basically doing a trim() on the stuff in between tags, but it messes up - // carefully crafted bbcode and especially other pre-formatted code. - // Commenting out until we come up with a use case where it's needed. Then let's try and - // special case rather than a heavy-handed approach like this. - -// $Text = preg_replace("/\[(\w*)\](\s*)/ism", '$2[$1]', $Text); -// $Text = preg_replace("/(\s*)\[\/(\w*)\]/ism", '[/$2]$1', $Text); - // Hide all [noparse] contained bbtags by spacefying them if (strpos($Text,'[noparse]') !== false) { $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); @@ -519,6 +584,15 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = str_replace(">", ">", $Text); + // Check for [code] text here, before the linefeeds are messed with. + // The highlighter will unescape and re-escape the content. + + if (strpos($Text,'[code=') !== false) { + $Text = preg_replace_callback("/\[code=(.*?)\](.*?)\[\/code\]/ism", 'bb_highlight', $Text); + } + + $Text = preg_replace_callback("/\[table\](.*?)\[\/table\]/ism",'bb_fixtable_lf',$Text); + // Convert new line chars to html <br /> tags // nlbr seems to be hopelessly messed up @@ -576,7 +650,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } if($tryoembed) { if (strpos($Text,'[/url]') !== false) { - $Text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism", 'tryoembed', $Text); + $Text = preg_replace_callback("/[^\^]\[url\]([$URLSearchString]*)\[\/url\]/ism", 'tryoembed', $Text); } } if (strpos($Text,'[/url]') !== false) { @@ -702,6 +776,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || + ((strpos($Text, "[/dl]") !== false) && (strpos($Text, "[dl") !== false)) || ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) { $Text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $Text); $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>', $Text); @@ -713,6 +788,13 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[ul\](.*?)\[\/ul\]/ism", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>', $Text); $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>', $Text); $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>', $Text); + + // [dl] tags have an optional [dl terms="bi"] form where bold/italic/underline/mono/large + // etc. style may be specified for the "terms" in the definition list. The quotation marks + // are also optional. The regex looks intimidating, but breaks down as: + // "[dl" <optional-whitespace> <optional-termStyles> "]" <matchGroup2> "[/dl]" + // where optional-termStyles are: "terms=" <optional-quote> <matchGroup1> <optional-quote> + $Text = preg_replace_callback('/\[dl[[:space:]]*(?:terms=(?:"|")?([a-zA-Z]+)(?:"|")?)?\](.*?)\[\/dl\]/ism', 'bb_definitionList', $Text); } if (strpos($Text,'[th]') !== false) { $Text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>', $Text); @@ -740,35 +822,30 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm", "<span style=\"font-family: $1;\">$2</span>", $Text); } - // Declare the format for [code] layout - $CodeLayout = '<code>$1</code>'; - // Check for [code] text if (strpos($Text,'[code]') !== false) { - $Text = preg_replace("/\[code\](.*?)\[\/code\]/ism", "$CodeLayout", $Text); + $Text = preg_replace_callback("/\[code\](.*?)\[\/code\]/ism", 'bb_code', $Text); } - // Declare the format for [spoiler] layout - $SpoilerLayout = '<blockquote class="spoiler">$1</blockquote>'; - // Check for [spoiler] text - // handle nested quotes $endlessloop = 0; - while ((strpos($Text, "[/spoiler]") !== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) - $Text = preg_replace("/\[spoiler\](.*?)\[\/spoiler\]/ism", "$SpoilerLayout", $Text); + while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler]") !== false) and (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[spoiler\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + } // Check for [spoiler=Author] text - - $t_wrote = t('$1 spoiler'); - - // handle nested quotes $endlessloop = 0; - while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler=") !== false) and (++$endlessloop < 20)) - $Text = preg_replace("/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism", - "<br /><strong class=".'"spoiler"'.">" . $t_wrote . "</strong><blockquote class=".'"spoiler"'.">$2</blockquote>", - $Text); + while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler=") !== false) and (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[spoiler=(.*?)\](.*?)\[\/spoiler\]/ism", 'bb_spoilertag', $Text); + } + // Check for [open] text + $endlessloop = 0; + while ((strpos($Text, "[/open]")!== false) and (strpos($Text, "[open]") !== false) and (++$endlessloop < 20)) { + $Text = preg_replace_callback("/\[open\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); + } + // Check for [open=Title] text $endlessloop = 0; while ((strpos($Text, "[/open]")!== false) and (strpos($Text, "[open=") !== false) and (++$endlessloop < 20)) { $Text = preg_replace_callback("/\[open=(.*?)\](.*?)\[\/open\]/ism", 'bb_opentag', $Text); @@ -792,7 +869,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $endlessloop = 0; while ((strpos($Text, "[/quote]")!== false) and (strpos($Text, "[quote=") !== false) and (++$endlessloop < 20)) $Text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", - "<br /><strong class=".'"author"'.">" . $t_wrote . "</strong><blockquote>$2</blockquote>", + "<span class=".'"bb-quote"'.">" . $t_wrote . "</span><blockquote>$2</blockquote>", $Text); // Images @@ -916,37 +993,6 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } } - // Youtube extensions -// if (strpos($Text,'[youtube]') !== false) { -// if ($tryoembed) { -// $Text = preg_replace_callback("/\[youtube\](https?:\/\/www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); -// $Text = preg_replace_callback("/\[youtube\](www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); -// $Text = preg_replace_callback("/\[youtube\](https?:\/\/youtu.be\/.*?)\[\/youtube\]/ism", 'tryoembed', $Text); -// } -// $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $Text); -// $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $Text); -// $Text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $Text); - -// if ($tryoembed) -// $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . App::$videowidth . '" height="' . App::$videoheight . '" src="http://www.youtube.com/embed/$1" frameborder="0"></iframe>', $Text); -// else -// $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", "http://www.youtube.com/watch?v=$1", $Text); -// } -// if (strpos($Text,'[vimeo]') !== false) { -// if ($tryoembed) { -// $Text = preg_replace_callback("/\[vimeo\](https?:\/\/player.vimeo.com\/video\/[0-9]+).*?\[\/vimeo\]/ism", 'tryoembed', $Text); -// $Text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism", 'tryoembed', $Text); -// } - -// $Text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $Text); -// $Text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $Text); - -// if ($tryoembed) -// $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . App::$videowidth . '" height="' . App::$videoheight . '" src="http://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $Text); -// else -// $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", "http://vimeo.com/$1", $Text); -// } - // oembed tag $Text = oembed_bbcode2html($Text); @@ -969,6 +1015,7 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) $Text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism",'',$Text); $Text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism",'',$Text); $Text = preg_replace("/\[event\-finish\](.*?)\[\/event\-finish\]/ism",'',$Text); + $Text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism",'',$Text); $Text = preg_replace("/\[event\-location\](.*?)\[\/event\-location\]/ism",'',$Text); $Text = preg_replace("/\[event\-adjust\](.*?)\[\/event\-adjust\]/ism",'',$Text); diff --git a/include/cache.php b/include/cache.php deleted file mode 100644 index 4a3f453e1..000000000 --- a/include/cache.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php /** @file */ - - /** - * cache api - */ - - class Cache { - public static function get($key){ - $r = q("SELECT v FROM cache WHERE k = '%s' limit 1", - dbesc($key) - ); - - if ($r) - return $r[0]['v']; - return null; - } - - public static function set($key,$value) { - - $r = q("SELECT * FROM cache WHERE k = '%s' limit 1", - dbesc($key) - ); - if($r) { - q("UPDATE cache SET v = '%s', updated = '%s' WHERE k = '%s'", - dbesc($value), - dbesc(datetime_convert()), - dbesc($key)); - } - else { - q("INSERT INTO cache ( k, v, updated) VALUES ('%s','%s','%s')", - dbesc($key), - dbesc($value), - dbesc(datetime_convert())); - } - } - - - public static function clear(){ - q("DELETE FROM cache WHERE updated < '%s'", - dbesc(datetime_convert('UTC','UTC',"now - 30 days"))); - } - - } - diff --git a/include/identity.php b/include/channel.php index 1c899048a..a5233743d 100644 --- a/include/identity.php +++ b/include/channel.php @@ -1,11 +1,12 @@ <?php /** - * @file include/identity.php + * @file include/channel.php */ require_once('include/zot.php'); require_once('include/crypto.php'); require_once('include/menu.php'); +require_once('include/perm_upgrade.php'); /** * @brief Called when creating a new channel. @@ -16,7 +17,7 @@ require_once('include/menu.php'); * @param int $account_id * Account_id used for this request * - * @returns assoziative array with: + * @returns associative array with: * * \e boolean \b success boolean true if creating a new channel is allowed for this account * * \e string \b message (optional) if success is false, optional error text * * \e int \b total_identities @@ -225,42 +226,26 @@ function create_identity($arr) { if(array_key_exists('publish', $arr)) $publish = intval($arr['publish']); - $primary = true; - - if(array_key_exists('primary', $arr)) - $primary = intval($arr['primary']); - $role_permissions = null; - $global_perms = get_perms(); if(array_key_exists('permissions_role',$arr) && $arr['permissions_role']) { - $role_permissions = get_role_perms($arr['permissions_role']); - - if($role_permissions) { - foreach($role_permissions as $p => $v) { - if(strpos($p,'channel_') !== false) { - $perms_keys .= ', ' . $p; - $perms_vals .= ', ' . intval($v); - } - if($p === 'directory_publish') - $publish = intval($v); - } - } - } - else { - $defperms = site_default_perms(); - foreach($defperms as $p => $v) { - $perms_keys .= ', ' . $global_perms[$p][0]; - $perms_vals .= ', ' . intval($v); - } + $role_permissions = \Zotlabs\Access\PermissionRoles::role_perms($arr['permissions_role']); } + if($role_permissions && array_key_exists('directory_publish',$role_permissions)) + $publish = intval($role_permissions['directory_publish']); + + $primary = true; + + if(array_key_exists('primary', $arr)) + $primary = intval($arr['primary']); + $expire = 0; $r = q("insert into channel ( channel_account_id, channel_primary, channel_name, channel_address, channel_guid, channel_guid_sig, - channel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_system, channel_expire_days, channel_timezone $perms_keys ) - values ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s' $perms_vals ) ", + channel_hash, channel_prvkey, channel_pubkey, channel_pageflags, channel_system, channel_expire_days, channel_timezone ) + values ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s' ) ", intval($arr['account_id']), intval($primary), @@ -288,6 +273,17 @@ function create_identity($arr) { return $ret; } + if($role_permissions && array_key_exists('limits',$role_permissions)) + $perm_limits = $role_permissions['limits']; + else + $perm_limits = site_default_perms(); + + foreach($perm_limits as $p => $v) + \Zotlabs\Access\PermissionLimits::Set($r[0]['channel_id'],$p,$v); + + if($role_permissions && array_key_exists('perms_auto',$role_permissions)) + set_pconfig($r[0]['channel_id'],'system','autoperms',intval($role_permissions['perms_auto'])); + $ret['channel'] = $r[0]; if(intval($arr['account_id'])) @@ -337,7 +333,7 @@ function create_identity($arr) { // Not checking return value. // It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate - $r = q("INSERT INTO profile ( aid, uid, profile_guid, profile_name, is_default, publish, name, photo, thumb) + $r = q("INSERT INTO profile ( aid, uid, profile_guid, profile_name, is_default, publish, fullname, photo, thumb) VALUES ( %d, %d, '%s', '%s', %d, %d, '%s', '%s', '%s') ", intval($ret['channel']['channel_account_id']), intval($newuid), @@ -351,25 +347,29 @@ function create_identity($arr) { ); if($role_permissions) { - $myperms = ((array_key_exists('perms_accept',$role_permissions)) ? intval($role_permissions['perms_accept']) : 0); + $myperms = ((array_key_exists('perms_connect',$role_permissions)) ? $role_permissions['perms_connect'] : array()); + } + else { + $x = \Zotlabs\Access\PermissionRoles::role_perms('social'); + $myperms = $x['perms_connect']; } - else - $myperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_self, abook_my_perms ) - values ( %d, %d, '%s', %d, '%s', '%s', %d, %d ) ", + $r = q("insert into abook ( abook_account, abook_channel, abook_xchan, abook_closeness, abook_created, abook_updated, abook_self ) + values ( %d, %d, '%s', %d, '%s', '%s', %d ) ", intval($ret['channel']['channel_account_id']), intval($newuid), dbesc($hash), intval(0), dbesc(datetime_convert()), dbesc(datetime_convert()), - intval(1), - intval($myperms) + intval(1) ); + $x = \Zotlabs\Access\Permissions::FilledPerms($myperms); + foreach($x as $k => $v) { + set_abconfig($newuid,$hash,'my_perms',$k,$v); + } + if(intval($ret['channel']['channel_account_id'])) { // Save our permissions role so we can perhaps call it up and modify it later. @@ -378,8 +378,21 @@ function create_identity($arr) { set_pconfig($newuid,'system','permissions_role',$arr['permissions_role']); if(array_key_exists('online',$role_permissions)) set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online'])); - if(array_key_exists('perms_auto',$role_permissions)) - set_pconfig($newuid,'system','autoperms',(($role_permissions['perms_auto']) ? $role_permissions['perms_accept'] : 0)); + if(array_key_exists('perms_auto',$role_permissions)) { + $autoperms = intval($role_permissions['perms_auto']); + set_pconfig($newuid,'system','autoperms',$autoperms); + if($autoperms) { + $x = \Zotlabs\Access\Permissions::FilledPerms($role_permissions['perms_connect']); + foreach($x as $k => $v) { + set_pconfig($newuid,'autoperms',$k,$v); + } + } + else { + $r = q("delete from pconfig where uid = %d and cat = 'autoperms'", + intval($newuid) + ); + } + } } // Create a group with yourself as a member. This allows somebody to use it @@ -392,7 +405,7 @@ function create_identity($arr) { // if our role_permissions indicate that we're using a default collection ACL, add it. if(is_array($role_permissions) && $role_permissions['default_collection']) { - $r = q("select hash from groups where uid = %d and name = '%s' limit 1", + $r = q("select hash from groups where uid = %d and gname = '%s' limit 1", intval($newuid), dbesc( t('Friends') ) ); @@ -436,7 +449,7 @@ function create_identity($arr) { call_hooks('create_identity', $newuid); - proc_run('php','include/directory.php', $ret['channel']['channel_id']); + Zotlabs\Daemon\Master::Summon(array('Directory', $ret['channel']['channel_id'])); } $ret['success'] = true; @@ -491,13 +504,16 @@ function identity_basic_export($channel_id, $items = false) { // use constants here as otherwise we will have no idea if we can import from a site // with a non-standard platform and version. - $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Project\System::get_server_role()); + $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => STD_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Lib\System::get_server_role()); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) ); - if($r) - $ret['channel'] = $r[0]; + if($r) { + translate_channel_perms_outbound($r[0]); + $ret['channel'] = $r[0]; + $ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()]; + } $r = q("select * from profile where uid = %d", intval($channel_id) @@ -514,9 +530,10 @@ function identity_basic_export($channel_id, $items = false) { for($x = 0; $x < count($ret['abook']); $x ++) { $xchans[] = $ret['abook'][$x]['abook_chan']; - $abconfig = load_abconfig($ret['channel']['channel_hash'],$ret['abook'][$x]['abook_xchan']); + $abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']); if($abconfig) $ret['abook'][$x]['abconfig'] = $abconfig; + translate_abook_perms_outbound($ret['abook'][$x]); } stringify_array_elms($xchans); } @@ -550,18 +567,18 @@ function identity_basic_export($channel_id, $items = false) { if($r) $ret['config'] = $r; - $r = q("select type, data, os_storage from photo where scale = 4 and photo_usage = %d and uid = %d limit 1", + $r = q("select mimetype, content, os_storage from photo where imgscale = 4 and photo_usage = %d and uid = %d limit 1", intval(PHOTO_PROFILE), intval($channel_id) ); if($r) { - $ret['photo'] = array('type' => $r[0]['type'], 'data' => (($r[0]['os_storage']) ? base64url_encode(file_get_contents($r[0]['data'])) : base64url_encode($r[0]['data']))); + $ret['photo'] = array('type' => $r[0]['mimetype'], 'data' => (($r[0]['os_storage']) ? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content']))); } // All other term types will be included in items, if requested. - $r = q("select * from term where type in (%d,%d) and uid = %d", + $r = q("select * from term where ttype in (%d,%d) and uid = %d", intval(TERM_SAVEDSEARCH), intval(TERM_THING), intval($channel_id) @@ -580,11 +597,18 @@ function identity_basic_export($channel_id, $items = false) { if($r) $ret['obj'] = $r; - $r = q("select * from app where app_channel = %d", + $r = q("select * from app where app_channel = %d and app_system = 0", intval($channel_id) ); - if($r) + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($r[$x]['id']) + ); + } $ret['app'] = $r; + } $r = q("select * from chatroom where cr_uid = %d", intval($channel_id) @@ -669,14 +693,6 @@ function identity_basic_export($channel_id, $items = false) { $ret['mail'] = $m; } - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d", - intval($channel_id) - ); - - if($r) - $ret['item_id'] = $r; - - //$key = get_config('system','prvkey'); /** @warning this may run into memory limits on smaller systems */ @@ -718,6 +734,10 @@ function identity_export_year($channel_id,$year,$month = 0) { $ret = array(); + $ch = channelx_by_n($channel_id); + if($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } $mindate = datetime_convert('UTC','UTC',$year . '-' . $target_month . '-01 00:00:00'); if($month && $month < 12) $maxdate = datetime_convert('UTC','UTC',$year . '-' . $target_month_plus . '-01 00:00:00'); @@ -739,16 +759,43 @@ function identity_export_year($channel_id,$year,$month = 0) { $ret['item'][] = encode_item($rr,true); } - $r = q("select item_id.*, item.mid from item_id left join item on item_id.iid = item.id where item_id.uid = %d - and item.created >= '%s' and item.created < '%s' order by created ", + return $ret; +} + +// export items within an arbitrary date range. Date/time is in UTC. + +function channel_export_items($channel_id,$start,$finish) { + + if(! $start) + return array(); + else + $start = datetime_convert('UTC','UTC',$start); + + $finish = datetime_convert('UTC','UTC',(($finish) ? $finish : 'now')); + if($finish < $start) + return array(); + + $ret = array(); + + $ch = channelx_by_n($channel_id); + if($ch) { + $ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()]; + } + + $r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created < '%s' and resource_type = '' order by created", + intval(ITEM_TYPE_POST), intval($channel_id), - dbesc($mindate), - dbesc($maxdate) + dbesc($start), + dbesc($finish) ); - if($r) - $ret['item_id'] = $r; - + if($r) { + $ret['item'] = array(); + xchan_query($r); + $r = fetch_post_tags($r,true); + foreach($r as $rr) + $ret['item'][] = encode_item($rr,true); + } return $ret; } @@ -767,11 +814,10 @@ function identity_export_year($channel_id,$year,$month = 0) { * * The channel default theme is also selected for use, unless over-riden elsewhere. * - * @param[in,out] App &$a * @param string $nickname * @param string $profile */ -function profile_load(&$a, $nickname, $profile = '') { +function profile_load($nickname, $profile = '') { // logger('profile_load: ' . $nickname . (($profile) ? ' profile: ' . $profile : '')); @@ -840,7 +886,7 @@ function profile_load(&$a, $nickname, $profile = '') { $extra_fields = array(); - require_once('include/identity.php'); + require_once('include/channel.php'); $profile_fields_basic = get_profile_fields_basic(); $profile_fields_advanced = get_profile_fields_advanced(); @@ -868,7 +914,7 @@ function profile_load(&$a, $nickname, $profile = '') { ); if($z) { $p[0]['picdate'] = $z[0]['xchan_photo_date']; - $p[0]['reddress'] = str_replace('@','@',$z[0]['xchan_addr']); + $p[0]['reddress'] = str_replace('@','@',$z[0]['xchan_addr']); } // fetch user tags if this isn't the default profile @@ -997,8 +1043,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa call_hooks('profile_sidebar_enter', $profile); - require_once('include/Contact.php'); - if($show_connect) { // This will return an empty string if we're already connected. @@ -1041,6 +1085,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa $diaspora = array( 'podloc' => z_root(), + 'guid' => $profile['channel_guid'] . str_replace('.','',App::get_hostname()), 'searchable' => (($block) ? 'false' : 'true'), 'nickname' => $profile['channel_address'], 'fullname' => $profile['channel_name'], @@ -1063,8 +1108,8 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa } $menublock = get_pconfig($profile['uid'],'system','channel_menublock'); if ($menublock && (! $block)) { - require_once('include/comanche.php'); - $channel_menu .= comanche_block($menublock); + $comanche = new Zotlabs\Render\Comanche(); + $channel_menu .= $comanche->block($menublock); } if($zcard) @@ -1103,156 +1148,12 @@ function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = fa } -/** - * @FIXME or remove - */ - function get_birthdays() { - - $o = ''; - - if(! local_channel()) - return $o; - - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 - $bd_short = t('F d'); - - $r = q("SELECT `event`.*, `event`.`id` AS `eid`, `contact`.* FROM `event` - LEFT JOIN `contact` ON `contact`.`id` = `event`.`cid` - WHERE `event`.`uid` = %d AND `type` = 'birthday' AND `start` < '%s' AND `finish` > '%s' - ORDER BY `start` ASC ", - intval(local_channel()), - dbesc(datetime_convert('UTC','UTC','now + 6 days')), - dbesc(datetime_convert('UTC','UTC','now')) - ); - - if($r && count($r)) { - $total = 0; - $now = strtotime('now'); - $cids = array(); - - $istoday = false; - foreach($r as $rr) { - if(strlen($rr['name'])) - $total ++; - if((strtotime($rr['start'] . ' +00:00') < $now) && (strtotime($rr['finish'] . ' +00:00') > $now)) - $istoday = true; - } - $classtoday = $istoday ? ' birthday-today ' : ''; - if($total) { - foreach($r as &$rr) { - if(! strlen($rr['name'])) - continue; - - // avoid duplicates - - if(in_array($rr['cid'],$cids)) - continue; - $cids[] = $rr['cid']; - - $today = (((strtotime($rr['start'] . ' +00:00') < $now) && (strtotime($rr['finish'] . ' +00:00') > $now)) ? true : false); - $sparkle = ''; - $url = $rr['url']; - if($rr['network'] === NETWORK_DFRN) { - $sparkle = " sparkle"; - $url = z_root() . '/redir/' . $rr['cid']; - } - - $rr['link'] = $url; - $rr['title'] = $rr['name']; - $rr['date'] = day_translate(datetime_convert('UTC', App::$timezone, $rr['start'], $rr['adjust'] ? $bd_format : $bd_short)) . (($today) ? ' ' . t('[today]') : ''); - $rr['startime'] = Null; - $rr['today'] = $today; - } - } - } - $tpl = get_markup_template("birthdays_reminder.tpl"); - return replace_macros($tpl, array( - '$baseurl' => z_root(), - '$classtoday' => $classtoday, - '$count' => $total, - '$event_reminders' => t('Birthday Reminders'), - '$event_title' => t('Birthdays this week:'), - '$events' => $r, - '$lbr' => '{', // raw brackets mess up if/endif macro processing - '$rbr' => '}' - )); - } - - -/** - * @FIXME - */ - function get_events() { - - require_once('include/bbcode.php'); - - if(! local_channel()) - return $o; - - $bd_format = t('g A l F d') ; // 8 AM Friday January 18 - $bd_short = t('F d'); - - $r = q("SELECT `event`.* FROM `event` - WHERE `event`.`uid` = %d AND `type` != 'birthday' AND `start` < '%s' AND `start` > '%s' - ORDER BY `start` ASC ", - intval(local_channel()), - dbesc(datetime_convert('UTC','UTC','now + 6 days')), - dbesc(datetime_convert('UTC','UTC','now - 1 days')) - ); - - if($r && count($r)) { - $now = strtotime('now'); - $istoday = false; - foreach($r as $rr) { - if(strlen($rr['name'])) - $total ++; - - $strt = datetime_convert('UTC',$rr['convert'] ? App::$timezone : 'UTC',$rr['start'],'Y-m-d'); - if($strt === datetime_convert('UTC',App::$timezone,'now','Y-m-d')) - $istoday = true; - } - $classtoday = (($istoday) ? 'event-today' : ''); - - foreach($r as &$rr) { - if($rr['adjust']) - $md = datetime_convert('UTC',App::$timezone,$rr['start'],'Y/m'); - else - $md = datetime_convert('UTC','UTC',$rr['start'],'Y/m'); - $md .= "/#link-".$rr['id']; - - $title = substr(strip_tags(bbcode($rr['desc'])),0,32) . '... '; - if(! $title) - $title = t('[No description]'); - - $strt = datetime_convert('UTC',$rr['convert'] ? App::$timezone : 'UTC',$rr['start']); - $today = ((substr($strt,0,10) === datetime_convert('UTC',App::$timezone,'now','Y-m-d')) ? true : false); - - $rr['link'] = $md; - $rr['title'] = $title; - $rr['date'] = day_translate(datetime_convert('UTC', $rr['adjust'] ? App::$timezone : 'UTC', $rr['start'], $bd_format)) . (($today) ? ' ' . t('[today]') : ''); - $rr['startime'] = $strt; - $rr['today'] = $today; - } - } - - $tpl = get_markup_template("events_reminder.tpl"); - return replace_macros($tpl, array( - '$baseurl' => z_root(), - '$classtoday' => $classtoday, - '$count' => count($r), - '$event_reminders' => t('Event Reminders'), - '$event_title' => t('Events this week:'), - '$events' => $r, - )); - } - - function advanced_profile(&$a) { require_once('include/text.php'); if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile')) return ''; - if(App::$profile['name']) { + if(App::$profile['fullname']) { $profile_fields_basic = get_profile_fields_basic(); $profile_fields_advanced = get_profile_fields_advanced(); @@ -1276,7 +1177,7 @@ function advanced_profile(&$a) { $profile = array(); - $profile['fullname'] = array( t('Full Name:'), App::$profile['name'] ) ; + $profile['fullname'] = array( t('Full Name:'), App::$profile['fullname'] ) ; if(App::$profile['gender']) $profile['gender'] = array( t('Gender:'), App::$profile['gender'] ); @@ -1324,8 +1225,8 @@ function advanced_profile(&$a) { if(App::$profile['marital']) $profile['marital'] = array( t('Status:'), App::$profile['marital']); - if(App::$profile['with']) - $profile['marital']['with'] = bbcode(App::$profile['with']); + if(App::$profile['partner']) + $profile['marital']['partner'] = bbcode(App::$profile['partner']); if(strlen(App::$profile['howlong']) && App::$profile['howlong'] !== NULL_DATE) { $profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s')); @@ -1365,7 +1266,7 @@ function advanced_profile(&$a) { if($txt = prepare_text(App::$profile['romance'])) $profile['romance'] = array( t('Love/Romance:'), $txt); - if($txt = prepare_text(App::$profile['work'])) $profile['work'] = array( t('Work/employment:'), $txt); + if($txt = prepare_text(App::$profile['employment'])) $profile['employment'] = array( t('Work/employment:'), $txt); if($txt = prepare_text(App::$profile['education'])) $profile['education'] = array( t('School/education:'), $txt ); @@ -1382,6 +1283,7 @@ function advanced_profile(&$a) { $things = get_things(App::$profile['profile_guid'],App::$profile['profile_uid']); + // logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA); return replace_macros($tpl, array( @@ -1423,16 +1325,15 @@ function get_my_address() { * If somebody arrives at our site using a zid, add their xchan to our DB if we don't have it already. * And if they aren't already authenticated here, attempt reverse magic auth. * - * @param App &$a * * @hooks 'zid_init' * string 'zid' - their zid * string 'url' - the destination url */ -function zid_init(&$a) { +function zid_init() { $tmp_str = get_my_address(); if(validate_email($tmp_str)) { - proc_run('php','include/gprobe.php',bin2hex($tmp_str)); + Zotlabs\Daemon\Master::Summon(array('Gprobe',bin2hex($tmp_str))); $arr = array('zid' => $tmp_str, 'url' => App::$cmd); call_hooks('zid_init',$arr); if(! local_channel()) { @@ -1456,6 +1357,29 @@ function zid_init(&$a) { } /** + * @brief + * + * If somebody arrives at our site using a zat, authenticate them + * + */ + +function zat_init() { + if(local_channel() || remote_channel()) + return; + + $r = q("select * from atoken where atoken_token = '%s' limit 1", + dbesc($_REQUEST['zat']) + ); + if($r) { + $xchan = atoken_xchan($r[0]); + atoken_login($xchan); + } + +} + + + +/** * @brief Adds a zid parameter to a url. * * @param string $s @@ -1562,7 +1486,7 @@ function get_online_status($nick) { $ret = array('result' => false); - if(get_config('system','block_public') && ! local_channel() && ! remote_channel()) + if(observer_prohibited()) return $ret; $r = q("select channel_id, channel_hash from channel where channel_address = '%s' limit 1", @@ -1643,9 +1567,11 @@ function is_public_profile() { if(intval(get_config('system','block_public'))) return false; $channel = App::get_channel(); - if($channel && $channel['channel_r_profile'] == PERMS_PUBLIC) - return true; - + if($channel) { + $perm = \Zotlabs\Access\PermissionLimit::Get($channel['channel_id'],'view_profile'); + if($perm == PERMS_PUBLIC) + return true; + } return false; } @@ -1653,7 +1579,7 @@ function get_profile_fields_basic($filter = 0) { $profile_fields_basic = (($filter == 0) ? get_config('system','profile_fields_basic') : null); if(! $profile_fields_basic) - $profile_fields_basic = array('name','pdesc','chandesc','gender','dob','dob_tz','address','locality','region','postal_code','country_name','marital','sexual','homepage','hometown','keywords','about','contact'); + $profile_fields_basic = array('fullname','pdesc','chandesc','gender','dob','dob_tz','address','locality','region','postal_code','country_name','marital','sexual','homepage','hometown','keywords','about','contact'); $x = array(); if($profile_fields_basic) @@ -1668,7 +1594,7 @@ function get_profile_fields_advanced($filter = 0) { $basic = get_profile_fields_basic($filter); $profile_fields_advanced = (($filter == 0) ? get_config('system','profile_fields_advanced') : null); if(! $profile_fields_advanced) - $profile_fields_advanced = array('with','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','work','education'); + $profile_fields_advanced = array('partner','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','employment','education'); $x = array(); if($basic) @@ -1717,13 +1643,13 @@ function notifications_on($channel_id,$value) { function get_channel_default_perms($uid) { - $r = q("select abook_my_perms from abook where abook_channel = %d and abook_self = 1 limit 1", + $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 1 limit 1", intval($uid) ); if($r) - return $r[0]['abook_my_perms']; + return load_abconfig($uid,$r[0]['abook_xchan'],'my_perms'); - return 0; + return array(); } @@ -1780,7 +1706,7 @@ function auto_channel_create($account_id) { function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_1200) { - $r = q("select height, width, resource_id, type from photo where uid = %d and scale = %d and photo_usage = %d", + $r = q("select height, width, resource_id, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", intval($channel_id), intval($res), intval(PHOTO_COVER) @@ -1803,8 +1729,8 @@ function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_ default: $output = array( 'width' => $r[0]['width'], - 'height' => $r[0]['type'], - 'type' => $r[0]['type'], + 'height' => $r[0]['height'], + 'type' => $r[0]['mimetype'], 'url' => $url ); break; @@ -1829,19 +1755,19 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { $width = 425; $size = 'hz_small'; $cover_size = PHOTO_RES_COVER_425; - $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); } elseif($maxwidth <= 900) { $width = 900; $size = 'hz_medium'; $cover_size = PHOTO_RES_COVER_850; - $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); } elseif($maxwidth <= 1200) { $width = 1200; $size = 'hz_large'; $cover_size = PHOTO_RES_COVER_1200; - $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); } // $scale = (float) $maxwidth / $width; @@ -1851,7 +1777,7 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { $channel['channel_addr'] = $channel['channel_address'] . '@' . App::get_hostname(); $zcard = array('chan' => $channel); - $r = q("select height, width, resource_id, scale, type from photo where uid = %d and scale = %d and photo_usage = %d", + $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", intval($channel['channel_id']), intval($cover_size), intval(PHOTO_COVER) @@ -1859,7 +1785,7 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { if($r) { $cover = $r[0]; - $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['scale']; + $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; } else { $cover = $pphoto; @@ -1878,3 +1804,88 @@ function get_zcard($channel,$observer_hash = '',$args = array()) { return $o; } + + +function get_zcard_embed($channel,$observer_hash = '',$args = array()) { + + logger('get_zcard_embed'); + + $maxwidth = (($args['width']) ? intval($args['width']) : 0); + $maxheight = (($args['height']) ? intval($args['height']) : 0); + + + if(($maxwidth > 1200) || ($maxwidth < 1)) + $maxwidth = 1200; + + if($maxwidth <= 425) { + $width = 425; + $size = 'hz_small'; + $cover_size = PHOTO_RES_COVER_425; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); + } + elseif($maxwidth <= 900) { + $width = 900; + $size = 'hz_medium'; + $cover_size = PHOTO_RES_COVER_850; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); + } + elseif($maxwidth <= 1200) { + $width = 1200; + $size = 'hz_large'; + $cover_size = PHOTO_RES_COVER_1200; + $pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); + } + + $channel['channel_addr'] = $channel['channel_address'] . '@' . App::get_hostname(); + $zcard = array('chan' => $channel); + + $r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d", + intval($channel['channel_id']), + intval($cover_size), + intval(PHOTO_COVER) + ); + + if($r) { + $cover = $r[0]; + $cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; + } + else { + $cover = $pphoto; + } + + $o .= replace_macros(get_markup_template('zcard_embed.tpl'),array( + '$maxwidth' => $maxwidth, + '$scale' => $scale, + '$translate' => $translate, + '$size' => $size, + '$cover' => $cover, + '$pphoto' => $pphoto, + '$zcard' => $zcard + )); + + return $o; + +} + + +function channelx_by_nick($nick) { + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and channel_removed = 0 LIMIT 1", + dbesc($nick) + ); + return(($r) ? $r[0] : false); +} + +function channelx_by_hash($hash) { + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and channel_removed = 0 LIMIT 1", + dbesc($hash) + ); + return(($r) ? $r[0] : false); +} + +function channelx_by_n($id) { + $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and channel_removed = 0 LIMIT 1", + dbesc($id) + ); + return(($r) ? $r[0] : false); +} + diff --git a/include/chat.php b/include/chat.php deleted file mode 100644 index 604402045..000000000 --- a/include/chat.php +++ /dev/null @@ -1,262 +0,0 @@ -<?php -/** - * @file include/chat.php - * @brief Chat related functions. - */ - - -/** - * @brief Creates a chatroom. - * - * @param array $channel - * @param array $arr - * @return An associative array containing: - * - success: A boolean - * - message: (optional) A string - */ -function chatroom_create($channel, $arr) { - - $ret = array('success' => false); - - $name = trim($arr['name']); - if(! $name) { - $ret['message'] = t('Missing room name'); - return $ret; - } - - $r = q("select cr_id from chatroom where cr_uid = %d and cr_name = '%s' limit 1", - intval($channel['channel_id']), - dbesc($name) - ); - if($r) { - $ret['message'] = t('Duplicate room name'); - return $ret; - } - - $r = q("select count(cr_id) as total from chatroom where cr_aid = %d", - intval($channel['channel_account_id']) - ); - if($r) - $limit = service_class_fetch($channel['channel_id'], 'chatrooms'); - - if(($r) && ($limit !== false) && ($r[0]['total'] >= $limit)) { - $ret['message'] = upgrade_message(); - return $ret; - } - - if(! array_key_exists('expire', $arr)) - $arr['expire'] = 120; // minutes, e.g. 2 hours - - $created = datetime_convert(); - - $x = q("insert into chatroom ( cr_aid, cr_uid, cr_name, cr_created, cr_edited, cr_expire, allow_cid, allow_gid, deny_cid, deny_gid ) - values ( %d, %d , '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s' ) ", - intval($channel['channel_account_id']), - intval($channel['channel_id']), - dbesc($name), - dbesc($created), - dbesc($created), - intval($arr['expire']), - dbesc($arr['allow_cid']), - dbesc($arr['allow_gid']), - dbesc($arr['deny_cid']), - dbesc($arr['deny_gid']) - ); - - if($x) - $ret['success'] = true; - - return $ret; -} - - -function chatroom_destroy($channel,$arr) { - - $ret = array('success' => false); - - if(intval($arr['cr_id'])) - $sql_extra = " and cr_id = " . intval($arr['cr_id']) . " "; - elseif(trim($arr['cr_name'])) - $sql_extra = " and cr_name = '" . protect_sprintf(dbesc(trim($arr['cr_name']))) . "' "; - else { - $ret['message'] = t('Invalid room specifier.'); - return $ret; - } - - $r = q("select * from chatroom where cr_uid = %d $sql_extra limit 1", - intval($channel['channel_id']) - ); - if(! $r) { - $ret['message'] = t('Invalid room specifier.'); - return $ret; - } - - build_sync_packet($channel['channel_id'],array('chatroom' => $r)); - - q("delete from chatroom where cr_id = %d", - intval($r[0]['cr_id']) - ); - if($r[0]['cr_id']) { - q("delete from chatpresence where cp_room = %d", - intval($r[0]['cr_id']) - ); - q("delete from chat where chat_room = %d", - intval($r[0]['cr_id']) - ); - } - - $ret['success'] = true; - return $ret; -} - - -function chatroom_enter($observer_xchan, $room_id, $status, $client) { - - if(! $room_id || ! $observer_xchan) - return; - - $r = q("select * from chatroom where cr_id = %d limit 1", - intval($room_id) - ); - if(! $r) { - notice( t('Room not found.') . EOL); - return false; - } - require_once('include/security.php'); - $sql_extra = permissions_sql($r[0]['cr_uid']); - - $x = q("select * from chatroom where cr_id = %d and cr_uid = %d $sql_extra limit 1", - intval($room_id), - intval($r[0]['cr_uid']) - ); - if(! $x) { - notice( t('Permission denied.') . EOL); - return false; - } - - $limit = service_class_fetch($r[0]['cr_uid'], 'chatters_inroom'); - if($limit !== false) { - $y = q("select count(*) as total from chatpresence where cp_room = %d", - intval($room_id) - ); - if($y && $y[0]['total'] > $limit) { - notice( t('Room is full') . EOL); - return false; - } - } - - if(intval($x[0]['cr_expire'])) { - $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", - db_utcnow(), - db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ), - intval($x[0]['cr_id']) - ); - } - - $r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d limit 1", - dbesc($observer_xchan), - intval($room_id) - ); - if($r) { - q("update chatpresence set cp_last = '%s' where cp_id = %d and cp_client = '%s'", - dbesc(datetime_convert()), - intval($r[0]['cp_id']), - dbesc($client) - ); - return true; - } - - $r = q("insert into chatpresence ( cp_room, cp_xchan, cp_last, cp_status, cp_client ) - values ( %d, '%s', '%s', '%s', '%s' )", - intval($room_id), - dbesc($observer_xchan), - dbesc(datetime_convert()), - dbesc($status), - dbesc($client) - ); - - return $r; -} - - -function chatroom_leave($observer_xchan, $room_id, $client) { - if(! $room_id || ! $observer_xchan) - return; - - $r = q("select * from chatpresence where cp_xchan = '%s' and cp_room = %d and cp_client = '%s' limit 1", - dbesc($observer_xchan), - intval($room_id), - dbesc($client) - ); - if($r) { - q("delete from chatpresence where cp_id = %d", - intval($r[0]['cp_id']) - ); - } - - return true; -} - - -function chatroom_list($uid) { - require_once('include/security.php'); - $sql_extra = permissions_sql($uid); - - $r = q("select allow_cid, allow_gid, deny_cid, deny_gid, cr_name, cr_expire, cr_id, count(cp_id) as cr_inroom from chatroom left join chatpresence on cr_id = cp_room where cr_uid = %d $sql_extra group by cr_name, cr_id order by cr_name", - intval($uid) - ); - - return $r; -} - -function chatroom_list_count($uid) { - require_once('include/security.php'); - $sql_extra = permissions_sql($uid); - - $r = q("select count(*) as total from chatroom where cr_uid = %d $sql_extra", - intval($uid) - ); - - return $r[0]['total']; -} - -/** - * create a chat message via API. - * It is the caller's responsibility to enter the room. - */ - -function chat_message($uid, $room_id, $xchan, $text) { - - $ret = array('success' => false); - - if(! $text) - return; - - $sql_extra = permissions_sql($uid); - - $r = q("select * from chatroom where cr_uid = %d and cr_id = %d $sql_extra", - intval($uid), - intval($room_id) - ); - if(! $r) - return $ret; - - $arr = array( - 'chat_room' => $room_id, - 'chat_xchan' => $xchan, - 'chat_text' => $text - ); - - call_hooks('chat_message', $arr); - - $x = q("insert into chat ( chat_room, chat_xchan, created, chat_text ) - values( %d, '%s', '%s', '%s' )", - intval($room_id), - dbesc($xchan), - dbesc(datetime_convert()), - dbesc($arr['chat_text']) - ); - - $ret['success'] = true; - return $ret; -} diff --git a/include/checksites.php b/include/checksites.php deleted file mode 100644 index e9c08c202..000000000 --- a/include/checksites.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/hubloc.php'); - - - -function checksites_run($argv, $argc){ - - - cli_startup(); - $a = get_app(); - - logger('checksites: start'); - - if(($argc > 1) && ($argv[1])) - $site_id = $argv[1]; - - if($site_id) - $sql_options = " and site_url = '" . dbesc($argv[1]) . "' "; - - $days = intval(get_config('system','sitecheckdays')); - if($days < 1) - $days = 30; - - $r = q("select * from site where site_dead = 0 and site_update < %s - INTERVAL %s and site_type = %d $sql_options ", - db_utcnow(), db_quoteinterval($days . ' DAY'), - intval(SITE_TYPE_ZOT) - ); - - if(! $r) - return; - - foreach($r as $rr) { - if(! strcasecmp($rr['site_url'],z_root())) - continue; - - $x = ping_site($rr['site_url']); - if($x['success']) { - logger('checksites: ' . $rr['site_url']); - q("update site set site_update = '%s' where site_url = '%s' ", - dbesc(datetime_convert()), - dbesc($rr['site_url']) - ); - } - else { - logger('marking dead site: ' . $x['message']); - q("update site set site_dead = 1 where site_url = '%s' ", - dbesc($rr['site_url']) - ); - } - } - - return; -} - -if (array_search(__file__,get_included_files())===0){ - checksites_run($argv,$argc); - killme(); -} diff --git a/include/cli_startup.php b/include/cli_startup.php index a99164d4c..a4c1f629a 100644 --- a/include/cli_startup.php +++ b/include/cli_startup.php @@ -6,36 +6,7 @@ require_once('boot.php'); function cli_startup() { - global $a, $db, $default_timezone; - - if(is_null($a)) { - $a = new miniApp; - } - - App::init(); - - if(is_null($db)) { - @include(".htconfig.php"); - - $a->convert(); - - if(! defined('UNO')) - define('UNO', 0); - - App::$timezone = ((x($default_timezone)) ? $default_timezone : 'UTC'); - date_default_timezone_set(App::$timezone); - - require_once('include/dba/dba_driver.php'); - $db = dba_factory($db_host, $db_port, $db_user, $db_pass, $db_data, $db_type); - unset($db_host, $db_port, $db_user, $db_pass, $db_data, $db_type); - }; - - \Zotlabs\Web\Session::init(); - - load_config('system'); - + sys_boot(); App::set_baseurl(get_config('system','baseurl')); - load_hooks(); - }
\ No newline at end of file diff --git a/include/cli_suggest.php b/include/cli_suggest.php deleted file mode 100644 index 321ffd2e0..000000000 --- a/include/cli_suggest.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); -require_once('include/socgraph.php'); - - -function cli_suggest_run($argv, $argc){ - - cli_startup(); - - $a = get_app(); - - update_suggestions(); - -} - -if (array_search(__file__,get_included_files())===0){ - cli_suggest_run($argv,$argc); - killme(); -} - diff --git a/include/comanche.php b/include/comanche.php deleted file mode 100644 index 4d55aee19..000000000 --- a/include/comanche.php +++ /dev/null @@ -1,383 +0,0 @@ -<?php /** @file */ - -require_once('include/security.php'); -require_once('include/menu.php'); -require_once('include/widgets.php'); - -// When editing a webpage - a dropdown is needed to select a page layout -// On submit, the pdl_select value (which is the mid of an item with item_type = ITEM_TYPE_PDL) is stored in -// the webpage's resource_id, with resource_type 'pdl'. - -// Then when displaying a webpage, we can see if it has a pdl attached. If not we'll -// use the default site/page layout. - -// If it has a pdl we'll load it as we know the mid and pass the body through comanche_parser() which will generate the -// page layout from the given description - - -function pdl_selector($uid, $current="") { - $o = ''; - - $sql_extra = item_permissions_sql($uid); - - $r = q("select item_id.*, mid from item_id left join item on iid = item.id where item_id.uid = %d and item_id.uid = item.uid and service = 'PDL' $sql_extra order by sid asc", - intval($uid) - ); - - $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); - call_hooks('pdl_selector',$arr); - - $entries = $arr['entries']; - $current = $arr['current']; - - $o .= '<select name="pdl_select" id="pdl_select" size="1">'; - $entries[] = array('title' => t('Default'), 'mid' => ''); - foreach($entries as $selection) { - $selected = (($selection == $current) ? ' selected="selected" ' : ''); - $o .= "<option value=\"{$selection['mid']}\" $selected >{$selection['sid']}</option>"; - } - - $o .= '</select>'; - return $o; -} - - - -function comanche_parser(&$a, $s, $pass = 0) { - $matches = array(); - - $cnt = preg_match_all("/\[comment\](.*?)\[\/comment\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0], '', $s); - } - } - - if($pass == 0) { - $cnt = preg_match("/\[layout\](.*?)\[\/layout\]/ism", $s, $matches); - if($cnt) - App::$page['template'] = trim($matches[1]); - - $cnt = preg_match("/\[template=(.*?)\](.*?)\[\/template\]/ism", $s, $matches); - if($cnt) { - App::$page['template'] = trim($matches[2]); - App::$page['template_style'] = trim($matches[2]) . '_' . $matches[1]; - } - - $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $s, $matches); - if($cnt) { - App::$page['template'] = trim($matches[1]); - } - - $cnt = preg_match("/\[theme=(.*?)\](.*?)\[\/theme\]/ism", $s, $matches); - if($cnt) { - App::$layout['schema'] = trim($matches[1]); - App::$layout['theme'] = trim($matches[2]); - } - - $cnt = preg_match("/\[theme\](.*?)\[\/theme\]/ism", $s, $matches); - if($cnt) - App::$layout['theme'] = trim($matches[1]); - - $cnt = preg_match_all("/\[webpage\](.*?)\[\/webpage\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - // only the last webpage definition is used if there is more than one - foreach($matches as $mtch) { - App::$layout['webpage'] = comanche_webpage($a,$mtch[1]); - } - } - - } - else { - $cnt = preg_match_all("/\[region=(.*?)\](.*?)\[\/region\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - App::$layout['region_' . $mtch[1]] = comanche_region($a,$mtch[2]); - } - } - - } - -} - - -function comanche_menu($s, $class = '') { - - $channel_id = comanche_get_channel_id(); - $name = $s; - - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $var[$mtch[1]] = $mtch[2]; - $name = str_replace($mtch[0], '', $name); - } - } - - if($channel_id) { - $m = menu_fetch($name,$channel_id, get_observer_hash()); - return menu_render($m, $class, $edit = false, $var); - } -} - -function comanche_replace_region($match) { - $a = get_app(); - if (array_key_exists($match[1], App::$page)) { - return App::$page[$match[1]]; - } -} - -/** - * @brief Returns the channel_id of the profile owner of the page. - * - * Returns the channel_id of the profile owner of the page, or the local_channel - * if there is no profile owner. Otherwise returns 0. - * - * @return channel_id - */ -function comanche_get_channel_id() { - $channel_id = ((is_array(App::$profile)) ? App::$profile['profile_uid'] : 0); - - if ((! $channel_id) && (local_channel())) - $channel_id = local_channel(); - - return $channel_id; -} - -function comanche_block($s, $class = '') { - $var = array(); - $matches = array(); - $name = $s; - $class = (($class) ? $class : 'bblock widget'); - - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $var[$mtch[1]] = $mtch[2]; - $name = str_replace($mtch[0], '', $name); - } - } - - $o = ''; - $channel_id = comanche_get_channel_id(); - - if($channel_id) { - $r = q("select * from item inner join item_id on iid = item.id and item_id.uid = item.uid and item.uid = %d and service = 'BUILDBLOCK' and sid = '%s' limit 1", - intval($channel_id), - dbesc($name) - ); - - if($r) { - //check for eventual menus in the block and parse them - $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $r[0]['body'] = str_replace($mtch[0], comanche_menu(trim($mtch[1])), $r[0]['body']); - } - } - $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $r[0]['body'], $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $r[0]['body'] = str_replace($mtch[0],comanche_menu(trim($mtch[2]),$mtch[1]),$r[0]['body']); - } - } - - //emit the block - $o .= (($var['wrap'] == 'none') ? '' : '<div class="' . $class . '">'); - - if($r[0]['title'] && trim($r[0]['body']) != '$content') { - $o .= '<h3>' . $r[0]['title'] . '</h3>'; - } - - if(trim($r[0]['body']) === '$content') { - $o .= App::$page['content']; - } - else { - $o .= prepare_text($r[0]['body'], $r[0]['mimetype']); - } - - $o .= (($var['wrap'] == 'none') ? '' : '</div>'); - } - } - - return $o; -} - -function comanche_js($s) { - - switch($s) { - case 'jquery': - $path = 'view/js/jquery.js'; - break; - case 'bootstrap': - $path = 'library/bootstrap/js/bootstrap.min.js'; - break; - case 'foundation': - $path = 'library/foundation/js/foundation.min.js'; - $init = "\r\n" . '<script>$(document).ready(function() { $(document).foundation(); });</script>'; - break; - } - - $ret = '<script src="' . z_root() . '/' . $path . '" ></script>'; - if($init) - $ret .= $init; - - return $ret; - -} - -function comanche_css($s) { - - switch($s) { - case 'bootstrap': - $path = 'library/bootstrap/css/bootstrap.min.css'; - break; - case 'foundation': - $path = 'library/foundation/css/foundation.min.css'; - break; - } - - $ret = '<link rel="stylesheet" href="' . z_root() . '/' . $path . '" type="text/css" media="screen">'; - - return $ret; - -} - -// This doesn't really belong in Comanche, but it could also be argued that it is the perfect place. -// We need to be able to select what kind of template and decoration to use for the webpage at the heart of our content. -// For now we'll allow an '[authored]' element which defaults to name and date, or 'none' to remove these, and perhaps -// 'full' to provide a social network style profile photo. -// But leave it open to have richer templating options and perhaps ultimately discard this one, once we have a better idea -// of what template and webpage options we might desire. - -function comanche_webpage(&$a,$s) { - $ret = array(); - $matches = array(); - - $cnt = preg_match_all("/\[authored\](.*?)\[\/authored\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $ret['authored'] = $mtch[1]; - } - } - return $ret; -} - - -/** - * Widgets will have to get any operational arguments from the session, the - * global app environment, or config storage until we implement argument passing - * - * @param string $name - * @param string $text - */ -function comanche_widget($name, $text) { - $vars = array(); - $matches = array(); - - - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - $vars[$mtch[1]] = $mtch[2]; - } - } - - $func = 'widget_' . trim($name); - - if(! function_exists($func)) { - if(file_exists('widget/' . trim($name) . '.php')) - require_once('widget/' . trim($name) . '.php'); - elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) - require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); - } - else { - $theme_widget = $func . '.php'; - if((! function_exists($func)) && theme_include($theme_widget)) - require_once(theme_include($theme_widget)); - } - - if (function_exists($func)) - return $func($vars); -} - - -function comanche_region(&$a, $s) { - $matches = array(); - - $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0], comanche_menu(trim($mtch[1])), $s); - } - } - - // menu class e.g. [menu=horizontal]my_menu[/menu] or [menu=tabbed]my_menu[/menu] - // allows different menu renderings to be applied - - $cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_menu(trim($mtch[2]),$mtch[1]),$s); - } - } - $cnt = preg_match_all("/\[block\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_block(trim($mtch[1])),$s); - } - } - - $cnt = preg_match_all("/\[block=(.*?)\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_block(trim($mtch[2]),trim($mtch[1])),$s); - } - } - - $cnt = preg_match_all("/\[js\](.*?)\[\/js\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_js(trim($mtch[1])),$s); - } - } - - $cnt = preg_match_all("/\[css\](.*?)\[\/css\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_css(trim($mtch[1])),$s); - } - } - // need to modify this to accept parameters - - $cnt = preg_match_all("/\[widget=(.*?)\](.*?)\[\/widget\]/ism", $s, $matches, PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - $s = str_replace($mtch[0],comanche_widget(trim($mtch[1]),$mtch[2]),$s); - } - } - - return $s; -} - - -/* - * @function register_page_template($arr) - * Registers a page template/variant for use by Comanche selectors - * @param array $arr - * 'template' => template name - * 'variant' => array( - * 'name' => variant name - * 'desc' => text description - * 'regions' => array( - * 'name' => name - * 'desc' => text description - * ) - * ) - */ - - -function register_page_template($arr) { - App::$page_layouts[$arr['template']] = array($arr['variant']); - return; -} diff --git a/include/config.php b/include/config.php index 712b4ca11..8c0469392 100644 --- a/include/config.php +++ b/include/config.php @@ -1,17 +1,13 @@ <?php + /** * @file include/config.php * @brief Arbitrary configuration storage. * - * @note Please do not store booleans - convert to 0/1 integer values. - * The get_?config() functions return boolean false for keys that are unset, - * and this could lead to subtle bugs. - * - * Arrays get stored as serialize strings. - * - * @todo There are a few places in the code (such as the admin panel) where - * boolean configurations need to be fixed as of 10/08/2011. + * Arrays get stored as serialized strings. + * Booleans are stored as integer 0/1. * + * - <b>config</b> is used for hub specific configurations. It overrides the * configurations from .htconfig file. The storage is of size TEXT. * - <b>pconfig</b> is used for channel specific configurations and takes a @@ -34,594 +30,101 @@ * */ -/** - * @brief Loads the hub's configuration from database to a cached storage. - * - * Retrieve a category ($family) of config variables from database to a cached - * storage in the global App::$config[$family]. - * - * @param string $family - * The category of the configuration value - */ -function load_config($family) { - global $a; - if(! array_key_exists($family, App::$config)) - App::$config[$family] = array(); +use Zotlabs\Lib as Zlib; - if(! array_key_exists('config_loaded', App::$config[$family])) { - $r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family)); - if($r !== false) { - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - App::$config[$family][$k] = $rr['v']; - } - } - App::$config[$family]['config_loaded'] = true; - } - } +function load_config($family) { + Zlib\Config::Load($family); } -/** - * @brief Get a particular config variable given the category name ($family) - * and a key. - * - * Get a particular config variable from the given category ($family) and the - * $key from a cached storage in App::$config[$family]. If a key is found in the - * DB but does not exist in local config cache, pull it into the cache so we - * do not have to hit the DB again for this item. - * - * Returns false if not set. - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @return mixed Return value or false on error or if not set - */ function get_config($family, $key) { - global $a; - - if((! array_key_exists($family, App::$config)) || (! array_key_exists('config_loaded', App::$config[$family]))) - load_config($family); - - if(array_key_exists('config_loaded', App::$config[$family])) { - if(! array_key_exists($key, App::$config[$family])) { - return false; - } - return ((! is_array(App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$family][$key])) - ? unserialize(App::$config[$family][$key]) - : App::$config[$family][$key] - ); - } - return false; + return Zlib\Config::Get($family,$key); } -/** - * @brief Returns a value directly from the database configuration storage. - * - * This function queries directly the database and bypasses the chached storage - * from get_config($family, $key). - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @return mixed - */ -function get_config_from_storage($family, $key) { - $ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - return $ret; -} - -/** - * @brief Sets a configuration value for the hub. - * - * Stores a config value ($value) in the category ($family) under the key ($key). - * - * @note Please do not store booleans - convert to 0/1 integer values! - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param mixed $value - * The value to store in the configuration - * @return mixed - * Return the set value, or false if the database update failed - */ function set_config($family, $key, $value) { - global $a; - - // manage array value - $dbvalue = ((is_array($value)) ? serialize($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - if(get_config($family, $key) === false || (! get_config_from_storage($family, $key))) { - $ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - if($ret) { - App::$config[$family][$key] = $value; - $ret = $value; - } - return $ret; - } - - $ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s'", - dbesc($dbvalue), - dbesc($family), - dbesc($key) - ); - - if($ret) { - App::$config[$family][$key] = $value; - $ret = $value; - } - return $ret; + return Zlib\Config::Set($family,$key,$value); } -/** - * @brief Deletes the given key from the hub's configuration database. - * - * Removes the configured value from the stored cache in App::$config[$family] - * and removes it from the database. - * - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ function del_config($family, $key) { - global $a; - $ret = false; - - if(array_key_exists($family, App::$config) && array_key_exists($key, App::$config[$family])) - unset(App::$config[$family][$key]); - $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'", - dbesc($family), - dbesc($key) - ); - return $ret; + return Zlib\Config::Delete($family,$key); } - -/** - * @brief Loads all configuration values of a channel into a cached storage. - * - * All configuration values of the given channel are stored in global cache - * which is available under the global variable App::$config[$uid]. - * - * @param string $uid - * The channel_id - * @return void|false Nothing or false if $uid is false - */ function load_pconfig($uid) { - global $a; - - if($uid === false) - return false; - - if(! array_key_exists($uid, App::$config)) - App::$config[$uid] = array(); - - $r = q("SELECT * FROM pconfig WHERE uid = %d", - intval($uid) - ); - - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - $c = $rr['cat']; - if(! array_key_exists($c, App::$config[$uid])) { - App::$config[$uid][$c] = array(); - App::$config[$uid][$c]['config_loaded'] = true; - } - App::$config[$uid][$c][$k] = $rr['v']; - } - } + Zlib\PConfig::Load($uid); } -/** - * @brief Get a particular channel's config variable given the category name - * ($family) and a key. - * - * Get a particular channel's config value from the given category ($family) - * and the $key from a cached storage in App::$config[$uid]. - * - * Returns false if not set. - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @param boolean $instore (deprecated, without function) - * @return mixed Stored value or false if it does not exist - */ function get_pconfig($uid, $family, $key, $instore = false) { -// logger('include/config.php: get_pconfig() deprecated instore param used', LOGGER_DEBUG); - global $a; - - if($uid === false) - return false; - - if(! array_key_exists($uid, App::$config)) - load_pconfig($uid); - - if((! array_key_exists($family, App::$config[$uid])) || (! array_key_exists($key, App::$config[$uid][$family]))) - return false; - - return ((! is_array(App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$uid][$family][$key])) - ? unserialize(App::$config[$uid][$family][$key]) - : App::$config[$uid][$family][$key] - ); + return Zlib\PConfig::Get($uid,$family,$key,$instore = false); } -/** - * @brief Sets a configuration value for a channel. - * - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the channel_id $uid. - * - * @note Please do not store booleans - convert to 0/1 integer values! - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param string $value - * The value to store - * @return mixed Stored $value or false - */ function set_pconfig($uid, $family, $key, $value) { - global $a; - - // this catches subtle errors where this function has been called - // with local_channel() when not logged in (which returns false) - // and throws an error in array_key_exists below. - // we provide a function backtrace in the logs so that we can find - // and fix the calling function. - - if($uid === false) { - btlogger('UID is FALSE!', LOGGER_NORMAL, LOG_ERR); - return; - } - - // manage array value - $dbvalue = ((is_array($value)) ? serialize($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - if(get_pconfig($uid, $family, $key) === false) { - if(! array_key_exists($uid, App::$config)) - App::$config[$uid] = array(); - if(! array_key_exists($family, App::$config[$uid])) - App::$config[$uid][$family] = array(); - - // keep a separate copy for all variables which were - // set in the life of this page. We need this to - // synchronise channel clones. - - if(! array_key_exists('transient', App::$config[$uid])) - App::$config[$uid]['transient'] = array(); - if(! array_key_exists($family, App::$config[$uid]['transient'])) - App::$config[$uid]['transient'][$family] = array(); - - App::$config[$uid][$family][$key] = $value; - App::$config[$uid]['transient'][$family][$key] = $value; - - $ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ", - intval($uid), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - if($ret) - return $value; - - return $ret; - } - - $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'", - dbesc($dbvalue), - intval($uid), - dbesc($family), - dbesc($key) - ); - - // keep a separate copy for all variables which were - // set in the life of this page. We need this to - // synchronise channel clones. - - if(! array_key_exists('transient', App::$config[$uid])) - App::$config[$uid]['transient'] = array(); - if(! array_key_exists($family, App::$config[$uid]['transient'])) - App::$config[$uid]['transient'][$family] = array(); - - App::$config[$uid][$family][$key] = $value; - App::$config[$uid]['transient'][$family][$key] = $value; - - if($ret) - return $value; - - return $ret; + return Zlib\PConfig::Set($uid,$family,$key,$value); } -/** - * @brief Deletes the given key from the channel's configuration. - * - * Removes the configured value from the stored cache in App::$config[$uid] - * and removes it from the database. - * - * @param string $uid - * The channel_id - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ function del_pconfig($uid, $family, $key) { - global $a; - $ret = false; - - if (x(App::$config[$uid][$family], $key)) - unset(App::$config[$uid][$family][$key]); - $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", - intval($uid), - dbesc($family), - dbesc($key) - ); - - return $ret; + return Zlib\PConfig::Delete($uid,$family,$key); } - -/** - * @brief Loads a full xchan's configuration into a cached storage. - * - * All configuration values of the given observer hash are stored in global - * cache which is available under the global variable App::$config[$xchan]. - * - * @param string $xchan - * The observer's hash - * @return void|false Returns false if xchan is not set - */ function load_xconfig($xchan) { - global $a; - - if(! $xchan) - return false; - - if(! array_key_exists($xchan, App::$config)) - App::$config[$xchan] = array(); - - $r = q("SELECT * FROM xconfig WHERE xchan = '%s'", - dbesc($xchan) - ); - - if($r) { - foreach($r as $rr) { - $k = $rr['k']; - $c = $rr['cat']; - if(! array_key_exists($c, App::$config[$xchan])) { - App::$config[$xchan][$c] = array(); - App::$config[$xchan][$c]['config_loaded'] = true; - } - App::$config[$xchan][$c][$k] = $rr['v']; - } - } + Zlib\XConfig::Load($xchan); } -/** - * @brief Get a particular observer's config variable given the category - * name ($family) and a key. - * - * Get a particular observer's config value from the given category ($family) - * and the $key from a cached storage in App::$config[$xchan]. - * - * Returns false if not set. - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to query - * @return mixed Stored $value or false if it does not exist - */ function get_xconfig($xchan, $family, $key) { - global $a; - - if(! $xchan) - return false; - - if(! array_key_exists($xchan, App::$config)) - load_xconfig($xchan); - - if((! array_key_exists($family, App::$config[$xchan])) || (! array_key_exists($key, App::$config[$xchan][$family]))) - return false; - - return ((! is_array(App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', App::$config[$xchan][$family][$key])) - ? unserialize(App::$config[$xchan][$family][$key]) - : App::$config[$xchan][$family][$key] - ); + return Zlib\XConfig::Get($xchan,$family,$key); } -/** - * @brief Sets a configuration value for an observer. - * - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the observer's $xchan hash. - * - * @note Please do not store booleans - convert to 0/1 integer values! - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to set - * @param string $value - * The value to store - * @return mixed Stored $value or false - */ function set_xconfig($xchan, $family, $key, $value) { - global $a; - - // manage array value - $dbvalue = ((is_array($value)) ? serialize($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - if(get_xconfig($xchan, $family, $key) === false) { - if(! array_key_exists($xchan, App::$config)) - App::$config[$xchan] = array(); - if(! array_key_exists($family, App::$config[$xchan])) - App::$config[$xchan][$family] = array(); - - App::$config[$xchan][$family][$key] = $value; - $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ", - dbesc($xchan), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - if($ret) - return $value; - return $ret; - } - - $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s'", - dbesc($dbvalue), - dbesc($xchan), - dbesc($family), - dbesc($key) - ); - - App::$config[$xchan][$family][$key] = $value; - - if($ret) - return $value; - return $ret; + return Zlib\XConfig::Set($xchan,$family,$key,$value); } -/** - * @brief Deletes the given key from the observer's config. - * - * Removes the configured value from the stored cache in App::$config[$xchan] - * and removes it from the database. - * - * @param string $xchan - * The observer's hash - * @param string $family - * The category of the configuration value - * @param string $key - * The configuration key to delete - * @return mixed - */ function del_xconfig($xchan, $family, $key) { - global $a; - $ret = false; - - if(x(App::$config[$xchan][$family], $key)) - unset(App::$config[$xchan][$family][$key]); - $ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'", - dbesc($xchan), - dbesc($family), - dbesc($key) - ); - return $ret; + return Zlib\XConfig::Delete($xchan,$family,$key); } - -// account configuration storage is built on top of the under-utilised xconfig - function load_aconfig($account_id) { - load_xconfig('a_' . $account_id); + Zlib\AConfig::Load($account_id); } function get_aconfig($account_id, $family, $key) { - return get_xconfig('a_' . $account_id, $family, $key); + return Zlib\AConfig::Get($account_id, $family, $key); } function set_aconfig($account_id, $family, $key, $value) { - return set_xconfig('a_' . $account_id, $family, $key, $value); + return Zlib\AConfig::Set($account_id, $family, $key, $value); } function del_aconfig($account_id, $family, $key) { - return del_xconfig('a_' . $account_id, $family, $key); + return Zlib\AConfig::Delete($account_id, $family, $key); } - -function load_abconfig($chash,$xhash) { - $r = q("select * from abconfig where chan = '%s' and xchan = '%s'", - dbesc($chash), - dbesc($xhash) - ); - return $r; +function load_abconfig($chan, $xhash, $family = '') { + return Zlib\AbConfig::Load($chan,$xhash,$family); } -function get_abconfig($chash,$xhash,$family,$key) { - $r = q("select * from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' limit 1", - dbesc($chash), - dbesc($xhash), - dbesc($family), - dbesc($key) - ); - if($r) { - return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); - } - return false; +function get_abconfig($chan,$xhash,$family,$key) { + return Zlib\AbConfig::Get($chan,$xhash,$family,$key); } +function set_abconfig($chan,$xhash,$family,$key,$value) { + return Zlib\AbConfig::Set($chan,$xhash,$family,$key,$value); +} -function set_abconfig($chash,$xhash,$family,$key,$value) { - - $dbvalue = ((is_array($value)) ? serialize($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - if(get_abconfig($chash,$xhash,$family,$key) === false) { - $r = q("insert into abconfig ( chan, xchan, cat, k, v ) values ( '%s', '%s', '%s', '%s', '%s' ) ", - dbesc($chash), - dbesc($xhash), - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - } - else { - $r = q("update abconfig set v = '%s' where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", - dbesc($dbvalue), - dbesc($chash), - dbesc($xhash), - dbesc($family), - dbesc($key) - ); - } - if($r) - return $value; - return false; +function del_abconfig($chan,$xhash,$family,$key) { + return Zlib\AbConfig::Delete($chan,$xhash,$family,$key); } +function load_iconfig(&$item) { + Zlib\IConfig::Load($item); +} -function del_abconfig($chash,$xhash,$family,$key) { +function get_iconfig(&$item, $family, $key) { + return Zlib\IConfig::Get($item, $family, $key); +} - $r = q("delete from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", - dbesc($chash), - dbesc($xhash), - dbesc($family), - dbesc($key) - ); +function set_iconfig(&$item, $family, $key, $value, $sharing = false) { + return Zlib\IConfig::Set($item, $family, $key, $value, $sharing); +} - return $r; +function del_iconfig(&$item, $family, $key) { + return Zlib\IConfig::Delete($item, $family, $key); } diff --git a/include/Contact.php b/include/connections.php index e011c60c8..9f55820cc 100644 --- a/include/Contact.php +++ b/include/connections.php @@ -48,32 +48,9 @@ function abook_self($channel_id) { return(($r) ? $r[0] : array()); } -function channelx_by_nick($nick) { - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and channel_removed = 0 LIMIT 1", - dbesc($nick) - ); - return(($r) ? $r[0] : false); -} - -function channelx_by_hash($hash) { - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and channel_removed = 0 LIMIT 1", - dbesc($hash) - ); - return(($r) ? $r[0] : false); -} - -function channelx_by_n($id) { - $r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_id = %d and channel_removed = 0 LIMIT 1", - dbesc($id) - ); - return(($r) ? $r[0] : false); -} - function vcard_from_xchan($xchan, $observer = null, $mode = '') { - $a = get_app(); - if(! $xchan) { if(App::$poi) { $xchan = App::$poi; @@ -198,7 +175,7 @@ function account_remove($account_id,$local = true,$unset_session=true) { // Don't let anybody nuke the only admin account. - $r = q("select account_id from account where (account_roles & %d)>0", + $r = q("select account_id from account where (account_roles & %d) > 0", intval(ACCOUNT_ROLE_ADMIN) ); @@ -267,7 +244,7 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { if(! $channel_id) return; - $a = get_app(); + logger('Removing channel: ' . $channel_id); logger('channel_remove: local only: ' . intval($local)); @@ -283,15 +260,15 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { if(! $local) { - $r = q("update channel set channel_deleted = '%s', channel_removed = 1, channel_r_stream = 0, channel_r_profile = 0, - channel_r_photos = 0, channel_r_abook = 0, channel_w_stream = 0, channel_w_wall = 0, channel_w_tagwall = 0, - channel_w_comment = 0, channel_w_mail = 0, channel_w_photos = 0, channel_w_chat = 0, channel_a_delegate = 0, - channel_r_storage = 0, channel_w_storage = 0, channel_r_pages = 0, channel_w_pages = 0, channel_a_republish = 0 - where channel_id = %d", + $r = q("update channel set channel_deleted = '%s', channel_removed = 1 where channel_id = %d", dbesc(datetime_convert()), intval($channel_id) ); + q("delete from pconfig where uid = %d", + intval($channel_id) + ); + logger('deleting hublocs',LOGGER_DEBUG); $r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s'", @@ -303,22 +280,33 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { dbesc($channel['channel_hash']) ); - proc_run('php','include/notifier.php','purge_all',$channel_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','purge_all',$channel_id)); + } + + $r = q("select * from iconfig left join item on item.id = iconfig.iid + where item.uid = %d", + intval($channel_id) + ); + if($r) { + foreach($r as $rr) { + q("delete from iconfig where iid = %d", + intval($rr['iid']) + ); + } } + q("DELETE FROM `groups` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `group_member` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `event` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `item` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `item_id` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `mail` WHERE `channel_id` = %d", intval($channel_id)); q("DELETE FROM `notify` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `photo` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `attach` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `profile` WHERE `uid` = %d", intval($channel_id)); q("DELETE FROM `pconfig` WHERE `uid` = %d", intval($channel_id)); - q("DELETE FROM `spam` WHERE `uid` = %d", intval($channel_id)); // @FIXME At this stage we need to remove the file resources located under /store/$nickname @@ -386,10 +374,10 @@ function channel_remove($channel_id, $local = true, $unset_session=false) { @rrmdir($f); } - proc_run('php','include/directory.php',$channel_id); + Zotlabs\Daemon\Master::Summon(array('Directory',$channel_id)); if($channel_id == local_channel() && $unset_session) { - \Zotlabs\Web\Session::nuke(); + App::$session->nuke(); goaway(z_root()); } @@ -614,7 +602,8 @@ function random_profile() { for($i = 0; $i < $retryrandom; $i++) { - $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where hubloc_connected > %s - interval %s order by $randfunc limit 1", + $r = q("select xchan_url from xchan left join hubloc on hubloc_hash = xchan_hash where xchan_addr not like '%s' and hubloc_connected > %s - interval %s order by $randfunc limit 1", + dbesc('sys@%'), db_utcnow(), db_quoteinterval('30 day') ); diff --git a/include/contact_selectors.php b/include/contact_selectors.php deleted file mode 100644 index 0de4ece00..000000000 --- a/include/contact_selectors.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php /** @file */ - - -function contact_profile_assign($current) { - - $o = ''; - - $o .= "<select id=\"contact-profile-selector\" name=\"profile_assign\" class=\"form-control\"/>\r\n"; - - $r = q("SELECT profile_guid, profile_name FROM `profile` WHERE `uid` = %d", - intval($_SESSION['uid'])); - - if($r) { - foreach($r as $rr) { - $selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : ""); - $o .= "<option value=\"{$rr['profile_guid']}\" $selected >{$rr['profile_name']}</option>\r\n"; - } - } - $o .= "</select>\r\n"; - return $o; -} - -/* unused currently - -function contact_reputation($current) { - - $o = ''; - $o .= "<select id=\"contact-reputation-selector\" name=\"reputation\" />\r\n"; - - $rep = array( - 0 => t('Unknown | Not categorized'), - 1 => t('Block immediately'), - 2 => t('Shady, spammer, self-marketer'), - 3 => t('Known to me, but no opinion'), - 4 => t('OK, probably harmless'), - 5 => t('Reputable, has my trust') - ); - - foreach($rep as $k => $v) { - $selected = (($k == $current) ? " selected=\"selected\" " : ""); - $o .= "<option value=\"$k\" $selected >$v</option>\r\n"; - } - $o .= "</select>\r\n"; - return $o; -} - -*/ - -function contact_poll_interval($current, $disabled = false) { - - $dis = (($disabled) ? ' disabled="disabled" ' : ''); - $o = ''; - $o .= "<select id=\"contact-poll-interval\" name=\"poll\" $dis />" . "\r\n"; - - $rep = array( - 0 => t('Frequently'), - 1 => t('Hourly'), - 2 => t('Twice daily'), - 3 => t('Daily'), - 4 => t('Weekly'), - 5 => t('Monthly') - ); - - foreach($rep as $k => $v) { - $selected = (($k == $current) ? " selected=\"selected\" " : ""); - $o .= "<option value=\"$k\" $selected >$v</option>\r\n"; - } - $o .= "</select>\r\n"; - return $o; -} - - -function network_to_name($s) { - - $nets = array( - NETWORK_DFRN => t('Friendica'), - NETWORK_FRND => t('Friendica'), - NETWORK_OSTATUS => t('OStatus'), - NETWORK_GNUSOCIAL => t('GNU-Social'), - NETWORK_FEED => t('RSS/Atom'), - NETWORK_MAIL => t('Email'), - NETWORK_DIASPORA => t('Diaspora'), - NETWORK_FACEBOOK => t('Facebook'), - NETWORK_ZOT => t('Zot'), - NETWORK_LINKEDIN => t('LinkedIn'), - NETWORK_XMPP => t('XMPP/IM'), - NETWORK_MYSPACE => t('MySpace'), - ); - - call_hooks('network_to_name', $nets); - - $search = array_keys($nets); - $replace = array_values($nets); - - return str_replace($search,$replace,$s); - -} diff --git a/include/contact_widgets.php b/include/contact_widgets.php index ba1241fcb..85c46b0d1 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -3,9 +3,6 @@ function findpeople_widget() { - require_once('include/Contact.php'); - - $a = get_app(); if(get_config('system','invitation_only')) { $x = get_pconfig(local_channel(),'system','invites_remaining'); @@ -37,13 +34,12 @@ function findpeople_widget() { function fileas_widget($baseurl,$selected = '') { - $a = get_app(); if(! local_channel()) return ''; $terms = array(); - $r = q("select distinct(term) from term where uid = %d and type = %d order by term asc", + $r = q("select distinct(term) from term where uid = %d and ttype = %d order by term asc", intval(local_channel()), intval(TERM_FILE) ); @@ -65,8 +61,6 @@ function fileas_widget($baseurl,$selected = '') { } function categories_widget($baseurl,$selected = '') { - - $a = get_app(); if(! feature_enabled(App::$profile['profile_uid'],'categories')) return ''; @@ -78,13 +72,15 @@ function categories_widget($baseurl,$selected = '') { from term join item on term.oid = item.id where item.uid = %d and term.uid = item.uid - and term.type = %d + and term.ttype = %d + and term.otype = %d and item.owner_xchan = '%s' and item.item_wall = 1 $item_normal order by term.term asc", intval(App::$profile['profile_uid']), intval(TERM_CATEGORY), + intval(TERM_OBJ_POST), dbesc(App::$profile['channel_hash']) ); if($r && count($r)) { @@ -106,8 +102,6 @@ function categories_widget($baseurl,$selected = '') { function common_friends_visitor_widget($profile_uid) { - $a = get_app(); - if(local_channel() == $profile_uid) return; diff --git a/include/conversation.php b/include/conversation.php index 7d80b08fc..1efca37f3 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -93,15 +93,15 @@ function localize_item(&$item){ if (activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE)){ - if(! $item['object']) + if(! $item['obj']) return; if(intval($item['item_thread_top'])) return; - $obj = json_decode_plus($item['object']); - if((! $obj) && ($item['object'])) { - logger('localize_item: failed to decode object: ' . print_r($item['object'],true)); + $obj = json_decode($item['obj'],true); + if((! $obj) && ($item['obj'])) { + logger('localize_item: failed to decode object: ' . print_r($item['obj'],true)); } if($obj['author'] && $obj['author']['link']) @@ -186,7 +186,7 @@ function localize_item(&$item){ $Alink = $item['author']['xchan_url']; - $obj= json_decode_plus($item['object']); + $obj= json_decode($item['obj'],true); $Blink = $Bphoto = ''; @@ -219,7 +219,7 @@ function localize_item(&$item){ $Aname = $item['author']['xchan_name']; $Alink = $item['author']['xchan_url']; - $obj= json_decode_plus($item['object']); + $obj= json_decode($item['obj'],true); $Blink = $Bphoto = ''; @@ -299,7 +299,7 @@ function localize_item(&$item){ } $plink = '[zrl=' . $obj['plink'] . ']' . $post_type . '[/zrl]'; - $parsedobj = parse_xml_string($xmlhead.$item['object']); + $parsedobj = parse_xml_string($xmlhead.$item['obj']); $tag = sprintf('#[zrl=%s]%s[/zrl]', $parsedobj->id, $parsedobj->content); $item['body'] = sprintf( t('%1$s tagged %2$s\'s %3$s with %4$s'), $author, $objauthor, $plink, $tag ); @@ -316,7 +316,7 @@ function localize_item(&$item){ $xmlhead="<"."?xml version='1.0' encoding='UTF-8' ?".">"; - $obj = parse_xml_string($xmlhead.$item['object']); + $obj = parse_xml_string($xmlhead.$item['obj']); if(strlen($obj->id)) { $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($obj->id), @@ -403,9 +403,12 @@ function count_descendants($item) { * @return boolean */ function visible_activity($item) { - $hidden_activities = array(ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE); + $hidden_activities = [ ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_AGREE, ACTIVITY_DISAGREE, ACTIVITY_ABSTAIN, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE ]; - $post_types = array(ACTIVITY_OBJ_NOTE,ACTIVITY_OBJ_COMMENT,basename(ACTIVITY_OBJ_NOTE),basename(ACTIVITY_OBJ_COMMENT)); + $post_types = [ ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT, basename(ACTIVITY_OBJ_NOTE), basename(ACTIVITY_OBJ_COMMENT)]; + + if(intval($item['item_notshown'])) + return false; foreach ($hidden_activities as $act) { if ((activity_match($item['verb'], $act)) && ($item['mid'] != $item['parent_mid'])) { @@ -658,7 +661,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ ); $star = false; - $isstarred = "unstarred icon-star-empty"; + $isstarred = "unstarred fa-star-o"; $lock = (($item['item_private'] || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) ? t('Private Message') @@ -754,10 +757,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ // Normal View // logger('conv: items: ' . print_r($items,true)); - require_once('include/ConversationObject.php'); - require_once('include/ItemObject.php'); - - $conv = new Conversation($mode, $preview, $prepared_item); + $conv = new Zotlabs\Lib\ThreadStream($mode, $preview, $prepared_item); // In the display mode we don't have a profile owner. @@ -806,7 +806,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ if($item['id'] == $item['parent']) { - $item_object = new Item($item); + $item_object = new Zotlabs\Lib\ThreadItem($item); $conv->add_thread($item_object); if($page_mode === 'list') { $item_object->set_template('conv_list.tpl'); @@ -861,8 +861,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ function best_link_url($item) { - $a = get_app(); - $best_url = ''; $sparkle = false; @@ -891,7 +889,7 @@ function best_link_url($item) { function item_photo_menu($item){ - $a = get_app(); + $contact = null; $ssl_state = false; @@ -1110,82 +1108,95 @@ function status_editor($a, $x, $popup = false) { $o = ''; - require_once('include/Contact.php'); $c = channelx_by_n($x['profile_uid']); if($c && $c['channel_moved']) return $o; - $geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : ''); - $plaintext = true; // if(feature_enabled(local_channel(),'richtext')) // $plaintext = false; - $voting = feature_enabled(local_channel(), 'consensus_tools'); - if(x($x, 'novoting')) - $voting = false; + $feature_voting = feature_enabled($x['profile_uid'], 'consensus_tools'); + if(x($x, 'hide_voting')) + $feature_voting = false; - $mimeselect = ''; - if(array_key_exists('mimetype', $x) && $x['mimetype']) { - if($x['mimetype'] != 'text/bbcode') - $plaintext = true; - if($x['mimetype'] === 'choose') { - $mimeselect = mimetype_select($x['profile_uid']); - } - else - $mimeselect = '<input type="hidden" name="mimetype" value="' . $x['mimetype'] . '" />'; - } + $feature_expire = ((feature_enabled($x['profile_uid'], 'content_expire') && (! $webpage)) ? true : false); + if(x($x, 'hide_expire')) + $feature_expire = false; - $layoutselect = ''; - if(array_key_exists('layout', $x) && $x['layout']) { - if($x['layout'] === 'choose') { - $layoutselect = layout_select($x['profile_uid']); - } - else - $layoutselect = '<input type="hidden" name="layout_mid" value="' . $x['layout'] . '" />'; - } + $feature_future = ((feature_enabled($x['profile_uid'], 'delayed_posting') && (! $webpage)) ? true : false); + if(x($x, 'hide_future')) + $feature_future = false; + + $geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : ''); + $setloc = t('Set your location'); + $clearloc = ((get_pconfig($x['profile_uid'], 'system', 'use_browser_location')) ? t('Clear browser location') : ''); + if(x($x, 'hide_location')) + $geotag = $setloc = $clearloc = ''; + + $mimetype = ((x($x,'mimetype')) ? $x['mimetype'] : 'text/bbcode'); + $mimeselect = ((x($x,'mimeselect')) ? $x['mimeselect'] : false); + if($mimeselect) + $mimeselect = mimetype_select($x['profile_uid'], $mimetype); + else + $mimeselect = '<input type="hidden" name="mimetype" value="' . $mimetype . '" />'; + + $weblink = (($mimetype === 'text/bbcode') ? t('Insert web link') : false); + if(x($x, 'hide_weblink')) + $weblink = false; + + $embedPhotos = t('Embed image from photo albums'); + + $writefiles = (($mimetype === 'text/bbcode') ? perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage') : false); + if(x($x, 'hide_attach')) + $writefiles = false; + + $layout = ((x($x,'layout')) ? $x['layout'] : ''); + + $layoutselect = ((x($x,'layoutselect')) ? $x['layoutselect'] : false); + if($layoutselect) + $layoutselect = layout_select($x['profile_uid'], $layout); + else + $layoutselect = '<input type="hidden" name="layout_mid" value="' . $layout . '" />'; if(array_key_exists('channel_select',$x) && $x['channel_select']) { - require_once('include/identity.php'); + require_once('include/channel.php'); $id_select = identity_selector(); } else $id_select = ''; - $webpage = ((x($x,'webpage')) ? $x['webpage'] : ''); $tpl = get_markup_template('jot-header.tpl'); App::$page['htmlhead'] .= replace_macros($tpl, array( - '$newpost' => 'true', '$baseurl' => z_root(), '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), '$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''), '$geotag' => $geotag, '$nickname' => $x['nickname'], - '$ispublic' => t('Visible to <strong>everybody</strong>'), '$linkurl' => t('Please enter a link URL:'), - '$vidurl' => t('Please enter a video link/URL:'), - '$audurl' => t('Please enter an audio link/URL:'), '$term' => t('Tag term:'), - '$fileas' => t('Save to Folder:'), '$whereareu' => t('Where are you right now?'), - '$expireswhen' => t('Expires YYYY-MM-DD HH:MM'), '$editor_autocomplete'=> ((x($x,'editor_autocomplete')) ? $x['editor_autocomplete'] : ''), '$bbco_autocomplete'=> ((x($x,'bbco_autocomplete')) ? $x['bbco_autocomplete'] : ''), + '$modalchooseimages' => t('Choose images to embed'), + '$modalchoosealbum' => t('Choose an album'), + '$modaldiffalbum' => t('Choose a different album...'), + '$modalerrorlist' => t('Error getting album list'), + '$modalerrorlink' => t('Error getting photo link'), + '$modalerroralbum' => t('Error getting album'), )); $tpl = get_markup_template('jot.tpl'); $jotplugins = ''; - $jotnets = ''; $preview = t('Preview'); -// $preview = ((feature_enabled($x['profile_uid'],'preview')) ? t('Preview') : ''); - if(x($x, 'nopreview')) + if(x($x, 'hide_preview')) $preview = ''; $defexpire = ((($z = get_pconfig($x['profile_uid'], 'system', 'default_post_expire')) && (! $webpage)) ? $z : ''); @@ -1201,7 +1212,6 @@ function status_editor($a, $x, $popup = false) { $cipher = 'aes256'; call_hooks('jot_tool', $jotplugins); - call_hooks('jot_networks', $jotnets); $o .= replace_macros($tpl, array( '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), @@ -1212,48 +1222,35 @@ function status_editor($a, $x, $popup = false) { '$pagetitle' => (x($x,'pagetitle') ? $x['pagetitle'] : ''), '$id_select' => $id_select, '$id_seltext' => t('Post as'), - '$writefiles' => perm_is_allowed($x['profile_uid'], get_observer_hash(), 'write_storage'), + '$writefiles' => $writefiles, '$bold' => t('Bold'), '$italic' => t('Italic'), '$underline' => t('Underline'), '$quote' => t('Quote'), '$code' => t('Code'), - '$upload' => t('Upload photo'), - '$shortupload' => t('upload photo'), '$attach' => t('Attach file'), - '$shortattach' => t('attach file'), - '$weblink' => t('Insert web link'), - '$shortweblink' => t('web link'), - '$video' => t('Insert video link'), - '$shortvideo' => t('video link'), - '$audio' => t('Insert audio link'), - '$shortaudio' => t('audio link'), - '$setloc' => t('Set your location'), - '$shortsetloc' => t('set location'), + '$weblink' => $weblink, + '$embedPhotos' => $embedPhotos, + '$embedPhotosModalTitle' => t('Embed an image from your albums'), + '$embedPhotosModalCancel' => t('Cancel'), + '$embedPhotosModalOK' => t('OK'), + '$setloc' => $setloc, '$voting' => t('Toggle voting'), - '$feature_voting' => $voting, + '$feature_voting' => $feature_voting, '$consensus' => 0, - '$noloc' => ((get_pconfig($x['profile_uid'], 'system', 'use_browser_location')) ? t('Clear browser location') : ''), - '$shortnoloc' => t('clear location'), + '$clearloc' => $clearloc, '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''), '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), - '$hidetitle' => ((x($x, 'hidetitle')) ? $x['hidetitle'] : false), '$catsenabled' => ((feature_enabled($x['profile_uid'], 'categories') && (! $webpage)) ? 'categories' : ''), - '$category' => "", + '$category' => ((x($x, 'category')) ? $x['category'] : ''), '$placeholdercategory' => t('Categories (optional, comma-separated list)'), - '$wait' => t('Please wait'), '$permset' => t('Permission settings'), - '$shortpermset' => t('permissions'), - '$ptyp' => '', + '$ptyp' => ((x($x, 'ptyp')) ? $x['ptyp'] : ''), '$content' => ((x($x,'body')) ? htmlspecialchars($x['body'], ENT_COMPAT,'UTF-8') : ''), '$attachment' => ((x($x, 'attachment')) ? $x['attachment'] : ''), - '$post_id' => '', - '$baseurl' => z_root(), + '$post_id' => ((x($x, 'post_id')) ? $x['post_id'] : ''), '$defloc' => $x['default_location'], '$visitor' => $x['visitor'], - '$public' => t('Public post'), - '$jotnets' => $jotnets, - '$emtitle' => t('Example: bob@example.com, mary@example.com'), '$lockstate' => $x['lockstate'], '$acl' => $x['acl'], '$mimeselect' => $mimeselect, @@ -1265,10 +1262,10 @@ function status_editor($a, $x, $popup = false) { '$source' => ((x($x, 'source')) ? $x['source'] : ''), '$jotplugins' => $jotplugins, '$defexpire' => $defexpire, - '$feature_expire' => ((feature_enabled($x['profile_uid'], 'content_expire') && (! $webpage)) ? true : false), + '$feature_expire' => $feature_expire, '$expires' => t('Set expiration date'), '$defpublish' => $defpublish, - '$feature_future' => ((feature_enabled($x['profile_uid'], 'delayed_posting') && (! $webpage)) ? true : false), + '$feature_future' => $feature_future, '$future_txt' => t('Set publish date'), '$feature_encrypt' => ((feature_enabled($x['profile_uid'], 'content_encrypt') && (! $webpage)) ? true : false), '$encrypt' => t('Encrypt text'), @@ -1424,7 +1421,7 @@ function render_location_default($item) { function prepare_page($item) { - $a = get_app(); + $naked = 1; // $naked = ((get_pconfig($item['uid'],'system','nakedpage')) ? 1 : 0); $observer = App::get_observer(); @@ -1458,7 +1455,7 @@ function prepare_page($item) { function network_tabs() { - $a = get_app(); + $no_active=''; $starred_active = ''; $new_active = ''; @@ -1674,8 +1671,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ if ($p['chat'] && feature_enabled($uid,'ajaxchat')) { - require_once('include/chat.php'); - $has_chats = chatroom_list_count($uid); + $has_chats = Zotlabs\Lib\Chatroom::list_count($uid); if ($has_chats) { $tabs[] = array( 'label' => t('Chatrooms'), @@ -1707,13 +1703,19 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'title' => t('Manage Webpages'), 'id' => 'webpages-tab', ); - } else { - /** - * @FIXME we probably need a listing of events that were created by - * this channel and are visible to the observer - */ + } + + if(feature_enabled($uid,'wiki') && (! UNO)) { + $tabs[] = array( + 'label' => t('Wiki'), + 'url' => z_root() . '/wiki/' . $nickname, + 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), + 'title' => t('Wiki'), + 'id' => 'wiki-tab', + ); } + $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); call_hooks('profile_tabs', $arr); diff --git a/include/cronhooks.php b/include/cronhooks.php deleted file mode 100644 index a314593d2..000000000 --- a/include/cronhooks.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); - - -function cronhooks_run($argv, $argc){ - - cli_startup(); - - logger('cronhooks: start'); - - $d = datetime_convert(); - - call_hooks('cron', $d); - - return; -} - -if (array_search(__file__,get_included_files())===0){ - cronhooks_run($argv,$argc); - killme(); -} diff --git a/include/crypto.php b/include/crypto.php index d636c6848..bc798d919 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -46,27 +46,15 @@ function pkcs5_unpad($text) } function AES256CBC_encrypt($data,$key,$iv) { - if(get_config('system','openssl_encrypt')) { - return openssl_encrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); - } - return mcrypt_encrypt( - MCRYPT_RIJNDAEL_128, - str_pad($key,32,"\0"), - pkcs5_pad($data,16), - MCRYPT_MODE_CBC, - str_pad($iv,16,"\0")); + + return openssl_encrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); + } function AES256CBC_decrypt($data,$key,$iv) { - if(get_config('system','openssl_encrypt')) { - return openssl_decrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); - } - return pkcs5_unpad(mcrypt_decrypt( - MCRYPT_RIJNDAEL_128, - str_pad($key,32,"\0"), - $data, - MCRYPT_MODE_CBC, - str_pad($iv,16,"\0"))); + + return openssl_decrypt($data,'aes-256-cbc',str_pad($key,32,"\0"),OPENSSL_RAW_DATA,str_pad($iv,16,"\0")); + } function crypto_encapsulate($data,$pubkey,$alg='aes256cbc') { diff --git a/include/datetime.php b/include/datetime.php index 83fb49d04..76bd6b8d6 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -119,7 +119,6 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d * @return string */ function dob($dob) { - $a = get_app(); list($year, $month, $day) = sscanf($dob, '%4d-%2d-%2d'); $f = get_config('system', 'birthday_input_format'); @@ -225,7 +224,7 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke $pickers = ''; if(!$pickdate) $pickers .= ',datepicker: false'; - if(!$picktime) $pickers .= ',timepicker: false'; + if(!$picktime) $pickers .= ',timepicker: false, closeOnDateSelect:true'; $extra_js = ''; if($minfrom != '') @@ -557,13 +556,13 @@ function update_birthdays() { $ev['uid'] = $rr['abook_channel']; $ev['account'] = $rr['abook_account']; $ev['event_xchan'] = $rr['xchan_hash']; - $ev['start'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); - $ev['finish'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); + $ev['dtstart'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); + $ev['dtend'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); $ev['adjust'] = intval(feature_enabled($rr['abook_channel'],'smart_birthdays')); $ev['summary'] = sprintf( t('%1$s\'s birthday'), $rr['xchan_name']); $ev['description'] = sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ; - $ev['type'] = 'birthday'; + $ev['etype'] = 'birthday'; $z = event_store_event($ev); if ($z) { diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 3c5b0b67e..f6091f6e1 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -1,50 +1,78 @@ <?php -/** - * @file dba_driver.php - * @brief some database related functions and abstract driver class. - * - * This file contains the abstract database driver class dba_driver and some - * functions for working with databases. - */ -/** - * @brief Returns the database driver object. - * - * If available it will use PHP's mysqli otherwise mysql driver. - * - * @param string $server DB server name - * @param string $port DB port - * @param string $user DB username - * @param string $pass DB password - * @param string $db database name - * @param string $dbtype 0 for mysql, 1 for postgres - * @param bool $install Defaults to false - * @return null|dba_driver A database driver object (dba_mysql|dba_mysqli) or null if no driver found. - */ -function dba_factory($server, $port,$user,$pass,$db,$dbtype,$install = false) { - $dba = null; - - $dbtype = intval($dbtype); - - if($dbtype == DBTYPE_POSTGRES) { - require_once('include/dba/dba_postgres.php'); - if(is_null($port)) $port = 5432; - $dba = new dba_postgres($server, $port, $user, $pass, $db, $install); - } else { - if(class_exists('mysqli')) { - if (is_null($port)) $port = ini_get("mysqli.default_port"); - require_once('include/dba/dba_mysqli.php'); - $dba = new dba_mysqli($server, $port,$user,$pass,$db,$install); - } else { - if (is_null($port)) $port = "3306"; - require_once('include/dba/dba_mysql.php'); - $dba = new dba_mysql($server, $port,$user,$pass,$db,$install); +class DBA { + + /** + * @file dba_driver.php + * @brief some database related functions and abstract driver class. + * + * This file contains the abstract database driver class dba_driver and some + * functions for working with databases. + */ + + static public $dba = null; + static public $dbtype = null; + static public $logging = false; + + /** + * @brief Returns the database driver object. + * + * If available it will use PHP's mysqli otherwise mysql driver. + * + * @param string $server DB server name + * @param string $port DB port + * @param string $user DB username + * @param string $pass DB password + * @param string $db database name + * @param string $dbtype 0 for mysql, 1 for postgres + * @param bool $install Defaults to false + * @return null|dba_driver A database driver object (dba_mysql|dba_mysqli) or null if no driver found. + */ + + static public function dba_factory($server, $port,$user,$pass,$db,$dbtype,$install = false) { + + self::$dba = null; + + self::$dbtype = intval($dbtype); + $set_port = $port; + + if(self::$dbtype == DBTYPE_POSTGRES) { + require_once('include/dba/dba_postgres.php'); + if(is_null($port)) $set_port = 5432; + self::$dba = new dba_postgres($server, $set_port, $user, $pass, $db, $install); } + else { + +// Highly experimental at the present time. +// require_once('include/dba/dba_pdo.php'); +// self::$dba = new dba_pdo($server, $set_port,$user,$pass,$db,$install); +// } + + if(class_exists('mysqli')) { + if (is_null($port)) $set_port = ini_get("mysqli.default_port"); + require_once('include/dba/dba_mysqli.php'); + self::$dba = new dba_mysqli($server, $set_port,$user,$pass,$db,$install); + } + } + + // Until we have a proper PDO driver, store the DB connection parameters for + // plugins/addons which use PDO natively (such as cdav). This is wasteful as + // it opens a separate connection to the DB, but saves a lot of effort re-writing + // third-party interfaces that are working and well tested. + + + if(is_object(self::$dba) && self::$dba->connected) { + $dns = ((self::$dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql') + . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port) + . ';dbname=' . $db; + self::$dba->pdo_set(array($dns,$user,$pass)); + } + + define('NULL_DATE', self::$dba->get_null_date()); + define('ACTIVE_DBTYPE', self::$dbtype); + return self::$dba; } - define('NULL_DATE', $dba->get_null_date()); - define('ACTIVE_DBTYPE', $dbtype); - return $dba; } /** @@ -59,8 +87,10 @@ abstract class dba_driver { const NULL_DATE = '0000-00-00 00:00:00'; const UTC_NOW = 'UTC_TIMESTAMP()'; - protected $debug = 0; protected $db; + protected $pdo = array(); + + public $debug = 0; public $connected = false; public $error = false; @@ -182,6 +212,15 @@ abstract class dba_driver { function unescapebin($str) { return $str; } + + function pdo_set($x) { + $this->pdo = $x; + } + + function pdo_get() { + return $this->pdo; + } + } // end abstract dba_driver class @@ -205,8 +244,8 @@ function printable($s) { function dbg($state) { global $db; - if($db) - $db->dbg($state); + if(\DBA::$dba) + \DBA::$dba->dbg($state); } /** @@ -220,21 +259,18 @@ function dbg($state) { * @return Return an escaped string of the value to pass to a DB query. */ function dbesc($str) { - global $db; - if($db && $db->connected) - return($db->escape($str)); + if(\DBA::$dba && \DBA::$dba->connected) + return(\DBA::$dba->escape($str)); else return(str_replace("'", "\\'", $str)); } function dbescbin($str) { - global $db; - return $db->escapebin($str); + return \DBA::$dba->escapebin($str); } function dbunescbin($str) { - global $db; - return $db->unescapebin($str); + return \DBA::$dba->unescapebin($str); } function dbescdate($date) { @@ -247,36 +283,25 @@ function dbescdate($date) { } function db_quoteinterval($txt) { - global $db; - return $db->quote_interval($txt); + return \DBA::$dba->quote_interval($txt); } function dbesc_identifier($str) { - global $db; - return $db->escape_identifier($str); + return \DBA::$dba->escape_identifier($str); } function db_utcnow() { - global $db; - return $db->utcnow(); + return \DBA::$dba->utcnow(); } function db_optimizetable($table) { - global $db; - $db->optimize_table($table); + \DBA::$dba->optimize_table($table); } function db_concat($fld, $sep) { - global $db; - return $db->concat($fld, $sep); + return \DBA::$dba->concat($fld, $sep); } -// Function: q($sql,$args); -// Description: execute SQL query with printf style args. -// Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d", -// 'user', 1); - - /** * @brief Execute a SQL query with printf style args. * @@ -292,29 +317,33 @@ function db_concat($fld, $sep) { * @param string $sql The SQL query to execute * @return bool|array */ + function q($sql) { - global $db; $args = func_get_args(); unset($args[0]); - if($db && $db->connected) { + if(\DBA::$dba && \DBA::$dba->connected) { $stmt = vsprintf($sql, $args); if($stmt === false) { if(version_compare(PHP_VERSION, '5.4.0') >= 0) - logger('dba: vsprintf error: ' . + db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); else - logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); + db_logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); } - return $db->q($stmt); + if(\DBA::$dba->debug) + db_logger('Sql: ' . $stmt, LOGGER_DEBUG, LOG_INFO); + + return \DBA::$dba->q($stmt); } /* * This will happen occasionally trying to store the * session data after abnormal program termination */ - logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); + + db_logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); return false; } @@ -327,10 +356,9 @@ function q($sql) { * @param string $sql The SQL query to execute */ function dbq($sql) { - global $db; - if($db && $db->connected) - $ret = $db->q($sql); + if(\DBA::$dba && \DBA::$dba->connected) + $ret = \DBA::$dba->q($sql); else $ret = false; @@ -385,7 +413,24 @@ function db_getfunc($f) { if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) return $lookup[$f][ACTIVE_DBTYPE]; - logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); + db_logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); return $f; } +// The logger function may make DB calls internally to query the system logging parameters. +// This can cause a recursion if database debugging is enabled. +// So this function preserves the current database debugging state and then turns it off +// temporarily while doing the logger() call + +function db_logger($s,$level = LOGGER_NORMAL,$syslog = LOG_INFO) { + + if(\DBA::$logging) + return; + + $saved = \DBA::$dba->debug; + \DBA::$dba->debug = false; + \DBA::$logging = true; + logger($s,$level,$syslog); + \DBA::$logging = false; + \DBA::$dba->debug = $saved; +}
\ No newline at end of file diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php index 6986d4586..afd2aa642 100755 --- a/include/dba/dba_mysqli.php +++ b/include/dba/dba_mysqli.php @@ -4,20 +4,26 @@ require_once('include/dba/dba_driver.php'); class dba_mysqli extends dba_driver { - function connect($server, $port, $user,$pass,$db) { + function connect($server,$port,$user,$pass,$db) { if($port) $this->db = new mysqli($server,$user,$pass,$db, $port); else $this->db = new mysqli($server,$user,$pass,$db); - if(! mysqli_connect_errno()) { - $this->connected = true; + if($this->db->connect_error) { + $this->connected = false; + $this->error = $this->db->connect_error; + + if(file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\nConnect: " . $this->error . "\n", FILE_APPEND); + } + + return false; } - if($this->connected) { + else { + $this->connected = true; return true; } - $this->error = $this->db->connect_error; - return false; } function q($sql) { @@ -32,7 +38,7 @@ class dba_mysqli extends dba_driver { if($this->error) { - logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); + db_logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); if(file_exists('dbfail.out')) { file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); } @@ -40,13 +46,13 @@ class dba_mysqli extends dba_driver { if(($result === true) || ($result === false)) { if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); + db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); } return $result; } if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO); + db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO); } $r = array(); @@ -55,7 +61,7 @@ class dba_mysqli extends dba_driver { $r[] = $x; $result->free_result(); if($this->debug) { - logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); + db_logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); } } return $r; diff --git a/include/dba/dba_pdo.php b/include/dba/dba_pdo.php new file mode 100755 index 000000000..7255a2b66 --- /dev/null +++ b/include/dba/dba_pdo.php @@ -0,0 +1,95 @@ +<?php /** @file */ + +require_once('include/dba/dba_driver.php'); + +class dba_pdo extends dba_driver { + + + public $driver_dbtype = null; + + function connect($server,$port,$user,$pass,$db) { + + $this->driver_dbtype = 'mysql'; // (($dbtype == DBTYPE_POSTGRES) ? 'postgres' : 'mysql'); + $dns = $this->driver_dbtype + . ':host=' . $server . (is_null($port) ? '' : ';port=' . $port) + . ';dbname=' . $db; + + + try { + $this->db = new PDO($dns,$user,$pass); + $this->db->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); + } + catch(PDOException $e) { + if(file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\nConnect: " . $e->getMessage() . "\n", FILE_APPEND); + } + + return false; + } + + $this->connected = true; + return true; + + } + + function q($sql) { + if((! $this->db) || (! $this->connected)) + return false; + + $this->error = ''; + $select = ((stripos($sql,'select') === 0) ? true : false); + + try { + $result = $this->db->query($sql); + } + catch(PDOException $e) { + + $this->error = $e->getMessage(); + if($this->error) { + db_logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error, LOGGER_NORMAL, LOG_ERR); + if(file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); + } + } + } + + if(!($select)) { + if($this->debug) { + db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); + } + return $result; + } + + if($this->debug) { + db_logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . count($result) . ' results.', LOGGER_NORMAL, LOG_INFO); + } + + $r = array(); + if($result) { + foreach($result as $x) { + $r[] = $x; + } + if($this->debug) { + db_logger('dba_pdo: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); + } + } + return $r; + } + + function escape($str) { + if($this->db && $this->connected) { + return substr(substr(@$this->db->quote($str),1),0,-1); + } + } + + function close() { + if($this->db) + $this->db = null; + $this->connected = false; + } + + function getdriver() { + return 'pdo'; + } + +}
\ No newline at end of file diff --git a/include/dba/dba_postgres.php b/include/dba/dba_postgres.php index ba4366d13..03b29d703 100644 --- a/include/dba/dba_postgres.php +++ b/include/dba/dba_postgres.php @@ -50,7 +50,7 @@ class dba_postgres extends dba_driver { $this->error = pg_last_error($this->db); if($result === false || $this->error) { - //logger('dba_postgres: ' . printable($sql) . ' returned false.' . "\n" . $this->error); + //db_logger('dba_postgres: ' . printable($sql) . ' returned false.' . "\n" . $this->error); if(file_exists('dbfail.out')) file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND); } @@ -67,7 +67,7 @@ class dba_postgres extends dba_driver { $r[] = $x; pg_free_result($result); if($this->debug) - logger('dba_postgres: ' . printable(print_r($r,true))); + db_logger('dba_postgres: ' . printable(print_r($r,true))); } return $r; } diff --git a/include/deliver.php b/include/deliver.php deleted file mode 100644 index 40df543d5..000000000 --- a/include/deliver.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php /** @file */ - -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/queue_fn.php'); - - -function deliver_run($argv, $argc) { - - cli_startup(); - - $a = get_app(); - - if($argc < 2) - return; - - logger('deliver: invoked: ' . print_r($argv,true), LOGGER_DATA); - - for($x = 1; $x < $argc; $x ++) { - - $dresult = null; - $r = q("select * from outq where outq_hash = '%s' limit 1", - dbesc($argv[$x]) - ); - if($r) { - - $notify = json_decode($r[0]['outq_notify'],true); - - // Messages without an outq_msg will need to go via the web, even if it's a - // local delivery. This includes conversation requests and refresh packets. - - if(($r[0]['outq_posturl'] === z_root() . '/post') && ($r[0]['outq_msg'])) { - logger('deliver: local delivery', LOGGER_DEBUG); - - // local delivery - // we should probably batch these and save a few delivery processes - - if($r[0]['outq_msg']) { - $m = json_decode($r[0]['outq_msg'],true); - if(array_key_exists('message_list',$m)) { - foreach($m['message_list'] as $mm) { - $msg = array('body' => json_encode(array('success' => true, 'pickup' => array(array('notify' => $notify,'message' => $mm))))); - zot_import($msg,z_root()); - } - } - else { - $msg = array('body' => json_encode(array('success' => true, 'pickup' => array(array('notify' => $notify,'message' => $m))))); - $dresult = zot_import($msg,z_root()); - } - - remove_queue_item($r[0]['outq_hash']); - - if($dresult && is_array($dresult)) { - foreach($dresult as $xx) { - if(is_array($xx) && array_key_exists('message_id',$xx)) { - if(delivery_report_is_storable($xx)) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s' ) ", - dbesc($xx['message_id']), - dbesc($xx['location']), - dbesc($xx['recipient']), - dbesc($xx['status']), - dbesc(datetime_convert($xx['date'])), - dbesc($xx['sender']) - ); - } - } - } - } - - q("delete from dreport where dreport_queue = '%s'", - dbesc($argv[$x]) - ); - } - } - - // otherwise it's a remote delivery - call queue_deliver() with the $immediate flag - - queue_deliver($r[0],true); - - } - } -} - -if (array_search(__file__,get_included_files())===0){ - deliver_run($argv,$argc); - killme(); -} diff --git a/include/deliver_hooks.php b/include/deliver_hooks.php deleted file mode 100644 index f0d6ba1b1..000000000 --- a/include/deliver_hooks.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - - -require_once('include/cli_startup.php'); -require_once('include/zot.php'); - - -function deliver_hooks_run($argv, $argc) { - - cli_startup(); - - $a = get_app(); - - if($argc < 2) - return; - - - $r = q("select * from item where id = '%d'", - intval($argv[1]) - ); - if($r) - call_hooks('notifier_normal',$r[0]); - -} - -if (array_search(__file__,get_included_files())===0){ - deliver_hooks_run($argv,$argc); - killme(); -} diff --git a/include/dir_fns.php b/include/dir_fns.php index 1c3149081..9f1be1a42 100644 --- a/include/dir_fns.php +++ b/include/dir_fns.php @@ -69,9 +69,8 @@ function check_upstream_directory() { if ($directory) { $h = parse_url($directory); if ($h) { - $x = zot_finger('[system]@' . $h['host']); - if ($x['success']) { - $j = json_decode($x['body'], true); + $j = Zotlabs\Zot\Finger::run('[system]@' . $h['host']); + if ($j['success']) { if (array_key_exists('site', $j) && array_key_exists('directory_mode', $j['site'])) { if ($j['site']['directory_mode'] === 'normal') { $isadir = false; @@ -95,6 +94,9 @@ function get_directory_setting($observer, $setting) { if($ret === false) $ret = get_config('directory', $setting); + + // 'safemode' is the default if there is no observer or no established preference. + if($setting == 'safemode' && $ret === false) $ret = 1; diff --git a/include/directory.php b/include/directory.php deleted file mode 100644 index 8792a15e1..000000000 --- a/include/directory.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php -/** - * @file include/directory.php - * @brief executes directory_run() - */ - -require_once('boot.php'); -require_once('include/zot.php'); -require_once('include/cli_startup.php'); -require_once('include/dir_fns.php'); -require_once('include/queue_fn.php'); - -/** - * @brief - * - * @param array $argv - * @param array $argc - */ -function directory_run($argv, $argc){ - - cli_startup(); - - if($argc < 2) - return; - - $force = false; - $pushall = true; - - if($argc > 2) { - if($argv[2] === 'force') - $force = true; - if($argv[2] === 'nopush') - $pushall = false; - } - - logger('directory update', LOGGER_DEBUG); - - $dirmode = get_config('system','directory_mode'); - if($dirmode === false) - $dirmode = DIRECTORY_MODE_NORMAL; - - $x = q("select * from channel where channel_id = %d limit 1", - intval($argv[1]) - ); - if(! $x) - return; - - $channel = $x[0]; - - if($dirmode != DIRECTORY_MODE_NORMAL) { - - // this is an in-memory update and we don't need to send a network packet. - - local_dir_update($argv[1],$force); - - q("update channel set channel_dirdate = '%s' where channel_id = %d", - dbesc(datetime_convert()), - intval($channel['channel_id']) - ); - - // Now update all the connections - if($pushall) - proc_run('php','include/notifier.php','refresh_all',$channel['channel_id']); - - return; - } - - // otherwise send the changes upstream - - $directory = find_upstream_directory($dirmode); - $url = $directory['url'] . '/post'; - - // ensure the upstream directory is updated - - $packet = zot_build_packet($channel,(($force) ? 'force_refresh' : 'refresh')); - $z = zot_zot($url,$packet); - - // re-queue if unsuccessful - - if(! $z['success']) { - - /** @FIXME we aren't updating channel_dirdate if we have to queue - * the directory packet. That means we'll try again on the next poll run. - */ - - $hash = random_string(); - - queue_insert(array( - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $url, - 'notify' => $packet, - )); - - } - else { - q("update channel set channel_dirdate = '%s' where channel_id = %d", - dbesc(datetime_convert()), - intval($channel['channel_id']) - ); - } - - // Now update all the connections - if($pushall) - proc_run('php','include/notifier.php','refresh_all',$channel['channel_id']); - -} - -if (array_search(__file__, get_included_files()) === 0) { - directory_run($argv, $argc); - killme(); -} diff --git a/include/enotify.php b/include/enotify.php deleted file mode 100644 index d87c5af11..000000000 --- a/include/enotify.php +++ /dev/null @@ -1,652 +0,0 @@ -<?php -/** - * @file include/enotify.php - * - * @brief File with functions and a class for email notifications. - */ - -/** - * @brief - * - * @param array $params an assoziative array with: - * * \e string \b from_xchan sender xchan hash - * * \e string \b to_xchan recipient xchan hash - * * \e array \b item an assoziative array - * * \e int \b type one of the NOTIFY_* constants from boot.php - * * \e string \b link - * * \e string \b parent_mid - * * \e string \b otype - * * \e string \b verb - * * \e string \b activity - */ -function notification($params) { - - logger('notification: entry', LOGGER_DEBUG); - - // throw a small amount of entropy into the system to breakup duplicates arriving at the same precise instant. - usleep(mt_rand(0, 10000)); - - $a = get_app(); - - - if ($params['from_xchan']) { - $x = q("select * from xchan where xchan_hash = '%s' limit 1", - dbesc($params['from_xchan']) - ); - } - if ($params['to_xchan']) { - $y = q("select channel.*, account.* from channel left join account on channel_account_id = account_id - where channel_hash = '%s' and channel_removed = 0 limit 1", - dbesc($params['to_xchan']) - ); - } - if ($x & $y) { - $sender = $x[0]; - $recip = $y[0]; - } else { - logger('notification: no sender or recipient.'); - logger('sender: ' . $params['from_xchan']); - logger('recip: ' . $params['to_xchan']); - return; - } - - // from here on everything is in the recipients language - - push_lang($recip['account_language']); // should probably have a channel language - - $banner = t('$Projectname Notification'); - $product = t('$projectname'); // PLATFORM_NAME; - $siteurl = z_root(); - $thanks = t('Thank You,'); - $sitename = get_config('system','sitename'); - $site_admin = sprintf( t('%s Administrator'), $sitename); - - $sender_name = $product; - $hostname = App::get_hostname(); - if(strpos($hostname,':')) - $hostname = substr($hostname,0,strpos($hostname,':')); - - // Do not translate 'noreply' as it must be a legal 7-bit email address - $sender_email = 'noreply' . '@' . $hostname; - - $additional_mail_header = ""; - - if (array_key_exists('item', $params)) { - require_once('include/conversation.php'); - // if it's a normal item... - if (array_key_exists('verb', $params['item'])) { - // localize_item() alters the original item so make a copy first - $i = $params['item']; - logger('calling localize'); - localize_item($i); - $title = $i['title']; - $body = $i['body']; - $private = (($i['item_private']) || intval($i['item_obscured'])); - } - else { - $title = $params['item']['title']; - $body = $params['item']['body']; - } - } else { - $title = $body = ''; - } - - - // e.g. "your post", "David's photo", etc. - $possess_desc = t('%s <!item_type!>'); - - if ($params['type'] == NOTIFY_MAIL) { - logger('notification: mail'); - $subject = sprintf( t('[Hubzilla:Notify] New mail received at %s'),$sitename); - - $preamble = sprintf( t('%1$s, %2$s sent you a new private message at %3$s.'),$recip['channel_name'], $sender['xchan_name'],$sitename); - $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); - $sitelink = t('Please visit %s to view and/or reply to your private messages.'); - $tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/mail/' . $params['item']['id'] . '">' . $sitename . '</a>'); - $itemlink = $siteurl . '/mail/' . $params['item']['id']; - } - - if ($params['type'] == NOTIFY_COMMENT) { -// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); - - $itemlink = $params['link']; - - // ignore like/unlike activity on posts - they probably require a sepearate notification preference - - if (array_key_exists('item',$params) && (! visible_activity($params['item']))) - return; - - $parent_mid = $params['parent_mid']; - - // Check to see if there was already a notify for this post. - // If so don't create a second notification - - $p = null; - $p = q("select id from notify where link = '%s' and uid = %d limit 1", - dbesc($params['link']), - intval($recip['channel_id']) - ); - if ($p) { - logger('notification: comment already notified'); - pop_lang(); - return; - } - - - // if it's a post figure out who's post it is. - - $p = null; - - if($params['otype'] === 'item' && $parent_mid) { - $p = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($parent_mid), - intval($recip['channel_id']) - ); - } - - xchan_query($p); - - - $item_post_type = item_post_type($p[0]); -// $private = $p[0]['item_private']; - $parent_id = $p[0]['id']; - - $parent_item = $p[0]; - - //$possess_desc = str_replace('<!item_type!>',$possess_desc); - - // "a post" - $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]a %4$s[/zrl]'), - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink, - $item_post_type); - - // "George Bull's post" - if($p) - $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]%4$s\'s %5$s[/zrl]'), - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink, - $p[0]['author']['xchan_name'], - $item_post_type); - - // "your post" - if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) - $dest_str = sprintf(t('%1$s, %2$s commented on [zrl=%3$s]your %4$s[/zrl]'), - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink, - $item_post_type); - - // Some mail softwares relies on subject field for threading. - // So, we cannot have different subjects for notifications of the same thread. - // Before this we have the name of the replier on the subject rendering - // differents subjects for messages on the same thread. - - $subject = sprintf( t('[Hubzilla:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); - $preamble = sprintf( t('%1$s, %2$s commented on an item/conversation you have been following.'), $recip['channel_name'], $sender['xchan_name']); - $epreamble = $dest_str; - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - } - - if($params['type'] == NOTIFY_WALL) { - $subject = sprintf( t('[Hubzilla:Notify] %s posted to your profile wall') , $sender['xchan_name']); - - $preamble = sprintf( t('%1$s, %2$s posted to your profile wall at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename); - - $epreamble = sprintf( t('%1$s, %2$s posted to [zrl=%3$s]your wall[/zrl]') , - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_TAGSELF) { - - $p = null; - $p = q("select id from notify where link = '%s' and uid = %d limit 1", - dbesc($params['link']), - intval($recip['channel_id']) - ); - if ($p) { - logger('enotify: tag: already notified about this post'); - pop_lang(); - return; - } - - $subject = sprintf( t('[Hubzilla:Notify] %s tagged you') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s, %2$s tagged you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s, %2$s [zrl=%3$s]tagged you[/zrl].') , - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_POKE) { - $subject = sprintf( t('[Hubzilla:Notify] %1$s poked you') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s, %2$s poked you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s, %2$s [zrl=%2$s]poked you[/zrl].') , - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $params['link']); - - $subject = str_replace('poked', t($params['activity']), $subject); - $preamble = str_replace('poked', t($params['activity']), $preamble); - $epreamble = str_replace('poked', t($params['activity']), $epreamble); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_TAGSHARE) { - $subject = sprintf( t('[Hubzilla:Notify] %s tagged your post') , $sender['xchan_name']); - $preamble = sprintf( t('%1$s, %2$s tagged your post at %3$s') , $recip['channel_name'],$sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s, %2$s tagged [zrl=%3$s]your post[/zrl]') , - $recip['channel_name'], - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', - $itemlink); - - $sitelink = t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_INTRO) { - $subject = sprintf( t('[Hubzilla:Notify] Introduction received')); - $preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'), - $recip['channel_name'], - $siteurl . '/connections/ifpending', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); - $body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']); - - $sitelink = t('Please visit %s to approve or reject the connection request.'); - $tsitelink = sprintf( $sitelink, $siteurl . '/connections/ifpending'); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/connections/ifpending">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_SUGGEST) { - $subject = sprintf( t('[Hubzilla:Notify] Friend suggestion received')); - $preamble = sprintf( t('%1$s, you\'ve received a friend suggestion from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a friend suggestion[/zrl] for %3$s from %4$s.'), - $recip['channel_name'], - $itemlink, - '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', - '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); - - $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; - $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; - $body .= sprintf( t('You may visit their profile at %s'),$params['item']['url']); - - $sitelink = t('Please visit %s to approve or reject the suggestion.'); - $tsitelink = sprintf( $sitelink, $siteurl ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; - } - - if ($params['type'] == NOTIFY_CONFIRM) { - // ? - } - - if ($params['type'] == NOTIFY_SYSTEM) { - // ? - } - - $h = array( - 'params' => $params, - 'subject' => $subject, - 'preamble' => $preamble, - 'epreamble' => $epreamble, - 'body' => $body, - 'sitelink' => $sitelink, - 'sitename' => $sitename, - 'tsitelink' => $tsitelink, - 'hsitelink' => $hsitelink, - 'itemlink' => $itemlink, - 'sender' => $sender, - 'recipient' => $recip - ); - - call_hooks('enotify', $h); - - $subject = $h['subject']; - $preamble = $h['preamble']; - $epreamble = $h['epreamble']; - $body = $h['body']; - $sitelink = $h['sitelink']; - $tsitelink = $h['tsitelink']; - $hsitelink = $h['hsitelink']; - $itemlink = $h['itemlink']; - - - require_once('include/html2bbcode.php'); - - do { - $dups = false; - $hash = random_string(); - $r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' LIMIT 1", - dbesc($hash)); - if (count($r)) - $dups = true; - } while ($dups === true); - - - $datarray = array(); - $datarray['hash'] = $hash; - $datarray['sender_hash'] = $sender['xchan_hash']; - $datarray['name'] = $sender['xchan_name']; - $datarray['url'] = $sender['xchan_url']; - $datarray['photo'] = $sender['xchan_photo_s']; - $datarray['date'] = datetime_convert(); - $datarray['aid'] = $recip['channel_account_id']; - $datarray['uid'] = $recip['channel_id']; - $datarray['link'] = $itemlink; - $datarray['parent'] = $parent_mid; - $datarray['parent_item'] = $parent_item; - $datarray['type'] = $params['type']; - $datarray['verb'] = $params['verb']; - $datarray['otype'] = $params['otype']; - $datarray['abort'] = false; - - $datarray['item'] = $params['item']; - - call_hooks('enotify_store', $datarray); - - if ($datarray['abort']) { - pop_lang(); - return; - } - - - // create notification entry in DB - $seen = 0; - - // Mark some notifications as seen right away - // Note! The notification have to be created, because they are used to send emails - // So easiest solution to hide them from Notices is to mark them as seen right away. - // Another option would be to not add them to the DB, and change how emails are handled (probably would be better that way) - $always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices'); - if (!$always_show_in_notices) { - if (($params['type'] == NOTIFY_WALL) || ($params['type'] == NOTIFY_MAIL) || ($params['type'] == NOTIFY_INTRO)) { - $seen = 1; - } - } - - $r = q("insert into notify (hash,name,url,photo,date,aid,uid,link,parent,seen,type,verb,otype) - values('%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')", - dbesc($datarray['hash']), - dbesc($datarray['name']), - dbesc($datarray['url']), - dbesc($datarray['photo']), - dbesc($datarray['date']), - intval($datarray['aid']), - intval($datarray['uid']), - dbesc($datarray['link']), - dbesc($datarray['parent']), - intval($seen), - intval($datarray['type']), - dbesc($datarray['verb']), - dbesc($datarray['otype']) - ); - - $r = q("select id from notify where hash = '%s' and uid = %d limit 1", - dbesc($hash), - intval($recip['channel_id']) - ); - if ($r) { - $notify_id = $r[0]['id']; - } else { - logger('notification not found.'); - pop_lang(); - return; - } - - $itemlink = z_root() . '/notify/view/' . $notify_id; - $msg = str_replace('$itemlink',$itemlink,$epreamble); - - // wretched hack, but we don't want to duplicate all the preamble variations and we also don't want to screw up a translation - - if ((App::$language === 'en' || (! App::$language)) && strpos($msg,', ')) - $msg = substr($msg,strpos($msg,', ')+1); - - $r = q("update notify set msg = '%s' where id = %d and uid = %d", - dbesc($msg), - intval($notify_id), - intval($datarray['uid']) - ); - - // send email notification if notification preferences permit - - require_once('bbcode.php'); - if ((intval($recip['channel_notifyflags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) { - - logger('notification: sending notification email'); - - $hn = get_pconfig($recip['channel_id'],'system','email_notify_host'); - if($hn && (! stristr(App::get_hostname(),$hn))) { - // this isn't the email notification host - pop_lang(); - return; - } - - $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r", "\\n"), array( "", "\n"), $body))),ENT_QUOTES,'UTF-8')); - - $htmlversion = bbcode(stripslashes(str_replace(array("\\r","\\n"), array("","<br />\n"),$body))); - - - // use $_SESSION['zid_override'] to force zid() to use - // the recipient address instead of the current observer - - $_SESSION['zid_override'] = $recip['channel_address'] . '@' . App::get_hostname(); - $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; - - $textversion = zidify_links($textversion); - $htmlversion = zidify_links($htmlversion); - - // unset when done to revert to normal behaviour - - unset($_SESSION['zid_override']); - unset($_SESSION['zrl_override']); - - $datarray = array(); - $datarray['banner'] = $banner; - $datarray['product'] = $product; - $datarray['preamble'] = $preamble; - $datarray['sitename'] = $sitename; - $datarray['siteurl'] = $siteurl; - $datarray['type'] = $params['type']; - $datarray['parent'] = $params['parent_mid']; - $datarray['source_name'] = $sender['xchan_name']; - $datarray['source_link'] = $sender['xchan_url']; - $datarray['source_photo'] = $sender['xchan_photo_s']; - $datarray['uid'] = $recip['channel_id']; - $datarray['username'] = $recip['channel_name']; - $datarray['hsitelink'] = $hsitelink; - $datarray['tsitelink'] = $tsitelink; - $datarray['hitemlink'] = '<a href="' . $itemlink . '">' . $itemlink . '</a>'; - $datarray['titemlink'] = $itemlink; - $datarray['thanks'] = $thanks; - $datarray['site_admin'] = $site_admin; - $datarray['title'] = stripslashes($title); - $datarray['htmlversion'] = $htmlversion; - $datarray['textversion'] = $textversion; - $datarray['subject'] = $subject; - $datarray['headers'] = $additional_mail_header; - $datarray['email_secure'] = false; - - call_hooks('enotify_mail', $datarray); - - // Default to private - don't disclose message contents over insecure channels (such as email) - // Might be interesting to use GPG,PGP,S/MIME encryption instead - // but we'll save that for a clever plugin developer to implement - - $private_activity = false; - - if (! $datarray['email_secure']) { - switch ($params['type']) { - case NOTIFY_WALL: - case NOTIFY_TAGSELF: - case NOTIFY_POKE: - case NOTIFY_COMMENT: - if (! $private) - break; - $private_activity = true; - case NOTIFY_MAIL: - $datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = ''; - $datarray['subject'] = preg_replace('/' . preg_quote(t('[Hubzilla:Notify]')) . '/','$0*',$datarray['subject']); - break; - default: - break; - } - } - - if ($private_activity - && intval(get_pconfig($datarray['uid'], 'system', 'ignore_private_notifications'))) { - - pop_lang(); - return; - } - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_html.tpl'); - $email_html_body = replace_macros($tpl,array( - '$banner' => $datarray['banner'], - '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), - '$product' => $datarray['product'], - '$preamble' => $datarray['preamble'], - '$sitename' => $datarray['sitename'], - '$siteurl' => $datarray['siteurl'], - '$source_name' => $datarray['source_name'], - '$source_link' => $datarray['source_link'], - '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $datarray['thanks'], - '$site_admin' => $datarray['site_admin'], - '$title' => $datarray['title'], - '$htmlversion' => $datarray['htmlversion'], - )); - - // load the template for private message notifications - $tpl = get_markup_template('email_notify_text.tpl'); - $email_text_body = replace_macros($tpl, array( - '$banner' => $datarray['banner'], - '$product' => $datarray['product'], - '$preamble' => $datarray['preamble'], - '$sitename' => $datarray['sitename'], - '$siteurl' => $datarray['siteurl'], - '$source_name' => $datarray['source_name'], - '$source_link' => $datarray['source_link'], - '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], - '$tsitelink' => $datarray['tsitelink'], - '$titemlink' => $datarray['titemlink'], - '$thanks' => $datarray['thanks'], - '$site_admin' => $datarray['site_admin'], - '$title' => $datarray['title'], - '$textversion' => $datarray['textversion'], - )); - -// logger('text: ' . $email_text_body); - - // use the EmailNotification library to send the message - - enotify::send(array( - 'fromName' => $sender_name, - 'fromEmail' => $sender_email, - 'replyTo' => $sender_email, - 'toEmail' => $recip['account_email'], - 'messageSubject' => $datarray['subject'], - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, - 'additionalMailHeader' => $datarray['headers'], - )); - } - - pop_lang(); - -} - - -/** - * @brief A class for sending email notifications. - * - * @fixme Class names start mostly with capital letter to distinguish them easier. - */ -class enotify { - /** - * @brief Send a multipart/alternative message with Text and HTML versions. - * - * @param array $params an assoziative array with: - * * \e string \b fromName name of the sender - * * \e string \b fromEmail email of the sender - * * \e string \b replyTo replyTo address to direct responses - * * \e string \b toEmail destination email address - * * \e string \b messageSubject subject of the message - * * \e string \b htmlVersion html version of the message - * * \e string \b textVersion text only version of the message - * * \e string \b additionalMailHeader additions to the smtp mail header - */ - static public function send($params) { - - $fromName = email_header_encode(html_entity_decode($params['fromName'],ENT_QUOTES,'UTF-8'),'UTF-8'); - $messageSubject = email_header_encode(html_entity_decode($params['messageSubject'],ENT_QUOTES,'UTF-8'),'UTF-8'); - - // generate a mime boundary - $mimeBoundary = rand(0, 9) . "-" - .rand(10000000000, 9999999999) . "-" - .rand(10000000000, 9999999999) . "=:" - .rand(10000, 99999); - - // generate a multipart/alternative message header - $messageHeader = - $params['additionalMailHeader'] . - "From: $fromName <{$params['fromEmail']}>\n" . - "Reply-To: $fromName <{$params['replyTo']}>\n" . - "MIME-Version: 1.0\n" . - "Content-Type: multipart/alternative; boundary=\"{$mimeBoundary}\""; - - // assemble the final multipart message body with the text and html types included - $textBody = chunk_split(base64_encode($params['textVersion'])); - $htmlBody = chunk_split(base64_encode($params['htmlVersion'])); - - $multipartMessageBody = - "--" . $mimeBoundary . "\n" . // plain text section - "Content-Type: text/plain; charset=UTF-8\n" . - "Content-Transfer-Encoding: base64\n\n" . - $textBody . "\n" . - "--" . $mimeBoundary . "\n" . // text/html section - "Content-Type: text/html; charset=UTF-8\n" . - "Content-Transfer-Encoding: base64\n\n" . - $htmlBody . "\n" . - "--" . $mimeBoundary . "--\n"; // message ending - - // send the message - $res = mail( - $params['toEmail'], // send to address - $messageSubject, // subject - $multipartMessageBody, // message body - $messageHeader // message headers - ); - logger("notification: enotify::send returns " . $res, LOGGER_DEBUG); - } -} diff --git a/include/environment.php b/include/environment.php index 47ad241a7..11d465b84 100644 --- a/include/environment.php +++ b/include/environment.php @@ -60,6 +60,8 @@ function phpiniSizeToBytes($val) { $val *= 1024; case 'k': $val *= 1024; + default: + break; } return (int)$val; diff --git a/include/event.php b/include/event.php index 7a99bc746..3d650cd14 100644 --- a/include/event.php +++ b/include/event.php @@ -25,25 +25,25 @@ function format_event_html($ev) { $o = '<div class="vevent">' . "\r\n"; - $o .= '<div class="event-title"><h3><i class="icon-calendar"></i> ' . bbcode($ev['summary']) . '</h3></div>' . "\r\n"; + $o .= '<div class="event-title"><h3><i class="fa fa-calendar"></i> ' . bbcode($ev['summary']) . '</h3></div>' . "\r\n"; $o .= '<div class="event-start"><span class="event-label">' . t('Starts:') . '</span> <span class="dtstart" title="' - . datetime_convert('UTC', 'UTC', $ev['start'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )) + . datetime_convert('UTC', 'UTC', $ev['dtstart'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )) . '" >' . (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), - $ev['start'] , $bd_format )) + $ev['dtstart'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', - $ev['start'] , $bd_format))) + $ev['dtstart'] , $bd_format))) . '</span></div>' . "\r\n"; if(! $ev['nofinish']) $o .= '<div class="event-end" ><span class="event-label">' . t('Finishes:') . '</span> <span class="dtend" title="' - . datetime_convert('UTC','UTC',$ev['finish'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )) + . datetime_convert('UTC','UTC',$ev['dtend'], (($ev['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )) . '" >' . (($ev['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), - $ev['finish'] , $bd_format )) + $ev['dtend'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', - $ev['finish'] , $bd_format ))) + $ev['dtend'] , $bd_format ))) . '</span></div>' . "\r\n"; $o .= '<div class="event-description">' . bbcode($ev['description']) . '</div>' . "\r\n"; @@ -58,6 +58,37 @@ function format_event_html($ev) { return $o; } +function format_event_obj($jobject) { + $event = array(); + + $object = json_decode($jobject,true); + + //ensure compatibility with older items - this check can be removed at a later point + if(array_key_exists('description', $object)) { + + $bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM + + $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'),array( + '$title' => bbcode($object['title']), + '$dtstart_label' => t('Starts:'), + '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['dtstart'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), + '$dtstart_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['dtstart'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtstart'] , $bd_format))), + '$finish' => (($object['nofinish']) ? false : true), + '$dtend_label' => t('Finishes:'), + '$dtend_title' => datetime_convert('UTC','UTC',$object['dtend'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), + '$dtend_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['dtend'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['dtend'] , $bd_format ))) + )); + + $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'),array( + '$description' => bbcode($object['description']), + '$location_label' => t('Location:'), + '$location' => bbcode($object['location']) + )); + + } + + return $event; +} function ical_wrapper($ev) { @@ -67,8 +98,8 @@ function ical_wrapper($ev) { $o .= "BEGIN:VCALENDAR"; $o .= "\r\nVERSION:2.0"; $o .= "\r\nMETHOD:PUBLISH"; - $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . Zotlabs\Project\System::get_platform_name() . "//" . strtoupper(App::$language). "\r\n"; - if(array_key_exists('start', $ev)) + $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . Zotlabs\Lib\System::get_platform_name() . "//" . strtoupper(App::$language). "\r\n"; + if(array_key_exists('dtstart', $ev)) $o .= format_event_ical($ev); else { foreach($ev as $e) { @@ -82,7 +113,7 @@ function ical_wrapper($ev) { function format_event_ical($ev) { - if($ev['type'] === 'task') + if($ev['etype'] === 'task') return format_todo_ical($ev); $o = ''; @@ -92,10 +123,10 @@ function format_event_ical($ev) { $o .= "\r\nCREATED:" . datetime_convert('UTC','UTC', $ev['created'],'Ymd\\THis\\Z'); $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); $o .= "\r\nDTSTAMP:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - if($ev['start']) - $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['start'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['finish'] && ! $ev['nofinish']) - $o .= "\r\nDTEND:" . datetime_convert('UTC','UTC', $ev['finish'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + if($ev['dtstart']) + $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + if($ev['dtend'] && ! $ev['nofinish']) + $o .= "\r\nDTEND:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); if($ev['summary']) $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); if($ev['location']) @@ -119,10 +150,10 @@ function format_todo_ical($ev) { $o .= "\r\nCREATED:" . datetime_convert('UTC','UTC', $ev['created'],'Ymd\\THis\\Z'); $o .= "\r\nLAST-MODIFIED:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); $o .= "\r\nDTSTAMP:" . datetime_convert('UTC','UTC', $ev['edited'],'Ymd\\THis\\Z'); - if($ev['start']) - $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['start'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); - if($ev['finish'] && ! $ev['nofinish']) - $o .= "\r\nDUE:" . datetime_convert('UTC','UTC', $ev['finish'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + if($ev['dtstart']) + $o .= "\r\nDTSTART:" . datetime_convert('UTC','UTC', $ev['dtstart'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); + if($ev['dtend'] && ! $ev['nofinish']) + $o .= "\r\nDUE:" . datetime_convert('UTC','UTC', $ev['dtend'],'Ymd\\THis' . (($ev['adjust']) ? '\\Z' : '')); if($ev['summary']) $o .= "\r\nSUMMARY:" . format_ical_text($ev['summary']); if($ev['event_status']) { @@ -152,7 +183,9 @@ function format_ical_text($s) { require_once('include/bbcode.php'); require_once('include/html2plain.php'); - return(wordwrap(str_replace(array(',',';','\\'),array('\\,','\\;','\\\\'),html2plain(bbcode($s))),72,"\r\n ",true)); + $s = html2plain(bbcode($s)); + $s = str_replace(["\r\n","\n"],["",""],$s); + return(wordwrap(str_replace(['\\',',',';'],['\\\\','\\,','\\;'],$s),72,"\r\n ",true)); } @@ -166,15 +199,18 @@ function format_event_bbcode($ev) { if($ev['description']) $o .= '[event-description]' . $ev['description'] . '[/event-description]'; - if($ev['start']) - $o .= '[event-start]' . $ev['start'] . '[/event-start]'; + if($ev['dtstart']) + $o .= '[event-start]' . $ev['dtstart'] . '[/event-start]'; - if(($ev['finish']) && (! $ev['nofinish'])) - $o .= '[event-finish]' . $ev['finish'] . '[/event-finish]'; + if(($ev['dtend']) && (! $ev['nofinish'])) + $o .= '[event-finish]' . $ev['dtend'] . '[/event-finish]'; if($ev['location']) $o .= '[event-location]' . $ev['location'] . '[/event-location]'; + if($ev['event_hash']) + $o .= '[event-id]' . $ev['event_hash'] . '[/event-id]'; + if($ev['adjust']) $o .= '[event-adjust]' . $ev['adjust'] . '[/event-adjust]'; @@ -204,21 +240,24 @@ function bbtoevent($s) { $ev['description'] = $match[1]; $match = ''; if(preg_match("/\[event\-start\](.*?)\[\/event\-start\]/is",$s,$match)) - $ev['start'] = $match[1]; + $ev['dtstart'] = $match[1]; $match = ''; if(preg_match("/\[event\-finish\](.*?)\[\/event\-finish\]/is",$s,$match)) - $ev['finish'] = $match[1]; + $ev['dtend'] = $match[1]; $match = ''; if(preg_match("/\[event\-location\](.*?)\[\/event\-location\]/is",$s,$match)) $ev['location'] = $match[1]; $match = ''; + if(preg_match("/\[event\-id\](.*?)\[\/event\-id\]/is",$s,$match)) + $ev['event_hash'] = $match[1]; + $match = ''; if(preg_match("/\[event\-adjust\](.*?)\[\/event\-adjust\]/is",$s,$match)) $ev['adjust'] = $match[1]; - if(array_key_exists('start',$ev)) { - if(array_key_exists('finish',$ev)) { - if($ev['finish'] === $ev['start']) + if(array_key_exists('dtstart',$ev)) { + if(array_key_exists('dtend',$ev)) { + if($ev['dtend'] === $ev['dtstart']) $ev['nofinish'] = 1; - elseif($ev['finish']) + elseif($ev['dtend']) $ev['nofinish'] = 0; else $ev['nofinish'] = 1; @@ -254,8 +293,8 @@ function sort_by_date($arr) { */ function ev_compare($a, $b) { - $date_a = (($a['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$a['start']) : $a['start']); - $date_b = (($b['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$b['start']) : $b['start']); + $date_a = (($a['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$a['dtstart']) : $a['dtstart']); + $date_b = (($b['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$b['dtstart']) : $b['dtstart']); if ($date_a === $date_b) return strcasecmp($a['description'], $b['description']); @@ -268,7 +307,7 @@ function event_store_event($arr) { $arr['created'] = (($arr['created']) ? $arr['created'] : datetime_convert()); $arr['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert()); - $arr['type'] = (($arr['type']) ? $arr['type'] : 'event' ); + $arr['etype'] = (($arr['etype']) ? $arr['etype'] : 'event' ); $arr['event_xchan'] = (($arr['event_xchan']) ? $arr['event_xchan'] : ''); $arr['event_priority'] = (($arr['event_priority']) ? $arr['event_priority'] : 0); @@ -278,45 +317,52 @@ function event_store_event($arr) { else $arr['event_status_date'] = NULL_DATE; - // Existing event being modified - if($arr['id'] || $arr['event_hash']) { + $existing_event = null; - // has the event actually changed? + if($arr['event_hash']) { + $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", + dbesc($arr['event_hash']), + intval($arr['uid']) + ); + if($r) { + $existing_event = $r[0]; + } + } - if($arr['event_hash']) { - $r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1", - dbesc($arr['event_hash']), - intval($arr['uid']) - ); + if($arr['id']) { + $r = q("SELECT * FROM event WHERE id = %d AND uid = %d LIMIT 1", + intval($arr['id']), + intval($arr['uid']) + ); + if($r) { + $existing_event = $r[0]; } else { - $r = q("SELECT * FROM event WHERE id = %d AND uid = %d LIMIT 1", - intval($arr['id']), - intval($arr['uid']) - ); + return false; } + } - if(! $r) - return false; - if($r[0]['edited'] === $arr['edited']) { - // Nothing has changed. Return the ID. - return $r[0]; + if($existing_event) { + + if($existing_event['edited'] >= $arr['edited']) { + // Nothing has changed. + return $existing_event; } - $hash = $r[0]['event_hash']; + $hash = $existing_event['event_hash']; // The event changed. Update it. $r = q("UPDATE `event` SET `edited` = '%s', - `start` = '%s', - `finish` = '%s', + `dtstart` = '%s', + `dtend` = '%s', `summary` = '%s', `description` = '%s', `location` = '%s', - `type` = '%s', + `etype` = '%s', `adjust` = %d, `nofinish` = %d, `event_status` = '%s', @@ -332,12 +378,12 @@ function event_store_event($arr) { WHERE `id` = %d AND `uid` = %d", dbesc($arr['edited']), - dbesc($arr['start']), - dbesc($arr['finish']), + dbesc($arr['dtstart']), + dbesc($arr['dtend']), dbesc($arr['summary']), dbesc($arr['description']), dbesc($arr['location']), - dbesc($arr['type']), + dbesc($arr['etype']), intval($arr['adjust']), intval($arr['nofinish']), dbesc($arr['event_status']), @@ -350,7 +396,7 @@ function event_store_event($arr) { dbesc($arr['allow_gid']), dbesc($arr['deny_cid']), dbesc($arr['deny_gid']), - intval($r[0]['id']), + intval($existing_event['id']), intval($arr['uid']) ); } else { @@ -360,10 +406,12 @@ function event_store_event($arr) { if(array_key_exists('external_id',$arr)) $hash = $arr['external_id']; + elseif(array_key_exists('event_hash',$arr)) + $hash = $arr['event_hash']; else $hash = random_string() . '@' . App::get_hostname(); - $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,start,finish,summary,description,location,type, + $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, allow_cid,allow_gid,deny_cid,deny_gid) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s' ) ", intval($arr['uid']), @@ -372,12 +420,12 @@ function event_store_event($arr) { dbesc($hash), dbesc($arr['created']), dbesc($arr['edited']), - dbesc($arr['start']), - dbesc($arr['finish']), + dbesc($arr['dtstart']), + dbesc($arr['dtend']), dbesc($arr['summary']), dbesc($arr['description']), dbesc($arr['location']), - dbesc($arr['type']), + dbesc($arr['etype']), intval($arr['adjust']), intval($arr['nofinish']), dbesc($arr['event_status']), @@ -426,7 +474,7 @@ function event_addtocal($item_id, $uid) { $ev = bbtoevent($r[0]['body']); - if(x($ev,'summary') && x($ev,'start')) { + if(x($ev,'summary') && x($ev,'dtstart')) { $ev['event_xchan'] = $item['author_xchan']; $ev['uid'] = $channel['channel_id']; $ev['account'] = $channel['channel_account_id']; @@ -436,7 +484,7 @@ function event_addtocal($item_id, $uid) { // is this an edit? - if($item['resource_type'] === 'event') { + if($item['resource_type'] === 'event' && (! $ev['event_hash'])) { $ev['event_hash'] = $item['resource_id']; } @@ -472,7 +520,6 @@ function event_addtocal($item_id, $uid) { if($z) { build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z)); } - return true; } } @@ -536,34 +583,24 @@ function event_import_ical($ical, $uid) { } $dtstart = $ical->DTSTART->getDateTime(); + $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 1 : 0); // logger('dtstart: ' . var_export($dtstart,true)); - - switch($dtstart->timezone_type) { - case VObject\Property\DateTime::UTC : - $ev['adjust'] = 0; - break; - case VObject\Property\DateTime::LOCALTZ : - default: - $ev['adjust'] = 1; - break; - } - - $ev['start'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', + $ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', $dtstart->format(\DateTime::W3C)); if(isset($ical->DTEND)) { $dtend = $ical->DTEND->getDateTime(); - $ev['finish'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', + $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', $dtend->format(\DateTime::W3C)); } else $ev['nofinish'] = 1; - if($ev['start'] === $ev['finish']) + if($ev['dtstart'] === $ev['dtend']) $ev['nofinish'] = 1; if(isset($ical->CREATED)) { @@ -597,7 +634,7 @@ function event_import_ical($ical, $uid) { $ev['external_id'] = $evuid; } - if($ev['summary'] && $ev['start']) { + if($ev['summary'] && $ev['dtstart']) { $ev['event_xchan'] = $channel['channel_hash']; $ev['uid'] = $channel['channel_id']; $ev['account'] = $channel['channel_account_id']; @@ -636,29 +673,24 @@ function event_import_ical_task($ical, $uid) { $dtstart = $ical->DTSTART->getDateTime(); + $ev['adjust'] = (($ical->DTSTART->isFloating()) ? 1 : 0); + // logger('dtstart: ' . var_export($dtstart,true)); - if(($dtstart->timezone_type == 2) || (($dtstart->timezone_type == 3) && ($dtstart->timezone === 'UTC'))) { - $ev['adjust'] = 1; - } - else { - $ev['adjust'] = 0; - } - - $ev['start'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', + $ev['dtstart'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', $dtstart->format(\DateTime::W3C)); if(isset($ical->DUE)) { $dtend = $ical->DUE->getDateTime(); - $ev['finish'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', + $ev['dtend'] = datetime_convert((($ev['adjust']) ? 'UTC' : date_default_timezone_get()),'UTC', $dtend->format(\DateTime::W3C)); } else $ev['nofinish'] = 1; - if($ev['start'] === $ev['finish']) + if($ev['dtstart'] === $ev['dtend']) $ev['nofinish'] = 1; if(isset($ical->CREATED)) { @@ -723,9 +755,9 @@ function event_import_ical_task($ical, $uid) { $ev['event_percent'] = (string) $ical->{'PERCENT-COMPLETE'} ; } - $ev['type'] = 'task'; + $ev['etype'] = 'task'; - if($ev['summary'] && $ev['start']) { + if($ev['summary'] && $ev['dtstart']) { $ev['event_xchan'] = $channel['channel_hash']; $ev['uid'] = $channel['channel_id']; $ev['account'] = $channel['channel_account_id']; @@ -774,7 +806,10 @@ function event_store_item($arr, $event) { $prefix = ''; // $birthday = false; - if($event['type'] === 'birthday') { + if(($event) && array_key_exists('event_hash',$event) && (! array_key_exists('event_hash',$arr))) + $arr['event_hash'] = $event['event_hash']; + + if($event['etype'] === 'birthday') { if(! is_sys_channel($arr['uid'])) $prefix = t('This event has been added to your calendar.'); // $birthday = true; @@ -798,21 +833,22 @@ function event_store_item($arr, $event) { 'type' => ACTIVITY_OBJ_EVENT, 'id' => z_root() . '/event/' . $r[0]['resource_id'], 'title' => $arr['summary'], - 'start' => $arr['start'], - 'finish' => $arr['finish'], + 'dtstart' => $arr['dtstart'], + 'dtend' => $arr['dtend'], 'nofinish' => $arr['nofinish'], 'description' => $arr['description'], 'location' => $arr['location'], 'adjust' => $arr['adjust'], 'content' => format_event_bbcode($arr), 'author' => array( - 'name' => $r[0]['xchan_name'], - 'address' => $r[0]['xchan_addr'], - 'guid' => $r[0]['xchan_guid'], - 'guid_sig' => $r[0]['xchan_guid_sig'], - 'link' => array( - array('rel' => 'alternate', 'type' => 'text/html', 'href' => $r[0]['xchan_url']), - array('rel' => 'photo', 'type' => $r[0]['xchan_photo_mimetype'], 'href' => $r[0]['xchan_photo_m'])), + 'name' => $r[0]['xchan_name'], + 'address' => $r[0]['xchan_addr'], + 'guid' => $r[0]['xchan_guid'], + 'guid_sig' => $r[0]['xchan_guid_sig'], + 'link' => array( + array('rel' => 'alternate', 'type' => 'text/html', 'href' => $r[0]['xchan_url']), + array('rel' => 'photo', 'type' => $r[0]['xchan_photo_mimetype'], 'href' => $r[0]['xchan_photo_m']) + ), ), )); @@ -823,7 +859,7 @@ function event_store_item($arr, $event) { $sig = ''; - q("UPDATE item SET title = '%s', body = '%s', object = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', sig = '%s', item_flags = %d, item_private = %d, obj_type = '%s' WHERE id = %d AND uid = %d", + q("UPDATE item SET title = '%s', body = '%s', obj = '%s', allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', edited = '%s', sig = '%s', item_flags = %d, item_private = %d, obj_type = '%s' WHERE id = %d AND uid = %d", dbesc($arr['summary']), dbesc($prefix . format_event_bbcode($arr)), dbesc($object), @@ -847,12 +883,12 @@ function event_store_item($arr, $event) { if(($arr['term']) && (is_array($arr['term']))) { foreach($arr['term'] as $t) { - q("insert into term (uid,oid,otype,type,term,url) + q("insert into term (uid,oid,otype,ttype,term,url) values(%d,%d,%d,%d,'%s','%s') ", intval($arr['uid']), intval($r[0]['id']), intval(TERM_OBJ_POST), - intval($t['type']), + intval($t['ttype']), dbesc($t['term']), dbesc($t['url']) ); @@ -939,12 +975,12 @@ function event_store_item($arr, $event) { dbesc($arr['event_xchan']) ); if($x) { - $item_arr['object'] = json_encode(array( + $item_arr['obj'] = json_encode(array( 'type' => ACTIVITY_OBJ_EVENT, 'id' => z_root() . '/event/' . $event['event_hash'], 'title' => $arr['summary'], - 'start' => $arr['start'], - 'finish' => $arr['finish'], + 'dtstart' => $arr['dtstart'], + 'dtend' => $arr['dtend'], 'nofinish' => $arr['nofinish'], 'description' => $arr['description'], 'location' => $arr['location'], @@ -994,7 +1030,7 @@ function tasks_fetch($arr) { if($arr && $arr['all'] == 1) $sql_extra = ''; - $r = q("select * from event where type = 'task' and uid = %d $sql_extra order by created desc", + $r = q("select * from event where etype = 'task' and uid = %d $sql_extra order by created desc", intval(local_channel()) ); diff --git a/include/expire.php b/include/expire.php deleted file mode 100644 index e75594b5f..000000000 --- a/include/expire.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * @file include/expire.php - */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); - -function expire_run($argv, $argc){ - - cli_startup(); - - // perform final cleanup on previously delete items - - $r = q("select id from item where item_deleted = 1 and item_pending_remove = 0 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('10 DAY') - ); - if ($r) { - foreach ($r as $rr) { - drop_item($rr['id'], false, DROPITEM_PHASE2); - } - } - - // physically remove anything that has been deleted for more than two months - /** @FIXME - this is a wretchedly inefficient query */ - - $r = q("delete from item where item_pending_remove = 1 and changed < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('36 DAY') - ); - - /** @FIXME make this optional as it could have a performance impact on large sites */ - - if (intval(get_config('system', 'optimize_items'))) - q("optimize table item"); - - logger('expire: start', LOGGER_DEBUG); - - $site_expire = get_config('system', 'default_expire_days'); - - logger('site_expire: ' . $site_expire); - - $r = q("SELECT channel_id, channel_address, channel_pageflags, channel_expire_days from channel where true"); - - if ($r) { - foreach ($r as $rr) { - - // expire the sys channel separately - if (intval($rr['channel_system'])) - continue; - - // service class default (if non-zero) over-rides the site default - - $service_class_expire = service_class_fetch($rr['channel_id'], 'expire_days'); - if (intval($service_class_expire)) - $channel_expire = $service_class_expire; - else - $channel_expire = $site_expire; - - if (intval($channel_expire) && (intval($channel_expire) < intval($rr['channel_expire_days'])) || - intval($rr['channel_expire_days'] == 0)) { - $expire_days = $channel_expire; - } else { - $expire_days = $rr['channel_expire_days']; - } - - // if the site or service class expiration is non-zero and less than person expiration, use that - logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG); - item_expire($rr['channel_id'], $expire_days); - } - } - - $x = get_sys_channel(); - if ($x) { - - // this should probably just fetch the channel_expire_days from the sys channel, - // but there's no convenient way to set it. - - $expire_days = get_config('system', 'sys_expire_days'); - if ($expire_days === false) - $expire_days = 30; - - if (intval($site_expire) && (intval($site_expire) < intval($expire_days))) { - $expire_days = $site_expire; - } - - logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG); - - if ($expire_days) - item_expire($x['channel_id'], $expire_days); - - logger('Expire: sys: done', LOGGER_DEBUG); - } -} - -if (array_search(__file__, get_included_files()) === 0){ - expire_run($argv, $argc); - killme(); -} diff --git a/include/externals.php b/include/externals.php deleted file mode 100644 index 3a3a32420..000000000 --- a/include/externals.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/identity.php'); - -function externals_run($argv, $argc){ - - cli_startup(); - $a = get_app(); - - - $total = 0; - $attempts = 0; - - logger('externals: startup', LOGGER_DEBUG); - - // pull in some public posts - - - while($total == 0 && $attempts < 3) { - $arr = array('url' => ''); - call_hooks('externals_url_select',$arr); - - if($arr['url']) { - $url = $arr['url']; - } - else { - $randfunc = db_getfunc('RAND'); - - // fixme this query does not deal with directory realms. - - $r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1", - dbesc(z_root()), - intval(DIRECTORY_MODE_STANDALONE), - intval(SITE_TYPE_ZOT) - ); - if($r) - $url = $r[0]['site_url']; - } - - $blacklisted = false; - - if(! check_siteallowed($url)) { - logger('blacklisted site: ' . $url); - $blacklisted = true; - } - - $attempts ++; - - // make sure we can eventually break out if somebody blacklists all known sites - - if($blacklisted) { - if($attempts > 20) - break; - $attempts --; - continue; - } - - if($url) { - if($r[0]['site_pull'] !== NULL_DATE) - $mindate = urlencode(datetime_convert('','',$r[0]['site_pull'] . ' - 1 day')); - else { - $days = get_config('externals','since_days'); - if($days === false) - $days = 15; - $mindate = urlencode(datetime_convert('','','now - ' . intval($days) . ' days')); - } - - $feedurl = $url . '/zotfeed?f=&mindate=' . $mindate; - - logger('externals: pulling public content from ' . $feedurl, LOGGER_DEBUG); - - $x = z_fetch_url($feedurl); - if(($x) && ($x['success'])) { - - q("update site set site_pull = '%s' where site_url = '%s'", - dbesc(datetime_convert()), - dbesc($url) - ); - - $j = json_decode($x['body'],true); - if($j['success'] && $j['messages']) { - $sys = get_sys_channel(); - foreach($j['messages'] as $message) { - // on these posts, clear any route info. - $message['route'] = ''; - $results = process_delivery(array('hash' => 'undefined'), get_item_elements($message), - array(array('hash' => $sys['xchan_hash'])), false, true); - $total ++; - } - logger('externals: import_public_posts: ' . $total . ' messages imported', LOGGER_DEBUG); - } - } - } - } -} - -if (array_search(__file__,get_included_files())===0){ - externals_run($argv,$argc); - killme(); -} diff --git a/include/features.php b/include/features.php index 38700f9f5..2d71aa9be 100644 --- a/include/features.php +++ b/include/features.php @@ -52,6 +52,7 @@ function get_features($filtered = true) { array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false,get_config('feature_lock','advanced_profiles')), array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false,get_config('feature_lock','profile_export')), array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false,get_config('feature_lock','webpages')), + array('wiki', t('Wiki'), t('Provide a wiki for your channel'),((UNO) ? false : true),get_config('feature_lock','wiki')), array('hide_rating', t('Hide Rating'), t('Hide the rating buttons on your channel and profile pages. Note: People can still rate you somewhere else.'),false,get_config('feature_lock','hide_rating')), array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders (note: not encrypted)'),false,get_config('feature_lock','private_notes')), array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu'),false,get_config('feature_lock','nav_channel_select')), @@ -94,6 +95,7 @@ function get_features($filtered = true) { t('Post/Comment Tools'), array('commtag', t('Community Tagging'), t('Ability to tag existing posts'),false,get_config('feature_lock','commtag')), array('categories', t('Post Categories'), t('Add categories to your posts'),false,get_config('feature_lock','categories')), + array('emojis', t('Emoji Reactions'), t('Add emoji reaction ability to posts'),true,get_config('feature_lock','emojis')), array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false,get_config('feature_lock','filing')), array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false,get_config('feature_lock','dislike')), array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false,get_config('feature_lock','star_posts')), diff --git a/include/feedutils.php b/include/feedutils.php new file mode 100644 index 000000000..01ec0687e --- /dev/null +++ b/include/feedutils.php @@ -0,0 +1,1315 @@ +<?php + + +/** + * @brief Generate an Atom feed. + * + * @param array $channel + * @param array $params + */ +function get_public_feed($channel, $params) { + + $type = 'xml'; + $begin = NULL_DATE; + $end = ''; + $start = 0; + $records = 40; + $direction = 'desc'; + $pages = 0; + + if(! $params) + $params = array(); + + $params['type'] = ((x($params,'type')) ? $params['type'] : 'xml'); + $params['begin'] = ((x($params,'begin')) ? $params['begin'] : NULL_DATE); + $params['end'] = ((x($params,'end')) ? $params['end'] : datetime_convert('UTC','UTC','now')); + $params['start'] = ((x($params,'start')) ? $params['start'] : 0); + $params['records'] = ((x($params,'records')) ? $params['records'] : 40); + $params['direction'] = ((x($params,'direction')) ? $params['direction'] : 'desc'); + $params['pages'] = ((x($params,'pages')) ? intval($params['pages']) : 0); + $params['top'] = ((x($params,'top')) ? intval($params['top']) : 0); + $params['cat'] = ((x($params,'cat')) ? $params['cat'] : ''); + + + // put a sane lower limit on feed requests if not specified + +// if($params['begin'] === NULL_DATE) +// $params['begin'] = datetime_convert('UTC','UTC','now - 1 month'); + + switch($params['type']) { + case 'json': + header("Content-type: application/atom+json"); + break; + case 'xml': + default: + header("Content-type: application/atom+xml"); + break; + } + + return get_feed_for($channel, get_observer_hash(), $params); +} + +/** + * @brief + * + * @param array $channel + * @param string $observer_hash + * @param array $params + * @return string + */ +function get_feed_for($channel, $observer_hash, $params) { + + if(! channel) + http_status_exit(401); + + if($params['pages']) { + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) + http_status_exit(403); + } else { + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) + http_status_exit(403); + } + $items = items_fetch(array( + 'wall' => '1', + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], + 'start' => $params['start'], // FIXME + 'records' => $params['records'], // FIXME + 'direction' => $params['direction'], // FIXME + 'pages' => $params['pages'], + 'order' => 'post', + 'top' => $params['top'], + 'cat' => $params['cat'] + ), $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module); + + + $feed_template = get_markup_template('atom_feed.tpl'); + + $atom = ''; + + $atom .= replace_macros($feed_template, array( + '$version' => xmlify(Zotlabs\Lib\System::get_project_version()), + '$red' => xmlify(Zotlabs\Lib\System::get_platform_name()), + '$feed_id' => xmlify($channel['xchan_url']), + '$feed_title' => xmlify($channel['channel_name']), + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , + '$hub' => '', // feed_hublinks(), + '$salmon' => '', // feed_salmonlinks($channel['channel_address']), + '$name' => xmlify($channel['channel_name']), + '$profile_page' => xmlify($channel['xchan_url']), + '$mimephoto' => xmlify($channel['xchan_photo_mimetype']), + '$photo' => xmlify($channel['xchan_photo_l']), + '$thumb' => xmlify($channel['xchan_photo_m']), + '$picdate' => '', + '$uridate' => '', + '$namdate' => '', + '$birthday' => '', + '$community' => '', + )); + + + call_hooks('atom_feed', $atom); + + if($items) { + $type = 'html'; + foreach($items as $item) { + if($item['item_private']) + continue; + + /** @BUG $owner is undefined in this call */ + $atom .= atom_entry($item, $type, null, $owner, true); + } + } + + call_hooks('atom_feed_end', $atom); + + $atom .= '</feed>' . "\r\n"; + + return $atom; +} + +/** + * @brief + * + * @param array $item an associative array with + * * \b string \b verb + * @return string item's verb if set, default ACTIVITY_POST see boot.php + */ +function construct_verb($item) { + if ($item['verb']) + return $item['verb']; + + return ACTIVITY_POST; +} + +function construct_activity_object($item) { + + if($item['obj']) { + $o = '<as:object>' . "\r\n"; + $r = json_decode($item['obj'],false); + + if(! $r) + return ''; + if($r->type) + $o .= '<as:obj_type>' . xmlify($r->type) . '</as:obj_type>' . "\r\n"; + if($r->id) + $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; + if($r->title) + $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; + if($r->links) { + /** @FIXME!! */ + if(substr($r->link,0,1) === '<') { + $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link); + $o .= $r->link; + } + else + $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n"; + } + if($r->content) + $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n"; + $o .= '</as:object>' . "\r\n"; + return $o; + } + + return ''; +} + +function construct_activity_target($item) { + + if($item['target']) { + $o = '<as:target>' . "\r\n"; + $r = json_decode($item['target'],false); + if(! $r) + return ''; + if($r->type) + $o .= '<as:obj_type>' . xmlify($r->type) . '</as:obj_type>' . "\r\n"; + if($r->id) + $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; + if($r->title) + $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; + if($r->links) { + /** @FIXME !!! */ + if(substr($r->link,0,1) === '<') { + if(strstr($r->link,'&') && (! strstr($r->link,'&'))) + $r->link = str_replace('&','&', $r->link); + $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link); + $o .= $r->link; + } + else + $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n"; + } + if($r->content) + $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n"; + + $o .= '</as:target>' . "\r\n"; + + return $o; + } + + return ''; +} + +/** + * @param object $feed + * @param array $item + * @param[out] array $author + * @return multitype:multitype: string NULL number Ambigous <NULL, string, number> Ambigous <mixed, string> Ambigous <multitype:multitype:string Ambigous <NULL, string> , multitype:multitype:string unknown > multitype:NULL unknown + */ +function get_atom_elements($feed, $item, &$author) { + + //$best_photo = array(); + + $res = array(); + + $found_author = $item->get_author(); + if($found_author) { + $author['author_name'] = unxmlify($found_author->get_name()); + $author['author_link'] = unxmlify($found_author->get_link()); + $author['author_is_feed'] = false; + } + else { + $author['author_name'] = unxmlify($feed->get_title()); + $author['author_link'] = unxmlify($feed->get_permalink()); + $author['author_is_feed'] = true; + } + + if(substr($author['author_link'],-1,1) == '/') + $author['author_link'] = substr($author['author_link'],0,-1); + + $res['mid'] = base64url_encode(unxmlify($item->get_id())); + $res['title'] = unxmlify($item->get_title()); + $res['body'] = unxmlify($item->get_content()); + $res['plink'] = unxmlify($item->get_link(0)); + $res['item_rss'] = 1; + + + // removing the content of the title if its identically to the body + // This helps with auto generated titles e.g. from tumblr + + if (title_is_body($res["title"], $res["body"])) + $res['title'] = ""; + + if($res['plink']) + $base_url = implode('/', array_slice(explode('/',$res['plink']),0,3)); + else + $base_url = ''; + + // look for a photo. We should check media size and find the best one, + // but for now let's just find any author photo + + $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); + + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + } + } + } + + $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); + + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { + $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + if($base && count($base)) { + foreach($base as $link) { + if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) + $author['author_link'] = unxmlify($link['attribs']['']['href']); + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + } + } + } + } + + // check for a yahoo media element (github etc.) + + if(! $author['author_photo']) { + $rawmedia = $item->get_item_tags(NAMESPACE_YMEDIA,'thumbnail'); + if($rawmedia && $rawmedia[0]['attribs']['']['url']) { + $author['author_photo'] = strip_tags(unxmlify($rawmedia[0]['attribs']['']['url'])); + } + } + + + // No photo/profile-link on the item - look at the feed level + + if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) { + $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); + if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link'])) { + $author['author_link'] = unxmlify($link['attribs']['']['href']); + $author['author_is_feed'] = true; + } + if(! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + } + } + } + + $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); + + if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { + $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + + if($base && count($base)) { + foreach($base as $link) { + if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) + $author['author_link'] = unxmlify($link['attribs']['']['href']); + if(! (x($author,'author_photo'))) { + if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + } + } + } + } + } + + $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info'); + if($apps && $apps[0]['attribs']['']['source']) { + $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); + } + + /* + * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it. + */ + + $have_real_body = false; + + $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env'); + if($rawenv) { + $have_real_body = true; + $res['body'] = $rawenv[0]['data']; + $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']); + // make sure nobody is trying to sneak some html tags by us + $res['body'] = notags(base64url_decode($res['body'])); + + // We could probably turn these old Friendica bbcode bookmarks into bookmark tags but we'd have to + // create a term table item for them. For now just make sure they stay as links. + + $res['body'] = preg_replace('/\[bookmark(.*?)\](.*?)\[\/bookmark\]/','[url$1]$2[/url]',$res['body']); + } + + $res['body'] = limit_body_size($res['body']); + + // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust + // the content type. Our own network only emits text normally, though it might have been converted to + // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will + // have to assume it is all html and needs to be purified. + + // It doesn't matter all that much security wise - because before this content is used anywhere, we are + // going to escape any tags we find regardless, but this lets us import a limited subset of html from + // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining + // html. + + if((strpos($res['body'],'<') !== false) && (strpos($res['body'],'>') !== false)) { + + $res['body'] = reltoabs($res['body'],$base_url); + + $res['body'] = html2bb_video($res['body']); + + $res['body'] = oembed_html2bbcode($res['body']); + + $res['body'] = purify_html($res['body']); + + $res['body'] = @html2bbcode($res['body']); + } + elseif(! $have_real_body) { + + // it's not one of our messages and it has no tags + // so it's probably just text. We'll escape it just to be safe. + + $res['body'] = escape_tags($res['body']); + } + + if($res['plink'] && $res['title']) { + $res['body'] = '#^[url=' . $res['plink'] . ']' . $res['title'] . '[/url]' . "\n\n" . $res['body']; + $terms = array(); + $terms[] = array( + 'otype' => TERM_OBJ_POST, + 'ttype' => TERM_BOOKMARK, + 'url' => $res['plink'], + 'term' => $res['title'], + ); + } + elseif($res['plink']) { + $res['body'] = '#^[url]' . $res['plink'] . '[/url]' . "\n\n" . $res['body']; + $terms = array(); + $terms[] = array( + 'otype' => TERM_OBJ_POST, + 'ttype' => TERM_BOOKMARK, + 'url' => $res['plink'], + 'term' => $res['plink'], + ); + } + + $private = $item->get_item_tags(NAMESPACE_DFRN,'private'); + if($private && intval($private[0]['data']) > 0) + $res['item_private'] = ((intval($private[0]['data'])) ? 1 : 0); + else + $res['item_private'] = 0; + + $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location'); + if($rawlocation) + $res['location'] = unxmlify($rawlocation[0]['data']); + + $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published'); + if($rawcreated) + $res['created'] = unxmlify($rawcreated[0]['data']); + + $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated'); + if($rawedited) + $res['edited'] = unxmlify($rawedited[0]['data']); + + if((x($res,'edited')) && (! (x($res,'created')))) + $res['created'] = $res['edited']; + + if(! $res['created']) + $res['created'] = $item->get_date('c'); + + if(! $res['edited']) + $res['edited'] = $item->get_date('c'); + + + // Disallow time travelling posts + + $d1 = strtotime($res['created']); + $d2 = strtotime($res['edited']); + $d3 = strtotime('now'); + + if($d1 > $d3) + $res['created'] = datetime_convert(); + if($d2 > $d3) + $res['edited'] = datetime_convert(); + + $res['created'] = datetime_convert('UTC','UTC',$res['created']); + $res['edited'] = datetime_convert('UTC','UTC',$res['edited']); + + $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); + if(! $rawowner) + $rawowner = $item->get_item_tags(NAMESPACE_ZOT,'owner'); + + if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']) + $author['owner_name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']); + elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']) + $author['owner_name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']); + if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']) + $author['owner_link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); + elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']) + $author['owner_link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']); + + if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + + foreach($base as $link) { + if(!x($author, 'owner_photo') || ! $author['owner_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + $author['owner_photo'] = unxmlify($link['attribs']['']['href']); + } + } + } + + $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point'); + if($rawgeo) + $res['coord'] = unxmlify($rawgeo[0]['data']); + + + $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb'); + + // select between supported verbs + + if($rawverb) { + $res['verb'] = unxmlify($rawverb[0]['data']); + } + + // translate OStatus unfollow to activity streams if it happened to get selected + + if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) + $res['verb'] = ACTIVITY_UNFOLLOW; + + $cats = $item->get_categories(); + if($cats) { + if(is_null($terms)) + $terms = array(); + foreach($cats as $cat) { + $term = $cat->get_term(); + if(! $term) + $term = $cat->get_label(); + $scheme = $cat->get_scheme(); + $termurl = ''; + if($scheme && $term && stristr($scheme,'X-DFRN:')) { + $termtype = ((substr($scheme,7,1) === '#') ? TERM_HASHTAG : TERM_MENTION); + $termurl = unxmlify(substr($scheme,9)); + } + else { + $termtype = TERM_CATEGORY; + } + $termterm = notags(trim(unxmlify($term))); + + if($termterm) { + $terms[] = array( + 'otype' => TERM_OBJ_POST, + 'ttype' => $termtype, + 'url' => $termurl, + 'term' => $termterm, + ); + } + } + } + + if(! is_null($terms)) + $res['term'] = $terms; + + $attach = $item->get_enclosures(); + if($attach) { + $res['attach'] = array(); + foreach($attach as $att) { + $len = intval($att->get_length()); + $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link())))); + $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title())))); + $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type())))); + if(strpos($type,';')) + $type = substr($type,0,strpos($type,';')); + if((! $link) || (strpos($link,'http') !== 0)) + continue; + + if(! $title) + $title = ' '; + if(! $type) + $type = 'application/octet-stream'; + + $res['attach'][] = array('href' => $link, 'length' => $len, 'type' => $type, 'title' => $title ); + } + } + + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); + + if($rawobj) { + $obj = array(); + + $child = $rawobj[0]['child']; + if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) { + $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; + } + if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { + $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + } + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) + $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) + $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']); + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) + $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']; + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { + $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; + if(! $body) + $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; + // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events + $obj['orig'] = xmlify($body); + if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { + $body = purify_html($body); + $body = html2bbcode($body); + } + + $obj['content'] = $body; + } + + $res['obj'] = $obj; + } + + $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target'); + + if($rawobj) { + $obj = array(); + + $child = $rawobj[0]['child']; + if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) { + $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; + } + if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { + $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; + } + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) + $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) + $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']); + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) + $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']; + if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { + $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; + if(! $body) + $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; + + // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events + $obj['orig'] = xmlify($body); + if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { + $body = purify_html($body); + $body = html2bbcode($body); + } + + $obj['content'] = $body; + } + + $res['target'] = $obj; + } + + $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); + + call_hooks('parse_atom', $arr); + logger('get_atom_elements: author: ' . print_r($author,true),LOGGER_DATA); + + logger('get_atom_elements: ' . print_r($res,true),LOGGER_DATA); + + return $res; +} + +function encode_rel_links($links) { + $o = array(); + if(! ((is_array($links)) && (count($links)))) + return $o; + + foreach($links as $link) { + $l = array(); + if($link['attribs']['']['rel']) + $l['rel'] = $link['attribs']['']['rel']; + if($link['attribs']['']['type']) + $l['type'] = $link['attribs']['']['type']; + if($link['attribs']['']['href']) + $l['href'] = $link['attribs']['']['href']; + if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width']) + $l['width'] = $link['attribs'][NAMESPACE_MEDIA]['width']; + if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height']) + $l['height'] = $link['attribs'][NAMESPACE_MEDIA]['height']; + + if($l) + $o[] = $l; + } + return $o; +} + +/** + * @brief Process atom feed and update anything/everything we might need to update. + * + * @param array $xml + * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. + * @param $importer + * The contact_record (joined to user_record) of the local user who owns this + * relationship. It is this person's stuff that is going to be updated. + * @param $contact + * The person who is sending us stuff. If not set, we MAY be processing a "follow" activity + * from an external network and MAY create an appropriate contact record. Otherwise, we MUST + * have a contact record. + * @param int $pass by default ($pass = 0) we cannot guarantee that a parent item has been + * imported prior to its children being seen in the stream unless we are certain + * of how the feed is arranged/ordered. + * * With $pass = 1, we only pull parent items out of the stream. + * * With $pass = 2, we only pull children (comments/likes). + * + * So running this twice, first with pass 1 and then with pass 2 will do the right + * thing regardless of feed ordering. This won't be adequate in a fully-threaded + * model where comments can have sub-threads. That would require some massive sorting + * to get all the feed items into a mostly linear ordering, and might still require + * recursion. + */ +function consume_feed($xml, $importer, &$contact, $pass = 0) { + + require_once('library/simplepie/simplepie.inc'); + + if(! strlen($xml)) { + logger('consume_feed: empty input'); + return; + } + + $sys_expire = intval(get_config('system','default_expire_days')); + $chn_expire = intval($importer['channel_expire_days']); + + $expire_days = $sys_expire; + + if(($chn_expire != 0) && ($chn_expire < $sys_expire)) + $expire_days = $chn_expire; + + // logger('expire_days: ' . $expire_days); + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) + logger('consume_feed: Error parsing XML: ' . $feed->error()); + + $permalink = $feed->get_permalink(); + + // Check at the feed level for updated contact name and/or photo + + // process any deleted entries + + $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); + if(is_array($del_entries) && count($del_entries) && $pass != 2) { + foreach($del_entries as $dentry) { + $deleted = false; + if(isset($dentry['attribs']['']['ref'])) { + $mid = $dentry['attribs']['']['ref']; + $deleted = true; + if(isset($dentry['attribs']['']['when'])) { + $when = $dentry['attribs']['']['when']; + $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); + } + else + $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); + } + + if($deleted && is_array($contact)) { + $r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1", + dbesc(base64url_encode($mid)), + dbesc($contact['xchan_hash']), + intval($importer['channel_id']) + ); + + if($r) { + $item = $r[0]; + + if(! intval($item['item_deleted'])) { + logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG); + drop_item($item['id'],false); + } + } + } + } + } + + // Now process the feed + + if($feed->get_item_quantity()) { + + logger('consume_feed: feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); + + $items = $feed->get_items(); + + foreach($items as $item) { + + $is_reply = false; + $item_id = base64url_encode($item->get_id()); + + logger('consume_feed: processing ' . $item_id, LOGGER_DEBUG); + + $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); + if(isset($rawthread[0]['attribs']['']['ref'])) { + $is_reply = true; + $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + } + + if($is_reply) { + + if($pass == 1) + continue; + + // Have we seen it? If not, import it. + + $item_id = base64url_encode($item->get_id()); + $author = array(); + $datarray = get_atom_elements($feed,$item,$author); + + if($contact['xchan_network'] === 'rss') { + $datarray['public_policy'] = 'specific'; + $datarray['comment_policy'] = 'none'; + } + + if((! x($author,'author_name')) || ($author['author_is_feed'])) + $author['author_name'] = $contact['xchan_name']; + if((! x($author,'author_link')) || ($author['author_is_feed'])) + $author['author_link'] = $contact['xchan_url']; + if((! x($author,'author_photo'))|| ($author['author_is_feed'])) + $author['author_photo'] = $contact['xchan_photo_m']; + + $datarray['author_xchan'] = ''; + + if($author['author_link'] != $contact['xchan_url']) { + $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); + if($x) + $datarray['author_xchan'] = $x; + } + if(! $datarray['author_xchan']) + $datarray['author_xchan'] = $contact['xchan_hash']; + + $datarray['owner_xchan'] = $contact['xchan_hash']; + + $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($item_id), + intval($importer['channel_id']) + ); + + + // Update content if 'updated' changes + + if($r) { + if((x($datarray,'edited') !== false) + && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { + + // do not accept (ignore) an earlier edit than one we currently have. + if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) + continue; + + update_feed_item($importer['channel_id'],$datarray); + } + continue; + } + + $datarray['parent_mid'] = $parent_mid; + $datarray['aid'] = $importer['channel_account_id']; + $datarray['uid'] = $importer['channel_id']; + + logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA); + + $xx = item_store($datarray); + $r = $xx['item_id']; + continue; + } + else { + + // Head post of a conversation. Have we seen it? If not, import it. + + $item_id = base64url_encode($item->get_id()); + $author = array(); + $datarray = get_atom_elements($feed,$item,$author); + + if($contact['xchan_network'] === 'rss') { + $datarray['public_policy'] = 'specific'; + $datarray['comment_policy'] = 'none'; + } + + + if(is_array($contact)) { + if((! x($author,'author_name')) || ($author['author_is_feed'])) + $author['author_name'] = $contact['xchan_name']; + if((! x($author,'author_link')) || ($author['author_is_feed'])) + $author['author_link'] = $contact['xchan_url']; + if((! x($author,'author_photo'))|| ($author['author_is_feed'])) + $author['author_photo'] = $contact['xchan_photo_m']; + } + + if((! x($author,'author_name')) || (! x($author,'author_link'))) { + logger('consume_feed: no author information! ' . print_r($author,true)); + continue; + } + + $datarray['author_xchan'] = ''; + + if(activity_match($datarray['verb'],ACTIVITY_FOLLOW) && $datarray['obj_type'] === ACTIVITY_OBJ_PERSON) { + $cb = array('item' => $datarray,'channel' => $importer, 'xchan' => null, 'author' => $author, 'caught' => false); + call_hooks('follow_from_feed',$cb); + if($cb['caught']) { + if($cb['return_code']) + http_status_exit($cb['return_code']); + continue; + } + } + + if($author['author_link'] != $contact['xchan_url']) { + $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); + if($x) + $datarray['author_xchan'] = $x; + } + if(! $datarray['author_xchan']) + $datarray['author_xchan'] = $contact['xchan_hash']; + + $datarray['owner_xchan'] = $contact['xchan_hash']; + + if(array_key_exists('created',$datarray) && $datarray['created'] != NULL_DATE && $expire_days) { + $t1 = $datarray['created']; + $t2 = datetime_convert('UTC','UTC','now - ' . $expire_days . 'days'); + if($t1 < $t2) { + logger('feed content older than expiration. Ignoring.', LOGGER_DEBUG, LOG_INFO); + continue; + } + } + + + + $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", + dbesc($item_id), + intval($importer['channel_id']) + ); + + // Update content if 'updated' changes + + if($r) { + if((x($datarray,'edited') !== false) + && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { + + // do not accept (ignore) an earlier edit than one we currently have. + if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) + continue; + + update_feed_item($importer['channel_id'],$datarray); + } + + continue; + } + + $datarray['parent_mid'] = $item_id; + $datarray['uid'] = $importer['channel_id']; + $datarray['aid'] = $importer['channel_account_id']; + + if(! link_compare($author['owner_link'],$contact['xchan_url'])) { + logger('consume_feed: Correcting item owner.', LOGGER_DEBUG); + $author['owner_name'] = $contact['name']; + $author['owner_link'] = $contact['url']; + $author['owner_avatar'] = $contact['thumb']; + } + + if(! post_is_importable($datarray,$contact)) + continue; + + logger('consume_feed: author ' . print_r($author,true),LOGGER_DEBUG); + + logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA); + + $xx = item_store($datarray); + $r = $xx['item_id']; + continue; + } + } + } +} + + +/** + * @brief Process atom feed and return the first post and structure + * + * @param array $xml + * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. + * @param $importer + * The contact_record (joined to user_record) of the local user who owns this + * relationship. It is this person's stuff that is going to be updated. + */ + +function process_salmon_feed($xml, $importer) { + + $ret = array(); + + require_once('library/simplepie/simplepie.inc'); + + if(! strlen($xml)) { + logger('process_feed: empty input'); + return; + } + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) + logger('Error parsing XML: ' . $feed->error()); + + $permalink = $feed->get_permalink(); + + if($feed->get_item_quantity()) { + + // this should be exactly one + + logger('feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); + + $items = $feed->get_items(); + + foreach($items as $item) { + + $item_id = base64url_encode($item->get_id()); + + logger('processing ' . $item_id, LOGGER_DEBUG); + + $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); + if(isset($rawthread[0]['attribs']['']['ref'])) { + $is_reply = true; + $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + } + + if($is_reply) + $ret['parent_mid'] = $parent_mid; + + $ret['author'] = array(); + + $datarray = get_atom_elements($feed,$item,$ret['author']); + + // reset policies which are restricted by default for RSS connections + // This item is likely coming from GNU-social via salmon and allows public interaction + $datarray['public_policy'] = ''; + $datarray['comment_policy'] = ''; + + $ret['item'] = $datarray; + } + } + + return $ret; +} + +/* + * Given an xml (atom) feed, find author and hub links + */ + + +function feed_meta($xml) { + require_once('library/simplepie/simplepie.inc'); + + $ret = array(); + + if(! strlen($xml)) { + logger('empty input'); + return $ret; + } + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) { + logger('Error parsing XML: ' . $feed->error()); + return $ret; + } + + $ret['hubs'] = $feed->get_links('hub'); + +// logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA); + + $author = array(); + + $found_author = $feed->get_author(); + if($found_author) { + $author['author_name'] = unxmlify($found_author->get_name()); + $author['author_link'] = unxmlify($found_author->get_link()); + + $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); + logger('rawauthor: ' . print_r($rawauthor,true)); + + if($rawauthor) { + if($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { + $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; + foreach($base as $link) { + if(!x($author, 'author_photo') || ! $author['author_photo']) { + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') { + $author['author_photo'] = unxmlify($link['attribs']['']['href']); + break; + } + } + } + } + if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) + $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); + if($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']) + $author['author_uri'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); + + } + } + + if(substr($author['author_link'],-1,1) == '/') + $author['author_link'] = substr($author['author_link'],0,-1); + + $ret['author'] = $author; + + return $ret; +} + + + +function update_feed_item($uid,$datarray) { + logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA); +} + + +function handle_feed($uid,$abook_id,$url) { + + $channel = channelx_by_n($uid); + if(! $channel) + return; + + $x = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d limit 1", + dbesc($abook_id), + intval($uid) + ); + + $recurse = 0; + $z = z_fetch_url($url,false,$recurse,array('novalidate' => true)); + +//logger('handle_feed:' . print_r($z,true)); + + if($z['success']) { + consume_feed($z['body'],$channel,$x[0],1); + consume_feed($z['body'],$channel,$x[0],2); + } +} + + +function atom_author($tag,$name,$uri,$h,$w,$type,$photo) { + $o = ''; + if(! $tag) + return $o; + + $name = xmlify($name); + $uri = xmlify($uri); + $h = intval($h); + $w = intval($w); + $photo = xmlify($photo); + + $o .= "<$tag>\r\n"; + $o .= "<name>$name</name>\r\n"; + $o .= "<uri>$uri</uri>\r\n"; + $o .= '<link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + $o .= '<link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + + call_hooks('atom_author', $o); + + $o .= "</$tag>\r\n"; + + return $o; +} + +function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { + + if(! $item['parent']) + return; + + if($item['deleted']) + return '<at:deleted-entry ref="' . xmlify($item['mid']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; + + + create_export_photo_body($item); + + if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) + $body = fix_private_photos($item['body'],$owner['uid'],$item,$cid); + else + $body = $item['body']; + + $o = "\r\n\r\n<entry>\r\n"; + + if(is_array($author)) + $o .= atom_author('author',$author['xchan_name'],$author['xchan_url'],80,80,$author['xchan_photo_mimetype'],$author['xchan_photo_m']); + else + $o .= atom_author('author',$item['author']['xchan_name'],$item['author']['xchan_url'],80,80,$item['author']['xchan_photo_mimetype'], $item['author']['xchan_photo_m']); + + $o .= atom_author('zot:owner',$item['owner']['xchan_name'],$item['owner']['xchan_url'],80,80,$item['owner']['xchan_photo_mimetype'],$item['owner']['xchan_photo_m']); + + if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { + $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); + $o .= '<thr:in-reply-to ref="' . z_root() . '/display/' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; + } + + if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { + $obj = ((is_array($item['obj'])) ? $item['obj'] : json_decode($item['obj'],true)); + + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<summary xmlns="urn:ietf:params:xml:ns:xcal">' . xmlify(bbcode($obj['title'])) . '</summary>' . "\r\n"; + $o .= '<dtstart xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['dtstart'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtstart>' . "\r\n"; + $o .= '<dtend xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['dtend'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtend>' . "\r\n"; + $o .= '<location xmlns="urn:ietf:params:xml:ns:xcal">' . xmlify(bbcode($obj['location'])) . '</location>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(bbcode($obj['description'])) . '</content>' . "\r\n"; + } + else { + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; + } + + $o .= '<id>' . z_root() . '/display/' . xmlify($item['mid']) . '</id>' . "\r\n"; + $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; + $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; + + $o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; + + if($item['location']) { + $o .= '<zot:location>' . xmlify($item['location']) . '</zot:location>' . "\r\n"; + $o .= '<poco:address><poco:formatted>' . xmlify($item['location']) . '</poco:formatted></poco:address>' . "\r\n"; + } + + if($item['coord']) + $o .= '<georss:point>' . xmlify($item['coord']) . '</georss:point>' . "\r\n"; + + if(($item['item_private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) + $o .= '<zot:private>' . (($item['item_private']) ? $item['item_private'] : 1) . '</zot:private>' . "\r\n"; + + if($item['app']) + $o .= '<statusnet:notice_info local_id="' . $item['id'] . '" source="' . xmlify($item['app']) . '" ></statusnet:notice_info>' . "\r\n"; + + $verb = construct_verb($item); + $o .= '<as:verb>' . xmlify($verb) . '</as:verb>' . "\r\n"; + $actobj = construct_activity_object($item); + if(strlen($actobj)) + $o .= $actobj; + $actarg = construct_activity_target($item); + if(strlen($actarg)) + $o .= $actarg; + + // FIXME +// $tags = item_getfeedtags($item); +// if(count($tags)) { +// foreach($tags as $t) { +// $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; +// } +// } + +// FIXME +// $o .= item_getfeedattach($item); + +// $mentioned = get_mentions($item,$tags); +// if($mentioned) +// $o .= $mentioned; + + call_hooks('atom_entry', $o); + + $o .= '</entry>' . "\r\n"; + + return $o; +} + + +function gen_asld($items) { + $ret = array(); + if(! $items) + return $ret; + foreach($items as $item) { + $ret[] = i2asld($item); + } + return $ret; +} + + +function i2asld($i) { + + if(! $i) + return array(); + + $ret = array(); + + $ret['@context'] = array( 'http://www.w3.org/ns/activitystreams', 'zot' => 'http://purl.org/zot/protocol'); + + if($i['verb']) { + if(strpos(dirname($i['verb'],'activitystrea.ms/schema/1.0'))) { + $ret['@type'] = ucfirst(basename($i['verb'])); + } + elseif(strpos(dirname($i['verb'],'purl.org/zot'))) { + $ret['@type'] = 'zot:' . ucfirst(basename($i['verb'])); + } + } + $ret['@id'] = $i['plink']; + + $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); + + // we need to pass the parent into this +// if($i['id'] != $i['parent'] && $i['obj_type'] === ACTIVITY_OBJ_NOTE) { +// $ret['inReplyTo'] = asencode_note +// } + + if($i['obj_type'] === ACTIVITY_OBJ_NOTE) + $ret['object'] = asencode_note($i); + + + $ret['actor'] = asencode_person($i['author']); + + + return $ret; + +} + +function asencode_note($i) { + + $ret = array(); + + $ret['@type'] = 'Note'; + $ret['@id'] = $i['plink']; + if($i['title']) + $ret['title'] = bbcode($i['title']); + $ret['content'] = bbcode($i['body']); + $ret['zot:owner'] = asencode_person($i['owner']); + $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); + if($i['created'] !== $i['edited']) + $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); + + return $ret; +} + + +function asencode_person($p) { + $ret = array(); + $ret['@type'] = 'Person'; + $ret['@id'] = 'acct:' . $p['xchan_addr']; + $ret['displayName'] = $p['xchan_name']; + $ret['icon'] = array( + '@type' => 'Link', + 'mediaType' => $p['xchan_photo_mimetype'], + 'href' => $p['xchan_photo_m'] + ); + $ret['url'] = array( + '@type' => 'Link', + 'mediaType' => 'text/html', + 'href' => $p['xchan_url'] + ); + + return $ret; +} diff --git a/include/follow.php b/include/follow.php index 70e717cfc..5f63687f8 100644 --- a/include/follow.php +++ b/include/follow.php @@ -17,7 +17,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $result = array('success' => false,'message' => ''); - $a = get_app(); $is_red = false; $is_http = ((strpos($url,'://') !== false) ? true : false); @@ -56,23 +55,22 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if($arr['channel']['success']) $ret = $arr['channel']; elseif(! $is_http) - $ret = zot_finger($url,$channel); + $ret = Zotlabs\Zot\Finger::run($url,$channel); - if($ret && $ret['success']) { + if($ret && is_array($ret) && $ret['success']) { $is_red = true; - $j = json_decode($ret['body'],true); + $j = $ret; } $my_perms = get_channel_default_perms($uid); $role = get_pconfig($uid,'system','permissions_role'); if($role) { - $x = get_role_perms($role); - if($x['perms_follow']) - $my_perms = $x['perms_follow']; + $x = \Zotlabs\Access\PermissionRoles::role_perms($role); + if($x['perms_connect']) + $my_perms = $x['perms_connect']; } - if($is_red && $j) { logger('follow: ' . $url . ' ' . print_r($j,true), LOGGER_DEBUG); @@ -105,10 +103,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $xchan_hash = $x['hash']; - $their_perms = 0; - - $global_perms = get_perms(); - if( array_key_exists('permissions',$j) && array_key_exists('data',$j['permissions'])) { $permissions = crypto_unencapsulate(array( 'data' => $j['permissions']['data'], @@ -122,16 +116,14 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) else $permissions = $j['permissions']; - - foreach($permissions as $k => $v) { - if($v) { - $their_perms = $their_perms | intval($global_perms[$k][1]); + if(is_array($permissions) && $permissions) { + foreach($permissions as $k => $v) { + set_abconfig($channel['channel_uid'],$xchan_hash,'their_perms',$k,intval($v)); } } } else { - $their_perms = 0; $xchan_hash = ''; $r = q("select * from xchan where xchan_hash = '%s' or xchan_url = '%s' limit 1", @@ -191,6 +183,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $result['message'] = t('Protocol disabled.'); return $result; } + $singleton = intval($x['singleton']); $aid = $channel['channel_account_id']; @@ -223,6 +216,15 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) intval($uid) ); + if($is_http) { + + // Always set these "remote" permissions for feeds since we cannot interact with them + // to negotiate a suitable permission response + + set_abconfig($uid,$xchan_hash,'their_perms','view_stream',1); + set_abconfig($uid,$xchan_hash,'their_perms','republish',1); + } + if($r) { $abook_instance = $r[0]['abook_instance']; @@ -232,8 +234,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $abook_instance .= z_root(); } - $x = q("update abook set abook_their_perms = %d, abook_instance = '%s' where abook_id = %d", - intval($their_perms), + $x = q("update abook set abook_instance = '%s' where abook_id = %d", dbesc($abook_instance), intval($r[0]['abook_id']) ); @@ -243,15 +244,13 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if($closeness === false) $closeness = 80; - $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated, abook_instance ) - values( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s' ) ", + $r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_created, abook_updated, abook_instance ) + values( %d, %d, %d, '%s', %d, '%s', '%s', '%s' ) ", intval($aid), intval($uid), intval($closeness), dbesc($xchan_hash), intval(($is_http) ? 1 : 0), - intval(($is_http) ? $their_perms|PERMS_R_STREAM|PERMS_A_REPUBLISH : $their_perms), - intval($my_perms), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc(($singleton) ? z_root() : '') @@ -261,6 +260,16 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if(! $r) logger('mod_follow: abook creation failed'); + $all_perms = \Zotlabs\Access\Permissions::Perms(); + if($all_perms) { + foreach($all_perms as $k => $v) { + if(in_array($k,$my_perms)) + set_abconfig($uid,$xchan_hash,'my_perms',$k,1); + else + set_abconfig($uid,$xchan_hash,'my_perms',$k,0); + } + } + $r = q("select abook.*, xchan.* from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($xchan_hash), @@ -269,14 +278,14 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) if($r) { $result['abook'] = $r[0]; - proc_run('php', 'include/notifier.php', 'permission_create', $result['abook']['abook_id']); + Zotlabs\Daemon\Master::Summon(array('Notifier', 'permission_create', $result['abook']['abook_id'])); } $arr = array('channel_id' => $uid, 'channel' => $channel, 'abook' => $result['abook']); call_hooks('follow', $arr); - /** If there is a default group for this channel, add this member to it */ + /** If there is a default group for this channel, add this connection to it */ if($default_group) { require_once('include/group.php'); diff --git a/include/gprobe.php b/include/gprobe.php deleted file mode 100644 index d8d893d9e..000000000 --- a/include/gprobe.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php /** @file */ - -require_once('include/cli_startup.php'); -require_once('include/zot.php'); - -function gprobe_run($argv, $argc){ - - cli_startup(); - - $a = get_app(); - - if($argc != 2) - return; - - $url = hex2bin($argv[1]); - - if(! strpos($url,'@')) - return; - - $r = q("select * from xchan where xchan_addr = '%s' limit 1", - dbesc($url) - ); - - if(! $r) { - $x = zot_finger($url,null); - if($x['success']) { - $j = json_decode($x['body'],true); - $y = import_xchan($j); - } - } - - return; -} - -if (array_search(__file__,get_included_files())===0){ - gprobe_run($argv,$argc); - killme(); -} diff --git a/include/group.php b/include/group.php index 748ec0c13..10853ff6b 100644 --- a/include/group.php +++ b/include/group.php @@ -17,8 +17,8 @@ function group_add($uid,$name,$public = 0) { $z = q("SELECT * FROM `groups` WHERE `id` = %d LIMIT 1", intval($r) ); - if(count($z) && $z[0]['deleted']) { - /*$r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `name` = '%s' LIMIT 1", + if(($z) && $z[0]['deleted']) { + /*$r = q("UPDATE `groups` SET `deleted` = 0 WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", intval($uid), dbesc($name) );*/ @@ -38,7 +38,7 @@ function group_add($uid,$name,$public = 0) { } while($dups == true); - $r = q("INSERT INTO `groups` ( hash, uid, visible, name ) + $r = q("INSERT INTO `groups` ( hash, uid, visible, gname ) VALUES( '%s', %d, %d, '%s' ) ", dbesc($hash), intval($uid), @@ -57,7 +57,7 @@ function group_add($uid,$name,$public = 0) { function group_rmv($uid,$name) { $ret = false; if(x($uid) && x($name)) { - $r = q("SELECT id, hash FROM `groups` WHERE `uid` = %d AND `name` = '%s' LIMIT 1", + $r = q("SELECT id, hash FROM `groups` WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", intval($uid), dbesc($name) ); @@ -108,7 +108,7 @@ function group_rmv($uid,$name) { ); // remove group - $r = q("UPDATE `groups` SET `deleted` = 1 WHERE `uid` = %d AND `name` = '%s'", + $r = q("UPDATE `groups` SET `deleted` = 1 WHERE `uid` = %d AND `gname` = '%s'", intval($uid), dbesc($name) ); @@ -125,11 +125,11 @@ function group_rmv($uid,$name) { function group_byname($uid,$name) { if((! $uid) || (! strlen($name))) return false; - $r = q("SELECT * FROM `groups` WHERE `uid` = %d AND `name` = '%s' LIMIT 1", + $r = q("SELECT * FROM `groups` WHERE `uid` = %d AND `gname` = '%s' LIMIT 1", intval($uid), dbesc($name) ); - if(count($r)) + if($r) return $r[0]['id']; return false; } @@ -178,11 +178,11 @@ function group_add_member($uid,$name,$member,$gid = 0) { intval($gid), dbesc($member) ); - if(count($r)) + if($r) return true; // You might question this, but // we indicate success because the group member was in fact created // -- It was just created at another time - if(! count($r)) + if(! $r) $r = q("INSERT INTO `group_member` (`uid`, `gid`, `xchan`) VALUES( %d, %d, '%s' ) ", intval($uid), @@ -205,7 +205,7 @@ function group_get_members($gid) { intval(local_channel()), intval(local_channel()) ); - if(count($r)) + if($r) $ret = $r; } return $ret; @@ -218,7 +218,7 @@ function group_get_members_xchan($gid) { intval($gid), intval(local_channel()) ); - if(count($r)) { + if($r) { foreach($r as $rr) { $ret[] = $rr['xchan']; } @@ -232,13 +232,13 @@ function mini_group_select($uid,$group = '') { $grps = array(); $o = ''; - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `name` ASC", + $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", intval($uid) ); $grps[] = array('name' => '', 'hash' => '0', 'selected' => ''); - if(count($r)) { + if($r) { foreach($r as $rr) { - $grps[] = array('name' => $rr['name'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '')); + $grps[] = array('name' => $rr['gname'], 'id' => $rr['hash'], 'selected' => (($group == $rr['hash']) ? 'true' : '')); } } @@ -271,7 +271,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id ); - $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `name` ASC", + $r = q("SELECT * FROM `groups` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `gname` ASC", intval($_SESSION['uid']) ); $member_of = array(); @@ -279,7 +279,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $member_of = groups_containing(local_channel(),$cid); } - if(count($r)) { + if($r) { foreach($r as $rr) { $selected = (($group_id == $rr['id']) ? ' group-selected' : ''); @@ -296,7 +296,7 @@ function group_side($every="connections",$each="group",$edit = false, $group_id 'id' => $rr['id'], 'enc_cid' => base64url_encode($cid), 'cid' => $cid, - 'text' => $rr['name'], + 'text' => $rr['gname'], 'selected' => $selected, 'href' => (($mode == 0) ? $each.'?f=&gid='.$rr['id'] : $each."/".$rr['id']) . ((x($_GET,'new')) ? '&new=' . $_GET['new'] : '') . ((x($_GET,'order')) ? '&order=' . $_GET['order'] : ''), 'edit' => $groupedit, @@ -340,7 +340,7 @@ function expand_groups($a) { function member_of($c) { - $r = q("SELECT `groups`.`name`, `groups`.`id` FROM `groups` LEFT JOIN `group_member` ON `group_member`.`gid` = `groups`.`id` WHERE `group_member`.`xchan` = '%s' AND `groups`.`deleted` = 0 ORDER BY `groups`.`name` ASC ", + $r = q("SELECT `groups`.`gname`, `groups`.`id` FROM `groups` LEFT JOIN `group_member` ON `group_member`.`gid` = `groups`.`id` WHERE `group_member`.`xchan` = '%s' AND `groups`.`deleted` = 0 ORDER BY `groups`.`gname` ASC ", dbesc($c) ); @@ -356,7 +356,7 @@ function groups_containing($uid,$c) { ); $ret = array(); - if(count($r)) { + if($r) { foreach($r as $rr) $ret[] = $rr['gid']; } diff --git a/include/help.php b/include/help.php new file mode 100644 index 000000000..7f57f3334 --- /dev/null +++ b/include/help.php @@ -0,0 +1,149 @@ +<?php + +function load_doc_file($s) { + $lang = \App::$language; + if(! isset($lang)) + $lang = 'en'; + $b = basename($s); + $d = dirname($s); + + $c = find_doc_file("$d/$lang/$b"); + if($c) + return $c; + $c = find_doc_file($s); + if($c) + return $c; + return ''; +} + +function find_doc_file($s) { + if(file_exists($s)) + return file_get_contents($s); + return ''; +} + +function search_doc_files($s) { + + $itemspage = get_pconfig(local_channel(),'system','itemspage'); + \App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); + + $regexop = db_getfunc('REGEXP'); + + $r = q("select iconfig.v, item.* from item left join iconfig on item.id = iconfig.iid + where iconfig.cat = 'system' and iconfig.k = 'docfile' and + body $regexop '%s' and item_type = %d $pager_sql", + dbesc($s), + intval(ITEM_TYPE_DOC) + ); + + $r = fetch_post_tags($r,true); + + for($x = 0; $x < count($r); $x ++) { + + $r[$x]['text'] = $r[$x]['body']; + + $r[$x]['rank'] = 0; + if($r[$x]['term']) { + foreach($r[$x]['term'] as $t) { + if(stristr($t['term'],$s)) { + $r[$x]['rank'] ++; + } + } + } + if(stristr($r[$x]['v'],$s)) + $r[$x]['rank'] ++; + $r[$x]['rank'] += substr_count(strtolower($r[$x]['text']),strtolower($s)); + // bias the results to the observer's native language + if($r[$x]['lang'] === \App::$language) + $r[$x]['rank'] = $r[$x]['rank'] + 10; + + } + usort($r,'doc_rank_sort'); + return $r; +} + + +function doc_rank_sort($s1,$s2) { + if($s1['rank'] == $s2['rank']) + return 0; + return (($s1['rank'] < $s2['rank']) ? 1 : (-1)); +} + + +function load_context_help() { + + $path = App::$cmd; + $args = App::$argv; + $lang = App::$language; + + if(! isset($lang) || !is_dir('doc/context/' . $lang . '/')) { + $lang = 'en'; + } + while($path) { + $context_help = load_doc_file('doc/context/' . $lang . '/' . $path . '/help.html'); + if(!$context_help) { + // Fallback to English if the translation is absent + $context_help = load_doc_file('doc/context/en/' . $path . '/help.html'); + } + if($context_help) + break; + array_pop($args); + $path = implode($args,'/'); + } + + return $context_help; +} + + +function store_doc_file($s) { + + if(is_dir($s)) + return; + + $item = array(); + $sys = get_sys_channel(); + + $item['aid'] = 0; + $item['uid'] = $sys['channel_id']; + + + if(strpos($s,'.md')) + $mimetype = 'text/markdown'; + elseif(strpos($s,'.html')) + $mimetype = 'text/html'; + else + $mimetype = 'text/bbcode'; + + require_once('include/html2plain.php'); + + $item['body'] = html2plain(prepare_text(file_get_contents($s),$mimetype, true)); + $item['mimetype'] = 'text/plain'; + + $item['plink'] = z_root() . '/' . str_replace('doc','help',$s); + $item['owner_xchan'] = $item['author_xchan'] = $sys['channel_hash']; + $item['item_type'] = ITEM_TYPE_DOC; + + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where iconfig.cat = 'system' and iconfig.k = 'docfile' and + iconfig.v = '%s' and item_type = %d limit 1", + dbesc($s), + intval(ITEM_TYPE_DOC) + ); + + \Zotlabs\Lib\IConfig::Set($item,'system','docfile',$s); + + if($r) { + $item['id'] = $r[0]['id']; + $item['mid'] = $item['parent_mid'] = $r[0]['mid']; + $x = item_store_update($item); + } + else { + $item['mid'] = $item['parent_mid'] = item_message_id(); + $x = item_store($item); + } + + return $x; + +} + diff --git a/include/hubloc.php b/include/hubloc.php index 695cada3c..397646449 100644 --- a/include/hubloc.php +++ b/include/hubloc.php @@ -106,7 +106,7 @@ function remove_obsolete_hublocs() { dbesc($rr['hubloc_hash']) ); if($x) { - proc_run('php','include/notifier.php','location',$x[0]['channel_id']); + Zotlabs\Daemon\Master::Summon(array('Notifier','location',$x[0]['channel_id'])); if($interval) @time_sleep_until(microtime(true) + (float) $interval); } diff --git a/include/import.php b/include/import.php index 3b5c8508c..42c902a0a 100644 --- a/include/import.php +++ b/include/import.php @@ -1,6 +1,7 @@ <?php require_once('include/menu.php'); +require_once('include/perm_upgrade.php'); function import_channel($channel, $account_id, $seize) { @@ -9,15 +10,21 @@ function import_channel($channel, $account_id, $seize) { $channel['channel_removed'] = (($channel['channel_pageflags'] & 0x8000) ? 1 : 0); } + // Ignore the hash provided and re-calculate + + $channel['channel_hash'] = make_xchan_hash($channel['channel_guid'],$channel['channel_guid_sig']); + + // Check for duplicate channels + $r = q("select * from channel where (channel_guid = '%s' or channel_hash = '%s' or channel_address = '%s' ) limit 1", dbesc($channel['channel_guid']), dbesc($channel['channel_hash']), dbesc($channel['channel_address']) ); + if($r && $r[0]['channel_guid'] == $channel['channel_guid'] && $r[0]['channel_pubkey'] === $channel['channel_pubkey'] && $r[0]['channel_hash'] === $channel['channel_hash']) + return $r[0]; - // We should probably also verify the hash - - if($r) { + if(($r) || (check_webbie(array($channel['channel_address'])) !== $channel['channel_address'])) { if($r[0]['channel_guid'] === $channel['channel_guid'] || $r[0]['channel_hash'] === $channel['channel_hash']) { logger('mod_import: duplicate channel. ', print_r($channel,true)); notice( t('Cannot create a duplicate channel identifier on this system. Import failed.') . EOL); @@ -55,15 +62,35 @@ function import_channel($channel, $account_id, $seize) { if(! is_site_admin()) $channel['channel_pageflags'] = $channel['channel_pageflags'] ^ PAGE_ALLOWCODE; } - - dbesc_array($channel); - $r = dbq("INSERT INTO channel (`" - . implode("`, `", array_keys($channel)) - . "`) VALUES ('" - . implode("', '", array_values($channel)) - . "')" - ); + // remove all the permissions related settings, we will import/upgrade them after the channel + // is created. + + $disallowed = [ + 'channel_id', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_a_delegate', 'perm_limits' + ]; + + $clean = array(); + foreach($channel as $k => $v) { + if(in_array($k,$disallowed)) + continue; + $clean[$k] = $v; + } + + if($clean) { + dbesc_array($clean); + + $r = dbq("INSERT INTO channel (`" + . implode("`, `", array_keys($clean)) + . "`) VALUES ('" + . implode("', '", array_values($clean)) + . "')" + ); + } if(! $r) { logger('mod_import: channel clone failed. ', print_r($channel,true)); @@ -80,13 +107,20 @@ function import_channel($channel, $account_id, $seize) { notice( t('Cloned channel not found. Import failed.') . EOL); return false; } + + // extract the permissions from the original imported array and use our new channel_id to set them + // These could be in the old channel permission stule or the new pconfig. We have a function to + // translate and store them no matter which they throw at us. + + $channel['channel_id'] = $r[0]['channel_id']; + translate_channel_perms_inbound($channel); + // reset $channel = $r[0]; set_default_login_identity($account_id,$channel['channel_id'],false); logger('import step 1'); $_SESSION['import_step'] = 1; - ref_session_write(session_id(), serialize($_SESSION)); return $channel; } @@ -117,6 +151,11 @@ function import_profiles($channel,$profiles) { $profile['aid'] = get_account_id(); $profile['uid'] = $channel['channel_id']; + convert_oldfields($profile,'name','fullname'); + convert_oldfields($profile,'with','partner'); + convert_oldfields($profile,'work','employment'); + + // we are going to reset all profile photos to the original // somebody will have to fix this later and put all the applicable photos into the export @@ -297,8 +336,11 @@ function import_apps($channel,$apps) { if($channel && $apps) { foreach($apps as $app) { + $term = ((array_key_exists('term',$app) && is_array($app['term'])) ? $app['term'] : null); + unset($app['id']); unset($app['app_channel']); + unset($app['term']); $app['app_channel'] = $channel['channel_id']; @@ -307,6 +349,8 @@ function import_apps($channel,$apps) { $app['app_photo'] = $x[0]; } + $hash = $app['app_id']; + dbesc_array($app); $r = dbq("INSERT INTO app (`" . implode("`, `", array_keys($app)) @@ -314,6 +358,23 @@ function import_apps($channel,$apps) { . implode("', '", array_values($app)) . "')" ); + + if($term) { + $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($hash), + intval($channel['channel_id']) + ); + if($x) { + foreach($term as $t) { + if(array_key_exists('type',$t)) + $t['ttype'] = $t['type']; + store_item_tag($channel['channel_id'],$x[0]['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); + } + } + } + + + } } } @@ -325,16 +386,41 @@ function sync_apps($channel,$apps) { if($channel && $apps) { foreach($apps as $app) { - if(array_key_exists('app_deleted',$app) && $app['app_deleted'] && $app['app_id']) { + $exists = false; + $term = ((array_key_exists('term',$app)) ? $app['term'] : null); + + $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($app['app_id']), + intval($channel['channel_id']) + ); + if($x) { + $exists = $x[0]; + } + + if(array_key_exists('app_deleted',$app) && $app['app_deleted'] && $app['app_id']) { q("delete from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['app_id']), intval($channel['channel_id']) ); + if($exists) { + q("delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($exists['id']) + ); + } continue; } unset($app['id']); unset($app['app_channel']); + unset($app['term']); + + if($exists) { + q("delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($exists['id']) + ); + } if(! $app['app_created'] || $app['app_created'] === NULL_DATE) $app['app_created'] = datetime_convert(); @@ -348,16 +434,17 @@ function sync_apps($channel,$apps) { $app['app_photo'] = $x[0]; } - $exists = false; + if($exists && $term) { + foreach($term as $t) { + if(array_key_exists('type',$t)) + $t['ttype'] = $t['type']; + store_item_tag($channel['channel_id'],$exists['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); + } + } - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", - dbesc($app['app_id']), - intval($channel['channel_id']) - ); - if($x) { - if($x[0]['app_edited'] >= $app['app_edited']) + if($exists) { + if($exists['app_edited'] >= $app['app_edited']) continue; - $exists = true; } $hash = $app['app_id']; @@ -380,6 +467,19 @@ function sync_apps($channel,$apps) { . implode("', '", array_values($app)) . "')" ); + if($term) { + $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($hash), + intval($channel['channel_id']) + ); + if($x) { + foreach($term as $t) { + if(array_key_exists('type',$t)) + $t['ttype'] = $t['type']; + store_item_tag($channel['channel_id'],$x[0]['id'],TERM_OBJ_APP,$t['ttype'],escape_tags($t['term']),escape_tags($t['url'])); + } + } + } } } } @@ -482,7 +582,7 @@ function sync_chatrooms($channel,$chatrooms) { -function import_items($channel,$items) { +function import_items($channel,$items,$sync = false,$relocate = null) { if($channel && $items) { $allow_code = false; @@ -499,20 +599,28 @@ function import_items($channel,$items) { $deliver = false; // Don't deliver any messages or notifications when importing foreach($items as $i) { + $item_result = false; $item = get_item_elements($i,$allow_code); if(! $item) continue; + if($relocate && $item['mid'] === $item['parent_mid']) { + item_url_replace($channel,$item,$relocate['url'],z_root(),$relocate['channel_address']); + } + $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", dbesc($item['mid']), intval($channel['channel_id']) ); if($r) { - if($item['edited'] > $r[0]['edited']) { + + // flags may have changed and we are probably relocating the post, + // so force an update even if we have the same timestamp + + if($item['edited'] >= $r[0]['edited']) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; - item_store_update($item,$allow_code,$deliver); - continue; + $item_result = item_store_update($item,$allow_code,$deliver); } } else { @@ -520,13 +628,20 @@ function import_items($channel,$items) { $item['uid'] = $channel['channel_id']; $item_result = item_store($item,$allow_code,$deliver); } + + if($sync && $item['item_wall']) { + // deliver singletons if we have any + if($item_result && $item_result['success']) { + Zotlabs\Daemon\Master::Summon( [ 'Notifier','single_activity',$item_result['item_id'] ]); + } + } } } } -function sync_items($channel,$items) { - import_items($channel,$items); +function sync_items($channel,$items,$relocate = null) { + import_items($channel,$items,true,$relocate); } @@ -540,19 +655,14 @@ function import_item_ids($channel,$itemids) { ); if(! $r) continue; - $z = q("select * from item_id where service = '%s' and sid = '%s' and iid = %d and uid = %d limit 1", + $z = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = '%s' + and iconfig.v = '%s' and iid = %d limit 1", dbesc($i['service']), dbesc($i['sid']), - intval($r[0]['id']), - intval($channel['channel_id']) + intval($r[0]['id']) ); if(! $z) { - q("insert into item_id (iid,uid,sid,service) values(%d,%d,'%s','%s')", - intval($r[0]['id']), - intval($channel['channel_id']), - dbesc($i['sid']), - dbesc($i['service']) - ); + \Zotlabs\Lib\IConfig::Set($r[0]['id'],'system',$i['service'],$i['sid'],true); } } } @@ -565,6 +675,10 @@ function import_events($channel,$events) { unset($event['id']); $event['aid'] = $channel['channel_account_id']; $event['uid'] = $channel['channel_id']; + convert_oldfields($event,'start','dtstart'); + convert_oldfields($event,'finish','dtend'); + convert_oldfields($event,'type','etype'); + convert_oldfields($event,'ignore','dismissed'); dbesc_array($event); $r = dbq("INSERT INTO event (`" @@ -598,6 +712,12 @@ function sync_events($channel,$events) { $event['aid'] = $channel['channel_account_id']; $event['uid'] = $channel['channel_id']; + convert_oldfields($event,'start','dtstart'); + convert_oldfields($event,'finish','dtend'); + convert_oldfields($event,'type','etype'); + convert_oldfields($event,'ignore','dismissed'); + + $exists = false; $x = q("select * from event where event_hash = '%s' and uid = %d limit 1", @@ -839,7 +959,7 @@ function import_conv($channel,$convs) { -function import_mail($channel,$mails) { +function import_mail($channel,$mails,$sync = false) { if($channel && $mails) { foreach($mails as $mail) { if(array_key_exists('flags',$mail) && in_array('deleted',$mail['flags'])) { @@ -863,12 +983,17 @@ function import_mail($channel,$mails) { $m['aid'] = $channel['channel_account_id']; $m['uid'] = $channel['channel_id']; - mail_store($m); + $mail_id = mail_store($m); + if($sync && $mail_id) { + Zotlabs\Daemon\Master::Summon(array('Notifier','single_mail',$mail_id)); + } } } } - +function sync_mail($channel,$mails) { + import_mail($channel,$mails,true); +} function sync_files($channel,$files) { @@ -890,6 +1015,8 @@ function sync_files($channel,$files) { $attachment_stored = false; foreach($f['attach'] as $att) { + convert_oldfields($att,'data','content'); + if($att['deleted']) { attach_delete($channel,$att['hash']); continue; @@ -897,13 +1024,16 @@ function sync_files($channel,$files) { $attach_exists = false; $x = attach_by_hash($att['hash']); + logger('sync_files duplicate check: attach_exists=' . $attach_exists, LOGGER_DEBUG); + logger('sync_files duplicate check: att=' . print_r($att,true), LOGGER_DEBUG); + logger('sync_files duplicate check: attach_by_hash() returned ' . print_r($x,true), LOGGER_DEBUG); - if($x) { + if($x['success']) { $attach_exists = true; $attach_id = $x[0]['id']; } - $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['data']); + $newfname = 'store/' . $channel['channel_address'] . '/' . get_attach_binname($att['content']); unset($att['id']); $att['aid'] = $channel['channel_account_id']; @@ -956,7 +1086,7 @@ function sync_files($channel,$files) { // @fixme - update attachment structures if they are modified rather than created - $att['data'] = $newfname; + $att['content'] = $newfname; // Note: we use $att['hash'] below after it has been escaped to // fetch the file contents. @@ -967,15 +1097,17 @@ function sync_files($channel,$files) { if($attach_exists) { - $str = ''; - foreach($att as $k => $v) { - if($str) - $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; - } - $r = dbq("update `attach` set " . $str . " where id = " . intval($attach_id) ); + logger('sync_files attach exists: ' . print_r($att,true), LOGGER_DEBUG); + $str = ''; + foreach($att as $k => $v) { + if($str) + $str .= ","; + $str .= " `" . $k . "` = '" . $v . "' "; + } + $r = dbq("update `attach` set " . $str . " where id = " . intval($attach_id) ); } else { + logger('sync_files attach does not exists: ' . print_r($att,true), LOGGER_DEBUG); $r = dbq("INSERT INTO attach (`" . implode("`, `", array_keys($att)) . "`) VALUES ('" @@ -988,6 +1120,7 @@ function sync_files($channel,$files) { if($att['filetype'] === 'multipart/mixed' && $att['is_dir']) { os_mkdir($newfname, STORAGE_DEFAULT_PERMISSIONS,true); + $attachment_stored = true; continue; } else { @@ -1035,6 +1168,11 @@ function sync_files($channel,$files) { $p['aid'] = $channel['channel_account_id']; $p['uid'] = $channel['channel_id']; + convert_oldfields($p,'data','content'); + convert_oldfields($p,'scale','imgscale'); + convert_oldfields($p,'size','filesize'); + convert_oldfields($p,'type','mimetype'); + // if this is a profile photo, undo the profile photo bit // for any other photo which previously held it. @@ -1060,15 +1198,15 @@ function sync_files($channel,$files) { ); } - if($p['scale'] === 0 && $p['os_storage']) - $p['data'] = $store_path; + if($p['imgscale'] === 0 && $p['os_storage']) + $p['content'] = $store_path; else - $p['data'] = base64_decode($p['data']); + $p['content'] = base64_decode($p['content']); - $exists = q("select * from photo where resource_id = '%s' and scale = %d and uid = %d limit 1", + $exists = q("select * from photo where resource_id = '%s' and imgscale = %d and uid = %d limit 1", dbesc($p['resource_id']), - intval($p['scale']), + intval($p['imgscale']), intval($channel['channel_id']) ); @@ -1093,34 +1231,18 @@ function sync_files($channel,$files) { } } if($f['item']) { - sync_items($channel,$f['item']); - foreach($f['item'] as $i) { - if($i['message_id'] !== $i['message_parent']) - continue; - $r = q("select * from item where mid = '%s' and uid = %d limit 1", - dbesc($i['message_id']), - intval($channel['channel_id']) - ); - if($r) { - $item = $r[0]; - item_url_replace($channel,$item,$oldbase,z_root(),$original_channel); - - dbesc_array($item); - $item_id = $item['id']; - unset($item['id']); - $str = ''; - foreach($item as $k => $v) { - if($str) - $str .= ","; - $str .= " `" . $k . "` = '" . $v . "' "; - } - - $r = dbq("update `item` set " . $str . " where id = " . $item_id ); - } - } + sync_items($channel,$f['item'], + ['channel_address' => $original_channel,'url' => $oldbase] + ); } } } } +function convert_oldfields(&$arr,$old,$new) { + if(array_key_exists($old,$arr)) { + $arr[$new] = $arr[$old]; + unset($arr[$old]); + } +} diff --git a/include/importdoc.php b/include/importdoc.php deleted file mode 100755 index 10f868697..000000000 --- a/include/importdoc.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - - - -require_once('include/cli_startup.php'); - - -function importdoc_run($argv, $argc){ - - cli_startup(); - - require_once('mod/help.php'); - - - update_docs_dir('doc/*'); - -} -if (array_search(__file__,get_included_files())===0){ - importdoc_run($argv,$argc); - killme(); -} - -function update_docs_dir($s) { - $f = basename($s); - $d = dirname($s); - if($s === 'doc/html') - return; - $files = glob("$d/$f"); - if($files) { - foreach($files as $fi) { - if($fi === 'doc/html') - continue; - if(is_dir($fi)) - update_docs_dir("$fi/*"); - else - store_doc_file($fi); - } - } -} - - diff --git a/include/items.php b/include/items.php index 95822c0ba..178fb30d6 100755 --- a/include/items.php +++ b/include/items.php @@ -3,12 +3,15 @@ * @file include/items.php */ -/** @todo deprecated in newer SabreDAV releases Sabre\HTTP\URLUtil */ -use Sabre\DAV\URLUtil; +// uncertain if this line is needed and why +use Sabre\HTTP\URLUtil; + +use Zotlabs\Lib as Zlib; require_once('include/bbcode.php'); require_once('include/oembed.php'); require_once('include/crypto.php'); +require_once('include/feedutils.php'); require_once('include/photo/photo_driver.php'); require_once('include/permissions.php'); @@ -33,7 +36,6 @@ function collect_recipients($item, &$private_envelope) { $allow_people = expand_acl($item['allow_cid']); $allow_groups = expand_groups(expand_acl($item['allow_gid'])); - $allow_groups = filter_insecure($item['uid'],$allow_groups); $recipients = array_unique(array_merge($allow_people,$allow_groups)); @@ -77,7 +79,7 @@ function collect_recipients($item, &$private_envelope) { // by the directives in $item['public_policy']. $private_envelope = false; - require_once('include/identity.php'); + require_once('include/channel.php'); //$sys = get_sys_channel(); if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') { @@ -140,46 +142,6 @@ function collect_recipients($item, &$private_envelope) { return $recipients; } -/** - * If channel is configured to filter insecure members of privacy groups - * (those whose networks leak privacy via email notifications or other criteria) - * remove them from any privacy groups (collections) that were included in a post. - * They can still be addressed individually. - * Networks may need to be added or removed from this list as circumstances change. - * - * Update: this may need to be the default, which will force people to opt-in to - * sending stuff privately to insecure platforms. - * - * @param int $channel_id - * @param array $arr - * @return array containing the sane xchan_hashes - */ -function filter_insecure($channel_id, $arr) { - $insecure_nets = " and not xchan_network in ('diaspora', 'friendica-over-diaspora') "; - - $ret = array(); - - if((! intval(get_pconfig($channel_id, 'system', 'filter_insecure_privacy_groups'))) || (! $arr)) - return $arr; - - $str = ''; - foreach($arr as $rr) { - if(strlen($str)) - $str .= ','; - - $str .= "'" . dbesc($rr) . "'"; - } - $r = q("select xchan_hash from xchan where xchan_hash in ($str) $insecure_nets"); - if($r) { - foreach($r as $rr) { - $ret[] = $rr['xchan_hash']; - } - } - - return $ret; -} - - function comments_are_now_closed($item) { if($item['comments_closed'] !== NULL_DATE) { $d = datetime_convert(); @@ -221,7 +183,7 @@ function is_item_normal($item) { * This function examines the comment_policy attached to an item and decides if the current observer has * sufficient privileges to comment. This will normally be called on a remote site where perm_is_allowed() * will not be suitable because the post owner does not have a local channel_id. - * Generally we should look at the item - in particular the author['book_flags'] and see if ABOOK_FLAG_SELF is set. + * Generally we should look at the item - in particular the author['abook_flags'] and see if ABOOK_FLAG_SELF is set. * If it is, you should be able to use perm_is_allowed( ... 'post_comments'), and if it isn't you need to call * can_comment_on_post() * We also check the comments_closed date/time on the item if this is set. @@ -262,8 +224,7 @@ function can_comment_on_post($observer_xchan, $item) { case 'contacts': case 'authenticated': case '': - if(array_key_exists('owner',$item)) { - if(($item['owner']['abook_xchan']) && ($item['owner']['abook_their_perms'] & PERMS_W_COMMENT)) + if(array_key_exists('owner',$item) && get_abconfig($item['uid'],$item['owner']['abook_xchan'],'their_perms','post_comments')) { return true; } break; @@ -424,7 +385,7 @@ function post_activity_item($arr) { return $ret; } - $arr['public_policy'] = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope($channel['channel_r_stream'],true)); + $arr['public_policy'] = ((x($_REQUEST,'public_policy')) ? escape_tags($_REQUEST['public_policy']) : map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true)); if($arr['public_policy']) $arr['item_private'] = 1; @@ -460,7 +421,7 @@ function post_activity_item($arr) { $arr['deny_cid'] = ((x($arr,'deny_cid')) ? $arr['deny_cid'] : $channel['channel_deny_cid']); $arr['deny_gid'] = ((x($arr,'deny_gid')) ? $arr['deny_gid'] : $channel['channel_deny_gid']); - $arr['comment_policy'] = map_scope($channel['channel_w_comment']); + $arr['comment_policy'] = map_scope(\Zotlabs\Access/PermissionLimits::Get($channel['channel_id'],'post_comments')); if ((! $arr['plink']) && (intval($arr['item_thread_top']))) { $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; @@ -485,13 +446,9 @@ function post_activity_item($arr) { if($post_id) { $arr['id'] = $post_id; call_hooks('post_local_end', $arr); - proc_run('php','include/notifier.php','activity',$post_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','activity',$post_id)); $ret['success'] = true; - $r = q("select * from item where id = %d limit 1", - intval($post_id) - ); - if($r) - $ret['activity'] = $r[0]; + $ret['activity'] = $post['item']; } return $ret; @@ -524,214 +481,6 @@ function validate_item_elements($message,$arr) { /** - * @brief Generate an Atom feed. - * - * @param array $channel - * @param array $params - */ -function get_public_feed($channel, $params) { - - $type = 'xml'; - $begin = NULL_DATE; - $end = ''; - $start = 0; - $records = 40; - $direction = 'desc'; - $pages = 0; - - if(! $params) - $params = array(); - - $params['type'] = ((x($params,'type')) ? $params['type'] : 'xml'); - $params['begin'] = ((x($params,'begin')) ? $params['begin'] : NULL_DATE); - $params['end'] = ((x($params,'end')) ? $params['end'] : datetime_convert('UTC','UTC','now')); - $params['start'] = ((x($params,'start')) ? $params['start'] : 0); - $params['records'] = ((x($params,'records')) ? $params['records'] : 40); - $params['direction'] = ((x($params,'direction')) ? $params['direction'] : 'desc'); - $params['pages'] = ((x($params,'pages')) ? intval($params['pages']) : 0); - $params['top'] = ((x($params,'top')) ? intval($params['top']) : 0); - $params['cat'] = ((x($params,'cat')) ? $params['cat'] : ''); - - - // put a sane lower limit on feed requests if not specified - - if($params['begin'] === NULL_DATE) - $params['begin'] = datetime_convert('UTC','UTC','now - 1 month'); - - switch($params['type']) { - case 'json': - header("Content-type: application/atom+json"); - break; - case 'xml': - default: - header("Content-type: application/atom+xml"); - break; - } - - return get_feed_for($channel, get_observer_hash(), $params); -} - -/** - * @brief - * - * @param array $channel - * @param string $observer_hash - * @param array $params - * @return string - */ -function get_feed_for($channel, $observer_hash, $params) { - - if(! channel) - http_status_exit(401); - - if($params['pages']) { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_pages')) - http_status_exit(403); - } else { - if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) - http_status_exit(403); - } - $items = items_fetch(array( - 'wall' => '1', - 'datequery' => $params['end'], - 'datequery2' => $params['begin'], - 'start' => $params['start'], // FIXME - 'records' => $params['records'], // FIXME - 'direction' => $params['direction'], // FIXME - 'pages' => $params['pages'], - 'order' => 'post', - 'top' => $params['top'], - 'cat' => $params['cat'] - ), $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module); - - - $feed_template = get_markup_template('atom_feed.tpl'); - - $atom = ''; - - $atom .= replace_macros($feed_template, array( - '$version' => xmlify(Zotlabs\Project\System::get_project_version()), - '$red' => xmlify(Zotlabs\Project\System::get_platform_name()), - '$feed_id' => xmlify($channel['xchan_url']), - '$feed_title' => xmlify($channel['channel_name']), - '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , - '$hub' => '', // feed_hublinks(), - '$salmon' => '', // feed_salmonlinks($channel['channel_address']), - '$name' => xmlify($channel['channel_name']), - '$profile_page' => xmlify($channel['xchan_url']), - '$mimephoto' => xmlify($channel['xchan_photo_mimetype']), - '$photo' => xmlify($channel['xchan_photo_l']), - '$thumb' => xmlify($channel['xchan_photo_m']), - '$picdate' => '', - '$uridate' => '', - '$namdate' => '', - '$birthday' => '', - '$community' => '', - )); - - - call_hooks('atom_feed', $atom); - - if($items) { - $type = 'html'; - foreach($items as $item) { - if($item['item_private']) - continue; - - /** @BUG $owner is undefined in this call */ - $atom .= atom_entry($item, $type, null, $owner, true); - } - } - - call_hooks('atom_feed_end', $atom); - - $atom .= '</feed>' . "\r\n"; - - return $atom; -} - -/** - * @brief - * - * @param array $item an associative array with - * * \b string \b verb - * @return string item's verb if set, default ACTIVITY_POST see boot.php - */ -function construct_verb($item) { - if ($item['verb']) - return $item['verb']; - - return ACTIVITY_POST; -} - -function construct_activity_object($item) { - - if($item['object']) { - $o = '<as:object>' . "\r\n"; - $r = json_decode($item['object'],false); - - if(! $r) - return ''; - if($r->type) - $o .= '<as:obj_type>' . xmlify($r->type) . '</as:obj_type>' . "\r\n"; - if($r->id) - $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; - if($r->title) - $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; - if($r->links) { - /** @FIXME!! */ - if(substr($r->link,0,1) === '<') { - $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link); - $o .= $r->link; - } - else - $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n"; - } - if($r->content) - $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n"; - $o .= '</as:object>' . "\r\n"; - return $o; - } - - return ''; -} - -function construct_activity_target($item) { - - if($item['target']) { - $o = '<as:target>' . "\r\n"; - $r = json_decode($item['target'],false); - if(! $r) - return ''; - if($r->type) - $o .= '<as:obj_type>' . xmlify($r->type) . '</as:obj_type>' . "\r\n"; - if($r->id) - $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; - if($r->title) - $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; - if($r->links) { - /** @FIXME !!! */ - if(substr($r->link,0,1) === '<') { - if(strstr($r->link,'&') && (! strstr($r->link,'&'))) - $r->link = str_replace('&','&', $r->link); - $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link); - $o .= $r->link; - } - else - $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n"; - } - if($r->content) - $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n"; - - $o .= '</as:target>' . "\r\n"; - - return $o; - } - - return ''; -} - -/** * @brief Limit lenght on imported system messages. * * The purpose of this function is to apply system message length limits to @@ -912,7 +661,7 @@ function get_item_elements($x,$allow_code = false) { $arr['diaspora_meta'] = (($x['diaspora_signature']) ? $x['diaspora_signature'] : ''); - $arr['object'] = activity_sanitise($x['object']); + $arr['obj'] = activity_sanitise($x['object']); $arr['target'] = activity_sanitise($x['target']); $arr['attach'] = activity_sanitise($x['attach']); @@ -923,13 +672,23 @@ function get_item_elements($x,$allow_code = false) { $arr['item_flags'] = 0; - if(array_key_exists('flags',$x) && in_array('consensus',$x['flags'])) - $arr['item_consensus'] = 1; + if(array_key_exists('flags',$x)) { - if(array_key_exists('flags',$x) && in_array('deleted',$x['flags'])) - $arr['item_deleted'] = 1; - if(array_key_exists('flags',$x) && in_array('hidden',$x['flags'])) - $arr['item_hidden'] = 1; + if(in_array('consensus',$x['flags'])) + $arr['item_consensus'] = 1; + + if(in_array('deleted',$x['flags'])) + $arr['item_deleted'] = 1; + + if(in_array('notshown',$x['flags'])) + $arr['item_notshown'] = 1; + + // hidden item are no longer propagated - notshown may be a suitable alternative + + if(in_array('hidden',$x['flags'])) + $arr['item_hidden'] = 1; + + } // Here's the deal - the site might be down or whatever but if there's a new person you've never // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their @@ -1211,12 +970,12 @@ function encode_item($item,$mirror = false) { // logger('encode_item: ' . print_r($item,true)); - $r = q("select channel_r_stream, channel_w_comment from channel where channel_id = %d limit 1", + $r = q("select channel_id from channel where channel_id = %d limit 1", intval($item['uid']) ); if($r) - $comment_scope = $r[0]['channel_w_comment']; + $comment_scope = \Zotlabs\Access\PermissionLimits::Get($item['uid'],'post_comments'); else $comment_scope = 0; @@ -1230,9 +989,9 @@ function encode_item($item,$mirror = false) { if(array_key_exists('item_obscured',$item) && intval($item['item_obscured'])) { if($item['title']) - $item['title'] = crypto_unencapsulate(json_decode_plus($item['title']),$key); + $item['title'] = crypto_unencapsulate(json_decode($item['title'],true),$key); if($item['body']) - $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key); + $item['body'] = crypto_unencapsulate(json_decode($item['body'],true),$key); } // If we're trying to backup an item so that it's recoverable or for export/imprt, @@ -1301,12 +1060,12 @@ function encode_item($item,$mirror = false) { $x['owner'] = encode_item_xchan($item['owner']); $x['author'] = encode_item_xchan($item['author']); - if($item['object']) - $x['object'] = json_decode_plus($item['object']); + if($item['obj']) + $x['object'] = json_decode($item['obj'],true); if($item['target']) - $x['target'] = json_decode_plus($item['target']); + $x['target'] = json_decode($item['target'],true); if($item['attach']) - $x['attach'] = json_decode_plus($item['attach']); + $x['attach'] = json_decode($item['attach'],true); if($y = encode_item_flags($item)) $x['flags'] = $y; @@ -1428,8 +1187,8 @@ function encode_item_terms($terms,$mirror = false) { if($terms) { foreach($terms as $term) { - if(in_array($term['type'],$allowed_export_terms)) - $ret[] = array('tag' => $term['term'], 'url' => $term['url'], 'type' => termtype($term['type'])); + if(in_array($term['ttype'],$allowed_export_terms)) + $ret[] = array('tag' => $term['term'], 'url' => $term['url'], 'type' => termtype($term['ttype'])); } } @@ -1490,35 +1249,35 @@ function decode_tags($t) { $tag['url'] = htmlspecialchars($x['url'], ENT_COMPAT, 'UTF-8', false); switch($x['type']) { case 'hashtag': - $tag['type'] = TERM_HASHTAG; + $tag['ttype'] = TERM_HASHTAG; break; case 'mention': - $tag['type'] = TERM_MENTION; + $tag['ttype'] = TERM_MENTION; break; case 'category': - $tag['type'] = TERM_CATEGORY; + $tag['ttype'] = TERM_CATEGORY; break; case 'private_category': - $tag['type'] = TERM_PCATEGORY; + $tag['ttype'] = TERM_PCATEGORY; break; case 'file': - $tag['type'] = TERM_FILE; + $tag['ttype'] = TERM_FILE; break; case 'search': - $tag['type'] = TERM_SEARCH; + $tag['ttype'] = TERM_SEARCH; break; case 'thing': - $tag['type'] = TERM_THING; + $tag['ttype'] = TERM_THING; break; case 'bookmark': - $tag['type'] = TERM_BOOKMARK; + $tag['ttype'] = TERM_BOOKMARK; break; case 'communitytag': - $tag['type'] = TERM_COMMUNITYTAG; + $tag['ttype'] = TERM_COMMUNITYTAG; break; default: case 'unknown': - $tag['type'] = TERM_UNKNOWN; + $tag['ttype'] = TERM_UNKNOWN; break; } $ret[] = $tag; @@ -1585,6 +1344,8 @@ function encode_item_flags($item) { $ret[] = 'deleted'; if(intval($item['item_hidden'])) $ret[] = 'hidden'; + if(intval($item['item_notshown'])) + $ret[] = 'notshown'; if(intval($item['item_thread_top'])) $ret[] = 'thread_parent'; if(intval($item['item_nsfw'])) @@ -1620,7 +1381,7 @@ function encode_mail($item,$extended = false) { $x['to'] = encode_item_xchan($item['to']); if($item['attach']) - $x['attach'] = json_decode_plus($item['attach']); + $x['attach'] = json_decode($item['attach'],true); $x['flags'] = array(); @@ -1742,453 +1503,6 @@ function get_profile_elements($x) { return $arr; } -/** - * @param object $feed - * @param array $item - * @param[out] array $author - * @return multitype:multitype: string NULL number Ambigous <NULL, string, number> Ambigous <mixed, string> Ambigous <multitype:multitype:string Ambigous <NULL, string> , multitype:multitype:string unknown > multitype:NULL unknown - */ -function get_atom_elements($feed, $item, &$author) { - - //$best_photo = array(); - - $res = array(); - - $found_author = $item->get_author(); - if($found_author) { - $author['author_name'] = unxmlify($found_author->get_name()); - $author['author_link'] = unxmlify($found_author->get_link()); - $author['author_is_feed'] = false; - } - else { - $author['author_name'] = unxmlify($feed->get_title()); - $author['author_link'] = unxmlify($feed->get_permalink()); - $author['author_is_feed'] = true; - } - - if(substr($author['author_link'],-1,1) == '/') - $author['author_link'] = substr($author['author_link'],0,-1); - - $res['mid'] = base64url_encode(unxmlify($item->get_id())); - $res['title'] = unxmlify($item->get_title()); - $res['body'] = unxmlify($item->get_content()); - $res['plink'] = unxmlify($item->get_link(0)); - $res['item_rss'] = 1; - - - // removing the content of the title if its identically to the body - // This helps with auto generated titles e.g. from tumblr - - if (title_is_body($res["title"], $res["body"])) - $res['title'] = ""; - - if($res['plink']) - $base_url = implode('/', array_slice(explode('/',$res['plink']),0,3)); - else - $base_url = ''; - - // look for a photo. We should check media size and find the best one, - // but for now let's just find any author photo - - $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if(!x($author, 'author_photo') || ! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); - - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { - $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - if($base && count($base)) { - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) - $author['author_link'] = unxmlify($link['attribs']['']['href']); - if(!x($author, 'author_photo') || ! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - } - - // check for a yahoo media element (github etc.) - - if(! $author['author_photo']) { - $rawmedia = $item->get_item_tags(NAMESPACE_YMEDIA,'thumbnail'); - if($rawmedia && $rawmedia[0]['attribs']['']['url']) { - $author['author_photo'] = strip_tags(unxmlify($rawmedia[0]['attribs']['']['url'])); - } - } - - - // No photo/profile-link on the item - look at the feed level - - if((! (x($author,'author_link'))) || (! (x($author,'author_photo')))) { - $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $author['author_link'])) { - $author['author_link'] = unxmlify($link['attribs']['']['href']); - $author['author_is_feed'] = true; - } - if(! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); - - if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['obj_type'][0]['data'],ACTIVITY_OBJ_PERSON)) { - $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - - if($base && count($base)) { - foreach($base as $link) { - if($link['attribs']['']['rel'] === 'alternate' && (! $res['author_link'])) - $author['author_link'] = unxmlify($link['attribs']['']['href']); - if(! (x($author,'author_photo'))) { - if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo') - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - } - } - - $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info'); - if($apps && $apps[0]['attribs']['']['source']) { - $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); - } - - /* - * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it. - */ - - $have_real_body = false; - - $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env'); - if($rawenv) { - $have_real_body = true; - $res['body'] = $rawenv[0]['data']; - $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']); - // make sure nobody is trying to sneak some html tags by us - $res['body'] = notags(base64url_decode($res['body'])); - - // We could probably turn these old Friendica bbcode bookmarks into bookmark tags but we'd have to - // create a term table item for them. For now just make sure they stay as links. - - $res['body'] = preg_replace('/\[bookmark(.*?)\](.*?)\[\/bookmark\]/','[url$1]$2[/url]',$res['body']); - } - - $res['body'] = limit_body_size($res['body']); - - // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust - // the content type. Our own network only emits text normally, though it might have been converted to - // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will - // have to assume it is all html and needs to be purified. - - // It doesn't matter all that much security wise - because before this content is used anywhere, we are - // going to escape any tags we find regardless, but this lets us import a limited subset of html from - // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining - // html. - - if((strpos($res['body'],'<') !== false) && (strpos($res['body'],'>') !== false)) { - - $res['body'] = reltoabs($res['body'],$base_url); - - $res['body'] = html2bb_video($res['body']); - - $res['body'] = oembed_html2bbcode($res['body']); - - $res['body'] = purify_html($res['body']); - - $res['body'] = @html2bbcode($res['body']); - } - elseif(! $have_real_body) { - - // it's not one of our messages and it has no tags - // so it's probably just text. We'll escape it just to be safe. - - $res['body'] = escape_tags($res['body']); - } - - if($res['plink'] && $res['title']) { - $res['body'] = '#^[url=' . $res['plink'] . ']' . $res['title'] . '[/url]' . "\n\n" . $res['body']; - $terms = array(); - $terms[] = array( - 'otype' => TERM_OBJ_POST, - 'type' => TERM_BOOKMARK, - 'url' => $res['plink'], - 'term' => $res['title'], - ); - } - elseif($res['plink']) { - $res['body'] = '#^[url]' . $res['plink'] . '[/url]' . "\n\n" . $res['body']; - $terms = array(); - $terms[] = array( - 'otype' => TERM_OBJ_POST, - 'type' => TERM_BOOKMARK, - 'url' => $res['plink'], - 'term' => $res['plink'], - ); - } - - $private = $item->get_item_tags(NAMESPACE_DFRN,'private'); - if($private && intval($private[0]['data']) > 0) - $res['item_private'] = ((intval($private[0]['data'])) ? 1 : 0); - else - $res['item_private'] = 0; - - $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location'); - if($rawlocation) - $res['location'] = unxmlify($rawlocation[0]['data']); - - $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published'); - if($rawcreated) - $res['created'] = unxmlify($rawcreated[0]['data']); - - $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated'); - if($rawedited) - $res['edited'] = unxmlify($rawedited[0]['data']); - - if((x($res,'edited')) && (! (x($res,'created')))) - $res['created'] = $res['edited']; - - if(! $res['created']) - $res['created'] = $item->get_date('c'); - - if(! $res['edited']) - $res['edited'] = $item->get_date('c'); - - - // Disallow time travelling posts - - $d1 = strtotime($res['created']); - $d2 = strtotime($res['edited']); - $d3 = strtotime('now'); - - if($d1 > $d3) - $res['created'] = datetime_convert(); - if($d2 > $d3) - $res['edited'] = datetime_convert(); - - $res['created'] = datetime_convert('UTC','UTC',$res['created']); - $res['edited'] = datetime_convert('UTC','UTC',$res['edited']); - - $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); - if(! $rawowner) - $rawowner = $item->get_item_tags(NAMESPACE_ZOT,'owner'); - - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']) - $author['owner_name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']); - elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']) - $author['owner_name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']); - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']) - $author['owner_link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); - elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']) - $author['owner_link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']); - - if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - - foreach($base as $link) { - if(!x($author, 'owner_photo') || ! $author['owner_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') - $author['owner_photo'] = unxmlify($link['attribs']['']['href']); - } - } - } - - $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point'); - if($rawgeo) - $res['coord'] = unxmlify($rawgeo[0]['data']); - - - $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb'); - - // select between supported verbs - - if($rawverb) { - $res['verb'] = unxmlify($rawverb[0]['data']); - } - - // translate OStatus unfollow to activity streams if it happened to get selected - - if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) - $res['verb'] = ACTIVITY_UNFOLLOW; - - $cats = $item->get_categories(); - if($cats) { - if(is_null($terms)) - $terms = array(); - foreach($cats as $cat) { - $term = $cat->get_term(); - if(! $term) - $term = $cat->get_label(); - $scheme = $cat->get_scheme(); - $termurl = ''; - if($scheme && $term && stristr($scheme,'X-DFRN:')) { - $termtype = ((substr($scheme,7,1) === '#') ? TERM_HASHTAG : TERM_MENTION); - $termurl = unxmlify(substr($scheme,9)); - } - else { - $termtype = TERM_CATEGORY; - } - $termterm = notags(trim(unxmlify($term))); - - if($termterm) { - $terms[] = array( - 'otype' => TERM_OBJ_POST, - 'type' => $termtype, - 'url' => $termurl, - 'term' => $termterm, - ); - } - } - } - - if(! is_null($terms)) - $res['term'] = $terms; - - $attach = $item->get_enclosures(); - if($attach) { - $res['attach'] = array(); - foreach($attach as $att) { - $len = intval($att->get_length()); - $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link())))); - $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title())))); - $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type())))); - if(strpos($type,';')) - $type = substr($type,0,strpos($type,';')); - if((! $link) || (strpos($link,'http') !== 0)) - continue; - - if(! $title) - $title = ' '; - if(! $type) - $type = 'application/octet-stream'; - - $res['attach'][] = array('href' => $link, 'length' => $len, 'type' => $type, 'title' => $title ); - } - } - - $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); - - if($rawobj) { - $obj = array(); - - $child = $rawobj[0]['child']; - if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) { - $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; - $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; - } - if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { - $res['obj_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; - $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; - } - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) - $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) - $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']); - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) - $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; - if(! $body) - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; - // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events - $obj['orig'] = xmlify($body); - if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - $body = purify_html($body); - $body = html2bbcode($body); - } - - $obj['content'] = $body; - } - - $res['object'] = $obj; - } - - $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target'); - - if($rawobj) { - $obj = array(); - - $child = $rawobj[0]['child']; - if($child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']) { - $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; - $obj['type'] = $child[NAMESPACE_ACTIVITY]['obj_type'][0]['data']; - } - if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) { - $res['tgt_type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; - $obj['type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data']; - } - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']) - $obj['id'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data']; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) - $obj['link'] = encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']); - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']) - $obj['title'] = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data']; - if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) { - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']; - if(! $body) - $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; - - // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events - $obj['orig'] = xmlify($body); - if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - $body = purify_html($body); - $body = html2bbcode($body); - } - - $obj['content'] = $body; - } - - $res['target'] = $obj; - } - - $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); - - call_hooks('parse_atom', $arr); - logger('get_atom_elements: author: ' . print_r($author,true),LOGGER_DATA); - - logger('get_atom_elements: ' . print_r($res,true),LOGGER_DATA); - - return $res; -} - -function encode_rel_links($links) { - $o = array(); - if(! ((is_array($links)) && (count($links)))) - return $o; - - foreach($links as $link) { - $l = array(); - if($link['attribs']['']['rel']) - $l['rel'] = $link['attribs']['']['rel']; - if($link['attribs']['']['type']) - $l['type'] = $link['attribs']['']['type']; - if($link['attribs']['']['href']) - $l['href'] = $link['attribs']['']['href']; - if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width']) - $l['width'] = $link['attribs'][NAMESPACE_MEDIA]['width']; - if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height']) - $l['height'] = $link['attribs'][NAMESPACE_MEDIA]['height']; - - if($l) - $o[] = $l; - } - return $o; -} /** * @brief @@ -2223,7 +1537,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { dbesc($arr['layout_mid']), intval($arr['uid']) ); - if((! $l) || (! ($l[0]['item_type'] != ITEM_TYPE_PDL))) + if((! $l) || ($l[0]['item_type'] != ITEM_TYPE_PDL)) unset($arr['layout_mid']); } @@ -2286,9 +1600,9 @@ function item_store($arr, $allow_exec = false, $deliver = true) { } } - if((x($arr,'object')) && is_array($arr['object'])) { - activity_sanitise($arr['object']); - $arr['object'] = json_encode($arr['object']); + if((x($arr,'obj')) && is_array($arr['obj'])) { + activity_sanitise($arr['obj']); + $arr['obj'] = json_encode($arr['obj']); } if((x($arr,'target')) && is_array($arr['target'])) { @@ -2319,7 +1633,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { $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['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : ''); $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'])) : ''); @@ -2388,7 +1702,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { return $ret; } - if(($arr['obj_type'] == ACTIVITY_OBJ_NOTE) && (! $arr['object'])) + if(($arr['obj_type'] == ACTIVITY_OBJ_NOTE) && (! $arr['obj'])) $arr['obj_type'] = ACTIVITY_OBJ_COMMENT; // is the new message multi-level threaded? @@ -2548,12 +1862,12 @@ function item_store($arr, $allow_exec = false, $deliver = true) { if(($terms) && (is_array($terms))) { foreach($terms as $t) { - q("insert into term (uid,oid,otype,type,term,url) + q("insert into term (uid,oid,otype,ttype,term,url) values(%d,%d,%d,%d,'%s','%s') ", intval($arr['uid']), intval($current_post), intval(TERM_OBJ_POST), - intval($t['type']), + intval($t['ttype']), dbesc($t['term']), dbesc($t['url']) ); @@ -2570,6 +1884,7 @@ function item_store($arr, $allow_exec = false, $deliver = true) { } + $ret['item'] = $arr; call_hooks('post_remote_end',$arr); @@ -2682,9 +1997,9 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { } } - if((x($arr,'object')) && is_array($arr['object'])) { - activity_sanitise($arr['object']); - $arr['object'] = json_encode($arr['object']); + if((x($arr,'obj')) && is_array($arr['obj'])) { + activity_sanitise($arr['obj']); + $arr['obj'] = json_encode($arr['obj']); } if((x($arr,'target')) && is_array($arr['target'])) { @@ -2726,7 +2041,7 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']); $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : $orig[0]['obj_type']); - $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : $orig[0]['object']); + $arr['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : $orig[0]['obj']); $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : $orig[0]['tgt_type']); $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : $orig[0]['target']); $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : $orig[0]['plink']); @@ -2820,6 +2135,15 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { return $ret; } + // fetch an unescaped complete copy of the stored item + + $r = q("select * from item where id = %d", + intval($orig_post_id) + ); + if($r) + $arr = $r[0]; + + $r = q("delete from term where oid = %d and otype = %d", intval($orig_post_id), intval(TERM_OBJ_POST) @@ -2827,12 +2151,12 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { if(is_array($terms)) { foreach($terms as $t) { - q("insert into term (uid,oid,otype,type,term,url) + q("insert into term (uid,oid,otype,ttype,term,url) values(%d,%d,%d,%d,'%s','%s') ", intval($uid), intval($orig_post_id), intval(TERM_OBJ_POST), - intval($t['type']), + intval($t['ttype']), dbesc($t['term']), dbesc($t['url']) ); @@ -2851,6 +2175,8 @@ function item_store_update($arr,$allow_exec = false, $deliver = true) { $arr['iconfig'] = $meta; } + $ret['item'] = $arr; + call_hooks('post_remote_update_end',$arr); if($deliver) { @@ -2979,8 +2305,8 @@ function send_status_notifications($post_id,$item) { if(! $notify) return; - require_once('include/enotify.php'); - notification(array( + + Zlib\Enotify::submit(array( 'type' => NOTIFY_COMMENT, 'from_xchan' => $item['author_xchan'], 'to_xchan' => $r[0]['channel_hash'], @@ -3060,10 +2386,10 @@ function tag_deliver($uid, $item_id) { if (stristr($item['verb'],ACTIVITY_POKE)) { $poke_notify = true; - if(($item['obj_type'] == "") || ($item['obj_type'] !== ACTIVITY_OBJ_PERSON) || (! $item['object'])) + if(($item['obj_type'] == "") || ($item['obj_type'] !== ACTIVITY_OBJ_PERSON) || (! $item['obj'])) $poke_notify = false; - $obj = json_decode_plus($item['object']); + $obj = json_decode($item['obj'],true); if($obj) { if($obj['id'] !== $u[0]['channel_hash']) $poke_notify = false; @@ -3073,8 +2399,7 @@ function tag_deliver($uid, $item_id) { $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); if($poke_notify) { - require_once('include/enotify.php'); - notification(array( + Zlib\Enotify::submit(array( 'to_xchan' => $u[0]['channel_hash'], 'from_xchan' => $item['author_xchan'], 'type' => NOTIFY_POKE, @@ -3101,14 +2426,14 @@ function tag_deliver($uid, $item_id) { if(($item['owner_xchan'] === $u[0]['channel_hash']) && (! get_pconfig($u[0]['channel_id'],'system','blocktags'))) { logger('tag_deliver: community tag recipient: ' . $u[0]['channel_name']); - $j_tgt = json_decode_plus($item['target']); + $j_tgt = json_decode($item['target'],true); if($j_tgt && $j_tgt['id']) { $p = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($j_tgt['id']), intval($u[0]['channel_id']) ); if($p) { - $j_obj = json_decode_plus($item['object']); + $j_obj = json_decode($item['obj'],true); logger('tag_deliver: tag object: ' . print_r($j_obj,true), LOGGER_DATA); if($j_obj && $j_obj['id'] && $j_obj['title']) { if(is_array($j_obj['link'])) @@ -3122,7 +2447,7 @@ function tag_deliver($uid, $item_id) { dbesc($j_tgt['id']), intval($u[0]['channel_id']) ); - proc_run('php','include/notifier.php','edit_post',$p[0]['id']); + Zotlabs\Daemon\Master::Summon(array('Notifier','edit_post',$p[0]['id'])); } } } @@ -3193,7 +2518,7 @@ function tag_deliver($uid, $item_id) { if(intval($item['item_obscured'])) { $key = get_config('system','prvkey'); if($item['body']) - $body = crypto_unencapsulate(json_decode_plus($item['body']),$key); + $body = crypto_unencapsulate(json_decode($item['body'],true),$key); } else $body = $item['body']; @@ -3239,8 +2564,7 @@ function tag_deliver($uid, $item_id) { * Kill two birds with one stone. As long as we're here, send a mention notification. */ - require_once('include/enotify.php'); - notification(array( + Zlib\Enotify::submit(array( 'to_xchan' => $u[0]['channel_hash'], 'from_xchan' => $item['author_xchan'], 'type' => NOTIFY_TAGSELF, @@ -3400,13 +2724,44 @@ function tgroup_check($uid,$item) { */ function start_delivery_chain($channel, $item, $item_id, $parent) { + $sourced = check_item_source($channel['channel_id'],$item); + + if($sourced) { + $r = q("select * from source where src_channel_id = %d and ( src_xchan = '%s' or src_xchan = '*' ) limit 1", + intval($channel['channel_id']), + dbesc(($item['source_xchan']) ? $item['source_xchan'] : $item['owner_xchan']) + ); + if($r) { + $t = trim($r[0]['src_tag']); + if($t) { + $tags = explode(',',$t); + if($tags) { + foreach($tags as $tt) { + $tt = trim($tt); + if($tt) { + q("insert into term (uid,oid,otype,ttype,term,url) + values(%d,%d,%d,%d,'%s','%s') ", + intval($channel['channel_id']), + intval($item_id), + intval(TERM_OBJ_POST), + intval(TERM_CATEGORY), + dbesc($tt), + dbesc(z_root() . '/channel/' . $channel['channel_address'] . '?f=&cat=' . urlencode($tt)) + ); + } + } + } + } + } + } + // Change this copy of the post to a forum head message and deliver to all the tgroup members // also reset all the privacy bits to the forum default permissions $private = (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 1 : 0); - $new_public_policy = map_scope($channel['channel_r_stream'],true); + $new_public_policy = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true); if((! $private) && $new_public_policy) $private = 1; @@ -3451,7 +2806,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { dbesc($channel['channel_deny_gid']), intval($private), dbesc($new_public_policy), - dbesc(map_scope($channel['channel_w_comment'])), + dbesc(map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'post_comments'))), dbesc($title), dbesc($body), intval($item_wall), @@ -3459,8 +2814,11 @@ function start_delivery_chain($channel, $item, $item_id, $parent) { intval($item_id) ); + + + if($r) - proc_run('php','include/notifier.php','tgroup',$item_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','tgroup',$item_id)); else { logger('start_delivery_chain: failed to update item'); // reset the source xchan to prevent loops @@ -3497,7 +2855,7 @@ function check_item_source($uid, $item) { if(! $x) return false; - if(! ($x[0]['abook_their_perms'] & PERMS_A_REPUBLISH)) + if(! get_abconfig($uid,$item['owner_xchan'],'their_perms','republish')) return false; if($item['item_private'] && (! intval($x[0]['abook_feed']))) @@ -3524,7 +2882,7 @@ function check_item_source($uid, $item) { foreach($words as $word) { if(substr($word,0,1) === '#' && $tags) { foreach($tags as $t) - if((($t['type'] == TERM_HASHTAG) || ($t['type'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) + if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) return true; } elseif((strpos($word,'/') === 0) && preg_match($word,$text)) @@ -3577,7 +2935,7 @@ function post_is_importable($item,$abook) { continue; if(substr($word,0,1) === '#' && $tags) { foreach($tags as $t) - if((($t['type'] == TERM_HASHTAG) || ($t['type'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) + if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) return false; } elseif((strpos($word,'/') === 0) && preg_match($word,$text)) @@ -3598,7 +2956,7 @@ function post_is_importable($item,$abook) { continue; if(substr($word,0,1) === '#' && $tags) { foreach($tags as $t) - if((($t['type'] == TERM_HASHTAG) || ($t['type'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) + if((($t['ttype'] == TERM_HASHTAG) || ($t['ttype'] == TERM_COMMUNITYTAG)) && (($t['term'] === substr($word,1)) || (substr($word,1) === '*'))) return true; } elseif((strpos($word,'/') === 0) && preg_match($word,$text)) @@ -3707,7 +3065,6 @@ function mail_store($arr) { ); } else { - require_once('include/enotify.php'); $notif_params = array( 'from_xchan' => $arr['from_xchan'], @@ -3718,584 +3075,13 @@ function mail_store($arr) { 'otype' => 'mail' ); - notification($notif_params); + Zlib\Enotify::submit($notif_params); } call_hooks('post_mail_end',$arr); return $current_post; } -/** - * @brief Process atom feed and update anything/everything we might need to update. - * - * @param array $xml - * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. - * @param $importer - * The contact_record (joined to user_record) of the local user who owns this - * relationship. It is this person's stuff that is going to be updated. - * @param $contact - * The person who is sending us stuff. If not set, we MAY be processing a "follow" activity - * from an external network and MAY create an appropriate contact record. Otherwise, we MUST - * have a contact record. - * @param int $pass by default ($pass = 0) we cannot guarantee that a parent item has been - * imported prior to its children being seen in the stream unless we are certain - * of how the feed is arranged/ordered. - * * With $pass = 1, we only pull parent items out of the stream. - * * With $pass = 2, we only pull children (comments/likes). - * - * So running this twice, first with pass 1 and then with pass 2 will do the right - * thing regardless of feed ordering. This won't be adequate in a fully-threaded - * model where comments can have sub-threads. That would require some massive sorting - * to get all the feed items into a mostly linear ordering, and might still require - * recursion. - */ -function consume_feed($xml, $importer, &$contact, $pass = 0) { - - require_once('library/simplepie/simplepie.inc'); - - if(! strlen($xml)) { - logger('consume_feed: empty input'); - return; - } - - $sys_expire = intval(get_config('system','default_expire_days')); - $chn_expire = intval($importer['channel_expire_days']); - - $expire_days = $sys_expire; - - if(($chn_expire != 0) && ($chn_expire < $sys_expire)) - $expire_days = $chn_expire; - - // logger('expire_days: ' . $expire_days); - - $feed = new SimplePie(); - $feed->set_raw_data($xml); - $feed->init(); - - if($feed->error()) - logger('consume_feed: Error parsing XML: ' . $feed->error()); - - $permalink = $feed->get_permalink(); - - // Check at the feed level for updated contact name and/or photo - - // process any deleted entries - - $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); - if(is_array($del_entries) && count($del_entries) && $pass != 2) { - foreach($del_entries as $dentry) { - $deleted = false; - if(isset($dentry['attribs']['']['ref'])) { - $mid = $dentry['attribs']['']['ref']; - $deleted = true; - if(isset($dentry['attribs']['']['when'])) { - $when = $dentry['attribs']['']['when']; - $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); - } - else - $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - } - - if($deleted && is_array($contact)) { - $r = q("SELECT * from item where mid = '%s' and author_xchan = '%s' and uid = %d limit 1", - dbesc(base64url_encode($mid)), - dbesc($contact['xchan_hash']), - intval($importer['channel_id']) - ); - - if($r) { - $item = $r[0]; - - if(! intval($item['item_deleted'])) { - logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . base64url_decode($item['mid']), LOGGER_DEBUG); - drop_item($item['id'],false); - } - } - } - } - } - - // Now process the feed - - if($feed->get_item_quantity()) { - - logger('consume_feed: feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); - - $items = $feed->get_items(); - - foreach($items as $item) { - - $is_reply = false; - $item_id = base64url_encode($item->get_id()); - - logger('consume_feed: processing ' . $item_id, LOGGER_DEBUG); - - $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); - if(isset($rawthread[0]['attribs']['']['ref'])) { - $is_reply = true; - $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); - } - - if($is_reply) { - - if($pass == 1) - continue; - - // Have we seen it? If not, import it. - - $item_id = base64url_encode($item->get_id()); - $author = array(); - $datarray = get_atom_elements($feed,$item,$author); - - if($contact['xchan_network'] === 'rss') { - $datarray['public_policy'] = 'specific'; - $datarray['comment_policy'] = 'none'; - } - - if((! x($author,'author_name')) || ($author['author_is_feed'])) - $author['author_name'] = $contact['xchan_name']; - if((! x($author,'author_link')) || ($author['author_is_feed'])) - $author['author_link'] = $contact['xchan_url']; - if((! x($author,'author_photo'))|| ($author['author_is_feed'])) - $author['author_photo'] = $contact['xchan_photo_m']; - - $datarray['author_xchan'] = ''; - - if($author['author_link'] != $contact['xchan_url']) { - $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); - if($x) - $datarray['author_xchan'] = $x; - } - if(! $datarray['author_xchan']) - $datarray['author_xchan'] = $contact['xchan_hash']; - - $datarray['owner_xchan'] = $contact['xchan_hash']; - - $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", - dbesc($item_id), - intval($importer['channel_id']) - ); - - - // Update content if 'updated' changes - - if($r) { - if((x($datarray,'edited') !== false) - && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - update_feed_item($importer['channel_id'],$datarray); - } - continue; - } - - $datarray['parent_mid'] = $parent_mid; - $datarray['aid'] = $importer['channel_account_id']; - $datarray['uid'] = $importer['channel_id']; - - logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA); - - $xx = item_store($datarray); - $r = $xx['item_id']; - continue; - } - else { - - // Head post of a conversation. Have we seen it? If not, import it. - - $item_id = base64url_encode($item->get_id()); - $author = array(); - $datarray = get_atom_elements($feed,$item,$author); - - if($contact['xchan_network'] === 'rss') { - $datarray['public_policy'] = 'specific'; - $datarray['comment_policy'] = 'none'; - } - - - if(is_array($contact)) { - if((! x($author,'author_name')) || ($author['author_is_feed'])) - $author['author_name'] = $contact['xchan_name']; - if((! x($author,'author_link')) || ($author['author_is_feed'])) - $author['author_link'] = $contact['xchan_url']; - if((! x($author,'author_photo'))|| ($author['author_is_feed'])) - $author['author_photo'] = $contact['xchan_photo_m']; - } - - if((! x($author,'author_name')) || (! x($author,'author_link'))) { - logger('consume_feed: no author information! ' . print_r($author,true)); - continue; - } - - $datarray['author_xchan'] = ''; - - if(activity_match($datarray['verb'],ACTIVITY_FOLLOW) && $datarray['obj_type'] === ACTIVITY_OBJ_PERSON) { - $cb = array('item' => $datarray,'channel' => $importer, 'xchan' => null, 'author' => $author, 'caught' => false); - call_hooks('follow_from_feed',$cb); - if($cb['caught']) { - if($cb['return_code']) - http_status_exit($cb['return_code']); - continue; - } - } - - if($author['author_link'] != $contact['xchan_url']) { - $x = import_author_unknown(array('name' => $author['author_name'],'url' => $author['author_link'],'photo' => array('src' => $author['author_photo']))); - if($x) - $datarray['author_xchan'] = $x; - } - if(! $datarray['author_xchan']) - $datarray['author_xchan'] = $contact['xchan_hash']; - - $datarray['owner_xchan'] = $contact['xchan_hash']; - - if(array_key_exists('created',$datarray) && $datarray['created'] != NULL_DATE && $expire_days) { - $t1 = $datarray['created']; - $t2 = datetime_convert('UTC','UTC','now - ' . $expire_days . 'days'); - if($t1 < $t2) { - logger('feed content older than expiration. Ignoring.', LOGGER_DEBUG, LOG_INFO); - continue; - } - } - - - - $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", - dbesc($item_id), - intval($importer['channel_id']) - ); - - // Update content if 'updated' changes - - if($r) { - if((x($datarray,'edited') !== false) - && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - update_feed_item($importer['channel_id'],$datarray); - } - - continue; - } - - $datarray['parent_mid'] = $item_id; - $datarray['uid'] = $importer['channel_id']; - $datarray['aid'] = $importer['channel_account_id']; - - if(! link_compare($author['owner_link'],$contact['xchan_url'])) { - logger('consume_feed: Correcting item owner.', LOGGER_DEBUG); - $author['owner_name'] = $contact['name']; - $author['owner_link'] = $contact['url']; - $author['owner_avatar'] = $contact['thumb']; - } - - if(! post_is_importable($datarray,$contact)) - continue; - - logger('consume_feed: author ' . print_r($author,true),LOGGER_DEBUG); - - logger('consume_feed: ' . print_r($datarray,true),LOGGER_DATA); - - $xx = item_store($datarray); - $r = $xx['item_id']; - continue; - } - } - } -} - - -/** - * @brief Process atom feed and return the first post and structure - * - * @param array $xml - * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. - * @param $importer - * The contact_record (joined to user_record) of the local user who owns this - * relationship. It is this person's stuff that is going to be updated. - */ - -function process_salmon_feed($xml, $importer) { - - $ret = array(); - - require_once('library/simplepie/simplepie.inc'); - - if(! strlen($xml)) { - logger('process_feed: empty input'); - return; - } - - $feed = new SimplePie(); - $feed->set_raw_data($xml); - $feed->init(); - - if($feed->error()) - logger('Error parsing XML: ' . $feed->error()); - - $permalink = $feed->get_permalink(); - - if($feed->get_item_quantity()) { - - // this should be exactly one - - logger('feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); - - $items = $feed->get_items(); - - foreach($items as $item) { - - $item_id = base64url_encode($item->get_id()); - - logger('processing ' . $item_id, LOGGER_DEBUG); - - $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); - if(isset($rawthread[0]['attribs']['']['ref'])) { - $is_reply = true; - $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); - } - - if($is_reply) - $ret['parent_mid'] = $parent_mid; - - $ret['author'] = array(); - - $datarray = get_atom_elements($feed,$item,$ret['author']); - - // reset policies which are restricted by default for RSS connections - // This item is likely coming from GNU-social via salmon and allows public interaction - $datarray['public_policy'] = ''; - $datarray['comment_policy'] = ''; - - $ret['item'] = $datarray; - } - } - - return $ret; -} - -/* - * Given an xml (atom) feed, find author and hub links - */ - - -function feed_meta($xml) { - require_once('library/simplepie/simplepie.inc'); - - $ret = array(); - - if(! strlen($xml)) { - logger('empty input'); - return $ret; - } - - $feed = new SimplePie(); - $feed->set_raw_data($xml); - $feed->init(); - - if($feed->error()) { - logger('Error parsing XML: ' . $feed->error()); - return $ret; - } - - $ret['hubs'] = $feed->get_links('hub'); - -// logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA); - - $author = array(); - - $found_author = $feed->get_author(); - if($found_author) { - $author['author_name'] = unxmlify($found_author->get_name()); - $author['author_link'] = unxmlify($found_author->get_link()); - - $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author'); - logger('rawauthor: ' . print_r($rawauthor,true)); - - if($rawauthor) { - if($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) { - $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']; - foreach($base as $link) { - if(!x($author, 'author_photo') || ! $author['author_photo']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') { - $author['author_photo'] = unxmlify($link['attribs']['']['href']); - break; - } - } - } - } - if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) - $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); - if($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']) - $author['author_uri'] = unxmlify($rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']); - - } - } - - if(substr($author['author_link'],-1,1) == '/') - $author['author_link'] = substr($author['author_link'],0,-1); - - $ret['author'] = $author; - - return $ret; -} - - - -function update_feed_item($uid,$datarray) { - logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA); -} - - -function handle_feed($uid,$abook_id,$url) { - - require_once('include/Contact.php'); - $channel = channelx_by_n($uid); - if(! $channel) - return; - - $x = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_channel = %d limit 1", - dbesc($abook_id), - intval($uid) - ); - - $recurse = 0; - $z = z_fetch_url($url,false,$recurse,array('novalidate' => true)); - -//logger('handle_feed:' . print_r($z,true)); - - if($z['success']) { - consume_feed($z['body'],$channel,$x[0],1); - consume_feed($z['body'],$channel,$x[0],2); - } -} - - -function atom_author($tag,$name,$uri,$h,$w,$type,$photo) { - $o = ''; - if(! $tag) - return $o; - - $name = xmlify($name); - $uri = xmlify($uri); - $h = intval($h); - $w = intval($w); - $photo = xmlify($photo); - - $o .= "<$tag>\r\n"; - $o .= "<name>$name</name>\r\n"; - $o .= "<uri>$uri</uri>\r\n"; - $o .= '<link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; - $o .= '<link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; - - call_hooks('atom_author', $o); - - $o .= "</$tag>\r\n"; - - return $o; -} - -function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { - - if(! $item['parent']) - return; - - if($item['deleted']) - return '<at:deleted-entry ref="' . xmlify($item['mid']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; - - - create_export_photo_body($item); - - if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) - $body = fix_private_photos($item['body'],$owner['uid'],$item,$cid); - else - $body = $item['body']; - - $o = "\r\n\r\n<entry>\r\n"; - - if(is_array($author)) - $o .= atom_author('author',$author['xchan_name'],$author['xchan_url'],80,80,$author['xchan_photo_mimetype'],$author['xchan_photo_m']); - else - $o .= atom_author('author',$item['author']['xchan_name'],$item['author']['xchan_url'],80,80,$item['author']['xchan_photo_mimetype'], $item['author']['xchan_photo_m']); - - $o .= atom_author('zot:owner',$item['owner']['xchan_name'],$item['owner']['xchan_url'],80,80,$item['owner']['xchan_photo_mimetype'],$item['owner']['xchan_photo_m']); - - if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { - $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); - $o .= '<thr:in-reply-to ref="' . z_root() . '/display/' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; - } - - if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { - $obj = ((is_array($item['obj'])) ? $item['object'] : json_decode($item['object'],true)); - - $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; - $o .= '<summary>' . xmlify(bbcode($obj['title'])) . '</summary>' . "\r\n"; - $o .= '<dtstart xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['start'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtstart>' . "\r\n"; - $o .= '<dtend xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['finish'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtend>' . "\r\n"; - $o .= '<location>' . bbcode($obj['location']) . '</location>' . "\r\n"; - $o .= '<content type="' . $type . '" >' . xmlify(bbcode($obj['description'])) . '</content>' . "\r\n"; - } - else { - $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; - $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; - } - - $o .= '<id>' . z_root() . '/display/' . xmlify($item['mid']) . '</id>' . "\r\n"; - $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; - $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; - - $o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; - - if($item['location']) { - $o .= '<zot:location>' . xmlify($item['location']) . '</zot:location>' . "\r\n"; - $o .= '<poco:address><poco:formatted>' . xmlify($item['location']) . '</poco:formatted></poco:address>' . "\r\n"; - } - - if($item['coord']) - $o .= '<georss:point>' . xmlify($item['coord']) . '</georss:point>' . "\r\n"; - - if(($item['item_private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) - $o .= '<zot:private>' . (($item['item_private']) ? $item['item_private'] : 1) . '</zot:private>' . "\r\n"; - - if($item['app']) - $o .= '<statusnet:notice_info local_id="' . $item['id'] . '" source="' . xmlify($item['app']) . '" ></statusnet:notice_info>' . "\r\n"; - - $verb = construct_verb($item); - $o .= '<as:verb>' . xmlify($verb) . '</as:verb>' . "\r\n"; - $actobj = construct_activity_object($item); - if(strlen($actobj)) - $o .= $actobj; - $actarg = construct_activity_target($item); - if(strlen($actarg)) - $o .= $actarg; - - // FIXME -// $tags = item_getfeedtags($item); -// if(count($tags)) { -// foreach($tags as $t) { -// $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; -// } -// } - -// FIXME -// $o .= item_getfeedattach($item); - -// $mentioned = get_mentions($item,$tags); -// if($mentioned) -// $o .= $mentioned; - - call_hooks('atom_entry', $o); - - $o .= '</entry>' . "\r\n"; - - return $o; -} function fix_private_photos($s, $uid, $item = null, $cid = 0) { @@ -4324,7 +3110,7 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) { if($x) { $res = substr($i,$x+1); $i = substr($i,0,$x); - $r = q("SELECT * FROM `photo` WHERE `resource_id` = '%s' AND `scale` = %d AND `uid` = %d", + $r = q("SELECT * FROM `photo` WHERE `resource_id` = '%s' AND `imgscale` = %d AND `uid` = %d", dbesc($i), intval($res), intval($uid) @@ -4447,7 +3233,7 @@ function item_getfeedtags($item) { if(count($terms)) { foreach($terms as $term) { - if(($term['type'] == TERM_HASHTAG) || ($term['type'] == TERM_COMMUNITYTAG)) + if(($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG)) $ret[] = array('#',$term['url'],$term['term']); else $ret[] = array('@',$term['url'],$term['term']); @@ -4499,15 +3285,17 @@ function item_expire($uid,$days) { $item_normal = item_normal(); - $r = q("SELECT * FROM `item` - WHERE `uid` = %d - AND `created` < %s - INTERVAL %s - AND `id` = `parent` - $sql_extra + $r = q("SELECT id FROM item + WHERE uid = %d + AND created < %s - INTERVAL %s AND item_retained = 0 - $item_normal LIMIT $expire_limit ", + AND item_thread_top = 1 + AND resource_type = '' + AND item_starred = 0 + $sql_extra $item_normal LIMIT $expire_limit ", intval($uid), - db_utcnow(), db_quoteinterval(intval($days).' DAY') + db_utcnow(), + db_quoteinterval(intval($days).' DAY') ); if(! $r) @@ -4525,21 +3313,10 @@ function item_expire($uid,$days) { continue; } - // Only expire posts, not photos and photo comments - - if($item['resource_type'] === 'photo') { - retain_item($item['id']); - continue; - } - if(intval($item['item_starred'])) { - retain_item($item['id']); - continue; - } - drop_item($item['id'],false); } -// proc_run('php',"include/notifier.php","expire","$uid"); +// Zotlabs\Daemon\Master::Summon(array('Notifier','expire',$uid)); } function retain_item($id) { @@ -4565,7 +3342,7 @@ function drop_items($items) { // multiple threads may have been deleted, send an expire notification if($uid) - proc_run('php',"include/notifier.php","expire","$uid"); + Zotlabs\Daemon\Master::Summon(array('Notifier','expire',$uid)); } @@ -4661,7 +3438,7 @@ function drop_item($id,$interactive = true,$stage = DROPITEM_NORMAL,$force = fal // set if we know we're going to send delete notifications out to others. if((intval($item['item_wall']) && ($stage != DROPITEM_PHASE2)) || ($stage == DROPITEM_PHASE1)) - proc_run('php','include/notifier.php','drop',$notify_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','drop',$notify_id)); goaway(z_root() . '/' . $_SESSION['return_url']); } @@ -4770,9 +3547,8 @@ function delete_item_lowlevel($item, $stage = DROPITEM_NORMAL, $force = false) { intval($item['id']) ); - q("delete from item_id where iid = %d and uid = %d", - intval($item['id']), - intval($item['uid']) + q("delete from iconfig where iid = %d", + intval($item['id']) ); q("delete from term where oid = %d and otype = %d", @@ -4918,7 +3694,7 @@ function fetch_post_tags($items,$link = false) { for($x = 0; $x < count($items); $x ++) { if($tags) { foreach($tags as $t) { - if(($link) && ($t['type'] == TERM_MENTION)) + if(($link) && ($t['ttype'] == TERM_MENTION)) $t['url'] = chanlink_url($t['url']); if(array_key_exists('item_id',$items[$x])) { if($t['oid'] == $items[$x]['item_id']) { @@ -5128,8 +3904,8 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } $contact_str = ''; - /** @FIXME $group is undefined */ - $contacts = group_get_members($group); + + $contacts = group_get_members($r[0]['id']); if ($contacts) { foreach($contacts as $c) { if($contact_str) @@ -5146,7 +3922,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND (( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str)) or allow_gid like '" . protect_sprintf('%<' . dbesc($r[0]['hash']) . '>%') . "' ) and id = parent $item_normal ) "; $x = group_rec_byhash($uid,$r[0]['hash']); - $result['headline'] = sprintf( t('Privacy group: %s'),$x['name']); + $result['headline'] = sprintf( t('Privacy group: %s'),$x['gname']); } elseif($arr['cid'] && $uid) { @@ -5338,6 +4114,23 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C return $items; } +function webpage_to_namespace($webpage) { + + if($webpage == ITEM_TYPE_WEBPAGE) + $page_type = 'WEBPAGE'; + elseif($webpage == ITEM_TYPE_BLOCK) + $page_type = 'BUILDBLOCK'; + elseif($webpage == ITEM_TYPE_PDL) + $page_type = 'PDL'; + elseif($webpage == ITEM_TYPE_DOC) + $page_type = 'docfile'; + else + $page_type = 'unknown'; + return $page_type; + +} + + function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remote_id,$mid) { @@ -5360,32 +4153,19 @@ function update_remote_id($channel,$post_id,$webpage,$pagetitle,$namespace,$remo } if($page_type) { - // store page info as an alternate message_id so we can access it via // https://sitename/page/$channelname/$pagetitle // if no pagetitle was given or it couldn't be transliterated into a url, use the first // sixteen bytes of the mid - which makes the link portable and not quite as daunting // as the entire mid. If it were the post_id the link would be less portable. - $r = q("select * from item_id where iid = %d and uid = %d and service = '%s' limit 1", + \Zotlabs\Lib\IConfig::Set( intval($post_id), - intval($channel['channel_id']), - dbesc($page_type) + 'system', + $page_type, + ($pagetitle) ? $pagetitle : substr($mid,0,16), + false ); - if($r) { - q("update item_id set sid = '%s' where id = %d", - dbesc(($pagetitle) ? $pagetitle : substr($mid,0,16)), - intval($r[0]['id']) - ); - } - else { - q("insert into item_id ( iid, uid, sid, service ) values ( %d, %d, '%s','%s' )", - intval($post_id), - intval($channel['channel_id']), - dbesc(($pagetitle) ? $pagetitle : substr($mid,0,16)), - dbesc($page_type) - ); - } } } @@ -5478,90 +4258,6 @@ function comment_local_origin($item) { } -function gen_asld($items) { - $ret = array(); - if(! $items) - return $ret; - foreach($items as $item) { - $ret[] = i2asld($item); - } - return $ret; -} - - -function i2asld($i) { - - if(! $i) - return array(); - - $ret = array(); - - $ret['@context'] = array( 'http://www.w3.org/ns/activitystreams', 'zot' => 'http://purl.org/zot/protocol'); - - if($i['verb']) { - if(strpos(dirname($i['verb'],'activitystrea.ms/schema/1.0'))) { - $ret['@type'] = ucfirst(basename($i['verb'])); - } - elseif(strpos(dirname($i['verb'],'purl.org/zot'))) { - $ret['@type'] = 'zot:' . ucfirst(basename($i['verb'])); - } - } - $ret['@id'] = $i['plink']; - - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - - // we need to pass the parent into this -// if($i['id'] != $i['parent'] && $i['obj_type'] === ACTIVITY_OBJ_NOTE) { -// $ret['inReplyTo'] = asencode_note -// } - - if($i['obj_type'] === ACTIVITY_OBJ_NOTE) - $ret['object'] = asencode_note($i); - - - $ret['actor'] = asencode_person($i['author']); - - - return $ret; - -} - -function asencode_note($i) { - - $ret = array(); - - $ret['@type'] = 'Note'; - $ret['@id'] = $i['plink']; - if($i['title']) - $ret['title'] = bbcode($i['title']); - $ret['content'] = bbcode($i['body']); - $ret['zot:owner'] = asencode_person($i['owner']); - $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); - if($i['created'] !== $i['edited']) - $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); - - return $ret; -} - - -function asencode_person($p) { - $ret = array(); - $ret['@type'] = 'Person'; - $ret['@id'] = 'acct:' . $p['xchan_addr']; - $ret['displayName'] = $p['xchan_name']; - $ret['icon'] = array( - '@type' => 'Link', - 'mediaType' => $p['xchan_photo_mimetype'], - 'href' => $p['xchan_photo_m'] - ); - $ret['url'] = array( - '@type' => 'Link', - 'mediaType' => 'text/html', - 'href' => $p['xchan_url'] - ); - - return $ret; -} function send_profile_photo_activity($channel,$photo,$profile) { @@ -5578,7 +4274,7 @@ function send_profile_photo_activity($channel,$photo,$profile) { $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; $arr['verb'] = ACTIVITY_UPDATE; - $arr['object'] = json_encode(array( + $arr['obj'] = json_encode(array( 'type' => $arr['obj_type'], 'id' => z_root() . '/photo/profile/l/' . $channel['channel_id'], 'link' => array('rel' => 'photo', 'type' => $photo['type'], 'href' => z_root() . '/photo/profile/l/' . $channel['channel_id']) @@ -5617,157 +4313,17 @@ function send_profile_photo_activity($channel,$photo,$profile) { } +function sync_an_item($channel_id,$item_id) { - - -function get_iconfig(&$item, $family, $key) { - - $is_item = false; - if(is_array($item)) { - $is_item = true; - if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) - $item['iconfig'] = array(); - - if(array_key_exists('item_id',$item)) - $iid = $item['item_id']; - else - $iid = $item['id']; - } - elseif(intval($item)) - $iid = $item; - - if(! $iid) - return false; - - if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { - foreach($item['iconfig'] as $c) { - if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key) - return $c['v']; - } - } - - $r = q("select * from iconfig where iid = %d and cat = '%s' and k = '%s' limit 1", - intval($iid), - dbesc($family), - dbesc($key) + $r = q("select * from item where id = %d", + intval($item_id) ); if($r) { - $r[0]['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); - if($is_item) - $item['iconfig'][] = $r[0]; - return $r[0]['v']; - } - return false; - -} - -/** - * set_iconfig(&$item, $family, $key, $value, $sharing = false); - * - * $item - item array or item id. If passed an array the iconfig meta information is - * added to the item structure (which will need to be saved with item_store eventually). - * If passed an id, the DB is updated, but may not be federated and/or cloned. - * $family - namespace of meta variable - * $key - key of meta variable - * $value - value of meta variable - * $sharing - boolean (default false); if true the meta information is propagated with the item - * to other sites/channels, mostly useful when $item is an array and has not yet been stored/delivered. - * If the meta information is added after delivery and you wish it to be shared, it may be necessary to - * alter the item edited timestamp and invoke the delivery process on the updated item. The edited - * timestamp needs to be altered in order to trigger an item_store_update() at the receiving end. - */ - - -function set_iconfig(&$item, $family, $key, $value, $sharing = false) { - - $dbvalue = ((is_array($value)) ? serialize($value) : $value); - $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); - - $is_item = false; - $idx = null; - - if(is_array($item)) { - $is_item = true; - if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) - $item['iconfig'] = array(); - elseif($item['iconfig']) { - for($x = 0; $x < count($item['iconfig']); $x ++) { - if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { - $idx = $x; - } - } - } - $entry = array('cat' => $family, 'k' => $key, 'v' => $value, 'sharing' => $sharing); - - if(is_null($idx)) - $item['iconfig'][] = $entry; - else - $item['iconfig'][$idx] = $entry; - return $value; - } - - if(intval($item)) - $iid = intval($item); - - if(! $iid) - return false; - - if(get_iconfig($item, $family, $key) === false) { - $r = q("insert into iconfig( iid, cat, k, v, sharing ) values ( %d, '%s', '%s', '%s', %d ) ", - intval($iid), - dbesc($family), - dbesc($key), - dbesc($dbvalue), - intval($sharing) - ); - } - else { - $r = q("update iconfig set v = '%s', sharing = %d where iid = %d and cat = '%s' and k = '%s' ", - dbesc($dbvalue), - intval($sharing), - intval($iid), - dbesc($family), - dbesc($key) + xchan_query($r); + $sync_item = fetch_post_tags($r); + $rid = q("select * from item_id where iid = %d", + intval($item_id) ); + build_sync_packet($channel_d,array('item' => array(encode_item($sync_item[0],true)),'item_id' => $rid)); } - - if(! $r) - return false; - - return $value; -} - - - -function del_iconfig(&$item, $family, $key) { - - - $is_item = false; - $idx = null; - - if(is_array($item)) { - $is_item = true; - if(is_array($item['iconfig'])) { - for($x = 0; $x < count($item['iconfig']); $x ++) { - if($item['iconfig'][$x]['cat'] == $family && $item['iconfig'][$x]['k'] == $key) { - unset($item['iconfig'][$x]); - } - } - } - return true; - } - - if(intval($item)) - $iid = intval($item); - - if(! $iid) - return false; - - return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ", - intval($iid), - dbesc($family), - dbesc($key) - ); - } - diff --git a/include/js_strings.php b/include/js_strings.php index b1817f373..1b4668061 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -4,10 +4,10 @@ function js_strings() { return replace_macros(get_markup_template('js_strings.tpl'), array( '$delitem' => t('Delete this item?'), '$comment' => t('Comment'), - '$showmore' => t('[+] show all'), - '$showfewer' => t('[-] show less'), - '$divgrowmore' => t('[+] expand'), - '$divgrowless' => t('[-] collapse'), + '$showmore' => sprintf( t('%s show all'), '<i class=\'fa fa-chevron-down\'></i>'), + '$showfewer' => sprintf( t('%s show less'), '<i class=\'fa fa-chevron-up\'></i>'), + '$divgrowmore' => sprintf( t('%s expand'), '<i class=\'fa fa-chevron-down\'></i>'), + '$divgrowless' => sprintf( t('%s collapse'),'<i class=\'fa fa-chevron-up\'></i>'), '$pwshort' => t("Password too short"), '$pwnomatch' => t("Passwords do not match"), '$everybody' => t('everybody'), diff --git a/include/language.php b/include/language.php index d6b7606ca..96d3e48a9 100644 --- a/include/language.php +++ b/include/language.php @@ -82,13 +82,11 @@ function get_best_language() { if($arr['preferred'] !== 'unset') return $arr['preferred']; - $a = get_app(); return ((isset(App::$config['system']['language'])) ? App::$config['system']['language'] : 'en'); } function push_lang($language) { - global $a; App::$langsave = App::$language; @@ -104,7 +102,6 @@ function push_lang($language) { } function pop_lang() { - global $a; if(App::$language === App::$langsave) return; @@ -124,7 +121,6 @@ function pop_lang() { * @param boolean $install (optional) default false */ function load_translation_table($lang, $install = false) { - global $a; App::$strings = array(); @@ -136,10 +132,10 @@ function load_translation_table($lang, $install = false) { } if(! $install) { - $plugins = q("SELECT name FROM addon WHERE installed=1;"); + $plugins = q("SELECT aname FROM addon WHERE installed=1;"); if ($plugins !== false) { foreach($plugins as $p) { - $name = $p['name']; + $name = $p['aname']; if(file_exists("addon/$name/lang/$lang/hstrings.php")) { include("addon/$name/lang/$lang/hstrings.php"); } @@ -170,7 +166,6 @@ function load_translation_table($lang, $install = false) { * */ function t($s, $ctx = '') { - global $a; $cs = $ctx ? '__ctx:' . $ctx . '__ ' . $s : $s; if (x(App::$strings, $cs)) { @@ -189,7 +184,7 @@ function t($s, $ctx = '') { function translate_projectname($s) { - return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Project\System::get_platform_name(),ucfirst(Zotlabs\Project\System::get_platform_name())),$s); + return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Lib\System::get_platform_name(),ucfirst(Zotlabs\Lib\System::get_platform_name())),$s); } @@ -205,7 +200,6 @@ function translate_projectname($s) { * @return string */ function tt($singular, $plural, $count, $ctx = ''){ - $a = get_app(); $cs = $ctx ? "__ctx:" . $ctx . "__ " . $singular : $singular; if (x(App::$strings,$cs)) { diff --git a/include/message.php b/include/message.php index a7883d50e..d3d8181ae 100644 --- a/include/message.php +++ b/include/message.php @@ -257,7 +257,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' } } - proc_run('php','include/notifier.php','mail',$post_id); + Zotlabs\Daemon\Master::Summon(array('Notifier','mail',$post_id)); $ret['success'] = true; $ret['message_item'] = intval($post_id); diff --git a/include/nav.php b/include/nav.php index 541ab3aed..1fb0e98dc 100644 --- a/include/nav.php +++ b/include/nav.php @@ -104,6 +104,8 @@ EOT; if(feature_enabled($channel['channel_id'],'webpages') && (! UNO)) $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); + if(feature_enabled($channel['channel_id'],'wiki') && (! UNO)) + $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wiki'),"",t('Your wiki'),'wiki_nav_btn'); } else { if(! get_account_id()) { @@ -126,7 +128,7 @@ EOT; $nav['lock'] = array('logout','','lock', sprintf( t('%s - click to logout'), $observer['xchan_addr'])); } - else { + elseif(! $_SESSION['authenticated']) { $nav['loginmenu'][] = Array('rmagic',t('Remote authentication'),'',t('Click to authenticate to your home hub'),'rmagic_nav_btn'); } @@ -143,16 +145,20 @@ EOT; if((App::$module != 'home') && (! (local_channel()))) $nav['home'] = array($homelink, t('Home'), "", t('Home Page'),'home_nav_btn'); - - if((App::$config['system']['register_policy'] == REGISTER_OPEN) && (! local_channel()) && (! remote_channel())) + if((App::$config['system']['register_policy'] == REGISTER_OPEN) && (! $_SESSION['authenticated'])) $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn'); - $help_url = z_root() . '/help?f=&cmd=' . App::$cmd; - if(! get_config('system','hide_help')) { - require_once('mod/help.php'); - $context_help = load_context_help(); - $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'),'help_nav_btn',$context_help); + $help_url = z_root() . '/help?f=&cmd=' . App::$cmd; + $context_help = ''; + $enable_context_help = ((intval(get_config('system','enable_context_help')) === 1 || get_config('system','enable_context_help') === false) ? true : false); + if($enable_context_help === true) { + require_once('include/help.php'); + $context_help = load_context_help(); + //point directly to /help if $context_help is empty - this can be removed once we have context help for all modules + $enable_context_help = (($context_help) ? true : false); + } + $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'), 'help_nav_btn', $context_help, $enable_context_help); } if(! UNO) @@ -160,7 +166,6 @@ EOT; $nav['search'] = array('search', t('Search'), "", t('Search site @name, #tag, ?docs, content')); - $nav['directory'] = array('directory', t('Directory'), "", t('Channel Directory'),'directory_nav_btn'); @@ -237,7 +242,7 @@ $powered_by = ''; $tpl = get_markup_template('nav.tpl'); App::$page['nav'] .= replace_macros($tpl, array( - '$baseurl' => z_root(), + '$baseurl' => z_root(), '$sitelocation' => $sitelocation, '$nav' => $x['nav'], '$banner' => $banner, @@ -250,6 +255,19 @@ $powered_by = ''; '$pleasewait' => t('Please wait...') )); + + if(x($_SESSION, 'reload_avatar') && $observer) { + // The avatar has been changed on the server but the browser doesn't know that, + // force the browser to reload the image from the server instead of its cache. + $tpl = get_markup_template('force_image_reload.tpl'); + + App::$page['nav'] .= replace_macros($tpl, array( + '$imgUrl' => $observer['xchan_photo_m'] + )); + unset($_SESSION['reload_avatar']); + } + + call_hooks('page_header', App::$page['nav']); } diff --git a/include/network.php b/include/network.php index ec255581d..47863b680 100644 --- a/include/network.php +++ b/include/network.php @@ -21,15 +21,18 @@ function get_capath() { * TRUE if asked to return binary results (file download) * @param int $redirects default 0 * internal use, recursion counter - * @param array $opts (optional parameters) assoziative array with: + * @param array $opts (optional parameters) associative array with: * * \b accept_content => supply Accept: header with 'accept_content' as the value * * \b timeout => int seconds, default system config value or 60 seconds * * \b http_auth => username:password * * \b novalidate => do not validate SSL certs, default is to validate using our CA list * * \b nobody => only return the header * * \b filep => stream resource to write body to. header and body are not returned when using this option. + * * \b custom => custom request method: e.g. 'PUT', 'DELETE' + * * \b cookiejar => cookie file (write) + * * \B cookiefile => cookie file (read) * - * @return array an assoziative array with: + * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure * * \e boolean \b success => boolean true (if HTTP 2xx result) or false * * \e string \b header => HTTP headers @@ -59,12 +62,27 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_HEADER, $false); } + if(x($opts,'upload')) + @curl_setopt($ch, CURLOPT_UPLOAD, $opts['upload']); + + if(x($opts,'infile')) + @curl_setopt($ch, CURLOPT_INFILE, $opts['infile']); + + if(x($opts,'infilesize')) + @curl_setopt($ch, CURLOPT_INFILESIZE, $opts['infilesize']); + + if(x($opts,'readfunc')) + @curl_setopt($ch, CURLOPT_READFUNCTION, $opts['readfunc']); + if(x($opts,'headers')) @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if(x($opts,'custom')) + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + if(x($opts,'timeout') && intval($opts['timeout'])) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } @@ -78,6 +96,14 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } + if(x($opts,'cookiejar')) + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + if(x($opts,'cookiefile')) + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + + if(x($opts,'cookie')) + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); @@ -165,7 +191,9 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { * 'http_auth' => username:password * 'novalidate' => do not validate SSL certs, default is to validate using our CA list * 'filep' => stream resource to write body to. header and body are not returned when using this option. - * @return array an assoziative array with: + * 'custom' => custom request method: e.g. 'PUT', 'DELETE' + * + * @return array an associative array with: * * \e int \b return_code => HTTP return code or 0 if timeout or failure * * \e boolean \b success => boolean true (if HTTP 2xx result) or false * * \e string \b header => HTTP headers @@ -174,6 +202,10 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { */ function z_post_url($url,$params, $redirects = 0, $opts = array()) { +// logger('url: ' . $url); +// logger('params: ' . print_r($params,true)); +// logger('opts: ' . print_r($opts,true)); + $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); $ch = curl_init($url); @@ -199,12 +231,17 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { if(x($opts,'headers')) { @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); -logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects); } if(x($opts,'nobody')) @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); + if(x($opts,'custom')) { + @curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $opts['custom']); + @curl_setopt($ch, CURLOPT_POST,0); + } + + if(x($opts,'timeout') && intval($opts['timeout'])) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } @@ -218,6 +255,16 @@ logger('headers: ' . print_r($opts['headers'],true) . 'redir: ' . $redirects); @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } + + if(x($opts,'cookiejar')) + @curl_setopt($ch, CURLOPT_COOKIEJAR, $opts['cookiejar']); + if(x($opts,'cookiefile')) + @curl_setopt($ch, CURLOPT_COOKIEFILE, $opts['cookiefile']); + + + if(x($opts,'cookie')) + @curl_setopt($ch, CURLOPT_COOKIE, $opts['cookie']); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); @@ -595,8 +642,6 @@ function parse_xml_string($s,$strict = true) { function scale_external_images($s, $include_link = true, $scale_replace = false) { - $a = get_app(); - // Picture addresses can contain special characters $s = htmlspecialchars_decode($s, ENT_COMPAT); @@ -1168,6 +1213,10 @@ function discover_by_webbie($webbie) { if(! $x) $probe_old = true; + + if((! $dfrn) && (! $has_salmon)) + $probe_old = true; + if($probe_old) { $y = old_webfinger($webbie); if($y) { @@ -1294,8 +1343,20 @@ function discover_by_webbie($webbie) { $fullname = $vcard['fn']; if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; + if(($vcard['key']) && (! $pubkey)) + $pubkey = $vcard['key']; if(! $avatar) $avatar = $vcard['photo']; + if($diaspora) { + if(($vcard['guid']) && (! $diaspora_guid)) + $diaspora_guid = $vcard['guid']; + if(($vcard['url']) && (! $diaspora_base)) + $diaspora_base = $vcard['url']; + + + + + } } } @@ -1614,8 +1675,6 @@ function fetch_xrd_links($url) { function scrape_vcard($url) { - $a = get_app(); - $ret = array(); logger('scrape_vcard: url=' . $url); @@ -1695,8 +1754,6 @@ function scrape_vcard($url) { function scrape_feed($url) { - $a = get_app(); - $ret = array(); $level = 0; $x = z_fetch_url($url,false,$level,array('novalidate' => true)); @@ -1815,8 +1872,6 @@ function service_plink($contact, $guid) { function format_and_send_email($sender,$xchan,$item) { - require_once('include/enotify.php'); - $title = $item['title']; $body = $item['body']; @@ -1835,7 +1890,7 @@ function format_and_send_email($sender,$xchan,$item) { $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( '$banner' => $banner, - '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), + '$notify_icon' => Zotlabs\Lib\System::get_notify_icon(), '$product' => $product, '$preamble' => '', '$sitename' => $sitename, @@ -1881,7 +1936,7 @@ function format_and_send_email($sender,$xchan,$item) { // use the EmailNotification library to send the message - enotify::send(array( + Zotlabs\Lib\Enotify::send(array( 'fromName' => $product, 'fromEmail' => $sender_email, 'replyTo' => $sender_email, @@ -1912,10 +1967,13 @@ function do_delivery($deliveries) { $deliver = array(); foreach($deliveries as $d) { + if(! $d) + continue; + $deliver[] = $d; if(count($deliver) >= $deliveries_per_process) { - proc_run('php','include/deliver.php',$deliver); + Zotlabs\Daemon\Master::Summon(array('Deliver',$deliver)); $deliver = array(); if($interval) @time_sleep_until(microtime(true) + (float) $interval); @@ -1925,7 +1983,7 @@ function do_delivery($deliveries) { // catch any stragglers if($deliver) - proc_run('php','include/deliver.php',$deliver); + Zotlabs\Daemon\Master::Summon(array('Deliver',$deliver)); } @@ -1933,9 +1991,6 @@ function do_delivery($deliveries) { function get_site_info() { - global $db; - global $a; - $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); @@ -1966,14 +2021,7 @@ function get_site_info() { else $service_class = false; - $visible_plugins = array(); - if(is_array(App::$plugins) && count(App::$plugins)) { - $r = q("select * from addon where hidden = 0"); - if(count($r)) - foreach($r as $rr) - $visible_plugins[] = $rr['name']; - } - sort($visible_plugins); + $visible_plugins = visible_plugin_list(); if(@is_dir('.git') && function_exists('shell_exec')) $commit = trim(@shell_exec('git log -1 --format="%h"')); @@ -1983,8 +2031,8 @@ function get_site_info() { $site_info = get_config('system','info'); $site_name = get_config('system','sitename'); if(! get_config('system','hidden_version_siteinfo')) { - $version = Zotlabs\Project\System::get_project_version(); - $tag = Zotlabs\Project\System::get_std_version(); + $version = Zotlabs\Lib\System::get_project_version(); + $tag = Zotlabs\Lib\System::get_std_version(); if(@is_dir('.git') && function_exists('shell_exec')) { $commit = trim( @shell_exec('git log -1 --format="%h"')); @@ -2020,7 +2068,7 @@ function get_site_info() { $data = Array( 'version' => $version, 'version_tag' => $tag, - 'server_role' => Zotlabs\Project\System::get_server_role(), + 'server_role' => Zotlabs\Lib\System::get_server_role(), 'commit' => $commit, 'url' => z_root(), 'plugins' => $visible_plugins, @@ -2034,8 +2082,8 @@ function get_site_info() { 'locked_features' => $locked_features, 'admin' => $admin, 'site_name' => (($site_name) ? $site_name : ''), - 'platform' => Zotlabs\Project\System::get_platform_name(), - 'dbdriver' => $db->getdriver(), + 'platform' => Zotlabs\Lib\System::get_platform_name(), + 'dbdriver' => DBA::$dba->getdriver(), 'lastpoll' => get_config('system','lastpoll'), 'info' => (($site_info) ? $site_info : ''), 'channels_total' => $channels_total_stat, @@ -2114,8 +2162,9 @@ function check_channelallowed($hash) { return $retvalue; } -function deliverable_singleton($xchan) { - $r = q("select abook_instance from abook where abook_xchan = '%s' limit 1", +function deliverable_singleton($channel_id,$xchan) { + $r = q("select abook_instance from abook where abook_channel = %d and abook_xchan = '%s' limit 1", + intval($channel_id), dbesc($xchan['xchan_hash']) ); if($r) { @@ -2127,3 +2176,44 @@ function deliverable_singleton($xchan) { return false; } + + +function get_repository_version($branch = 'master') { + + $path = "https://raw.githubusercontent.com/redmatrix/hubzilla/$branch/boot.php"; + + $x = z_fetch_url($path); + if($x['success']) { + $y = preg_match('/define(.*?)STD_VERSION(.*?)([0-9.].*)\'/',$x['body'],$matches); + if($y) + return $matches[3]; + } + return '?.?'; + +} + +function network_to_name($s) { + + $nets = array( + NETWORK_DFRN => t('Friendica'), + NETWORK_FRND => t('Friendica'), + NETWORK_OSTATUS => t('OStatus'), + NETWORK_GNUSOCIAL => t('GNU-Social'), + NETWORK_FEED => t('RSS/Atom'), + NETWORK_MAIL => t('Email'), + NETWORK_DIASPORA => t('Diaspora'), + NETWORK_FACEBOOK => t('Facebook'), + NETWORK_ZOT => t('Zot'), + NETWORK_LINKEDIN => t('LinkedIn'), + NETWORK_XMPP => t('XMPP/IM'), + NETWORK_MYSPACE => t('MySpace'), + ); + + call_hooks('network_to_name', $nets); + + $search = array_keys($nets); + $replace = array_values($nets); + + return str_replace($search,$replace,$s); + +} diff --git a/include/notifier.php b/include/notifier.php deleted file mode 100644 index 628847d54..000000000 --- a/include/notifier.php +++ /dev/null @@ -1,646 +0,0 @@ -<?php /** @file */ - -require_once("boot.php"); -require_once('include/queue_fn.php'); -require_once('include/html2plain.php'); - -/* - * This file was at one time responsible for doing all deliveries, but this caused - * big problems on shared hosting systems, where the process might get killed by the - * hosting provider and nothing would get delivered. - * It now only delivers one message under certain cases, and invokes a queued - * delivery mechanism (include/deliver.php) to deliver individual contacts at - * controlled intervals. - * This has a much better chance of surviving random processes getting killed - * by the hosting provider. - * - * The basic flow is: - * Identify the type of message - * Collect any information that needs to be sent - * Convert it into a suitable generic format for sending - * Figure out who the recipients are and if we need to relay - * through a conversation owner - * Once we know what recipients are involved, collect a list of - * destination sites - * Build and store a queue item for each unique site and invoke - * a delivery process for each site or a small number of sites (1-3) - * and add a slight delay between each delivery invocation if desired (usually) - * - */ - -/* - * The notifier is typically called with: - * - * proc_run('php', "include/notifier.php", COMMAND, ITEM_ID); - * - * where COMMAND is one of the following: - * - * activity (in diaspora.php, dfrn_confirm.php, profiles.php) - * comment-import (in diaspora.php, items.php) - * comment-new (in item.php) - * drop (in diaspora.php, items.php, photos.php) - * edit_post (in item.php) - * event (in events.php) - * expire (in items.php) - * like (in like.php, poke.php) - * mail (in message.php) - * tag (in photos.php, poke.php, tagger.php) - * tgroup (in items.php) - * wall-new (in photos.php, item.php) - * - * and ITEM_ID is the id of the item in the database that needs to be sent to others. - * - * ZOT - * permission_create abook_id - * permission_update abook_id - * refresh_all channel_id - * purge_all channel_id - * expire channel_id - * relay item_id (item was relayed to owner, we will deliver it as owner) - * single_activity item_id (deliver to a singleton network from the appropriate clone) - * single_mail mail_id (deliver to a singleton network from the appropriate clone) - * location channel_id - * request channel_id xchan_hash message_id - * rating xlink_id - * - */ - -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/queue_fn.php'); -require_once('include/datetime.php'); -require_once('include/items.php'); -require_once('include/bbcode.php'); -require_once('include/identity.php'); -require_once('include/Contact.php'); - -function notifier_run($argv, $argc){ - - cli_startup(); - - $a = get_app(); - - - if($argc < 3) - return; - - logger('notifier: invoked: ' . print_r($argv,true), LOGGER_DEBUG); - - $cmd = $argv[1]; - - $item_id = $argv[2]; - - $extra = (($argc > 3) ? $argv[3] : null); - - if(! $item_id) - return; - - $sys = get_sys_channel(); - - $deliveries = array(); - - $dead_hubs = array(); - - $dh = q("select site_url from site where site_dead = 1"); - if($dh) { - foreach($dh as $dead) { - $dead_hubs[] = $dead['site_url']; - } - } - - - $request = false; - $mail = false; - $top_level = false; - $location = false; - $recipients = array(); - $url_recipients = array(); - $normal_mode = true; - $packet_type = 'undefined'; - - if($cmd === 'mail') { - $normal_mode = false; - $mail = true; - $private = true; - $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1", - intval($item_id) - ); - if(! $message) { - return; - } - xchan_mail_query($message[0]); - $uid = $message[0]['channel_id']; - $recipients[] = $message[0]['from_xchan']; // include clones - $recipients[] = $message[0]['to_xchan']; - $item = $message[0]; - - $encoded_item = encode_mail($item); - - $s = q("select * from channel where channel_id = %d limit 1", - intval($item['channel_id']) - ); - if($s) - $channel = $s[0]; - - } - elseif($cmd === 'request') { - $channel_id = $item_id; - $xchan = $argv[3]; - $request_message_id = $argv[4]; - - $s = q("select * from channel where channel_id = %d limit 1", - intval($channel_id) - ); - if($s) - $channel = $s[0]; - - $private = true; - $recipients[] = $xchan; - $packet_type = 'request'; - $normal_mode = false; - } - elseif($cmd == 'permission_update' || $cmd == 'permission_create') { - // Get the (single) recipient - $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", - intval($item_id) - ); - if($r) { - $uid = $r[0]['abook_channel']; - // Get the sender - $channel = channelx_by_n($uid); - if($channel) { - $perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => ''); - - if($cmd == 'permission_create') - call_hooks('permissions_create',$perm_update); - else - call_hooks('permissions_update',$perm_update); - - if($perm_update['success']) { - if($perm_update['deliveries']) { - $deliveries[] = $perm_update['deliveries']; - do_delivery($deliveries); - } - return; - } - else { - $recipients[] = $r[0]['abook_xchan']; - $private = false; - $packet_type = 'refresh'; - $packet_recips = array(array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig'],'hash' => $r[0]['xchan_hash'])); - } - } - } - } - elseif($cmd === 'refresh_all') { - logger('notifier: refresh_all: ' . $item_id); - $uid = $item_id; - $channel = channelx_by_n($item_id); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; - } - } - $private = false; - $packet_type = 'refresh'; - } - elseif($cmd === 'location') { - logger('notifier: location: ' . $item_id); - $s = q("select * from channel where channel_id = %d limit 1", - intval($item_id) - ); - if($s) - $channel = $s[0]; - $uid = $item_id; - $recipients = array(); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; - } - } - - $encoded_item = array('locations' => zot_encode_locations($channel),'type' => 'location', 'encoding' => 'zot'); - $target_item = array('aid' => $channel['channel_account_id'],'uid' => $channel['channel_id']); - $private = false; - $packet_type = 'location'; - $location = true; - } - elseif($cmd === 'purge_all') { - logger('notifier: purge_all: ' . $item_id); - $s = q("select * from channel where channel_id = %d limit 1", - intval($item_id) - ); - if($s) - $channel = $s[0]; - $uid = $item_id; - $recipients = array(); - $r = q("select abook_xchan from abook where abook_channel = %d", - intval($item_id) - ); - if($r) { - foreach($r as $rr) { - $recipients[] = $rr['abook_xchan']; - } - } - $private = false; - $packet_type = 'purge'; - } - else { - - // Normal items - - // Fetch the target item - - $r = q("SELECT * FROM item WHERE id = %d and parent != 0 LIMIT 1", - intval($item_id) - ); - - if(! $r) - return; - - xchan_query($r); - - $r = fetch_post_tags($r); - - $target_item = $r[0]; - $deleted_item = false; - - if(intval($target_item['item_deleted'])) { - logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); - $deleted_item = true; - } - - if(intval($target_item['item_type']) != ITEM_TYPE_POST) { - logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG); - return; - } - if(intval($target_item['item_unpublished']) || intval($target_item['item_delayed'])) { - logger('notifier: target item not published, so not forwardable', LOGGER_DEBUG); - return; - } - - if(strpos($target_item['postopts'],'nodeliver') !== false) { - logger('notifier: target item is undeliverable', LOGGER_DEBUG); - return; - } - - $s = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", - intval($target_item['uid']) - ); - if($s) - $channel = $s[0]; - - if($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) { - logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); - return; - } - - - if($target_item['id'] == $target_item['parent']) { - $parent_item = $target_item; - $top_level_post = true; - } - else { - // fetch the parent item - $r = q("SELECT * from item where id = %d order by id asc", - intval($target_item['parent']) - ); - - if(! $r) - return; - - if(strpos($r[0]['postopts'],'nodeliver') !== false) { - logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE); - return; - } - - xchan_query($r); - $r = fetch_post_tags($r); - - $parent_item = $r[0]; - $top_level_post = false; - } - - // avoid looping of discover items 12/4/2014 - - if($sys && $parent_item['uid'] == $sys['channel_id']) - return; - - $encoded_item = encode_item($target_item); - - // Send comments to the owner to re-deliver to everybody in the conversation - // We only do this if the item in question originated on this site. This prevents looping. - // To clarify, a site accepting a new comment is responsible for sending it to the owner for relay. - // Relaying should never be initiated on a post that arrived from elsewhere. - - // We should normally be able to rely on ITEM_ORIGIN, but start_delivery_chain() incorrectly set this - // flag on comments for an extended period. So we'll also call comment_local_origin() which looks at - // the hostname in the message_id and provides a second (fallback) opinion. - - $relay_to_owner = (((! $top_level_post) && (intval($target_item['item_origin'])) && comment_local_origin($target_item)) ? true : false); - - - - $uplink = false; - - // $cmd === 'relay' indicates the owner is sending it to the original recipients - // don't allow the item in the relay command to relay to owner under any circumstances, it will loop - - logger('notifier: relay_to_owner: ' . (($relay_to_owner) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); - logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA, LOG_DEBUG); - - // tag_deliver'd post which needs to be sent back to the original author - - if(($cmd === 'uplink') && intval($parent_item['item_uplink']) && (! $top_level_post)) { - logger('notifier: uplink'); - $uplink = true; - } - - if(($relay_to_owner || $uplink) && ($cmd !== 'relay')) { - logger('notifier: followup relay', LOGGER_DEBUG); - $recipients = array(($uplink) ? $parent_item['source_xchan'] : $parent_item['owner_xchan']); - $private = true; - if(! $encoded_item['flags']) - $encoded_item['flags'] = array(); - $encoded_item['flags'][] = 'relay'; - } - else { - logger('notifier: normal distribution', LOGGER_DEBUG); - if($cmd === 'relay') - logger('notifier: owner relay'); - - // if our parent is a tag_delivery recipient, uplink to the original author causing - // a delivery fork. - - if(($parent_item) && intval($parent_item['item_uplink']) && (! $top_level_post) && ($cmd !== 'uplink')) { - // don't uplink a relayed post to the relay owner - if($parent_item['source_xchan'] !== $parent_item['owner_xchan']) { - logger('notifier: uplinking this item'); - proc_run('php','include/notifier.php','uplink',$item_id); - } - } - - $private = false; - $recipients = collect_recipients($parent_item,$private); - - // FIXME add any additional recipients such as mentions, etc. - - // don't send deletions onward for other people's stuff - // TODO verify this is needed - copied logic from same place in old code - - if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { - logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); - return; - } - } - - } - - $walltowall = (($top_level_post && $channel['xchan_hash'] === $target_item['author_xchan']) ? true : false); - - // Generic delivery section, we have an encoded item and recipients - // Now start the delivery process - - $x = $encoded_item; - $x['title'] = 'private'; - $x['body'] = 'private'; - logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); - - stringify_array_elms($recipients); - if(! $recipients) - return; - -// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); - - $env_recips = (($private) ? array() : null); - - $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); - - - $recip_list = array(); - - if($details) { - foreach($details as $d) { - - $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; - if($private) - $env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig'],'hash' => $d['xchan_hash']); - - if($d['xchan_network'] === 'mail' && $normal_mode) { - $delivery_options = get_xconfig($d['xchan_hash'],'system','delivery_mode'); - if(! $delivery_options) - format_and_send_email($channel,$d,$target_item); - } - } - } - - - $narr = array( - 'channel' => $channel, - 'env_recips' => $env_recips, - 'packet_recips' => $packet_recips, - 'recipients' => $recipients, - 'item' => $item, - 'target_item' => $target_item, - 'top_level_post' => $top_level_post, - 'private' => $private, - 'followup' => $followup, - 'relay_to_owner' => $relay_to_owner, - 'uplink' => $uplink, - 'cmd' => $cmd, - 'mail' => $mail, - 'location' => $location, - 'request' => $request, - 'normal_mode' => $normal_mode, - 'packet_type' => $packet_type, - 'walltowall' => $walltowall, - 'queued' => array() - ); - - call_hooks('notifier_process', $narr); - if($narr['queued']) { - foreach($narr['queued'] as $pq) - $deliveries[] = $pq; - } - - - if(($private) && (! $env_recips)) { - // shouldn't happen - logger('notifier: private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE); - } - - logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); - - - // Now we have collected recipients (except for external mentions, FIXME) - // Let's reduce this to a set of hubs. - - $r = q("select * from hubloc where hubloc_hash in (" . implode(',',$recipients) . ") - and hubloc_error = 0 and hubloc_deleted = 0" - ); - - - if(! $r) { - logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); - return; - } - - $hubs = $r; - - - - /** - * Reduce the hubs to those that are unique. For zot hubs, we need to verify uniqueness by the sitekey, since it may have been - * a re-install which has not yet been detected and pruned. - * For other networks which don't have or require sitekeys, we'll have to use the URL - */ - - - $hublist = array(); // this provides an easily printable list for the logs - $dhubs = array(); // delivery hubs where we store our resulting unique array - $keys = array(); // array of keys to check uniquness for zot hubs - $urls = array(); // array of urls to check uniqueness of hubs from other networks - - - foreach($hubs as $hub) { - if(in_array($hub['hubloc_url'],$dead_hubs)) { - logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO); - continue; - } - - if($hub['hubloc_network'] == 'zot') { - if(! in_array($hub['hubloc_sitekey'],$keys)) { - $hublist[] = $hub['hubloc_host']; - $dhubs[] = $hub; - $keys[] = $hub['hubloc_sitekey']; - } - } - else { - if(! in_array($hub['hubloc_url'],$urls)) { - $hublist[] = $hub['hubloc_host']; - $dhubs[] = $hub; - $urls[] = $hub['hubloc_url']; - } - } - } - - logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG); - - - foreach($dhubs as $hub) { - - if($hub['hubloc_network'] !== 'zot') { - - $narr = array( - 'channel' => $channel, - 'env_recips' => $env_recips, - 'packet_recips' => $packet_recips, - 'recipients' => $recipients, - 'item' => $item, - 'target_item' => $target_item, - 'hub' => $hub, - 'top_level_post' => $top_level_post, - 'private' => $private, - 'followup' => $followup, - 'relay_to_owner' => $relay_to_owner, - 'uplink' => $uplink, - 'cmd' => $cmd, - 'mail' => $mail, - 'location' => $location, - 'request' => $request, - 'normal_mode' => $normal_mode, - 'packet_type' => $packet_type, - 'walltowall' => $walltowall, - 'queued' => array() - ); - - - call_hooks('notifier_hub',$narr); - if($narr['queued']) { - foreach($narr['queued'] as $pq) - $deliveries[] = $pq; - } - continue; - - } - - // default: zot protocol - - $hash = random_string(); - $packet = null; - - if($packet_type === 'refresh' || $packet_type === 'purge') { - $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); - } - elseif($packet_type === 'request') { - $packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hash, - array('message_id' => $request_message_id) - ); - } - - if($packet) { - queue_insert(array( - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $hub['hubloc_callback'], - 'notify' => $packet - )); - } - else { - $packet = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash); - queue_insert(array( - 'hash' => $hash, - 'account_id' => $target_item['aid'], - 'channel_id' => $target_item['uid'], - 'posturl' => $hub['hubloc_callback'], - 'notify' => $packet, - 'msg' => json_encode($encoded_item) - )); - - // only create delivery reports for normal undeleted items - if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", - dbesc($target_item['mid']), - dbesc($hub['hubloc_host']), - dbesc($hub['hubloc_host']), - dbesc('queued'), - dbesc(datetime_convert()), - dbesc($channel['channel_hash']), - dbesc($hash) - ); - } - } - - $deliveries[] = $hash; - } - - if($normal_mode) { - $x = q("select * from hook where hook = 'notifier_normal'"); - if($x) - proc_run('php','include/deliver_hooks.php', $target_item['id']); - } - - if($deliveries) - do_delivery($deliveries); - - logger('notifier: basic loop complete.', LOGGER_DEBUG); - - call_hooks('notifier_end',$target_item); - - logger('notifer: complete.'); - return; - -} - - -if (array_search(__file__,get_included_files())===0){ - notifier_run($argv,$argc); - killme(); -} diff --git a/include/notify.php b/include/notify.php deleted file mode 100644 index 2b032b56b..000000000 --- a/include/notify.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php /** @file */ - - -function format_notification($item) { - - $ret = ''; - - require_once('include/conversation.php'); - - // Call localize_item with the "brief" flag to get a one line status for activities. - // This should set $item['localized'] to indicate we have a brief summary. - - localize_item($item); - - if($item_localize) { - $itemem_text = $item['localize']; - } - else { - $itemem_text = (($item['item_thread_top']) - ? t('created a new post') - : sprintf( t('commented on %s\'s post'), $item['owner']['xchan_name'])); - } - - // convert this logic into a json array just like the system notifications - - return array( - 'notify_link' => $item['llink'], - 'name' => $item['author']['xchan_name'], - 'url' => $item['author']['xchan_url'], - 'photo' => $item['author']['xchan_photo_s'], - 'when' => relative_date($item['created']), - 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), - 'message' => strip_tags(bbcode($itemem_text)) - ); - -} - diff --git a/include/oauth.php b/include/oauth.php index ec41a5dd2..a3c52bf27 100644 --- a/include/oauth.php +++ b/include/oauth.php @@ -37,7 +37,7 @@ class ZotOAuth1DataStore extends OAuth1DataStore { logger(__function__.":".$consumer.", ". $token_type.", ".$token, LOGGER_DEBUG); - $r = q("SELECT id, secret, scope, expires, uid FROM tokens WHERE client_id = '%s' AND scope = '%s' AND id = '%s'", + $r = q("SELECT id, secret, auth_scope, expires, uid FROM tokens WHERE client_id = '%s' AND auth_scope = '%s' AND id = '%s'", dbesc($consumer->key), dbesc($token_type), dbesc($token) @@ -45,7 +45,7 @@ class ZotOAuth1DataStore extends OAuth1DataStore { if (count($r)){ $ot=new OAuth1Token($r[0]['id'],$r[0]['secret']); - $ot->scope=$r[0]['scope']; + $ot->scope=$r[0]['auth_scope']; $ot->expires = $r[0]['expires']; $ot->uid = $r[0]['uid']; return $ot; @@ -79,7 +79,7 @@ class ZotOAuth1DataStore extends OAuth1DataStore { $k = $consumer; } - $r = q("INSERT INTO tokens (id, secret, client_id, scope, expires) VALUES ('%s','%s','%s','%s', %d)", + $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires) VALUES ('%s','%s','%s','%s', %d)", dbesc($key), dbesc($sec), dbesc($k), @@ -110,7 +110,7 @@ class ZotOAuth1DataStore extends OAuth1DataStore { $key = $this->gen_token(); $sec = $this->gen_token(); - $r = q("INSERT INTO tokens (id, secret, client_id, scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)", + $r = q("INSERT INTO tokens (id, secret, client_id, auth_scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)", dbesc($key), dbesc($sec), dbesc($consumer->key), @@ -170,7 +170,7 @@ class ZotOAuth1 extends OAuth1Server { ); if($x) { require_once('include/security.php'); - authenticate_success($x[0],true,false,true,true); + authenticate_success($x[0],null,true,false,true,true); $_SESSION['allow_api'] = true; } } @@ -249,7 +249,7 @@ class FKOAuth2 extends OAuth2 { protected function getAuthCode($code) { - $r = q("SELECT id, client_id, redirect_uri, expires, scope FROM auth_codes WHERE id = '%s'", + $r = q("SELECT id, client_id, redirect_uri, expires, auth_scope FROM auth_codes WHERE id = '%s'", dbesc($code)); if (count($r)) @@ -259,7 +259,7 @@ class FKOAuth2 extends OAuth2 { protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) { $r = q("INSERT INTO auth_codes - (id, client_id, redirect_uri, expires, scope) VALUES + (id, client_id, redirect_uri, expires, auth_scope) VALUES ('%s', '%s', '%s', %d, '%s')", dbesc($code), dbesc($client_id), diff --git a/include/oembed.php b/include/oembed.php index 3994af0fb..fe3a3c33c 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -1,69 +1,102 @@ <?php /** @file */ + + +use Zotlabs\Lib as Zlib; + function oembed_replacecb($matches){ $embedurl=$matches[1]; + $result = oembed_action($embedurl); + if($result['action'] === 'block') { + return '<a href="' . $result['url'] . '">' . $result['url'] . '</a>'; + } + + $j = oembed_fetch_url($result['url']); + $s = oembed_format_object($j); + return $s; +} + + +function oembed_action($embedurl) { + + $host = ''; + $action = 'filter'; + + $embedurl = trim(str_replace('&','&', $embedurl)); + + logger('oembed_action: ' . $embedurl, LOGGER_DEBUG, LOG_INFO); + + if(strpos($embedurl,'http://') === 0) { + if(intval(get_config('system','embed_sslonly'))) { + $action = 'block'; + } + } // site white/black list if(($x = get_config('system','embed_deny'))) { - $l = explode("\n",$x); - if($l) { - foreach($l as $ll) { - if(trim($ll) && strpos($embedurl,trim($ll)) !== false) - return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + if(($x) && (! is_array($x))) + $x = explode("\n",$x); + if($x) { + foreach($x as $ll) { + $t = trim($ll); + if(($t) && (strpos($embedurl,$t) !== false)) { + $action = 'block'; + break; + } } } } + + $found = false; + if(($x = get_config('system','embed_allow'))) { - $found = false; - $l = explode("\n",$x); - if($l) { - foreach($l as $ll) { - if(trim($ll) && strpos($embedurl,trim($ll)) !== false) { + if(($x) && (! is_array($x))) + $x = explode("\n",$x); + if($x) { + foreach($x as $ll) { + $t = trim($ll); + if(($t) && (strpos($embedurl,$t) !== false) && ($action !== 'block')) { $found = true; + $action = 'allow'; break; } } } - if(! $found) { - return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + if((! $found) && ($action !== 'block')) { + $action = 'filter'; } } - // implements a personal embed white/black list for logged in members + // allow individual members to block something that wasn't blocked already. + // They cannot over-ride the site to allow or change the filtering on an + // embed that is not allowed by the site admin. + if(local_channel()) { if(($x = get_pconfig(local_channel(),'system','embed_deny'))) { - $l = explode("\n",$x); - if($l) { - foreach($l as $ll) { - if(trim($ll) && strpos($embedurl,trim($ll)) !== false) - return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; - } - } - } - if(($x = get_pconfig(local_channel(),'system','embed_allow'))) { - $found = false; - $l = explode("\n",$x); - if($l) { - foreach($l as $ll) { - if(trim($ll) && strpos($embedurl,trim($ll)) !== false) { - $found = true; + if(($x) && (! is_array($x))) + $x = explode("\n",$x); + if($x) { + foreach($x as $ll) { + $t = trim($ll); + if(($t) && (strpos($embedurl,$t) !== false)) { + $action = 'block'; break; } } } - if(! $found) { - return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; - } } } - $j = oembed_fetch_url($embedurl); - $s = oembed_format_object($j); - return $s; -} + $arr = array('url' => $embedurl, 'action' => $action); + call_hooks('oembed_action',$arr); + + logger('action: ' . $arr['action'] . ' url: ' . $arr['url'], LOGGER_DEBUG,LOG_DEBUG); + return $arr; + +} // if the url is embeddable with oembed, return the bbcode link. @@ -79,42 +112,54 @@ function oembed_process($url) { function oembed_fetch_url($embedurl){ - $a = get_app(); + // These media files should now be caught in bbcode.php + // left here as a fallback in case this is called from another source - $embedurl = str_replace('&','&', $embedurl); + $noexts = array(".mp3",".mp4",".ogg",".ogv",".oga",".ogm",".webm",".opus"); -// logger('fetch: ' . $embedurl); + $result = oembed_action($embedurl); - $txt = Cache::get(App::$videowidth . $embedurl); + $embedurl = $result['url']; + $action = $result['action']; - if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { - $txt = str_replace('http:','https:',$txt); + foreach($noexts as $ext) { + if(strpos(strtolower($embedurl),$ext) !== false) { + $action = 'block'; + } } - // These media files should now be caught in bbcode.php - // left here as a fallback in case this is called from another source + $txt = null; - $noexts = array("mp3","mp4","ogg","ogv","oga","ogm","webm","opus"); - $ext = pathinfo(strtolower($embedurl),PATHINFO_EXTENSION); - - - if(is_null($txt)){ - $txt = ""; + if($action !== 'block') { + $txt = Zlib\Cache::get('[' . App::$videowidth . '] ' . $embedurl); + + if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { + $txt = str_replace('http:','https:',$txt); + } + } - if (in_array($ext, $noexts)) { + if(is_null($txt)) { + + $txt = ""; + $furl = $embedurl; + $zrl = false; + + if(local_channel()) { require_once('include/hubloc.php'); - $zrl = is_matrix_url($embedurl); + $zrl = is_matrix_url($furl); if($zrl) - $embedurl = zid($embedurl); + $furl = zid($furl); } - else { + + + if ($action !== 'block') { // try oembed autodiscovery $redirects = 0; - $result = z_fetch_url($embedurl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); + $result = z_fetch_url($furl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); if($result['success']) $html_text = $result['body']; - if($html_text){ + if($html_text) { $dom = @DOMDocument::loadHTML($html_text); if ($dom){ $xpath = new DOMXPath($dom); @@ -149,17 +194,41 @@ function oembed_fetch_url($embedurl){ } $txt=trim($txt); + if ($txt[0]!="{") $txt='{"type":"error"}'; //save in cache if(! get_config('system','oembed_cache_disable')) - Cache::set(App::$videowidth . $embedurl,$txt); + Zlib\Cache::set('[' . App::$videowidth . '] ' . $embedurl,$txt); } $j = json_decode($txt); + + if($action === 'filter') { + if($j->html) { + $orig = $j->html; + $allow_position = (($zrl) ? true : false); + $j->html = purify_html($j->html,$allow_position); + if($j->html != $orig) { + logger('oembed html was purified. original: ' . $orig . ' purified: ' . $j->html, LOGGER_DEBUG, LOG_INFO); + } + + $orig_len = trim(mb_strlen($orig)); + $new_len = trim(mb_strlen($j->html)); + if(! $new_len) + $j->type = 'error'; + elseif($orig_len) { + $ratio = $new_len / $orig_len; + if($ratio < 0.8) + $j->type = 'error'; + } + + } + } + $j->embedurl = $embedurl; // logger('fetch return: ' . print_r($j,true)); @@ -170,7 +239,7 @@ function oembed_fetch_url($embedurl){ } function oembed_format_object($j){ - $a = get_app(); + $embedurl = $j->embedurl; // logger('format: ' . print_r($j,true)); diff --git a/include/onedirsync.php b/include/onedirsync.php deleted file mode 100644 index ce516da9d..000000000 --- a/include/onedirsync.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/dir_fns.php'); - - -function onedirsync_run($argv, $argc){ - - - cli_startup(); - $a = get_app(); - - logger('onedirsync: start ' . intval($argv[1])); - - if(($argc > 1) && (intval($argv[1]))) - $update_id = intval($argv[1]); - - if(! $update_id) { - logger('onedirsync: no update'); - return; - } - - $r = q("select * from updates where ud_id = %d limit 1", - intval($update_id) - ); - - if(! $r) - return; - if(($r[0]['ud_flags'] & UPDATE_FLAGS_UPDATED) || (! $r[0]['ud_addr'])) - return; - - // Have we probed this channel more recently than the other directory server - // (where we received this update from) ? - // If we have, we don't need to do anything except mark any older entries updated - - $x = q("select * from updates where ud_addr = '%s' and ud_date > '%s' and ( ud_flags & %d )>0 order by ud_date desc limit 1", - dbesc($r[0]['ud_addr']), - dbesc($r[0]['ud_date']), - intval(UPDATE_FLAGS_UPDATED) - ); - if($x) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 and ud_date != '%s'", - intval(UPDATE_FLAGS_UPDATED), - dbesc($r[0]['ud_addr']), - intval(UPDATE_FLAGS_UPDATED), - dbesc($x[0]['ud_date']) - ); - return; - } - - // ignore doing an update if this ud_addr refers to a known dead hubloc - - $h = q("select * from hubloc where hubloc_addr = '%s' limit 1", - dbesc($r[0]['ud_addr']) - ); - if(($h) && ($h[0]['hubloc_status'] & HUBLOC_OFFLINE)) { - $y = q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and ( ud_flags & %d ) = 0 ", - intval(UPDATE_FLAGS_UPDATED), - dbesc($r[0]['ud_addr']), - intval(UPDATE_FLAGS_UPDATED) - ); - - return; - } - - // we might have to pull this out some day, but for now update_directory_entry() - // runs zot_finger() and is kind of zot specific - - if($h && $h[0]['hubloc_network'] !== 'zot') - return; - - update_directory_entry($r[0]); - - return; -} - -if (array_search(__file__,get_included_files())===0){ - onedirsync_run($argv,$argc); - killme(); -} diff --git a/include/onepoll.php b/include/onepoll.php deleted file mode 100644 index fedeb1e95..000000000 --- a/include/onepoll.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php /** @file */ - -require_once('boot.php'); -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/socgraph.php'); -require_once('include/Contact.php'); - - -function onepoll_run($argv, $argc){ - - - cli_startup(); - $a = get_app(); - - logger('onepoll: start'); - - if(($argc > 1) && (intval($argv[1]))) - $contact_id = intval($argv[1]); - - if(! $contact_id) { - logger('onepoll: no contact'); - return; - } - - $d = datetime_convert(); - - $contacts = q("SELECT abook.*, xchan.*, account.* - FROM abook LEFT JOIN account on abook_account = account_id left join xchan on xchan_hash = abook_xchan - where abook_id = %d - and abook_pending = 0 and abook_archived = 0 and abook_blocked = 0 and abook_ignored = 0 - AND (( account_flags = %d ) OR ( account_flags = %d )) limit 1", - intval($contact_id), - intval(ACCOUNT_OK), - intval(ACCOUNT_UNVERIFIED) - ); - - if(! $contacts) { - logger('onepoll: abook_id not found: ' . $contact_id); - return; - } - - $contact = $contacts[0]; - - $t = $contact['abook_updated']; - - $importer_uid = $contact['abook_channel']; - - $r = q("SELECT * from channel left join xchan on channel_hash = xchan_hash where channel_id = %d limit 1", - intval($importer_uid) - ); - - if(! $r) - return; - - $importer = $r[0]; - - logger("onepoll: poll: ({$contact['id']}) IMPORTER: {$importer['xchan_name']}, CONTACT: {$contact['xchan_name']}"); - - $last_update = ((($contact['abook_updated'] === $contact['abook_created']) || ($contact['abook_updated'] === NULL_DATE)) - ? datetime_convert('UTC','UTC','now - 7 days') - : datetime_convert('UTC','UTC',$contact['abook_updated'] . ' - 2 days') - ); - - if($contact['xchan_network'] === 'rss') { - logger('onepoll: processing feed ' . $contact['xchan_name'], LOGGER_DEBUG); - handle_feed($importer['channel_id'],$contact_id,$contact['xchan_hash']); - q("update abook set abook_connected = '%s' where abook_id = %d", - dbesc(datetime_convert()), - intval($contact['abook_id']) - ); - return; - } - - if($contact['xchan_network'] !== 'zot') - return; - - // update permissions - - $x = zot_refresh($contact,$importer); - - $responded = false; - $updated = datetime_convert(); - $connected = datetime_convert(); - if(! $x) { - // mark for death by not updating abook_connected, this is caught in include/poller.php - q("update abook set abook_updated = '%s' where abook_id = %d", - dbesc($updated), - intval($contact['abook_id']) - ); - } - else { - q("update abook set abook_updated = '%s', abook_connected = '%s' where abook_id = %d", - dbesc($updated), - dbesc($connected), - intval($contact['abook_id']) - ); - $responded = true; - } - - if(! $responded) - return; - - if($contact['xchan_connurl']) { - $fetch_feed = true; - $x = null; - - if(! ($contact['abook_their_perms'] & PERMS_R_STREAM )) - $fetch_feed = false; - - if($fetch_feed) { - - $feedurl = str_replace('/poco/','/zotfeed/',$contact['xchan_connurl']); - $feedurl .= '?f=&mindate=' . urlencode($last_update); - - $x = z_fetch_url($feedurl); - - logger('feed_update: ' . print_r($x,true), LOGGER_DATA); - - } - - if(($x) && ($x['success'])) { - $total = 0; - logger('onepoll: feed update ' . $contact['xchan_name'] . ' ' . $feedurl); - - $j = json_decode($x['body'],true); - if($j['success'] && $j['messages']) { - foreach($j['messages'] as $message) { - $results = process_delivery(array('hash' => $contact['xchan_hash']), get_item_elements($message), - array(array('hash' => $importer['xchan_hash'])), false); - logger('onepoll: feed_update: process_delivery: ' . print_r($results,true), LOGGER_DATA); - $total ++; - } - logger("onepoll: $total messages processed"); - } - } - } - - - // update the poco details for this connection - - if($contact['xchan_connurl']) { - $r = q("SELECT xlink_id from xlink - where xlink_xchan = '%s' and xlink_updated > %s - INTERVAL %s and xlink_static = 0 limit 1", - intval($contact['xchan_hash']), - db_utcnow(), db_quoteinterval('1 DAY') - ); - if(! $r) { - poco_load($contact['xchan_hash'],$contact['xchan_connurl']); - } - } - - return; -} - -if (array_search(__file__,get_included_files())===0){ - onepoll_run($argv,$argc); - killme(); -} diff --git a/include/page_widgets.php b/include/page_widgets.php index 49d1439be..3270de4a3 100644 --- a/include/page_widgets.php +++ b/include/page_widgets.php @@ -1,7 +1,8 @@ <?php // A basic toolbar for observers with write_pages permissions -function writepages_widget ($who,$which){ + +function writepages_widget ($who,$which) { return replace_macros(get_markup_template('write_pages.tpl'), array( '$new' => t('New Page'), '$newurl' => "webpages/$who", @@ -13,9 +14,11 @@ function writepages_widget ($who,$which){ // Chan is channel_id, $which is channel_address - we'll need to pass observer later too. -function pagelist_widget ($owner,$which){ - $r = q("select * from item_id left join item on item_id.iid = item.id where item_id.uid = %d and service = 'WEBPAGE' order by item.created desc", +function pagelist_widget ($owner,$which) { + + $r = q("select * from iconfig left join item on iconfig.iid = item.id where item_id.uid = %d + and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' order by item.created desc", intval($owner) ); @@ -24,7 +27,7 @@ function pagelist_widget ($owner,$which){ if($r) { $pages = array(); foreach($r as $rr) { - $pages[$rr['iid']][] = array('url' => $rr['iid'],'pagetitle' => $rr['sid'],'title' => $rr['title'],'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']),'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited'])); + $pages[$rr['iid']][] = array('url' => $rr['iid'],'pagetitle' => $rr['v'],'title' => $rr['title'],'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']),'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited'])); } } diff --git a/include/perm_upgrade.php b/include/perm_upgrade.php new file mode 100644 index 000000000..5be1ffbb2 --- /dev/null +++ b/include/perm_upgrade.php @@ -0,0 +1,236 @@ +<?php + +function perm_limits_upgrade($channel) { + set_pconfig($channel['channel_id'],'perm_limits','view_stream',$channel['channel_r_stream']); + set_pconfig($channel['channel_id'],'perm_limits','view_profile',$channel['channel_r_profile']); + set_pconfig($channel['channel_id'],'perm_limits','view_contacts',$channel['channel_r_abook']); + set_pconfig($channel['channel_id'],'perm_limits','view_storage',$channel['channel_r_storage']); + set_pconfig($channel['channel_id'],'perm_limits','view_pages',$channel['channel_r_pages']); + set_pconfig($channel['channel_id'],'perm_limits','send_stream',$channel['channel_w_stream']); + set_pconfig($channel['channel_id'],'perm_limits','post_wall',$channel['channel_w_wall']); + set_pconfig($channel['channel_id'],'perm_limits','post_comments',$channel['channel_w_comment']); + set_pconfig($channel['channel_id'],'perm_limits','post_mail',$channel['channel_w_mail']); + set_pconfig($channel['channel_id'],'perm_limits','post_like',$channel['channel_w_like']); + set_pconfig($channel['channel_id'],'perm_limits','tag_deliver',$channel['channel_w_tagwall']); + set_pconfig($channel['channel_id'],'perm_limits','chat',$channel['channel_w_chat']); + set_pconfig($channel['channel_id'],'perm_limits','write_storage',$channel['channel_w_storage']); + set_pconfig($channel['channel_id'],'perm_limits','write_pages',$channel['channel_w_pages']); + set_pconfig($channel['channel_id'],'perm_limits','republish',$channel['channel_a_republish']); + set_pconfig($channel['channel_id'],'perm_limits','delegate',$channel['channel_a_delegate']); +} + +function perms_int_to_array($p) { + + $ret = []; + + $ret['view_stream'] = (($p & PERMS_R_STREAM) ? 1 : 0); + $ret['view_profile'] = (($p & PERMS_R_PROFILE) ? 1 : 0); + $ret['view_contacts'] = (($p & PERMS_R_ABOOK) ? 1 : 0); + $ret['view_storage'] = (($p & PERMS_R_STORAGE) ? 1 : 0); + $ret['view_pages'] = (($p & PERMS_R_PAGES) ? 1 : 0); + $ret['send_stream'] = (($p & PERMS_W_STREAM) ? 1 : 0); + $ret['post_wall'] = (($p & PERMS_W_WALL) ? 1 : 0); + $ret['post_comments'] = (($p & PERMS_W_COMMENT) ? 1 : 0); + $ret['post_mail'] = (($p & PERMS_W_MAIL) ? 1 : 0); + $ret['post_like'] = (($p & PERMS_W_LIKE) ? 1 : 0); + $ret['tag_deliver'] = (($p & PERMS_W_TAGWALL) ? 1 : 0); + $ret['chat'] = (($p & PERMS_W_CHAT) ? 1 : 0); + $ret['write_storage'] = (($p & PERMS_W_STORAGE) ? 1 : 0); + $ret['write_pages'] = (($p & PERMS_W_PAGES) ? 1 : 0); + $ret['republish'] = (($p & PERMS_A_REPUBLISH) ? 1 : 0); + $ret['delegate'] = (($p & PERMS_A_DELEGATE) ? 1 : 0); + + return $ret; +} + +function autoperms_upgrade($channel) { + $x = get_pconfig($channel['channel_id'],'system','autoperms'); + if(intval($x)) { + $y = perms_int_to_array($x); + if($y) { + foreach($y as $k => $v) { + set_pconfig($channel['channel_id'],'autoperms',$k,$v); + } + } + } +} + + +function perm_abook_upgrade($abook) { + + $x = perms_int_to_array($abook['abook_their_perms']); + if($x) { + foreach($x as $k => $v) { + set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'their_perms',$k, $v); + } + } + + $x = perms_int_to_array($abook['abook_my_perms']); + if($x) { + foreach($x as $k => $v) { + set_abconfig($abook['abook_channel'],$abook['abook_xchan'],'my_perms',$k, $v); + } + } +} + +function translate_channel_perms_outbound(&$channel) { + $r = q("select * from pconfig where uid = %d and cat = 'perm_limits' ", + intval($channel['channel_id']) + ); + + if($r) { + foreach($r as $rr) { + if($rr['k'] === 'view_stream') + $channel['channel_r_stream'] = $rr['v']; + if($rr['k'] === 'view_profile') + $channel['channel_r_profile'] = $rr['v']; + if($rr['k'] === 'view_contacts') + $channel['channel_r_abook'] = $rr['v']; + if($rr['k'] === 'view_storage') + $channel['channel_r_storage'] = $rr['v']; + if($rr['k'] === 'view_pages') + $channel['channel_r_pages'] = $rr['v']; + if($rr['k'] === 'send_stream') + $channel['channel_w_stream'] = $rr['v']; + if($rr['k'] === 'post_wall') + $channel['channel_w_wall'] = $rr['v']; + if($rr['k'] === 'post_comments') + $channel['channel_w_comment'] = $rr['v']; + if($rr['k'] === 'post_mail') + $channel['channel_w_mail'] = $rr['v']; + if($rr['k'] === 'post_like') + $channel['channel_w_like'] = $rr['v']; + if($rr['k'] === 'tag_deliver') + $channel['channel_w_tagwall'] = $rr['v']; + if($rr['k'] === 'chat') + $channel['channel_w_chat'] = $rr['v']; + if($rr['k'] === 'write_storage') + $channel['channel_w_storage'] = $rr['v']; + if($rr['k'] === 'write_pages') + $channel['channel_w_pages'] = $rr['v']; + if($rr['k'] === 'republish') + $channel['channel_a_republish'] = $rr['v']; + if($rr['k'] === 'delegate') + $channel['channel_a_delegate'] = $rr['v']; + + } + $channel['perm_limits'] = $r; + } +} + +function translate_channel_perms_inbound($channel) { + + if($channel['perm_limits']) { + foreach($channel['perm_limits'] as $p) { + set_pconfig($channel['channel_id'],'perm_limits',$p['k'],$p['v']); + } + } + else { + perm_limits_upgrade($channel); + } + +} + +function translate_abook_perms_outbound(&$abook) { + $my_perms = 0; + $their_perms = 0; + + if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && $abook['abconfig']) { + foreach($abook['abconfig'] as $p) { + if($p['cat'] === 'their_perms') { + if($p['k'] === 'view_stream' && intval($p['v'])) + $their_perms += PERMS_R_STREAM; + if($p['k'] === 'view_profile' && intval($p['v'])) + $their_perms += PERMS_R_PROFILE; + if($p['k'] === 'view_contacts' && intval($p['v'])) + $their_perms += PERMS_R_ABOOK; + if($p['k'] === 'view_storage' && intval($p['v'])) + $their_perms += PERMS_R_STORAGE; + if($p['k'] === 'view_pages' && intval($p['v'])) + $their_perms += PERMS_R_PAGES; + if($p['k'] === 'send_stream' && intval($p['v'])) + $their_perms += PERMS_W_STREAM; + if($p['k'] === 'post_wall' && intval($p['v'])) + $their_perms += PERMS_W_WALL; + if($p['k'] === 'post_comments' && intval($p['v'])) + $their_perms += PERMS_W_COMMENT; + if($p['k'] === 'post_mail' && intval($p['v'])) + $their_perms += PERMS_W_MAIL; + if($p['k'] === 'post_like' && intval($p['v'])) + $their_perms += PERMS_W_LIKE; + if($p['k'] === 'tag_deliver' && intval($p['v'])) + $their_perms += PERMS_W_TAGWALL; + if($p['k'] === 'chat' && intval($p['v'])) + $their_perms += PERMS_W_CHAT; + if($p['k'] === 'write_storage' && intval($p['v'])) + $their_perms += PERMS_W_STORAGE; + if($p['k'] === 'write_pages' && intval($p['v'])) + $their_perms += PERMS_W_PAGES; + if($p['k'] === 'republish' && intval($p['v'])) + $their_perms += PERMS_A_REPUBLISH; + if($p['k'] === 'delegate' && intval($p['v'])) + $their_perms += PERMS_A_DELEGATE; + } + if($p['cat'] === 'my_perms') { + if($p['k'] === 'view_stream' && intval($p['v'])) + $my_perms += PERMS_R_STREAM; + if($p['k'] === 'view_profile' && intval($p['v'])) + $my_perms += PERMS_R_PROFILE; + if($p['k'] === 'view_contacts' && intval($p['v'])) + $my_perms += PERMS_R_ABOOK; + if($p['k'] === 'view_storage' && intval($p['v'])) + $my_perms += PERMS_R_STORAGE; + if($p['k'] === 'view_pages' && intval($p['v'])) + $my_perms += PERMS_R_PAGES; + if($p['k'] === 'send_stream' && intval($p['v'])) + $my_perms += PERMS_W_STREAM; + if($p['k'] === 'post_wall' && intval($p['v'])) + $my_perms += PERMS_W_WALL; + if($p['k'] === 'post_comments' && intval($p['v'])) + $my_perms += PERMS_W_COMMENT; + if($p['k'] === 'post_mail' && intval($p['v'])) + $my_perms += PERMS_W_MAIL; + if($p['k'] === 'post_like' && intval($p['v'])) + $my_perms += PERMS_W_LIKE; + if($p['k'] === 'tag_deliver' && intval($p['v'])) + $my_perms += PERMS_W_TAGWALL; + if($p['k'] === 'chat' && intval($p['v'])) + $my_perms += PERMS_W_CHAT; + if($p['k'] === 'write_storage' && intval($p['v'])) + $my_perms += PERMS_W_STORAGE; + if($p['k'] === 'write_pages' && intval($p['v'])) + $my_perms += PERMS_W_PAGES; + if($p['k'] === 'republish' && intval($p['v'])) + $my_perms += PERMS_A_REPUBLISH; + if($p['k'] === 'delegate' && intval($p['v'])) + $my_perms += PERMS_A_DELEGATE; + } + } + + $abook['abook_their_perms'] = $their_perms; + $abook['abook_my_perms'] = $my_perms; + } +} + +function translate_abook_perms_inbound($channel,$abook) { + + $new_perms = false; + $abook['abook_channel'] = $channel['channel_id']; + + if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && $abook['abconfig']) { + foreach($abook['abconfig'] as $p) { + if($p['cat'] == 'their_perms' || $p['cat'] == 'my_perms') { + $new_perms = true; + break; + } + } + } + + if($new_perms == false) { + perm_abook_upgrade($abook); + } + +} + + + diff --git a/include/permissions.php b/include/permissions.php index 19242d29f..638bedb24 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -67,7 +67,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { if($api) return get_all_api_perms($uid,$api); - $global_perms = get_perms(); + $global_perms = \Zotlabs\Access\Permissions::Perms(); // Save lots of individual lookups @@ -81,11 +81,13 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { $ret = array(); + $abperms = (($uid && $observer_xchan) ? load_abconfig($uid,$observer_xchan,'my_perms') : array()); + foreach($global_perms as $perm_name => $permission) { // First find out what the channel owner declared permissions to be. - $channel_perm = $permission[0]; + $channel_perm = \Zotlabs\Access\PermissionLimits::Get($uid,$perm_name); if(! $channel_checked) { $r = q("select * from channel where channel_id = %d limit 1", @@ -105,7 +107,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // These take priority over all other settings. if($observer_xchan) { - if($r[0][$channel_perm] & PERMS_AUTHED) { + if($channel_perm & PERMS_AUTHED) { $ret[$perm_name] = true; continue; } @@ -136,7 +138,10 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // Check if this is a write permission and they are being ignored // This flag is only visible internally. - if(($x) && ($internal_use) && (! $global_perms[$perm_name][2]) && intval($x[0]['abook_ignored'])) { + $blocked_anon_perms = \Zotlabs\Access\Permissions::BlockedAnonPerms(); + + + if(($x) && ($internal_use) && in_array($perm_name,$blocked_anon_perms) && intval($x[0]['abook_ignored'])) { $ret[$perm_name] = false; continue; } @@ -154,7 +159,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // if you've moved elsewhere, you will only have read only access if(($observer_xchan) && ($r[0]['channel_hash'] === $observer_xchan)) { - if($r[0]['channel_moved'] && (! $permission[2])) + if($r[0]['channel_moved'] && (in_array($perm_name,$blocked_anon_perms))) $ret[$perm_name] = false; else $ret[$perm_name] = true; @@ -163,7 +168,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // Anybody at all (that wasn't blocked or ignored). They have permission. - if($r[0][$channel_perm] & PERMS_PUBLIC) { + if($channel_perm & PERMS_PUBLIC) { $ret[$perm_name] = true; continue; } @@ -178,7 +183,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // If we're still here, we have an observer, check the network. - if($r[0][$channel_perm] & PERMS_NETWORK) { + if($channel_perm & PERMS_NETWORK) { if(($x && $x[0]['xchan_network'] === 'zot') || ($y && $y[0]['xchan_network'] === 'zot')) { $ret[$perm_name] = true; continue; @@ -187,7 +192,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // If PERMS_SITE is specified, find out if they've got an account on this hub - if($r[0][$channel_perm] & PERMS_SITE) { + if($channel_perm & PERMS_SITE) { if(! $onsite_checked) { $c = q("select channel_hash from channel where channel_hash = '%s' limit 1", dbesc($observer_xchan) @@ -214,7 +219,7 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // They are in your address book, but haven't been approved - if($r[0][$channel_perm] & PERMS_PENDING) { + if($channel_perm & PERMS_PENDING) { $ret[$perm_name] = true; continue; } @@ -226,16 +231,21 @@ function get_all_perms($uid, $observer_xchan, $internal_use = true) { // They're a contact, so they have permission - if($r[0][$channel_perm] & PERMS_CONTACTS) { + if($channel_perm & PERMS_CONTACTS) { $ret[$perm_name] = true; continue; } // Permission granted to certain channels. Let's see if the observer is one of them - if($r[0][$channel_perm] & PERMS_SPECIFIC) { - if(($x[0]['abook_my_perms'] & $global_perms[$perm_name][1])) { - $ret[$perm_name] = true; + if($channel_perm & PERMS_SPECIFIC) { + if($abperms) { + foreach($abperms as $ab) { + if(($ab['cat'] == 'my_perms') && ($ab['k'] == $perm_name)) { + $ret[$perm_name] = (intval($ab['v']) ? true : false); + break; + } + } continue; } } @@ -284,21 +294,23 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { if($arr['result']) return true; - $global_perms = get_perms(); + $global_perms = \Zotlabs\Access\Permissions::Perms(); // First find out what the channel owner declared permissions to be. - $channel_perm = $global_perms[$permission][0]; + $channel_perm = \Zotlabs\Access\PermissionLimits::Get($uid,$permission); - $r = q("select %s, channel_pageflags, channel_moved, channel_hash from channel where channel_id = %d limit 1", - dbesc($channel_perm), + $r = q("select channel_pageflags, channel_moved, channel_hash from channel where channel_id = %d limit 1", intval($uid) ); if(! $r) return false; + + $blocked_anon_perms = \Zotlabs\Access\Permissions::BlockedAnonPerms(); + if($observer_xchan) { - if($r[0][$channel_perm] & PERMS_AUTHED) + if($channel_perm & PERMS_AUTHED) return true; $x = q("select abook_my_perms, abook_blocked, abook_ignored, abook_pending, xchan_network from abook left join xchan on abook_xchan = xchan_hash @@ -312,7 +324,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { if(($x) && intval($x[0]['abook_blocked'])) return false; - if(($x) && (! $global_perms[$permission][2]) && intval($x[0]['abook_ignored'])) + if(($x) && in_array($permission,$blocked_anon_perms) && intval($x[0]['abook_ignored'])) return false; if(! $x) { @@ -321,7 +333,9 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { dbesc($observer_xchan) ); } + $abperms = load_abconfig($uid,$observer_xchan,'my_perms'); } + // system is blocked to anybody who is not authenticated @@ -333,13 +347,13 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { // in which case you will have read_only access if($r[0]['channel_hash'] === $observer_xchan) { - if($r[0]['channel_moved'] && (! $global_perms[$permission][2])) + if($r[0]['channel_moved'] && (in_array($permission,$blocked_anon_perms))) return false; else return true; } - if($r[0][$channel_perm] & PERMS_PUBLIC) + if($channel_perm & PERMS_PUBLIC) return true; // If it's an unauthenticated observer, we only need to see if PERMS_PUBLIC is set @@ -350,14 +364,14 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { // If we're still here, we have an observer, check the network. - if($r[0][$channel_perm] & PERMS_NETWORK) { + if($channel_perm & PERMS_NETWORK) { if (($x && $x[0]['xchan_network'] === 'zot') || ($y && $y[0]['xchan_network'] === 'zot')) return true; } // If PERMS_SITE is specified, find out if they've got an account on this hub - if($r[0][$channel_perm] & PERMS_SITE) { + if($channel_perm & PERMS_SITE) { $c = q("select channel_hash from channel where channel_hash = '%s' limit 1", dbesc($observer_xchan) ); @@ -376,7 +390,7 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { // They are in your address book, but haven't been approved - if($r[0][$channel_perm] & PERMS_PENDING) { + if($channel_perm & PERMS_PENDING) { return true; } @@ -386,15 +400,20 @@ function perm_is_allowed($uid, $observer_xchan, $permission) { // They're a contact, so they have permission - if($r[0][$channel_perm] & PERMS_CONTACTS) { + if($channel_perm & PERMS_CONTACTS) { return true; } // Permission granted to certain channels. Let's see if the observer is one of them - if(($r) && $r[0][$channel_perm] & PERMS_SPECIFIC) { - if($x[0]['abook_my_perms'] & $global_perms[$permission][1]) - return true; + if(($r) && ($channel_perm & PERMS_SPECIFIC)) { + if($abperms) { + foreach($abperms as $ab) { + if($ab['cat'] == 'my_perms' && $ab['k'] == $permission) { + return ((intval($ab['v'])) ? true : false); + } + } + } } // No permissions allowed. @@ -560,28 +579,28 @@ function get_role_perms($role) { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = true; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_NETWORK; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'chat', 'post_like', 'republish' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -590,28 +609,29 @@ function get_role_perms($role) { $ret['default_collection'] = true; $ret['directory_publish'] = true; $ret['online'] = true; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_SPECIFIC; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'chat', 'post_like' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; + break; @@ -620,28 +640,28 @@ function get_role_perms($role) { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_SPECIFIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_SPECIFIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_SPECIFIC; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_SPECIFIC, + 'view_storage' => PERMS_SPECIFIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -650,28 +670,28 @@ function get_role_perms($role) { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_NETWORK; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'post_wall', 'post_comments', 'tag_deliver', + 'post_mail', 'post_like' , 'republish', 'chat' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -680,28 +700,28 @@ function get_role_perms($role) { $ret['default_collection'] = true; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_SPECIFIC; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'post_wall', 'post_comments', 'tag_deliver', + 'post_mail', 'post_like' , 'chat' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -710,28 +730,29 @@ function get_role_perms($role) { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILEPERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_SPECIFIC; - $ret['channel_r_abook'] = PERMS_SPECIFIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_SPECIFIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_SPECIFIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_SPECIFIC; + + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'post_wall', 'post_comments', + 'post_mail', 'post_like' , 'chat' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_SPECIFIC, + 'view_contacts' => PERMS_SPECIFIC, + 'view_storage' => PERMS_SPECIFIC, + 'view_pages' => PERMS_SPECIFIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -740,28 +761,29 @@ function get_role_perms($role) { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_NETWORK; - $ret['channel_w_like'] = PERMS_NETWORK; + + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' , 'republish' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -770,28 +792,28 @@ function get_role_perms($role) { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_NETWORK; + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' , 'republish' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -800,26 +822,29 @@ function get_role_perms($role) { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_NETWORK; + + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'post_like' , 'republish' ]; + + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; break; @@ -828,28 +853,30 @@ function get_role_perms($role) { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_W_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT - |PERMS_R_STORAGE|PERMS_W_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; - $ret['channel_r_stream'] = PERMS_PUBLIC; - $ret['channel_r_profile'] = PERMS_PUBLIC; - $ret['channel_r_abook'] = PERMS_PUBLIC; - $ret['channel_w_stream'] = PERMS_SPECIFIC; - $ret['channel_w_wall'] = PERMS_SPECIFIC; - $ret['channel_w_tagwall'] = PERMS_SPECIFIC; - $ret['channel_w_comment'] = PERMS_SPECIFIC; - $ret['channel_w_mail'] = PERMS_SPECIFIC; - $ret['channel_w_chat'] = PERMS_SPECIFIC; - $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; - $ret['channel_w_storage'] = PERMS_SPECIFIC; - $ret['channel_r_pages'] = PERMS_PUBLIC; - $ret['channel_w_pages'] = PERMS_SPECIFIC; - $ret['channel_a_republish'] = PERMS_SPECIFIC; - $ret['channel_w_like'] = PERMS_NETWORK; + + $ret['perms_connect'] = [ + 'view_stream', 'view_profile', 'view_contacts', 'view_storage', + 'view_pages', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver', + 'post_mail', 'post_like' , 'republish', 'chat' ]; + $ret['limits'] = [ + 'view_stream' => PERMS_PUBLIC, + 'view_profile' => PERMS_PUBLIC, + 'view_contacts' => PERMS_PUBLIC, + 'view_storage' => PERMS_PUBLIC, + 'view_pages' => PERMS_PUBLIC, + 'send_stream' => PERMS_SPECIFIC, + 'post_wall' => PERMS_SPECIFIC, + 'post_comments' => PERMS_SPECIFIC, + 'post_mail' => PERMS_SPECIFIC, + 'post_like' => PERMS_SPECIFIC, + 'tag_deliver' => PERMS_SPECIFIC, + 'chat' => PERMS_SPECIFIC, + 'write_storage' => PERMS_SPECIFIC, + 'write_pages' => PERMS_SPECIFIC, + 'republish' => PERMS_SPECIFIC, + 'delegate' => PERMS_SPECIFIC + ]; + break; diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 3bea54fd4..6de75d497 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -316,7 +316,7 @@ abstract class photo_driver { $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : ''); $p['filename'] = (($arr['filename']) ? $arr['filename'] : ''); $p['album'] = (($arr['album']) ? $arr['album'] : ''); - $p['scale'] = ((intval($arr['scale'])) ? intval($arr['scale']) : 0); + $p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0); $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : ''); $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : ''); $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : ''); @@ -329,14 +329,14 @@ abstract class photo_driver { $p['os_storage'] = intval($arr['os_storage']); $p['os_path'] = $arr['os_path']; - if(! intval($p['scale'])) - logger('save: ' . print_r($arr,true)); + if(! intval($p['imgscale'])) + logger('save: ' . print_r($arr,true), LOGGER_DATA); - $x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and `scale` = %d limit 1", + $x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), - intval($p['scale']) + intval($p['imgscale']) ); if($x) { $r = q("UPDATE `photo` set @@ -347,14 +347,14 @@ abstract class photo_driver { `created` = '%s', `edited` = '%s', `filename` = '%s', - `type` = '%s', + `mimetype` = '%s', `album` = '%s', `height` = %d, `width` = %d, - `data` = '%s', + `content` = '%s', `os_storage` = %d, - `size` = %d, - `scale` = %d, + `filesize` = %d, + `imgscale` = %d, `photo_usage` = %d, `title` = '%s', `description` = '%s', @@ -378,7 +378,7 @@ abstract class photo_driver { (intval($p['os_storage']) ? dbesc($p['os_path']) : dbescbin($this->imageString())), intval($p['os_storage']), intval(strlen($this->imageString())), - intval($p['scale']), + intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), @@ -391,7 +391,7 @@ abstract class photo_driver { } else { $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `os_storage`, `size`, `scale`, `photo_usage`, `title`, `description`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `os_storage`, `filesize`, `imgscale`, `photo_usage`, `title`, `description`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), @@ -407,7 +407,7 @@ abstract class photo_driver { (intval($p['os_storage']) ? dbesc($p['os_path']) : dbescbin($this->imageString())), intval($p['os_storage']), intval(strlen($this->imageString())), - intval($p['scale']), + intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), @@ -422,7 +422,7 @@ abstract class photo_driver { public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $usage = PHOTO_NORMAL, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') { - $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `scale` = %d limit 1", + $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `imgscale` = %d limit 1", dbesc($rid), intval($uid), dbesc($xchan), @@ -437,13 +437,13 @@ abstract class photo_driver { `created` = '%s', `edited` = '%s', `filename` = '%s', - `type` = '%s', + `mimetype` = '%s', `album` = '%s', `height` = %d, `width` = %d, - `data` = '%s', - `size` = %d, - `scale` = %d, + `content` = '%s', + `filesize` = %d, + `imgscale` = %d, `photo_usage` = %d, `allow_cid` = '%s', `allow_gid` = '%s', @@ -475,7 +475,7 @@ abstract class photo_driver { } else { $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `photo_usage`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, mimetype, `album`, `height`, `width`, `content`, `filesize`, `imgscale`, `photo_usage`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s' )", intval($aid), intval($uid), @@ -521,7 +521,7 @@ function guess_image_type($filename, $headers = '') { logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG); $type = null; if ($headers) { - $a = get_app(); + $hdrs=array(); $h = explode("\n",$headers); foreach ($h as $l) { @@ -580,8 +580,6 @@ function guess_image_type($filename, $headers = '') { function import_xchan_photo($photo,$xchan,$thing = false) { - $a = get_app(); - $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN); $album = (($thing) ? 'Things' : 'Contact Photos'); @@ -590,7 +588,7 @@ function import_xchan_photo($photo,$xchan,$thing = false) { if($thing) $hash = photo_new_resource(); else { - $r = q("select resource_id from photo where xchan = '%s' and photo_usage = %d and scale = 4 limit 1", + $r = q("select resource_id from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1", dbesc($xchan), intval(PHOTO_XCHAN) ); @@ -656,7 +654,7 @@ function import_xchan_photo($photo,$xchan,$thing = false) { else $photo_failure = true; - $p = array('xchan' => $xchan,'resource_id' => $hash, 'filename' => basename($photo), 'album' => $album, 'photo_usage' => $flags, 'scale' => 4); + $p = array('xchan' => $xchan,'resource_id' => $hash, 'filename' => basename($photo), 'album' => $album, 'photo_usage' => $flags, 'imgscale' => 4); $r = $img->save($p); @@ -664,7 +662,7 @@ function import_xchan_photo($photo,$xchan,$thing = false) { $photo_failure = true; $img->scaleImage(80); - $p['scale'] = 5; + $p['imgscale'] = 5; $r = $img->save($p); @@ -672,7 +670,7 @@ function import_xchan_photo($photo,$xchan,$thing = false) { $photo_failure = true; $img->scaleImage(48); - $p['scale'] = 6; + $p['imgscale'] = 6; $r = $img->save($p); @@ -703,8 +701,6 @@ function import_xchan_photo($photo,$xchan,$thing = false) { function import_channel_photo($photo,$type,$aid,$uid) { - $a = get_app(); - logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG); $hash = photo_new_resource(); @@ -719,7 +715,7 @@ function import_channel_photo($photo,$type,$aid,$uid) { $img->scaleImageSquare(300); - $p = array('aid' => $aid, 'uid' => $uid, 'resource_id' => $hash, 'filename' => $filename, 'album' => t('Profile Photos'), 'photo_usage' => PHOTO_PROFILE, 'scale' => 4); + $p = array('aid' => $aid, 'uid' => $uid, 'resource_id' => $hash, 'filename' => $filename, 'album' => t('Profile Photos'), 'photo_usage' => PHOTO_PROFILE, 'imgscale' => 4); $r = $img->save($p); @@ -727,7 +723,7 @@ function import_channel_photo($photo,$type,$aid,$uid) { $photo_failure = true; $img->scaleImage(80); - $p['scale'] = 5; + $p['imgscale'] = 5; $r = $img->save($p); @@ -735,7 +731,7 @@ function import_channel_photo($photo,$type,$aid,$uid) { $photo_failure = true; $img->scaleImage(48); - $p['scale'] = 6; + $p['imgscale'] = 6; $r = $img->save($p); diff --git a/include/photos.php b/include/photos.php index 943d7d503..d14c12d84 100644 --- a/include/photos.php +++ b/include/photos.php @@ -19,8 +19,6 @@ require_once('include/text.php'); */ function photo_upload($channel, $observer, $args) { - $a = get_app(); - $ret = array('success' => false); $channel_id = $channel['channel_id']; $account_id = $channel['channel_account_id']; @@ -43,6 +41,10 @@ function photo_upload($channel, $observer, $args) { else $visible = 0; + $deliver = true; + if(array_key_exists('deliver',$args)) + $deliver = intval($args['deliver']); + // Set to default channel permissions. If the parent directory (album) has permissions set, // use those instead. If we have specific permissions supplied, they take precedence over // all other settings. 'allow_cid' being passed from an external source takes priority over channel settings. @@ -73,17 +75,17 @@ function photo_upload($channel, $observer, $args) { $type = $args['getimagesize']['mime']; $os_storage = 1; } - elseif ($args['data']) { + elseif ($args['data'] || $args['content']) { // allow an import from a binary string representing the image. // This bypasses the upload step and max size limit checking - $imagedata = $args['data']; + $imagedata = (($args['content']) ? $args['content'] : $args['data']); $filename = $args['filename']; $filesize = strlen($imagedata); // this is going to be deleted if it exists $src = '/tmp/deletemenow'; - $type = $args['type']; + $type = (($args['mimetype']) ? $args['mimetype'] : $args['type']); } else { $f = array('src' => '', 'filename' => '', 'filesize' => 0, 'type' => ''); @@ -127,11 +129,11 @@ function photo_upload($channel, $observer, $args) { $imagedata = @file_get_contents($src); } - $r = q("select sum(size) as total from photo where aid = %d and scale = 0 ", + $r = q("select sum(filesize) as total from photo where aid = %d and imgscale = 0 ", intval($account_id) ); - $limit = service_class_fetch($channel_id,'photo_upload_limit'); + $limit = engr_units_to_bytes(service_class_fetch($channel_id,'photo_upload_limit')); if (($r) && ($limit !== false) && (($r[0]['total'] + strlen($imagedata)) > $limit)) { $ret['message'] = upgrade_message(); @@ -174,7 +176,7 @@ function photo_upload($channel, $observer, $args) { $errors = false; $p = array('aid' => $account_id, 'uid' => $channel_id, 'xchan' => $visitor, 'resource_id' => $photo_hash, - 'filename' => $filename, 'album' => $album, 'scale' => 0, 'photo_usage' => PHOTO_NORMAL, + 'filename' => $filename, 'album' => $album, 'imgscale' => 0, 'photo_usage' => PHOTO_NORMAL, 'allow_cid' => $ac['allow_cid'], 'allow_gid' => $ac['allow_gid'], 'deny_cid' => $ac['deny_cid'], 'deny_gid' => $ac['deny_gid'], 'os_storage' => $os_storage, 'os_path' => $args['os_path'] @@ -207,7 +209,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 1024 || $height > 1024) && (! $errors)) $ph->scaleImage(1024); - $p['scale'] = 1; + $p['imgscale'] = 1; $r1 = $ph->save($p); $link[1] = array( 'rel' => 'alternate', @@ -222,7 +224,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 640 || $height > 640) && (! $errors)) $ph->scaleImage(640); - $p['scale'] = 2; + $p['imgscale'] = 2; $r2 = $ph->save($p); $link[2] = array( 'rel' => 'alternate', @@ -237,7 +239,7 @@ function photo_upload($channel, $observer, $args) { if(($width > 320 || $height > 320) && (! $errors)) $ph->scaleImage(320); - $p['scale'] = 3; + $p['imgscale'] = 3; $r3 = $ph->save($p); $link[3] = array( 'rel' => 'alternate', @@ -296,7 +298,7 @@ function photo_upload($channel, $observer, $args) { $photo_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . t('a new photo') . '[/zrl]'; - $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album) . ']' . $album . '[/zrl]'; + $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album) . ']' . ((strlen($album)) ? $album : '/') . '[/zrl]'; $activity_format = sprintf(t('%1$s posted %2$s to %3$s','photo_upload'), $author_link, $photo_link, $album_link); @@ -332,9 +334,9 @@ function photo_upload($channel, $observer, $args) { if($item['mid'] === $item['parent_mid']) { - $item['body'] = $args['body']; + $item['body'] = $summary; $item['obj_type'] = ACTIVITY_OBJ_PHOTO; - $item['object'] = json_encode($object); + $item['obj'] = json_encode($object); $item['tgt_type'] = ACTIVITY_OBJ_ALBUM; $item['target'] = json_encode($target); @@ -357,14 +359,14 @@ function photo_upload($channel, $observer, $args) { if(($item['edited'] > $r[0]['edited']) || $force) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; - item_store_update($item); + item_store_update($item,false,$deliver); continue; } } else { $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; - $item_result = item_store($item); + $item_result = item_store($item,false,$deliver); } } } @@ -391,8 +393,8 @@ function photo_upload($channel, $observer, $args) { $arr['deny_cid'] = $ac['deny_cid']; $arr['deny_gid'] = $ac['deny_gid']; $arr['verb'] = ACTIVITY_POST; - $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; - $arr['object'] = json_encode($object); + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['obj'] = json_encode($object); $arr['tgt_type'] = ACTIVITY_OBJ_ALBUM; $arr['target'] = json_encode($target); $arr['item_wall'] = 1; @@ -410,17 +412,17 @@ function photo_upload($channel, $observer, $args) { // in the photos pages - using the photos permissions instead. We need the public policy to keep the photo // linked item from leaking into the feed when somebody has a channel with read_stream restrictions. - $arr['public_policy'] = map_scope($channel['channel_r_stream'],true); + $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_stream'),true); if($arr['public_policy']) $arr['item_private'] = 1; - $result = item_store($arr); + $result = item_store($arr,false,$deliver); $item_id = $result['item_id']; - if($visible) - proc_run('php', "include/notifier.php", 'wall-new', $item_id); + if($visible && $deliver) + Zotlabs\Daemon\Master::Summon(array('Notifier', 'wall-new', $item_id)); } $ret['success'] = true; @@ -445,7 +447,7 @@ function photo_upload($channel, $observer, $args) { * * success (bool) * * albums (array) */ -function photos_albums_list($channel, $observer) { +function photos_albums_list($channel, $observer, $sort_key = 'album', $direction = 'asc') { $channel_id = $channel['channel_id']; $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); @@ -453,11 +455,15 @@ function photos_albums_list($channel, $observer) { if(! perm_is_allowed($channel_id, $observer_xchan, 'view_storage')) return false; - /** @FIXME create a permissions SQL which works on arbitrary observers and channels, regardless of login or web status */ - $sql_extra = permissions_sql($channel_id); + $sql_extra = permissions_sql($channel_id,$observer_xchan); + + $sort_key = dbesc($sort_key); + $direction = dbesc($direction); - $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and photo_usage IN ( %d, %d ) $sql_extra group by album order by max(created) desc", + + + $albums = q("SELECT count( distinct resource_id ) as total, album from photo where uid = %d and photo_usage IN ( %d, %d ) $sql_extra group by album order by $sort_key $direction", intval($channel_id), intval(PHOTO_NORMAL), intval(PHOTO_PROFILE) @@ -485,20 +491,14 @@ function photos_albums_list($channel, $observer) { return $ret; } -function photos_album_widget($channelx,$observer,$albums = null) { +function photos_album_widget($channelx,$observer,$sortkey = 'album',$direction = 'asc') { $o = ''; - // If we weren't passed an album list, see if the photos module - // dropped one for us to find in App::$data['albums']. - // If all else fails, load it. - - if(! $albums) { - if(array_key_exists('albums', App::$data)) - $albums = App::$data['albums']; - else - $albums = photos_albums_list($channelx,$observer); - } + if(array_key_exists('albums', App::$data)) + $albums = App::$data['albums']; + else + $albums = photos_albums_list($channelx,$observer,$sortkey,$direction); if($albums['success']) { $o = replace_macros(get_markup_template('photo_albums.tpl'),array( @@ -537,7 +537,7 @@ function photos_list_photos($channel, $observer, $album = '') { $ret = array('success' => false); - $r = q("select resource_id, created, edited, title, description, album, filename, type, height, width, size, scale, photo_usage, allow_cid, allow_gid, deny_cid, deny_gid from photo where uid = %d and photo_usage in ( %d, %d ) $sql_extra ", + $r = q("select resource_id, created, edited, title, description, album, filename, mimetype, height, width, filesize, imgscale, photo_usage, allow_cid, allow_gid, deny_cid, deny_gid from photo where uid = %d and photo_usage in ( %d, %d ) $sql_extra ", intval($channel_id), intval(PHOTO_NORMAL), intval(PHOTO_PROFILE) @@ -545,7 +545,7 @@ function photos_list_photos($channel, $observer, $album = '') { if($r) { for($x = 0; $x < count($r); $x ++) { - $r[$x]['src'] = z_root() . '/photo/' . $r[$x]['resource_id'] . '-' . $r[$x]['scale']; + $r[$x]['src'] = z_root() . '/photo/' . $r[$x]['resource_id'] . '-' . $r[$x]['imgscale']; } $ret['success'] = true; $ret['photos'] = $r; @@ -663,7 +663,7 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' - . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['scale'] . '[/zmg]' + . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['imgscale'] . '[/zmg]' . '[/zrl]'; $result = item_store($arr); @@ -706,3 +706,68 @@ function gps2Num($coordPart) { return floatval($parts[0]) / floatval($parts[1]); } + +function profile_photo_set_profile_perms($uid, $profileid = 0) { + + $allowcid = ''; + if($profileid) { + $r = q("SELECT photo, profile_guid, id, is_default, uid + FROM profile WHERE uid = %d and ( profile.id = %d OR profile.profile_guid = '%s') LIMIT 1", + intval($profileid), + dbesc($profileid) + ); + } + else { + logger('Resetting permissions on default-profile-photo for user'.local_channel()); + + $r = q("SELECT photo, profile_guid, id, is_default, uid FROM profile + WHERE profile.uid = %d AND is_default = 1 LIMIT 1", + intval($uid) + ); //If no profile is given, we update the default profile + } + if(! $r) + return; + + $profile = $r[0]; + + if($profile['id'] && $profile['photo']) { + preg_match("@\w*(?=-\d*$)@i", $profile['photo'], $resource_id); + $resource_id = $resource_id[0]; + + if (! intval($profile['is_default'])) { + $r0 = q("SELECT channel_hash FROM channel WHERE channel_id = %d LIMIT 1", + intval($uid) + ); + //Should not be needed in future. Catches old int-profile-ids. + $r1 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%d' ", + intval($profile['id']) + ); + $r2 = q("SELECT abook.abook_xchan FROM abook WHERE abook_profile = '%s'", + dbesc($profile['profile_guid']) + ); + $allowcid = "<" . $r0[0]['channel_hash'] . ">"; + foreach ($r1 as $entry) { + $allowcid .= "<" . $entry['abook_xchan'] . ">"; + } + foreach ($r2 as $entry) { + $allowcid .= "<" . $entry['abook_xchan'] . ">"; + } + + q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d", + dbesc($allowcid), + dbesc($resource_id), + intval($uid) + ); + + } + else { + //Reset permissions on default profile picture to public + q("UPDATE photo SET allow_cid = '' WHERE photo_usage = %d AND uid = %d", + intval(PHOTO_PROFILE), + intval($uid) + ); + } + } + + return; + } diff --git a/include/plugin.php b/include/plugin.php index 8dceb8fb1..cb206d944 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -5,8 +5,6 @@ * @brief Some functions to handle addons and themes. */ -require_once("include/smarty.php"); - /** * @brief unloads an addon. @@ -43,7 +41,7 @@ function uninstall_plugin($plugin) { $func(); } - q("DELETE FROM `addon` WHERE `name` = '%s' ", + q("DELETE FROM `addon` WHERE `aname` = '%s' ", dbesc($plugin) ); } @@ -68,7 +66,7 @@ function install_plugin($plugin) { $plugin_admin = (function_exists($plugin . '_plugin_admin') ? 1 : 0); - q("INSERT INTO `addon` (`name`, `installed`, `timestamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ", + q("INSERT INTO `addon` (`aname`, `installed`, `tstamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ", dbesc($plugin), intval($t), $plugin_admin @@ -113,7 +111,7 @@ function load_plugin($plugin) { } function plugin_is_installed($name) { - $r = q("select name from addon where name = '%s' and installed = 1 limit 1", + $r = q("select aname from addon where aname = '%s' and installed = 1 limit 1", dbesc($name) ); if($r) @@ -145,8 +143,8 @@ function reload_plugins() { if(file_exists($fname)) { $t = @filemtime($fname); foreach($installed as $i) { - if(($i['name'] == $pl) && ($i['timestamp'] != $t)) { - logger('Reloading plugin: ' . $i['name']); + if(($i['aname'] == $pl) && ($i['tstamp'] != $t)) { + logger('Reloading plugin: ' . $i['aname']); @include_once($fname); if(function_exists($pl . '_unload')) { @@ -157,7 +155,7 @@ function reload_plugins() { $func = $pl . '_load'; $func(); } - q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d", + q("UPDATE `addon` SET `tstamp` = %d WHERE `id` = %d", intval($t), intval($i['id']) ); @@ -169,6 +167,12 @@ function reload_plugins() { } } +function visible_plugin_list() { + $r = q("select * from addon where hidden = 0 order by aname asc"); + return(($r) ? ids_to_array($r,'aname') : array()); +} + + /** * @brief registers a hook. @@ -180,15 +184,15 @@ function reload_plugins() { * @return mixed|bool */ function register_hook($hook, $file, $function, $priority = 0) { - $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", + $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `fn` = '%s' LIMIT 1", dbesc($hook), dbesc($file), dbesc($function) ); - if(count($r)) + if($r) return true; - $r = q("INSERT INTO `hook` (`hook`, `file`, `function`, `priority`) VALUES ( '%s', '%s', '%s', '%s' )", + $r = q("INSERT INTO `hook` (`hook`, `file`, `fn`, `priority`) VALUES ( '%s', '%s', '%s', '%s' )", dbesc($hook), dbesc($file), dbesc($function), @@ -208,7 +212,7 @@ function register_hook($hook, $file, $function, $priority = 0) { * @return array */ function unregister_hook($hook, $file, $function) { - $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `function` = '%s'", + $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `fn` = '%s'", dbesc($hook), dbesc($file), dbesc($function) @@ -226,9 +230,8 @@ function unregister_hook($hook, $file, $function) { function load_hooks() { - $a = get_app(); -// if(! is_array(App::$hooks)) - App::$hooks = array(); + + App::$hooks = array(); $r = q("SELECT * FROM hook WHERE true ORDER BY priority DESC"); if($r) { @@ -236,10 +239,10 @@ function load_hooks() { if(! array_key_exists($rr['hook'],App::$hooks)) App::$hooks[$rr['hook']] = array(); - App::$hooks[$rr['hook']][] = array($rr['file'],$rr['function']); + App::$hooks[$rr['hook']][] = array($rr['file'],$rr['fn'],$rr['priority'],$rr['hook_version']); } } -//logger('hooks: ' . print_r(App::$hooks,true)); + //logger('hooks: ' . print_r(App::$hooks,true)); } /** @@ -259,15 +262,15 @@ function load_hooks() { * @param string $fn * function name of callback handler */ -function insert_hook($hook, $fn) { - $a = get_app(); +function insert_hook($hook, $fn, $version = 0, $priority = 0) { + if(! is_array(App::$hooks)) App::$hooks = array(); if(! array_key_exists($hook, App::$hooks)) App::$hooks[$hook] = array(); - App::$hooks[$hook][] = array('', $fn); + App::$hooks[$hook][] = array('', $fn, $priority, $version); } /** @@ -280,23 +283,41 @@ function insert_hook($hook, $fn) { * @param string|array &$data to transmit to the callback handler */ function call_hooks($name, &$data = null) { - $a = get_app(); - + $a = 0; if((is_array(App::$hooks)) && (array_key_exists($name, App::$hooks))) { foreach(App::$hooks[$name] as $hook) { + $origfn = $hook[1]; if($hook[0]) @include_once($hook[0]); + if(preg_match('|^a:[0-9]+:{.*}$|s', $hook[1])) { + $hook[1] = unserialize($hook[1]); + } + elseif(strpos($hook[1],'::')) { + // We shouldn't need to do this, but it appears that PHP + // isn't able to directly execute a string variable with a class + // method in the manner we are attempting it, so we'll + // turn it into an array. + $hook[1] = explode('::',$hook[1]); + } - if(function_exists($hook[1])) { + if(is_callable($hook[1])) { $func = $hook[1]; - $func($a, $data); - } else { - - q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND function = '%s'", - dbesc($name), - dbesc($hook[0]), - dbesc($hook[1]) - ); + if($hook[3]) + $func($data); + else + $func($a, $data); + } + else { + + // Don't do any DB write calls if we're currently logging a possibly failed DB call. + if(! DBA::$logging) { + // The hook should be removed so we don't process it. + q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND fn = '%s'", + dbesc($name), + dbesc($hook[0]), + dbesc($origfn) + ); + } } } } @@ -491,7 +512,7 @@ function get_theme_info($theme){ * @return string */ function get_theme_screenshot($theme) { - $a = get_app(); + $exts = array('.png', '.jpg'); foreach($exts as $ext) { if(file_exists('view/theme/' . $theme . '/img/screenshot' . $ext)) @@ -512,7 +533,7 @@ function head_add_css($src, $media = 'screen') { } function head_remove_css($src, $media = 'screen') { - $a = get_app(); + $index = array_search(array($src, $media), App::$css_sources); if ($index !== false) unset(App::$css_sources[$index]); @@ -530,13 +551,22 @@ function head_get_css() { } function format_css_if_exists($source) { - if (strpos($source[0], '/') !== false) + $path_prefix = script_path() . '/'; + + if (strpos($source[0], '/') !== false) { + // The source is a URL $path = $source[0]; - else + // If the url starts with // then it's an absolute URL + if($source[0][0] === '/' && $source[0][1] === '/') $path_prefix = ''; + } else { + // It's a file from the theme $path = theme_include($source[0]); + } - if($path) - return '<link rel="stylesheet" href="' . script_path() . '/' . $path . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; + if($path) { + $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; + return '<link rel="stylesheet" href="' . $path_prefix . $path . $qstring . '" type="text/css" media="' . $source[1] . '">' . "\r\n"; + } } /* @@ -575,26 +605,38 @@ function script_path() { return $scheme . '://' . $hostname; } -function head_add_js($src) { - App::$js_sources[] = $src; +function head_add_js($src, $priority = 0) { + if(! is_array(App::$js_sources[$priority])) + App::$js_sources[$priority] = array(); + App::$js_sources[$priority][] = $src; } -function head_remove_js($src) { - $a = get_app(); - $index = array_search($src, App::$js_sources); +function head_remove_js($src, $priority = 0) { + + $index = array_search($src, App::$js_sources[$priority]); if($index !== false) - unset(App::$js_sources[$index]); + unset(App::$js_sources[$priority][$index]); } +// We should probably try to register main.js with a high priority, but currently we handle it +// separately and put it at the end of the html head block in case any other javascript is +// added outside the head_add_js construct. + function head_get_js() { + $str = ''; - $sources = App::$js_sources; - if(count($sources)) - foreach($sources as $source) { - if($source === 'main.js') - continue; - $str .= format_js_if_exists($source); + if(App::$js_sources) { + ksort(App::$js_sources,SORT_NUMERIC); + foreach(App::$js_sources as $sources) { + if(count($sources)) { + foreach($sources as $source) { + if($src === 'main.js') + continue; + $str .= format_js_if_exists($source); + } + } } + } return $str; } @@ -608,17 +650,25 @@ function head_get_main_js() { } function format_js_if_exists($source) { - if(strpos($source,'/') !== false) + $path_prefix = script_path() . '/'; + + if(strpos($source,'/') !== false) { + // The source is a URL $path = $source; - else + // If the url starts with // then it's an absolute URL + if($source[0] === '/' && $source[1] === '/') $path_prefix = ''; + } else { + // It's a file from the theme $path = theme_include($source); - if($path) - return '<script src="' . script_path() . '/' . $path . '" ></script>' . "\r\n" ; + } + if($path) { + $qstring = ((parse_url($path, PHP_URL_QUERY)) ? '&' : '?') . 'v=' . STD_VERSION; + return '<script src="' . $path_prefix . $path . $qstring . '" ></script>' . "\r\n" ; + } } function theme_include($file, $root = '') { - $a = get_app(); // Make sure $root ends with a slash / if it's not blank if($root !== '' && $root[strlen($root)-1] !== '/') @@ -631,12 +681,13 @@ function theme_include($file, $root = '') { else $parent = 'NOPATH'; - $theme = current_theme(); + $theme = Zotlabs\Render\Theme::current(); + $thname = $theme[0]; $ext = substr($file,strrpos($file,'.')+1); $paths = array( - "{$root}view/theme/$theme/$ext/$file", + "{$root}view/theme/$thname/$ext/$file", "{$root}view/theme/$parent/$ext/$file", "{$root}view/site/$ext/$file", "{$root}view/$ext/$file", @@ -655,7 +706,7 @@ function theme_include($file, $root = '') { function get_intltext_template($s, $root = '') { - $a = get_app(); + $t = App::template_engine(); $template = $t->get_intltext_template($s, $root); @@ -664,7 +715,7 @@ function get_intltext_template($s, $root = '') { function get_markup_template($s, $root = '') { - $a = get_app(); + $t = App::template_engine(); $template = $t->get_markup_template($s, $root); return $template; @@ -678,4 +729,4 @@ function folder_exists($folder) // If it exist, check if it's a directory return (($path !== false) && is_dir($path)) ? $path : false; -}
\ No newline at end of file +} diff --git a/include/poller.php b/include/poller.php index 808b54ee5..e72121a8a 100644 --- a/include/poller.php +++ b/include/poller.php @@ -1,445 +1,15 @@ <?php /** @file */ -require_once('boot.php'); require_once('include/cli_startup.php'); - -function poller_run($argv, $argc){ +function poller_run($argc,$argv){ cli_startup(); + \Zotlabs\Daemon\Master::Summon(array('Cron')); - $a = get_app(); - - $maxsysload = intval(get_config('system','maxloadavg')); - if($maxsysload < 1) - $maxsysload = 50; - if(function_exists('sys_getloadavg')) { - $load = sys_getloadavg(); - if(intval($load[0]) > $maxsysload) { - logger('system: load ' . $load . ' too high. Poller deferred to next scheduled run.'); - return; - } - } - - $interval = intval(get_config('system','poll_interval')); - if(! $interval) - $interval = ((get_config('system','delivery_interval') === false) ? 3 : intval(get_config('system','delivery_interval'))); - - // Check for a lockfile. If it exists, but is over an hour old, it's stale. Ignore it. - $lockfile = 'store/[data]/poller'; - if((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 3600)) - && (! get_config('system','override_poll_lockfile'))) { - logger("poller: Already running"); - return; - } - - // Create a lockfile. Needs two vars, but $x doesn't need to contain anything. - file_put_contents($lockfile, $x); - - logger('poller: start'); - - // run queue delivery process in the background - - proc_run('php',"include/queue.php"); - - - // maintenance for mod sharedwithme - check for updated items and remove them - - require_once('include/sharedwithme.php'); - apply_updates(); - - - // expire any expired mail - - q("delete from mail where expires != '%s' and expires < %s ", - dbesc(NULL_DATE), - db_utcnow() - ); - - // expire any expired items - - $r = q("select id from item where expires != '%s' and expires < %s - and item_deleted = 0 ", - dbesc(NULL_DATE), - db_utcnow() - ); - if($r) { - require_once('include/items.php'); - foreach($r as $rr) - drop_item($rr['id'],false); - } - - - // Ensure that every channel pings a directory server once a month. This way we can discover - // channels and sites that quietly vanished and prevent the directory from accumulating stale - // or dead entries. - - $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval('30 DAY') - ); - if($r) { - foreach($r as $rr) { - proc_run('php','include/directory.php',$rr['channel_id'],'force'); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } - - // publish any applicable items that were set to be published in the future - // (time travel posts). Restrict to items that have come of age in the last - // couple of days to limit the query to something reasonable. - - $r = q("select id from item where item_delayed = 1 and created <= %s and created > '%s' ", - db_utcnow(), - dbesc(datetime_convert('UTC','UTC','now - 2 days')) - ); - if($r) { - foreach($r as $rr) { - $x = q("update item set item_delayed = 0 where id = %d", - intval($rr['id']) - ); - if($x) { - proc_run('php','include/notifier.php','wall-new',$rr['id']); - } - } - } - - $abandon_days = intval(get_config('system','account_abandon_days')); - if($abandon_days < 1) - $abandon_days = 0; - - - // once daily run birthday_updates and then expire in background - - // FIXME: add birthday updates, both locally and for xprof for use - // by directory servers - - $d1 = intval(get_config('system','last_expire_day')); - $d2 = intval(datetime_convert('UTC','UTC','now','d')); - - // Allow somebody to staggger daily activities if they have more than one site on their server, - // or if it happens at an inconvenient (busy) hour. - - $h1 = intval(get_config('system','cron_hour')); - $h2 = intval(datetime_convert('UTC','UTC','now','G')); - - $dirmode = get_config('system','directory_mode'); - - /** - * Cron Daily - * - * Actions in the following block are executed once per day, not on every poller run - * - */ - - if(($d2 != $d1) && ($h1 == $h2)) { - - require_once('include/dir_fns.php'); - check_upstream_directory(); - - call_hooks('cron_daily',datetime_convert()); - - - $d3 = intval(datetime_convert('UTC','UTC','now','N')); - if($d3 == 7) { - - /** - * Cron Weekly - * - * Actions in the following block are executed once per day only on Sunday (once per week). - * - */ - - - call_hooks('cron_weekly',datetime_convert()); - - - z_check_cert(); - - require_once('include/hubloc.php'); - prune_hub_reinstalls(); - - require_once('include/Contact.php'); - mark_orphan_hubsxchans(); - - - // get rid of really old poco records - - q("delete from xlink where xlink_updated < %s - INTERVAL %s and xlink_static = 0 ", - db_utcnow(), db_quoteinterval('14 DAY') - ); - - $dirmode = intval(get_config('system','directory_mode')); - if($dirmode === DIRECTORY_MODE_SECONDARY || $dirmode === DIRECTORY_MODE_PRIMARY) { - logger('regdir: ' . print_r(z_fetch_url(get_directory_primary() . '/regdir?f=&url=' . urlencode(z_root()) . '&realm=' . urlencode(get_directory_realm())),true)); - } - - // Check for dead sites - proc_run('php', 'include/checksites.php'); - - // update searchable doc indexes - proc_run('php', 'include/importdoc.php'); - - /** - * End Cron Weekly - */ - } - - update_birthdays(); - - //update statistics in config - require_once('include/statistics_fns.php'); - update_channels_total_stat(); - update_channels_active_halfyear_stat(); - update_channels_active_monthly_stat(); - update_local_posts_stat(); - - // expire any read notifications over a month old - - q("delete from notify where seen = 1 and date < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('30 DAY') - ); - - // expire old delivery reports - - $keep_reports = intval(get_config('system','expire_delivery_reports')); - if($keep_reports === 0) - $keep_reports = 10; - - q("delete from dreport where dreport_time < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval($keep_reports . ' DAY') - ); - - // expire any expired accounts - downgrade_accounts(); - - // If this is a directory server, request a sync with an upstream - // directory at least once a day, up to once every poll interval. - // Pull remote changes and push local changes. - // potential issue: how do we keep from creating an endless update loop? - - if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { - require_once('include/dir_fns.php'); - sync_directories($dirmode); - } - - set_config('system','last_expire_day',$d2); - - proc_run('php','include/expire.php'); - proc_run('php','include/cli_suggest.php'); - - require_once('include/hubloc.php'); - remove_obsolete_hublocs(); - - /** - * End Cron Daily - */ - } - - // update any photos which didn't get imported properly - // This should be rare - - $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' - and xchan_photo_date < %s - INTERVAL %s", - db_utcnow(), - db_quoteinterval('1 DAY') - ); - if($r) { - require_once('include/photo/photo_driver.php'); - foreach($r as $rr) { - $photos = import_xchan_photo($rr['xchan_photo_l'],$rr['xchan_hash']); - $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' - where xchan_hash = '%s'", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($rr['xchan_hash']) - ); - } - } - - - // pull in some public posts - - if(! get_config('system','disable_discover_tab')) - proc_run('php','include/externals.php'); - - - $manual_id = 0; - $generation = 0; - - $force = false; - $restart = false; - - if(($argc > 1) && ($argv[1] == 'force')) - $force = true; - - if(($argc > 1) && ($argv[1] == 'restart')) { - $restart = true; - $generation = intval($argv[2]); - if(! $generation) - killme(); - } - - if(($argc > 1) && intval($argv[1])) { - $manual_id = intval($argv[1]); - $force = true; - } - - - $sql_extra = (($manual_id) ? " AND abook_id = " . intval($manual_id) . " " : ""); - - reload_plugins(); - - $d = datetime_convert(); - - // TODO check to see if there are any cronhooks before wasting a process - - if(! $restart) - proc_run('php','include/cronhooks.php'); - - // Only poll from those with suitable relationships - - $abandon_sql = (($abandon_days) - ? sprintf(" AND account_lastlog > %s - INTERVAL %s ", db_utcnow(), db_quoteinterval(intval($abandon_days).' DAY')) - : '' - ); - - $randfunc = db_getfunc('RAND'); - - $contacts = q("SELECT * FROM abook LEFT JOIN xchan on abook_xchan = xchan_hash - LEFT JOIN account on abook_account = account_id - where abook_self = 0 - $sql_extra - AND (( account_flags = %d ) OR ( account_flags = %d )) $abandon_sql ORDER BY $randfunc", - intval(ACCOUNT_OK), - intval(ACCOUNT_UNVERIFIED) // FIXME - - ); - - if($contacts) { - - foreach($contacts as $contact) { - - $update = false; - - $t = $contact['abook_updated']; - $c = $contact['abook_connected']; - - if(intval($contact['abook_feed'])) { - $min = service_class_fetch($contact['abook_channel'],'minimum_feedcheck_minutes'); - if(! $min) - $min = intval(get_config('system','minimum_feedcheck_minutes')); - if(! $min) - $min = 60; - $x = datetime_convert('UTC','UTC',"now - $min minutes"); - if($c < $x) { - proc_run('php','include/onepoll.php',$contact['abook_id']); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - continue; - } - - - if($contact['xchan_network'] !== 'zot') - continue; - - if($c == $t) { - if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) - $update = true; - } - else { - - // if we've never connected with them, start the mark for death countdown from now - - if($c == NULL_DATE) { - $r = q("update abook set abook_connected = '%s' where abook_id = %d", - dbesc(datetime_convert()), - intval($contact['abook_id']) - ); - $c = datetime_convert(); - $update = true; - } - - // He's dead, Jim - - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 30 day")) > 0) { - $r = q("update abook set abook_archived = 1 where abook_id = %d", - intval($contact['abook_id']) - ); - $update = false; - continue; - } - - if(intval($contact['abook_archived'])) { - $update = false; - continue; - } - - // might be dead, so maybe don't poll quite so often - - // recently deceased, so keep up the regular schedule for 3 days - - if((strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $c . " + 3 day")) > 0) - && (strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 1 day")) > 0)) - $update = true; - - // After that back off and put them on a morphine drip - - if(strcmp(datetime_convert('UTC','UTC', 'now'),datetime_convert('UTC','UTC', $t . " + 2 day")) > 0) { - $update = true; - } - - } - - if(intval($contact['abook_pending']) || intval($contact['abook_archived']) || intval($contact['abook_ignored']) || intval($contact['abook_blocked'])) - continue; - - if((! $update) && (! $force)) - continue; - - proc_run('php','include/onepoll.php',$contact['abook_id']); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - - } - } - - if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { - $r = q("SELECT u.ud_addr, u.ud_id, u.ud_last FROM updates AS u INNER JOIN (SELECT ud_addr, max(ud_id) AS ud_id FROM updates WHERE ( ud_flags & %d ) = 0 AND ud_addr != '' AND ( ud_last = '%s' OR ud_last > %s - INTERVAL %s ) GROUP BY ud_addr) AS s ON s.ud_id = u.ud_id ", - intval(UPDATE_FLAGS_UPDATED), - dbesc(NULL_DATE), - db_utcnow(), db_quoteinterval('7 DAY') - ); - if($r) { - foreach($r as $rr) { - - // If they didn't respond when we attempted before, back off to once a day - // After 7 days we won't bother anymore - - if($rr['ud_last'] != NULL_DATE) - if($rr['ud_last'] > datetime_convert('UTC','UTC', 'now - 1 day')) - continue; - proc_run('php','include/onedirsync.php',$rr['ud_id']); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } - } - - set_config('system','lastpoll',datetime_convert()); - - //All done - clear the lockfile - @unlink($lockfile); - - return; } if (array_search(__file__,get_included_files())===0){ - poller_run($argv,$argc); + poller_run($argc,$argv); killme(); } diff --git a/include/queue.php b/include/queue.php deleted file mode 100644 index 8a3b2aa58..000000000 --- a/include/queue.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php /** @file */ -require_once("boot.php"); -require_once('include/cli_startup.php'); -require_once('include/queue_fn.php'); -require_once('include/zot.php'); - -function queue_run($argv, $argc){ - - cli_startup(); - - global $a; - - require_once('include/items.php'); - require_once('include/bbcode.php'); - - if(argc() > 1) - $queue_id = argv(1); - else - $queue_id = 0; - - logger('queue: start'); - - // delete all queue items more than 3 days old - // but first mark these sites dead if we haven't heard from them in a month - - $r = q("select outq_posturl from outq where outq_created < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('3 DAY') - ); - if($r) { - foreach($r as $rr) { - $site_url = ''; - $h = parse_url($rr['outq_posturl']); - $desturl = $h['scheme'] . '://' . $h['host'] . (($h['port']) ? ':' . $h['port'] : ''); - q("update site set site_dead = 1 where site_dead = 0 and site_url = '%s' and site_update < %s - INTERVAL %s", - dbesc($desturl), - db_utcnow(), db_quoteinterval('1 MONTH') - ); - } - } - - $r = q("DELETE FROM outq WHERE outq_created < %s - INTERVAL %s", - db_utcnow(), db_quoteinterval('3 DAY') - ); - - if($queue_id) { - $r = q("SELECT * FROM outq WHERE outq_hash = '%s' LIMIT 1", - dbesc($queue_id) - ); - } - else { - - // For the first 12 hours we'll try to deliver every 15 minutes - // After that, we'll only attempt delivery once per hour. - // This currently only handles the default queue drivers ('zot' or '') which we will group by posturl - // so that we don't start off a thousand deliveries for a couple of dead hubs. - // The zot driver will deliver everything destined for a single hub once contact is made (*if* contact is made). - // Other drivers will have to do something different here and may need their own query. - - // Note: this requires some tweaking as new posts to long dead hubs once a day will keep them in the - // "every 15 minutes" category. We probably need to prioritise them when inserted into the queue - // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe - // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once - // or twice a day. - - // FIXME: can we sort postgres on outq_priority and maintain the 'distinct' ? - // The order by max(outq_priority) might be a dodgy query because of the group by. - // The desired result is to return a sequence in the order most likely to be delivered in this run. - // If a hub has already been sitting in the queue for a few days, they should be delivered last; - // hence every failure should drop them further down the priority list. - - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $prefix = 'DISTINCT ON (outq_posturl)'; - $suffix = 'ORDER BY outq_posturl'; - } else { - $prefix = ''; - $suffix = 'GROUP BY outq_posturl ORDER BY max(outq_priority)'; - } - $r = q("SELECT $prefix * FROM outq WHERE outq_delivered = 0 and (( outq_created > %s - INTERVAL %s and outq_updated < %s - INTERVAL %s ) OR ( outq_updated < %s - INTERVAL %s )) $suffix", - db_utcnow(), db_quoteinterval('12 HOUR'), - db_utcnow(), db_quoteinterval('15 MINUTE'), - db_utcnow(), db_quoteinterval('1 HOUR') - ); - } - if(! $r) - return; - - foreach($r as $rr) { - queue_deliver($rr); - } -} - -if (array_search(__file__,get_included_files())===0){ - queue_run($argv,$argc); - killme(); -} diff --git a/include/ratenotif.php b/include/ratenotif.php deleted file mode 100644 index 2c636c710..000000000 --- a/include/ratenotif.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -require_once('include/cli_startup.php'); -require_once('include/zot.php'); -require_once('include/queue_fn.php'); - - -function ratenotif_run($argv, $argc){ - - cli_startup(); - - $a = get_app(); - - require_once("datetime.php"); - require_once('include/items.php'); - require_once('include/Contact.php'); - - if($argc < 3) - return; - - - logger('ratenotif: invoked: ' . print_r($argv,true), LOGGER_DEBUG); - - $cmd = $argv[1]; - - $item_id = $argv[2]; - - - if($cmd === 'rating') { - $r = q("select * from xlink where xlink_id = %d and xlink_static = 1 limit 1", - intval($item_id) - ); - if(! $r) { - logger('rating not found'); - return; - } - - $encoded_item = array( - 'type' => 'rating', - 'encoding' => 'zot', - 'target' => $r[0]['xlink_link'], - 'rating' => intval($r[0]['xlink_rating']), - 'rating_text' => $r[0]['xlink_rating_text'], - 'signature' => $r[0]['xlink_sig'], - 'edited' => $r[0]['xlink_updated'] - ); - } - - $channel = channelx_by_hash($r[0]['xlink_xchan']); - if(! $channel) { - logger('no channel'); - return; - } - - - $primary = get_directory_primary(); - - if(! $primary) - return; - - - $interval = ((get_config('system','delivery_interval') !== false) - ? intval(get_config('system','delivery_interval')) : 2 ); - - $deliveries_per_process = intval(get_config('system','delivery_batch_count')); - - if($deliveries_per_process <= 0) - $deliveries_per_process = 1; - - $deliver = array(); - - $x = z_fetch_url($primary . '/regdir'); - if($x['success']) { - $j = json_decode($x['body'],true); - if($j && $j['success'] && is_array($j['directories'])) { - - foreach($j['directories'] as $h) { - if($h == z_root()) - continue; - - $hash = random_string(); - $n = zot_build_packet($channel,'notify',null,null,$hash); - - queue_insert(array( - 'hash' => $hash, - 'account_id' => $channel['channel_account_id'], - 'channel_id' => $channel['channel_id'], - 'posturl' => $h . '/post', - 'notify' => $n, - 'msg' => json_encode($encoded_item) - )); - - $deliver[] = $hash; - - if(count($deliver) >= $deliveries_per_process) { - proc_run('php','include/deliver.php',$deliver); - $deliver = array(); - if($interval) - @time_sleep_until(microtime(true) + (float) $interval); - } - } - - // catch any stragglers - - if(count($deliver)) { - proc_run('php','include/deliver.php',$deliver); - } - } - } - - logger('ratenotif: complete.'); - return; - -} - -if (array_search(__file__,get_included_files())===0){ - ratenotif_run($argv,$argc); - killme(); -} diff --git a/include/reddav.php b/include/reddav.php deleted file mode 100644 index abf21b86d..000000000 --- a/include/reddav.php +++ /dev/null @@ -1,299 +0,0 @@ -<?php -/** - * @file include/reddav.php - * @brief some DAV related functions for Hubzilla. - * - * This file contains some functions which did not fit into one of the RedDAV - * classes. - * - * The extended SabreDAV classes you will find in the RedDAV namespace under - * @ref includes/RedDAV/. - * The original SabreDAV classes you can find under @ref vendor/sabre/dav/. - * We need to use SabreDAV 1.8.x for PHP5.3 compatibility. SabreDAV >= 2.0 - * requires PHP >= 5.4. - * - * @todo split up the classes into own files. - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ - -use Sabre\DAV; -use Zotlabs\Storage; - -require_once('vendor/autoload.php'); -require_once('include/attach.php'); - -/** - * @brief Returns an array with viewable channels. - * - * Get a list of RedDirectory objects with all the channels where the visitor - * has <b>view_storage</b> perms. - * - * @todo Is there any reason why this is not inside RedDirectory class? - * @fixme function name looks like a class name, should we rename it? - * - * @param RedBasicAuth &$auth - * @return array RedDirectory[] - */ -function RedChannelList(&$auth) { - $ret = array(); - - $r = q("SELECT channel_id, channel_address FROM channel WHERE channel_removed = 0 AND channel_system = 0 AND NOT (channel_pageflags & %d)>0", - intval(PAGE_HIDDEN) - ); - - if ($r) { - foreach ($r as $rr) { - if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { - logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); - // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new Zotlabs\Storage\Directory('/cloud/' . $rr['channel_address'], $auth); - } - } - } - return $ret; -} - - -/** - * @brief TODO what exactly does this function? - * - * Array with all RedDirectory and RedFile DAV\Node items for the given path. - * - * @todo Is there any reason why this is not inside RedDirectory class? Seems - * only to be used there and we could simplify it a bit there. - * @fixme function name looks like a class name, should we rename it? - * - * @param string $file path to a directory - * @param RedBasicAuth &$auth - * @returns null|array \Sabre\DAV\INode[] - * @throw \Sabre\DAV\Exception\Forbidden - * @throw \Sabre\DAV\Exception\NotFound - */ -function RedCollectionData($file, &$auth) { - $ret = array(); - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - - // return a list of channel if we are not inside a channel - if ((! $file) || ($file === '/')) { - return RedChannelList($auth); - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) - return null; - - $channel_name = $path_arr[0]; - - $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' LIMIT 1", - dbesc($channel_name) - ); - - if (! $r) - return null; - - $channel_id = $r[0]['channel_id']; - $perms = permissions_sql($channel_id); - - $auth->owner_id = $channel_id; - - $path = '/' . $channel_name; - - $folder = ''; - $errors = false; - $permission_error = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("SELECT id, hash, filename, flags, is_dir FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND is_dir != 0 $perms LIMIT 1", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if (! $r) { - // path wasn't found. Try without permissions to see if it was the result of permissions. - $errors = true; - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 limit 1", - dbesc($folder), - basename($path_arr[$x]), - intval($channel_id) - ); - if ($r) { - $permission_error = true; - } - break; - } - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - } - - if ($errors) { - if ($permission_error) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } else { - throw new DAV\Exception\NotFound('A component of the request file path could not be found.'); - } - } - - // This should no longer be needed since we just returned errors for paths not found - if ($path !== '/' . $file) { - logger("Path mismatch: $path !== /$file"); - return NULL; - } - if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) { - $prefix = 'DISTINCT ON (filename)'; - $suffix = 'ORDER BY filename'; - } else { - $prefix = ''; - $suffix = 'GROUP BY filename'; - } - $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", - dbesc($folder), - intval($channel_id) - ); - - foreach ($r as $rr) { - //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); - if (intval($rr['is_dir'])) { - $ret[] = new Zotlabs\Storage\Directory($path . '/' . $rr['filename'], $auth); - } else { - $ret[] = new Zotlabs\Storage\File($path . '/' . $rr['filename'], $rr, $auth); - } - } - - return $ret; -} - - -/** - * @brief TODO What exactly is this function for? - * - * @fixme function name looks like a class name, should we rename it? - * - * @param string $file - * path to file or directory - * @param RedBasicAuth &$auth - * @param boolean $test (optional) enable test mode - * @return RedFile|RedDirectory|boolean|null - * @throw \Sabre\DAV\Exception\Forbidden - */ -function RedFileData($file, &$auth, $test = false) { - logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DATA); - - $x = strpos($file, '/cloud'); - if ($x === 0) { - $file = substr($file, 6); - } - else { - $x = strpos($file,'/dav'); - if($x === 0) - $file = substr($file,4); - } - - - if ((! $file) || ($file === '/')) { - return new Zotlabs\Storage\Directory('/', $auth); - } - - $file = trim($file, '/'); - - $path_arr = explode('/', $file); - - if (! $path_arr) - return null; - - $channel_name = $path_arr[0]; - - $r = q("select channel_id from channel where channel_address = '%s' limit 1", - dbesc($channel_name) - ); - - if (! $r) - return null; - - $channel_id = $r[0]['channel_id']; - - $path = '/' . $channel_name; - - $auth->owner_id = $channel_id; - - $permission_error = false; - - $folder = ''; - - require_once('include/security.php'); - $perms = permissions_sql($channel_id); - - $errors = false; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0 $perms", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - $path = $path . '/' . $r[0]['filename']; - } - if (! $r) { - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach - where folder = '%s' and filename = '%s' and uid = %d $perms order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - } - if (! $r) { - $errors = true; - $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, os_storage, created, edited from attach - where folder = '%s' and filename = '%s' and uid = %d order by filename limit 1", - dbesc($folder), - dbesc(basename($file)), - intval($channel_id) - ); - if ($r) - $permission_error = true; - } - } - - if ($path === '/' . $file) { - if ($test) - return true; - // final component was a directory. - return new Zotlabs\Storage\Directory($file, $auth); - } - - if ($errors) { - logger('not found ' . $file); - if ($test) - return false; - if ($permission_error) { - logger('permission error ' . $file); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - return; - } - - if ($r) { - if ($test) - return true; - - if (intval($r[0]['is_dir'])) { - return new Zotlabs\Storage\Directory($path . '/' . $r[0]['filename'], $auth); - } else { - return new Zotlabs\Storage\File($path . '/' . $r[0]['filename'], $r[0], $auth); - } - } - return false; -}
\ No newline at end of file diff --git a/include/security.php b/include/security.php index 38045c8a9..c67a1b400 100644 --- a/include/security.php +++ b/include/security.php @@ -12,7 +12,7 @@ * @param bool $return * @param bool $update_lastlog */ -function authenticate_success($user_record, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { +function authenticate_success($user_record, $channel = null, $login_initial = false, $interactive = false, $return = false, $update_lastlog = false) { $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; @@ -23,11 +23,15 @@ function authenticate_success($user_record, $login_initial = false, $interactive $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; + if($channel) + $uid_to_load = $channel['channel_id']; - $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) - ? intval($_SESSION['uid']) - : intval(App::$account['account_default_channel']) - ); + if(! $uid_to_load) { + $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) + ? intval($_SESSION['uid']) + : intval(App::$account['account_default_channel']) + ); + } if($uid_to_load) { change_channel($uid_to_load); @@ -82,6 +86,41 @@ function authenticate_success($user_record, $login_initial = false, $interactive /* else just return */ } +function atoken_login($atoken) { + if(! $atoken) + return false; + $_SESSION['authenticated'] = 1; + $_SESSION['visitor_id'] = $atoken['xchan_hash']; + $_SESSION['atoken'] = $atoken['atoken_id']; + + \App::set_observer($atoken); + return true; +} + + +function atoken_xchan($atoken) { + + $c = channelx_by_n($atoken['atoken_uid']); + if($c) { + return [ + 'atoken_id' => $atoken['atoken_id'], + 'xchan_hash' => substr($c['channel_hash'],0,16) . '.' . $atoken['atoken_name'], + 'xchan_name' => $atoken['atoken_name'], + 'xchan_addr' => t('guest:') . $atoken['atoken_name'] . '@' . \App::get_hostname(), + 'xchan_network' => 'unknown', + 'xchan_hidden' => 1, + 'xchan_photo_mimetype' => 'image/jpeg', + 'xchan_photo_l' => get_default_profile_photo(300), + 'xchan_photo_m' => get_default_profile_photo(80), + 'xchan_photo_s' => get_default_profile_photo(48) + + ]; + } + return null; +} + + + /** * @brief Change to another channel with current logged-in account. * @@ -89,6 +128,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive * * @return bool|array false or channel record of the new channel */ + function change_channel($change_channel) { $ret = false; @@ -125,13 +165,17 @@ function change_channel($change_channel) { ); if($x) { $_SESSION['my_url'] = $x[0]['xchan_url']; - $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . substr(z_root(), strpos(z_root(), '://') + 3); + $_SESSION['my_address'] = $r[0]['channel_address'] . '@' . App::get_hostname(); App::set_observer($x[0]); App::set_perms(get_all_perms(local_channel(), $hash)); } if(! is_dir('store/' . $r[0]['channel_address'])) @os_mkdir('store/' . $r[0]['channel_address'], STORAGE_DEFAULT_PERMISSIONS,true); + + $arr = [ 'channel_id' => $change_channel, 'chanx' => $ret ]; + call_hooks('change_channel', $arr); + } return $ret; @@ -434,14 +478,19 @@ function stream_perms_api_uids($perms = NULL, $limit = 0, $rand = 0 ) { $random_sql = (($rand) ? " ORDER BY " . db_getfunc('RAND') . " " : ''); if(local_channel()) $ret[] = local_channel(); - $r = q("select channel_id from channel where channel_r_stream > 0 and ( channel_r_stream & %d )>0 and ( channel_pageflags & %d ) = 0 and channel_system = 0 and channel_removed = 0 $random_sql $limit_sql ", - intval($perms), - intval(PAGE_ADULT|PAGE_CENSORED) + $x = q("select uid from pconfig where cat = 'perm_limits' and k = 'view_stream' and ( v & %d ) > 0 ", + intval($perms) ); - if($r) { - foreach($r as $rr) - if(! in_array($rr['channel_id'], $ret)) - $ret[] = $rr['channel_id']; + if($x) { + $ids = ids_to_querystr($x,'uid'); + $r = q("select channel_id from channel where channel_id in ( $ids ) and ( channel_pageflags & %d ) = 0 and channel_system = 0 and channel_removed = 0 $random_sql $limit_sql ", + intval(PAGE_ADULT|PAGE_CENSORED) + ); + if($r) { + foreach($r as $rr) + if(! in_array($rr['channel_id'], $ret)) + $ret[] = $rr['channel_id']; + } } $str = ''; @@ -467,16 +516,21 @@ function stream_perms_xchans($perms = NULL ) { if(local_channel()) $ret[] = get_observer_hash(); - $r = q("select channel_hash from channel where channel_r_stream > 0 and (channel_r_stream & %d)>0 and not (channel_pageflags & %d)>0 and channel_system = 0 and channel_removed = 0 ", - intval($perms), - intval(PAGE_ADULT|PAGE_CENSORED) + $x = q("select uid from pconfig where cat = 'perm_limits' and k = 'view_stream' and ( v & %d ) > 0 ", + intval($perms) ); - if($r) { - foreach($r as $rr) - if(! in_array($rr['channel_hash'], $ret)) - $ret[] = $rr['channel_hash']; - } + if($x) { + $ids = ids_to_querystr($x,'uid'); + $r = q("select channel_hash from channel where channel_id in ( $ids ) and ( channel_pageflags & %d ) = 0 and channel_system = 0 and channel_removed = 0 ", + intval(PAGE_ADULT|PAGE_CENSORED) + ); + if($r) { + foreach($r as $rr) + if(! in_array($rr['channel_hash'], $ret)) + $ret[] = $rr['channel_hash']; + } + } $str = ''; if($ret) { foreach($ret as $rr) { diff --git a/include/profile_selectors.php b/include/selectors.php index 9f993f803..d7d070d31 100644 --- a/include/profile_selectors.php +++ b/include/selectors.php @@ -1,6 +1,49 @@ <?php /** @file */ +function contact_profile_assign($current) { + + $o = ''; + + $o .= "<select id=\"contact-profile-selector\" name=\"profile_assign\" class=\"form-control\"/>\r\n"; + + $r = q("SELECT profile_guid, profile_name FROM `profile` WHERE `uid` = %d", + intval($_SESSION['uid'])); + + if($r) { + foreach($r as $rr) { + $selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : ""); + $o .= "<option value=\"{$rr['profile_guid']}\" $selected >{$rr['profile_name']}</option>\r\n"; + } + } + $o .= "</select>\r\n"; + return $o; +} + +function contact_poll_interval($current, $disabled = false) { + + $dis = (($disabled) ? ' disabled="disabled" ' : ''); + $o = ''; + $o .= "<select id=\"contact-poll-interval\" name=\"poll\" $dis />" . "\r\n"; + + $rep = array( + 0 => t('Frequently'), + 1 => t('Hourly'), + 2 => t('Twice daily'), + 3 => t('Daily'), + 4 => t('Weekly'), + 5 => t('Monthly') + ); + + foreach($rep as $k => $v) { + $selected = (($k == $current) ? " selected=\"selected\" " : ""); + $o .= "<option value=\"$k\" $selected >$v</option>\r\n"; + } + $o .= "</select>\r\n"; + return $o; +} + + function gender_selector($current="",$suffix="") { $o = ''; $select = array('', t('Male'), t('Female'), t('Currently Male'), t('Currently Female'), t('Mostly Male'), t('Mostly Female'), t('Transgender'), t('Intersex'), t('Transsexual'), t('Hermaphrodite'), t('Neuter'), t('Non-specific'), t('Other'), t('Undecided')); @@ -108,3 +151,4 @@ function marital_selector_min($current="",$suffix="") { $o .= '</select>'; return $o; } + diff --git a/include/sharedwithme.php b/include/sharedwithme.php index b01764ad3..b342f51d5 100644 --- a/include/sharedwithme.php +++ b/include/sharedwithme.php @@ -3,7 +3,7 @@ function apply_updates() { //check for updated items and remove them - $x = q("SELECT mid, max(object) AS object FROM item WHERE verb = '%s' AND obj_type = '%s' GROUP BY mid", + $x = q("SELECT mid, max(obj) AS obj FROM item WHERE verb = '%s' AND obj_type = '%s' GROUP BY mid", dbesc(ACTIVITY_UPDATE), dbesc(ACTIVITY_OBJ_FILE) ); @@ -12,7 +12,7 @@ function apply_updates() { foreach($x as $xx) { - $object = json_decode($xx['object'],true); + $object = json_decode($xx['obj'],true); $d_mid = $object['d_mid']; $u_mid = $xx['mid']; diff --git a/include/smarty.php b/include/smarty.php deleted file mode 100755 index 3812c6021..000000000 --- a/include/smarty.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php /** @file */ -require_once 'include/ITemplateEngine.php'; -require_once("library/Smarty/libs/Smarty.class.php"); - - -class FriendicaSmarty extends Smarty { - - public $filename; - - function __construct() { - parent::__construct(); - - $a = get_app(); - $theme = current_theme(); - - // setTemplateDir can be set to an array, which Smarty will parse in order. - // The order is thus very important here - $template_dirs = array('theme' => "view/theme/$theme/tpl/"); - if( x(App::$theme_info,"extends") ) - $template_dirs = $template_dirs + array('extends' => "view/theme/".App::$theme_info["extends"]."/tpl/"); - $template_dirs = $template_dirs + array('base' => 'view/tpl/'); - $this->setTemplateDir($template_dirs); - - $basecompiledir = App::$config['system']['smarty3_folder']; - - $this->setCompileDir($basecompiledir.'/compiled/'); - $this->setConfigDir($basecompiledir.'/config/'); - $this->setCacheDir($basecompiledir.'/cache/'); - - $this->left_delimiter = App::get_template_ldelim('smarty3'); - $this->right_delimiter = App::get_template_rdelim('smarty3'); - - // Don't report errors so verbosely - $this->error_reporting = E_ALL & ~E_NOTICE; - } - - function parsed($template = '') { - if($template) { - return $this->fetch('string:' . $template); - } - return $this->fetch('file:' . $this->filename); - } -} - - - -class FriendicaSmartyEngine implements ITemplateEngine { - static $name ="smarty3"; - - public function __construct(){ - $a = get_app(); - - // Cannot use get_config() here because it is called during installation when there is no DB. - // FIXME: this may leak private information such as system pathnames. - - $basecompiledir = ((array_key_exists('smarty3_folder',App::$config['system'])) ? App::$config['system']['smarty3_folder'] : ''); - if (!$basecompiledir) $basecompiledir = dirname(__dir__) . "/" . TEMPLATE_BUILD_PATH; - if (!is_dir($basecompiledir)) { - echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> does not exist."; killme(); - } - if(!is_writable($basecompiledir)){ - echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> must be writable by webserver."; killme(); - } - App::$config['system']['smarty3_folder'] = $basecompiledir; - } - - // ITemplateEngine interface - public function replace_macros($s, $r) { - $template = ''; - if(gettype($s) === 'string') { - $template = $s; - $s = new FriendicaSmarty(); - } - foreach($r as $key=>$value) { - if($key[0] === '$') { - $key = substr($key, 1); - } - $s->assign($key, $value); - } - return $s->parsed($template); - } - - public function get_markup_template($file, $root=''){ - $template_file = theme_include($file, $root); - if($template_file) { - $template = new FriendicaSmarty(); - $template->filename = $template_file; - - return $template; - } - return ""; - } - - public function get_intltext_template($file, $root='') { - $a = get_app(); - - if(file_exists("view/{App::$language}/$file")) - $template_file = "view/{App::$language}/$file"; - elseif(file_exists("view/en/$file")) - $template_file = "view/en/$file"; - else - $template_file = theme_include($file,$root); - if($template_file) { - $template = new FriendicaSmarty(); - $template->filename = $template_file; - - return $template; - } - return ""; - } - - - -} diff --git a/include/socgraph.php b/include/socgraph.php index 1b1bccf20..4cb5600ec 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -152,11 +152,9 @@ function poco_load($xchan = '', $url = null) { if(($x !== false) && (! count($x))) { if($address) { if($network === 'zot') { - $z = zot_finger($address,null); - if($z['success']) { - $j = json_decode($z['body'],true); - if($j) - import_xchan($j); + $j = Zotlabs\Zot\Finger::run($address,null); + if($j['success']) { + import_xchan($j); } $x = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", dbesc($hash) @@ -404,7 +402,7 @@ function poco($a,$extended = false) { $system_mode = false; - if(intval(get_config('system','block_public')) && (! local_channel()) && (! remote_channel())) { + if(observer_prohibited()) { logger('mod_poco: block_public'); http_status_exit(401); } diff --git a/include/spam.php b/include/spam.php deleted file mode 100644 index 8b158b7ae..000000000 --- a/include/spam.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php /** @file */ - - -function string_splitter($s) { - - if(! $s) - return array(); - - $s = preg_replace('/\pP+/','',$s); - - $x = mb_split("\[|\]|\s",$s); - - $ret = array(); - if($x) { - foreach($x as $y) { - if(mb_strlen($y) > 2) - $ret[] = substr($y,0,64); - } - } - return $ret; -} - - - -function get_words($uid,$list) { - - stringify($list,true); - - $r = q("select * from spam where term in ( " . $list . ") and uid = %d", - intval($uid) - ); - - return $r; -} - diff --git a/include/taxonomy.php b/include/taxonomy.php index 71ed6e91d..067bd3246 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -20,7 +20,7 @@ function file_tag_file_query($table,$s,$type = 'file') { else $termtype = TERM_CATEGORY; - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", intval($termtype), protect_sprintf(dbesc($s)) ); @@ -29,14 +29,14 @@ function file_tag_file_query($table,$s,$type = 'file') { function term_query($table,$s,$type = TERM_UNKNOWN, $type2 = '') { if($type2) { - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type in (%d, %d) and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype in (%d, %d) and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", intval($type), intval($type2), protect_sprintf(dbesc($s)) ); } else { - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", intval($type), protect_sprintf(dbesc($s)) ); @@ -49,7 +49,7 @@ function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { return false; $r = q("select * from term - where uid = %d and oid = %d and otype = %d and type = %d + where uid = %d and oid = %d and otype = %d and ttype = %d and term = '%s' and url = '%s' ", intval($uid), intval($iid), @@ -61,7 +61,7 @@ function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { if($r) return false; - $r = q("insert into term (uid, oid, otype, type, term, url) + $r = q("insert into term (uid, oid, otype, ttype, term, url) values( %d, %d, %d, %d, '%s', '%s') ", intval($uid), intval($iid), @@ -85,7 +85,7 @@ function get_terms_oftype($arr,$type) { foreach($type as $t) foreach($arr as $x) - if($x['type'] == $t) + if($x['ttype'] == $t) $ret[] = $x; return $ret; @@ -93,9 +93,9 @@ function get_terms_oftype($arr,$type) { function format_term_for_display($term) { $s = ''; - if(($term['type'] == TERM_HASHTAG) || ($term['type'] == TERM_COMMUNITYTAG)) + if(($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG)) $s .= '#'; - elseif($term['type'] == TERM_MENTION) + elseif($term['ttype'] == TERM_MENTION) $s .= '@'; else return $s; @@ -142,7 +142,7 @@ function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $re // Fetch tags $r = q("select term, count(term) as total from term left join item on term.oid = item.id - where term.uid = %d and term.type = %d + where term.uid = %d and term.ttype = %d and otype = %d and item_type = %d and item_private = 0 $sql_options $item_normal group by term order by total desc %s", @@ -156,78 +156,66 @@ function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $re if(! $r) return array(); - // Find minimum and maximum log-count. - $tags = array(); - $min = 1e9; - $max = -1e9; - - $x = 0; - foreach($r as $rr) { - $tags[$x][0] = $rr['term']; - $tags[$x][1] = log($rr['total']); - $tags[$x][2] = 0; - $min = min($min,$tags[$x][1]); - $max = max($max,$tags[$x][1]); - $x ++; - } + return Zotlabs\Text\Tagadelic::calc($r); - usort($tags,'tags_sort'); +} - $range = max(.01, $max - $min) * 1.0001; +function dir_tagadelic($count = 0) { - for($x = 0; $x < count($tags); $x ++) { - $tags[$x][2] = 1 + floor(9 * ($tags[$x][1] - $min) / $range); - } + $count = intval($count); + + // Fetch tags + $r = q("select xtag_term as term, count(xtag_term) as total from xtag where xtag_flags = 0 + group by xtag_term order by total desc %s", + ((intval($count)) ? "limit $count" : '') + ); + + if(! $r) + return array(); + + + return Zotlabs\Text\Tagadelic::calc($r); - return $tags; } -function tags_sort($a,$b) { - if(strtolower($a[0]) == strtolower($b[0])) - return 0; +function app_tagblock($link,$count = 0) { + $o = ''; + + $r = app_tagadelic($count); - return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1); + if($r) { + $o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">'; + foreach($r as $rr) { + $o .= '<a href="'.$link .'/' . '?f=&cat=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n"; + } + $o .= '</div></div>'; + } + + return $o; } +function app_tagadelic($count = 0) { -function dir_tagadelic($count = 0) { + if(! local_channel()) + return ''; $count = intval($count); + // Fetch tags - $r = q("select xtag_term, count(xtag_term) as total from xtag where xtag_flags = 0 - group by xtag_term order by total desc %s", + $r = q("select term, count(term) as total from term left join app on term.uid = app_channel where term.uid = %d + and term.otype = %d group by term order by total desc %s", + intval(local_channel()), + intval(TERM_OBJ_APP), ((intval($count)) ? "limit $count" : '') ); if(! $r) return array(); - // Find minimum and maximum log-count. - $tags = array(); - $min = 1e9; - $max = -1e9; - - $x = 0; - foreach($r as $rr) { - $tags[$x][0] = $rr['xtag_term']; - $tags[$x][1] = log($rr['total']); - $tags[$x][2] = 0; - $min = min($min,$tags[$x][1]); - $max = max($max,$tags[$x][1]); - $x ++; - } - - usort($tags,'tags_sort'); - - $range = max(.01, $max - $min) * 1.0001; - - for($x = 0; $x < count($tags); $x ++) { - $tags[$x][2] = 1 + floor(9 * ($tags[$x][1] - $min) / $range); - } + return Zotlabs\Text\Tagadelic::calc($r); - return $tags; } @@ -412,7 +400,7 @@ function get_things($profile_hash,$uid) { if(! $things[$rr['obj_verb']]) $things[$rr['obj_verb']] = array(); - $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like','Likes',count($l),'noun')); + $things[$rr['obj_verb']][] = array('term' => $rr['obj_term'],'url' => $rr['obj_url'],'img' => $rr['obj_imgurl'], 'editurl' => z_root() . '/thing/' . $rr['obj_obj'], 'profile' => $rr['profile_name'],'term_hash' => $rr['obj_obj'], 'likes' => $l,'like_count' => count($l),'like_label' => tt('Like','Likes',count($l),'noun')); } $sorted_things = array(); if($things) { diff --git a/include/template_processor.php b/include/template_processor.php deleted file mode 100755 index d2bf283e3..000000000 --- a/include/template_processor.php +++ /dev/null @@ -1,307 +0,0 @@ -<?php -require_once 'include/ITemplateEngine.php'; - -define ("KEY_NOT_EXISTS", '^R_key_not_Exists^'); - -class Template implements ITemplateEngine { - static $name ="internal"; - - var $r; - var $search; - var $replace; - var $stack = array(); - var $nodes = array(); - var $done = false; - var $d = false; - var $lang = null; - var $debug=false; - - private function _preg_error() { - switch(preg_last_error()) { - case PREG_INTERNAL_ERROR: echo('PREG_INTERNAL_ERROR'); break; - case PREG_BACKTRACK_LIMIT_ERROR: echo('PREG_BACKTRACK_LIMIT_ERROR'); break; - case PREG_RECURSION_LIMIT_ERROR: echo('PREG_RECURSION_LIMIT_ERROR'); break; - case PREG_BAD_UTF8_ERROR: echo('PREG_BAD_UTF8_ERROR'); break; -// This is only valid for php > 5.3, not certain how to code around it for unit tests -// case PREG_BAD_UTF8_OFFSET_ERROR: echo('PREG_BAD_UTF8_OFFSET_ERROR'); break; - default: - //die("Unknown preg error."); - return; - } - echo "<hr><pre>"; - debug_print_backtrace(); - die(); - } - - private function _push_stack() { - $this->stack[] = array($this->r, $this->nodes); - } - - private function _pop_stack(){ - list($this->r, $this->nodes) = array_pop($this->stack); - } - - private function _get_var($name, $retNoKey=false) { - $keys = array_map('trim',explode(".",$name)); - if ($retNoKey && !array_key_exists($keys[0], $this->r)) - return KEY_NOT_EXISTS; - - $val = $this->r; - foreach($keys as $k) { - $val = (isset($val[$k]) ? $val[$k] : null); - } - - return template_escape($val); - } - - /** - * IF node - * \code - * {{ if <$var> }}...[{{ else }} ...] {{ endif }} - * {{ if <$var>==<val|$var> }}...[{{ else }} ...]{{ endif }} - * {{ if <$var>!=<val|$var> }}...[{{ else }} ...]{{ endif }} - * \endcode - */ - private function _replcb_if($args) { - if (strpos($args[2],"==")>0){ - list($a,$b) = array_map("trim",explode("==",$args[2])); - $a = $this->_get_var($a); - if ($b[0]=="$") $b = $this->_get_var($b); - $val = ($a == $b); - } else if (strpos($args[2],"!=")>0){ - list($a,$b) = array_map("trim", explode("!=",$args[2])); - $a = $this->_get_var($a); - if ($b[0]=="$") $b = $this->_get_var($b); - $val = ($a != $b); - } else { - $val = $this->_get_var($args[2]); - } - $x = preg_split("|{{ *else *}}|", $args[3]); - - return ( ($val) ? $x[0] : (isset($x[1]) ? $x[1] : "")); - } - - /** - * FOR node - * \code - * {{ for <$var> as $name }}...{{ endfor }} - * {{ for <$var> as $key=>$name }}...{{ endfor }} - * \endcode - */ - private function _replcb_for($args) { - $m = array_map('trim', explode(" as ", $args[2])); - $x = explode("=>",$m[1]); - if (count($x) == 1) { - $varname = $x[0]; - $keyname = ""; - } else { - list($keyname, $varname) = $x; - } - if ($m[0]=="" || $varname=="" || is_null($varname)) die("template error: 'for ".$m[0]." as ".$varname."'") ; - //$vals = $this->r[$m[0]]; - $vals = $this->_get_var($m[0]); - $ret=""; - if (!is_array($vals)) return $ret; - - foreach ($vals as $k=>$v){ - $this->_push_stack(); - $r = $this->r; - $r[$varname] = $v; - if ($keyname!='') $r[$keyname] = (($k === 0) ? '0' : $k); - $ret .= $this->replace($args[3], $r); - $this->_pop_stack(); - } - - return $ret; - } - - /** - * INC node - * \code - * {{ inc <templatefile> [with $var1=$var2] }}{{ endinc }} - * \endcode - */ - private function _replcb_inc($args) { - if (strpos($args[2],"with")) { - list($tplfile, $newctx) = array_map('trim', explode("with",$args[2])); - } else { - $tplfile = trim($args[2]); - $newctx = null; - } - - if ($tplfile[0]=="$") $tplfile = $this->_get_var($tplfile); - - $this->_push_stack(); - $r = $this->r; - if (!is_null($newctx)) { - list($a,$b) = array_map('trim', explode("=",$newctx)); - $r[$a] = $this->_get_var($b); - } - $this->nodes = Array(); - $tpl = get_markup_template($tplfile); - $ret = $this->replace($tpl, $r); - $this->_pop_stack(); - - return $ret; - } - - /** - * DEBUG node - * \code - * {{ debug $var [$var [$var [...]]] }}{{ enddebug }} - * \endcode - * replace node with <pre>var_dump($var, $var, ...);</pre> - */ - private function _replcb_debug($args) { - $vars = array_map('trim', explode(" ",$args[2])); - $vars[] = $args[1]; - - $ret = "<pre>"; - foreach ($vars as $var){ - $ret .= htmlspecialchars(var_export( $this->_get_var($var), true )); - $ret .= "\n"; - } - $ret .= "</pre>"; - - return $ret; - } - - private function _replcb_node($m) { - $node = $this->nodes[$m[1]]; - if (method_exists($this, "_replcb_".$node[1])){ - $s = call_user_func(array($this, "_replcb_".$node[1]), $node); - } else { - $s = ""; - } - $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); - - return $s; - } - - private function _replcb($m) { - //var_dump(array_map('htmlspecialchars', $m)); - $this->done = false; - $this->nodes[] = (array) $m; - - return "||". (count($this->nodes)-1) ."||"; - } - - private function _build_nodes($s) { - $this->done = false; - while (!$this->done) { - $this->done=true; - $s = preg_replace_callback('|{{ *([a-z]*) *([^}]*)}}([^{]*({{ *else *}}[^{]*)?){{ *end\1 *}}|', array($this, "_replcb"), $s); - if ($s==Null) $this->_preg_error(); - } - //({{ *else *}}[^{]*)? - krsort($this->nodes); - - return $s; - } - - private function var_replace($s) { - $m = array(); - /** regexp: - * \$ literal $ - * (\[)? optional open square bracket - * ([a-zA-Z0-9-_]+\.?)+ var name, followed by optional - * dot, repeated at least 1 time - * (?(1)\]) if there was opened square bracket - * (subgrup 1), match close bracket - */ - if (preg_match_all('/\$(\[)?([a-zA-Z0-9-_]+\.?)+(?(1)\])/', $s,$m)) { - foreach ($m[0] as $var) { - $exp = str_replace(array("[", "]"), array("", ""), $var); - $exptks = explode("|", $exp); - - $varn = $exptks[0]; - unset($exptks[0]); - $val = $this->_get_var($varn, true); - if ($val != KEY_NOT_EXISTS) { - /* run filters */ - /* - * Filter are in form of: - * filtername:arg:arg:arg - * - * "filtername" is function name - * "arg"s are optional, var value is appended to the end - * if one "arg"==='x' , is replaced with var value - * - * examples: - * $item.body|htmlspecialchars // escape html chars - * $item.body|htmlspecialchars|strtoupper // escape html and uppercase result - * $item.created|date:%Y %M %j // format date (created is a timestamp) - * $item.body|str_replace:cat:dog // replace all "cat" with "dog" - * $item.body|str_replace:cat:dog:x:1 // replace one "cat" with "dog" - */ - foreach ($exptks as $filterstr) { - $filter = explode(":", $filterstr); - $filtername = $filter[0]; - unset($filter[0]); - $valkey = array_search("x", $filter); - if ($valkey === false) { - $filter[] = $val; - } else { - $filter[$valkey] = $val; - } - if (function_exists($filtername)) { - $val = call_user_func_array($filtername, $filter); - } - } - $s = str_replace($var, $val, $s); - } - } - } - - return $s; - } - - private function replace($s, $r) { - $this->replace_macros($s, $r); - } - - // TemplateEngine interface - public function replace_macros($s, $r) { - $this->r = $r; - - $s = $this->_build_nodes($s); - - $s = preg_replace_callback('/\|\|([0-9]+)\|\|/', array($this, "_replcb_node"), $s); - if ($s == Null) - $this->_preg_error(); - - // remove comments block - $s = preg_replace('/{#[^#]*#}/', "" , $s); - - //$t2 = dba_timer(); - - // replace strings recursively (limit to 10 loops) - $os = ""; - $count=0; - while (($os !== $s) && $count<10) { - $os=$s; - $count++; - $s = $this->var_replace($s); - } - - return $s; - } - - public function get_markup_template($file, $root='') { - $template_file = theme_include($file, $root); - if ($template_file) { - $content = file_get_contents($template_file); - } - - return $content; - } -} - - -function template_escape($s) { - return str_replace(array('$','{{'),array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'),$s); -} - -function template_unescape($s) { - return str_replace(array('!_Doll^Ars1Az_!','!_DoubLe^BraceS4Rw_!'),array('$','{{'),$s); -} diff --git a/include/text.php b/include/text.php index 0a7f84b01..dd7d9eaa8 100644 --- a/include/text.php +++ b/include/text.php @@ -3,8 +3,6 @@ * @file include/text.php */ -require_once("include/template_processor.php"); -require_once("include/smarty.php"); require_once("include/bbcode.php"); // random string, there are 86 characters max in text mode, 128 for hex @@ -16,13 +14,12 @@ define('RANDOM_STRING_TEXT', 0x01 ); /** * @brief This is our template processor. * - * @param string|FriendicaSmarty $s the string requiring macro substitution, - * or an instance of FriendicaSmarty + * @param string|SmartyEngine $s the string requiring macro substitution, + * or an instance of SmartyEngine * @param array $r key value pairs (search => replace) * @return string substituted string */ function replace_macros($s, $r) { - $a = get_app(); $arr = array('template' => $s, 'params' => $r); call_hooks('replace_macros', $arr); @@ -98,7 +95,6 @@ function z_input_filter($channel_id,$s,$type = 'text/bbcode') { if($type == 'application/x-pdl') return escape_tags($s); - $a = get_app(); if(App::$is_sys) { return $s; } @@ -122,7 +118,7 @@ function z_input_filter($channel_id,$s,$type = 'text/bbcode') { -function purify_html($s) { +function purify_html($s, $allow_position = false) { require_once('library/HTMLPurifier.auto.php'); require_once('include/html2bbcode.php'); @@ -202,6 +198,35 @@ function purify_html($s) { $def->addElement('header', 'Block', 'Flow', 'Common'); $def->addElement('footer', 'Block', 'Flow', 'Common'); + + if($allow_position) { + $cssDefinition = $config->getCSSDefinition(); + + $cssDefinition->info['position'] = new HTMLPurifier_AttrDef_Enum(array('absolute', 'fixed', 'relative', 'static', 'inherit'), false); + + $cssDefinition->info['left'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + $cssDefinition->info['right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + $cssDefinition->info['top'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + $cssDefinition->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + )); + + } + + $purifier = new HTMLPurifier($config); return $purifier->purify($s); @@ -297,6 +322,15 @@ function autoname($len) { function xmlify($str) { $buffer = ''; + if(is_array($str)) { + + // allow to fall through so we ge a PHP error, as the log statement will + // probably get lost in the noise unless we're specifically looking for it. + + btlogger('xmlify called with array: ' . print_r($str,true), LOGGER_NORMAL, LOG_WARNING); + } + + $len = mb_strlen($str); for($x = 0; $x < $len; $x ++) { $char = mb_substr($str,$x,1); @@ -342,30 +376,6 @@ function unxmlify($s) { return $ret; } -/** - * Convenience wrapper, reverse the operation "bin2hex" - * This is a built-in function in php >= 5.4 - * - * @FIXME We already have php >= 5.4 requirements, so can we remove this? - */ -if(! function_exists('hex2bin')) { -function hex2bin($s) { - if(! (is_string($s) && strlen($s))) - return ''; - - if(strlen($s) & 1) { - logger('hex2bin: illegal hex string: ' . $s); - return $s; - } - - if(! ctype_xdigit($s)) { - return($s); - } - - return(pack("H*",$s)); -}} - - // Automatic pagination. // To use, get the count of total items. // Then call App::set_pager_total($number_items); @@ -540,21 +550,25 @@ function attribute_contains($attr, $s) { */ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { - // turn off logger in install mode - global $a; - global $db; - if((App::$module == 'install') || (! ($db && $db->connected))) - return; - - $debugging = get_config('system', 'debugging'); - $loglevel = intval(get_config('system', 'loglevel')); - $logfile = get_config('system', 'logfile'); + if(App::$module == 'setup' && is_writable('install.log')) { + $debugging = true; + $logfile = 'install.log'; + $loglevel = LOGGER_ALL; + } + else { + $debugging = get_config('system', 'debugging'); + $loglevel = intval(get_config('system', 'loglevel')); + $logfile = get_config('system', 'logfile'); + } if((! $debugging) || (! $logfile) || ($level > $loglevel)) return; $where = ''; + + // We require > 5.4 but leave the version check so that install issues (including version) can be logged + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; @@ -563,7 +577,8 @@ function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { $s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); - call_hooks('logger',$pluginfo); + if(! (App::$module == 'setup')) + call_hooks('logger',$pluginfo); if(! $pluginfo['logged']) @file_put_contents($pluginfo['filename'], $pluginfo['message'], FILE_APPEND); @@ -621,11 +636,10 @@ function log_priority_str($priority) { * @param int $level A log level. */ function dlogger($msg, $level = 0) { + // turn off logger in install mode - global $a; - global $db; - if((App::$module == 'install') || (! ($db && $db->connected))) + if(App::$module == 'setup') return; $debugging = get_config('system','debugging'); @@ -673,7 +687,7 @@ function get_tags($s) { // ignore anything in a code block - $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s); + $s = preg_replace('/\[code(.*?)\](.*?)\[\/code\]/sm','',$s); // ignore anything in [style= ] $s = preg_replace('/\[style=(.*?)\]/sm','',$s); @@ -696,6 +710,10 @@ function get_tags($s) { // '=' needs to be avoided because when the replacement is made (in handle_tag()) it has to be ignored there // Feel free to allow '=' if the issue with '=' is solved in handle_tag() // added / ? and [ to avoid issues with hashchars in url paths + + // added ; to single word tags to allow emojis and other unicode character constructs in bbcode + // (this would actually be &#xnnnnn; but the ampersand will have been escaped to & by the time we see it.) + if(preg_match_all('/(?<![a-zA-Z0-9=\/\?])(@[^ \x0D\x0A,:?\[]+ [^ \x0D\x0A@,:?\[]+)/',$s,$match)) { foreach($match[1] as $mtch) { if(substr($mtch,-1,1) === '.') @@ -708,7 +726,7 @@ function get_tags($s) { // Otherwise pull out single word tags. These can be @nickname, @first_last // and #hash tags. - if(preg_match_all('/(?<![a-zA-Z0-9=\/\?])([@#][^ \x0D\x0A,;:?\[]+)/',$s,$match)) { + if(preg_match_all('/(?<![a-zA-Z0-9=\/\?\;])([@#][^ \x0D\x0A,;:?\[]+)/',$s,$match)) { foreach($match[1] as $mtch) { if(substr($mtch,-1,1) === '.') $mtch = substr($mtch,0,-1); @@ -756,6 +774,10 @@ function strip_zids($s) { return preg_replace('/[\?&]zid=(.*?)(&|$)/ism','$2',$s); } +function strip_zats($s) { + return preg_replace('/[\?&]zat=(.*?)(&|$)/ism','$2',$s); +} + // quick and dirty quoted_printable encoding @@ -772,7 +794,7 @@ function get_mentions($item,$tags) { return $o; foreach($tags as $x) { - if($x['type'] == TERM_MENTION) { + if($x['ttype'] == TERM_MENTION) { $o .= "\t\t" . '<link rel="mentioned" href="' . $x['url'] . '" />' . "\r\n"; $o .= "\t\t" . '<link rel="ostatus:attention" href="' . $x['url'] . '" />' . "\r\n"; } @@ -783,7 +805,6 @@ function get_mentions($item,$tags) { function contact_block() { $o = ''; - $a = get_app(); if(! App::$profile['uid']) return; @@ -896,7 +917,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { function search($s,$id='search-box',$url='/search',$save = false) { - $a = get_app(); + return replace_macros(get_markup_template('searchbox.tpl'),array( '$s' => $s, '$id' => $id, @@ -1041,7 +1062,7 @@ function get_mood_verbs() { // Function to list all smilies, both internal and from addons // Returns array with keys 'texts' and 'icons' function list_smilies() { - $a = get_app(); + $texts = array( '<3', '</3', @@ -1075,9 +1096,7 @@ function list_smilies() { ':facepalm', ':like', ':dislike', - 'red#matrix', - 'red#', - 'r#' + ':hubzilla' ); $icons = array( @@ -1113,12 +1132,24 @@ function list_smilies() { '<img class="smiley" src="' . z_root() . '/images/smiley-facepalm.gif" alt=":facepalm" />', '<img class="smiley" src="' . z_root() . '/images/like.gif" alt=":like" />', '<img class="smiley" src="' . z_root() . '/images/dislike.gif" alt=":dislike" />', - '<a href="http://getzot.com"><strong>red<img class="smiley bb_rm-logo" src="' . z_root() . '/images/rm-32.png" alt="' . urlencode('red#matrix') . '" />matrix</strong></a>', - '<a href="http://getzot.com"><strong>red<img class="smiley bb_rm-logo" src="' . z_root() . '/images/rm-32.png" alt="' . urlencode('red#') . '" />matrix</strong></a>', - '<a href="http://getzot.com"><strong>red<img class="smiley bb_rm-logo" src="' . z_root() . '/images/rm-32.png" alt="r#" />matrix</strong></a>' + '<img class="smiley" src="' . z_root() . '/images/hz-16.png" alt=":hubzilla" />', ); + $x = get_config('feature','emoji'); + if($x === false) + $x = 1; + if($x) { + if(! App::$emojitab) + App::$emojitab = json_decode(file_get_contents('library/emoji.json'),true); + foreach(App::$emojitab as $e) { + if(strpos($e['shortname'],':tone') === 0) + continue; + $texts[] = $e['shortname']; + $icons[] = '<img class="smiley emoji" height="16" width="16" src="images/emoji/' . $e['unicode'] . '.png' . '" alt="' . $e['name'] . '" />'; + } + } + $params = array('texts' => $texts, 'icons' => $icons); call_hooks('smilie', $params); @@ -1186,7 +1217,7 @@ function smile_unshield($m) { * @param array $x */ function preg_heart($x) { - $a = get_app(); + if (strlen($x[1]) == 1) return $x[0]; @@ -1232,7 +1263,7 @@ function normalise_link($url) { * is https and the other isn't, or if one is www.something and the other * isn't - and also ignore case differences. * - * @see normalis_link() + * @see normalise_link() * * @param string $a * @param string $b @@ -1253,9 +1284,9 @@ function unobscure(&$item) { if(array_key_exists('item_obscured',$item) && intval($item['item_obscured'])) { $key = get_config('system','prvkey'); if($item['title']) - $item['title'] = crypto_unencapsulate(json_decode_plus($item['title']),$key); + $item['title'] = crypto_unencapsulate(json_decode($item['title'],true),$key); if($item['body']) - $item['body'] = crypto_unencapsulate(json_decode_plus($item['body']),$key); + $item['body'] = crypto_unencapsulate(json_decode($item['body'],true),$key); if(get_config('system','item_cache')) { q("update item set title = '%s', body = '%s', item_obscured = 0 where id = %d", dbesc($item['title']), @@ -1278,7 +1309,7 @@ function unobscure_mail(&$item) { function theme_attachments(&$item) { - $arr = json_decode_plus($item['attach']); + $arr = json_decode($item['attach'],true); if(is_array($arr) && count($arr)) { $attaches = array(); foreach($arr as $r) { @@ -1292,7 +1323,7 @@ function theme_attachments(&$item) { $title = t('Size') . ' ' . (($r['length']) ? userReadableSize($r['length']) : t('unknown')); - require_once('include/identity.php'); + require_once('include/channel.php'); if(is_foreigner($item['author_xchan'])) $url = $r['href']; else @@ -1426,40 +1457,8 @@ function generate_named_map($location) { return (($arr['html']) ? $arr['html'] : $location); } -function format_event($jobject) { - $event = array(); - - $object = json_decode($jobject,true); - - //ensure compatibility with older items - this check can be removed at a later point - if(array_key_exists('description', $object)) { - - $bd_format = t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8:01 AM - - $event['header'] = replace_macros(get_markup_template('event_item_header.tpl'),array( - '$title' => bbcode($object['title']), - '$dtstart_label' => t('Starts:'), - '$dtstart_title' => datetime_convert('UTC', 'UTC', $object['start'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), - '$dtstart_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['start'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['start'] , $bd_format))), - '$finish' => (($object['nofinish']) ? false : true), - '$dtend_label' => t('Finishes:'), - '$dtend_title' => datetime_convert('UTC','UTC',$object['finish'], (($object['adjust']) ? ATOM_TIME : 'Y-m-d\TH:i:s' )), - '$dtend_dt' => (($object['adjust']) ? day_translate(datetime_convert('UTC', date_default_timezone_get(), $object['finish'] , $bd_format )) : day_translate(datetime_convert('UTC', 'UTC', $object['finish'] , $bd_format ))) - )); - - $event['content'] = replace_macros(get_markup_template('event_item_content.tpl'),array( - '$description' => bbcode($object['description']), - '$location_label' => t('Location:'), - '$location' => bbcode($object['location']) - )); - - } - - return $event; -} function prepare_body(&$item,$attach = false) { - require_once('include/identity.php'); call_hooks('prepare_body_init', $item); @@ -1469,7 +1468,7 @@ function prepare_body(&$item,$attach = false) { if($is_photo) { - $object = json_decode($item['object'],true); + $object = json_decode($item['obj'],true); // if original photo width is <= 640px prepend it to item body if($object['link'][0]['width'] && $object['link'][0]['width'] <= 640) { @@ -1485,7 +1484,7 @@ function prepare_body(&$item,$attach = false) { $s .= prepare_text($item['body'],$item['mimetype'], false); - $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event($item['object']) : false); + $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); $prep_arr = array( 'item' => $item, @@ -1532,35 +1531,6 @@ function prepare_body(&$item,$attach = false) { $s = sslify($s); - // Look for spoiler - $spoilersearch = '<blockquote class="spoiler">'; - - // Remove line breaks before the spoiler - while ((strpos($s, "\n".$spoilersearch) !== false)) - $s = str_replace("\n".$spoilersearch, $spoilersearch, $s); - while ((strpos($s, "<br />".$spoilersearch) !== false)) - $s = str_replace("<br />".$spoilersearch, $spoilersearch, $s); - - while ((strpos($s, $spoilersearch) !== false)) { - - $pos = strpos($s, $spoilersearch); - $rnd = random_string(8); - $spoilerreplace = '<br /> <span id="spoiler-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'spoiler-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'. - '<blockquote class="spoiler" id="spoiler-'.$rnd.'" style="display: none;">'; - $s = substr($s, 0, $pos).$spoilerreplace.substr($s, $pos+strlen($spoilersearch)); - } - - // Look for quote with author - $authorsearch = '<blockquote class="author">'; - - while ((strpos($s, $authorsearch) !== false)) { - $pos = strpos($s, $authorsearch); - $rnd = random_string(8); - $authorreplace = '<br /> <span id="author-wrap-'.$rnd.'" style="white-space:nowrap;" class="fakelink" onclick="openClose(\'author-'.$rnd.'\');">'.sprintf(t('Click to open/close')).'</span>'. - '<blockquote class="author" id="author-'.$rnd.'" style="display: block;">'; - $s = substr($s, 0, $pos).$authorreplace.substr($s, $pos+strlen($authorsearch)); - } - $prep_arr = array( 'item' => $item, 'photo' => $photo, @@ -1645,7 +1615,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $cache = false) { function create_export_photo_body(&$item) { if(($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) { - $j = json_decode($item['object'],true); + $j = json_decode($item['obj'],true); if($j) { $item['body'] .= "\n\n" . (($j['body']) ? $j['body'] : $j['bbcode']); $item['sig'] = ''; @@ -1718,7 +1688,6 @@ function feed_hublinks() { /* return atom link elements for salmon endpoints */ function feed_salmonlinks($nick) { - $a = get_app(); $salmon = '<link rel="salmon" href="' . xmlify(z_root() . '/salmon/' . $nick) . '" />' . "\n" ; @@ -1754,7 +1723,8 @@ function unamp($s) { } function layout_select($channel_id, $current = '') { - $r = q("select mid,sid from item left join item_id on iid = item.id where service = 'PDL' and item.uid = item_id.uid and item_id.uid = %d and item_type = %d ", + $r = q("select mid, v from item left join iconfig on iconfig.iid = item.id + where iconfig.cat = 'system' and iconfig.k = 'PDL' and item.uid = %d and item_type = %d ", intval($channel_id), intval(ITEM_TYPE_PDL) ); @@ -1764,7 +1734,7 @@ function layout_select($channel_id, $current = '') { $options .= '<option value="" ' . $empty_selected . '>' . t('default') . '</option>'; foreach($r as $rr) { $selected = (($rr['mid'] == $current) ? ' selected="selected" ' : ''); - $options .= '<option value="' . $rr['mid'] . '"' . $selected . '>' . $rr['sid'] . '</option>'; + $options .= '<option value="' . $rr['mid'] . '"' . $selected . '>' . $rr['v'] . '</option>'; } } @@ -1786,7 +1756,7 @@ function mimetype_select($channel_id, $current = 'text/bbcode') { 'application/x-pdl' ); - $a = get_app(); + if(App::$is_sys) { $x[] = 'application/x-php'; } @@ -1819,7 +1789,6 @@ function mimetype_select($channel_id, $current = 'text/bbcode') { function lang_selector() { - global $a; $langs = glob('view/*/hstrings.php'); @@ -1854,8 +1823,10 @@ function lang_selector() { } -function return_bytes ($size_str) { - switch (substr ($size_str, -1)) { +function engr_units_to_bytes ($size_str) { + if(! $size_str) + return $size_str; + switch (substr(trim($size_str), -1)) { case 'M': case 'm': return (int)$size_str * 1048576; case 'K': case 'k': return (int)$size_str * 1024; case 'G': case 'g': return (int)$size_str * 1073741824; @@ -1893,32 +1864,15 @@ function cleardiv() { function bb_translate_video($s) { - - $matches = null; - $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER); - if($r) { - foreach($matches as $mtch) { - if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be'))) - $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s); - elseif(stristr($mtch[1],'vimeo')) - $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s); - } - } - return $s; + $arr = array('string' => $s); + call_hooks('bb_translate_video',$arr); + return $arr['string']; } function html2bb_video($s) { - - $s = preg_replace('#<object[^>]+>(.*?)https?://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism', - '[youtube]$2[/youtube]', $s); - - $s = preg_replace('#<iframe[^>](.*?)https?://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism', - '[youtube]$2[/youtube]', $s); - - $s = preg_replace('#<iframe[^>](.*?)https?://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism', - '[vimeo]$2[/vimeo]', $s); - - return $s; + $arr = array('string' => $s); + call_hooks('html2bb_video',$arr); + return $arr['string']; } /** @@ -2040,7 +1994,7 @@ function check_webbie($arr) { if(strlen($reservechan)) $taken = explode(',', $reservechan); else - $taken = array(); + $taken = array('principals','addressbooks','calendars'); $str = ''; if(count($arr)) { @@ -2072,6 +2026,20 @@ function check_webbie($arr) { return ''; } +function ids_to_array($arr,$idx = 'id') { + $t = array(); + if($arr) { + foreach($arr as $x) { + if(array_key_exists($idx,$x) && strlen($x[$idx]) && (! in_array($x[$idx],$t))) { + $t[] = $x[$idx]; + } + } + } + return($t); +} + + + function ids_to_querystr($arr,$idx = 'id') { $t = array(); @@ -2101,9 +2069,9 @@ function xchan_query(&$items,$abook = true,$effective_uid = 0) { } foreach($items as $item) { - if($item['owner_xchan'] && (! in_array($item['owner_xchan'],$arr))) + if($item['owner_xchan'] && (! in_array("'" . dbesc($item['owner_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['owner_xchan']) . "'"; - if($item['author_xchan'] && (! in_array($item['author_xchan'],$arr))) + if($item['author_xchan'] && (! in_array("'" . dbesc($item['author_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['author_xchan']) . "'"; } } @@ -2136,9 +2104,9 @@ function xchan_mail_query(&$item) { $arr = array(); $chans = null; if($item) { - if($item['from_xchan'] && (! in_array($item['from_xchan'],$arr))) + if($item['from_xchan'] && (! in_array("'" . dbesc($item['from_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['from_xchan']) . "'"; - if($item['to_xchan'] && (! in_array($item['to_xchan'],$arr))) + if($item['to_xchan'] && (! in_array("'" . dbesc($item['to_xchan']) . "'",$arr))) $arr[] = "'" . dbesc($item['to_xchan']) . "'"; } @@ -2244,27 +2212,19 @@ function jindent($json) { return $result; } - -function json_decode_plus($s) { - $x = json_decode($s,true); - if(! $x) - $x = json_decode(str_replace(array('\\"','\\\\'),array('"','\\'),$s),true); - - return $x; -} - /** * @brief Creates navigation menu for webpage, layout, blocks, menu sites. * * @return string */ + function design_tools() { $channel = App::get_channel(); $sys = false; if(App::$is_sys && is_site_admin()) { - require_once('include/identity.php'); + require_once('include/channel.php'); $channel = get_sys_channel(); $sys = true; } @@ -2390,7 +2350,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d $str_tags .= $newtag; } - return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $basetag, 'url' => $url, 'contact' => $r[0]); + return [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $basetag, + 'url' => $url, + 'contact' => $r[0] + ]; } //is it a person tag? @@ -2581,7 +2547,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $d } } - return array('replaced' => $replaced, 'termtype' => $termtype, 'term' => $newname, 'url' => $url, 'contact' => $r[0]); + return [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $newname, + 'url' => $url, + 'contact' => $r[0] + ]; } function linkify_tags($a, &$body, $uid, $diaspora = false) { @@ -2630,41 +2602,41 @@ function linkify_tags($a, &$body, $uid, $diaspora = false) { function getIconFromType($type) { $iconMap = array( //Folder - t('Collection') => 'icon-folder-close', - 'multipart/mixed' => 'icon-folder-close', //dirs in attach use this mime type + t('Collection') => 'fa-folder', + 'multipart/mixed' => 'fa-folder', //dirs in attach use this mime type //Common file - 'application/octet-stream' => 'icon-file-alt', + 'application/octet-stream' => 'fa-file-o', //Text - 'text/plain' => 'icon-file-text-alt', - 'application/msword' => 'icon-file-text-alt', - 'application/pdf' => 'icon-file-text-alt', - 'application/vnd.oasis.opendocument.text' => 'icon-file-text-alt', - 'application/epub+zip' => 'icon-book', + 'text/plain' => 'fa-file-text-o', + 'application/msword' => 'fa-file-text-o', + 'application/pdf' => 'fa-file-text-o', + 'application/vnd.oasis.opendocument.text' => 'fa-file-text-o', + 'application/epub+zip' => 'fa-book', //Spreadsheet - 'application/vnd.oasis.opendocument.spreadsheet' => 'icon-table', - 'application/vnd.ms-excel' => 'icon-table', + 'application/vnd.oasis.opendocument.spreadsheet' => 'fa-table', + 'application/vnd.ms-excel' => 'fa-table', //Image - 'image/jpeg' => 'icon-picture', - 'image/png' => 'icon-picture', - 'image/gif' => 'icon-picture', - 'image/svg+xml' => 'icon-picture', + 'image/jpeg' => 'fa-picture-o', + 'image/png' => 'fa-picture-o', + 'image/gif' => 'fa-picture-o', + 'image/svg+xml' => 'fa-picture-o', //Archive - 'application/zip' => 'icon-archive', - 'application/x-rar-compressed' => 'icon-archive', + 'application/zip' => 'fa-archive', + 'application/x-rar-compressed' => 'fa-archive', //Audio - 'audio/mpeg' => 'icon-music', - 'audio/wav' => 'icon-music', - 'application/ogg' => 'icon-music', - 'audio/ogg' => 'icon-music', - 'audio/webm' => 'icon-music', - 'audio/mp4' => 'icon-music', + 'audio/mpeg' => 'fa-music', + 'audio/wav' => 'fa-music', + 'application/ogg' => 'fa-music', + 'audio/ogg' => 'fa-music', + 'audio/webm' => 'fa-music', + 'audio/mp4' => 'fa-music', //Video - 'video/quicktime' => 'icon-film', - 'video/webm' => 'icon-film', - 'video/mp4' => 'icon-film' + 'video/quicktime' => 'fa-film', + 'video/webm' => 'fa-film', + 'video/mp4' => 'fa-film' ); - $iconFromType = 'icon-file-alt'; + $iconFromType = 'fa-file-o'; if (array_key_exists($type, $iconMap)) { $iconFromType = $iconMap[$type]; @@ -2822,3 +2794,125 @@ function expand_acl($s) { return $ret; } + + +// When editing a webpage - a dropdown is needed to select a page layout +// On submit, the pdl_select value (which is the mid of an item with item_type = ITEM_TYPE_PDL) is stored in +// the webpage's resource_id, with resource_type 'pdl'. + +// Then when displaying a webpage, we can see if it has a pdl attached. If not we'll +// use the default site/page layout. + +// If it has a pdl we'll load it as we know the mid and pass the body through comanche_parser() which will generate the +// page layout from the given description + +// @FIXME - there is apparently a very similar function called layout_select; this one should probably take precedence +// and the other should be checked for compatibility and removed + +function pdl_selector($uid, $current="") { + $o = ''; + + $sql_extra = item_permissions_sql($uid); + + $r = q("select iconfig.*, mid from item_id left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' $sql_extra order by v asc", + intval($uid) + ); + + $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); + call_hooks('pdl_selector',$arr); + + $entries = $arr['entries']; + $current = $arr['current']; + + $o .= '<select name="pdl_select" id="pdl_select" size="1">'; + $entries[] = array('title' => t('Default'), 'mid' => ''); + foreach($entries as $selection) { + $selected = (($selection == $current) ? ' selected="selected" ' : ''); + $o .= "<option value=\"{$selection['mid']}\" $selected >{$selection['v']}</option>"; + } + + $o .= '</select>'; + return $o; +} + +/* + * array flatten_array_recursive(array); + * returns a one-dimensional array from a multi-dimensional array + * empty values are discarded + * example: print_r(flatten_array_recursive(array('foo','bar',array('baz','blip',array('zob','glob')),'','grip'))); + * + * Array ( [0] => foo [1] => bar [2] => baz [3] => blip [4] => zob [5] => glob [6] => grip ) + * + */ + +function flatten_array_recursive($arr) { + $ret = array(); + + if(! $arr) + return $ret; + + foreach($arr as $a) { + if(is_array($a)) { + $tmp = flatten_array_recursive($a); + if($tmp) { + $ret = array_merge($ret,$tmp); + } + } + elseif($a) { + $ret[] = $a; + } + } + return($ret); +} + +function text_highlight($s,$lang) { + + if($lang === 'js') + $lang = 'javascript'; + + if($lang === 'json') { + $lang = 'javascript'; + if(! strpos(trim($s),"\n")) + $s = jindent($s); + } + + if(! strpos('Text_Highlighter',get_include_path())) { + set_include_path(get_include_path() . PATH_SEPARATOR . 'library/Text_Highlighter'); + } + require_once('library/Text_Highlighter/Text/Highlighter.php'); + require_once('library/Text_Highlighter/Text/Highlighter/Renderer/Html.php'); + $options = array( + 'numbers' => HL_NUMBERS_LI, + 'tabsize' => 4, + ); + $tag_added = false; + $s = trim(html_entity_decode($s,ENT_COMPAT)); + $s = str_replace(" ","\t",$s); + + // The highlighter library insists on an opening php tag for php code blocks. If + // it isn't present, nothing is highlighted. So we're going to see if it's present. + // If not, we'll add it, and then quietly remove it after we get the processed output back. + + if($lang === 'php') { + if(strpos('<?php',$s) !== 0) { + $s = '<?php' . "\n" . $s; + $tag_added = true; + } + + } + $renderer = new Text_Highlighter_Renderer_HTML($options); + $hl = Text_Highlighter::factory($lang); + $hl->setRenderer($renderer); + $o = $hl->highlight($s); + $o = str_replace([" ","\n"],[" ",''],$o); + + if($tag_added) { + $b = substr($o,0,strpos($o,'<li>')); + $e = substr($o,strpos($o,'</li>')); + $o = $b . $e; + } + + return('<code>' . $o . '</code>'); +} + diff --git a/include/widgets.php b/include/widgets.php index 0355ebd8c..3516e82da 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -8,17 +8,17 @@ require_once('include/dir_fns.php'); require_once('include/contact_widgets.php'); require_once('include/attach.php'); -require_once('include/Contact.php'); + function widget_profile($args) { - $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $block = observer_prohibited(); return profile_sidebar(App::$profile, $block, true); } function widget_zcard($args) { - $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $block = observer_prohibited(); $channel = channelx_by_n(App::$profile_uid); return get_zcard($channel,get_observer_hash(),array('width' => 875)); } @@ -103,8 +103,8 @@ function widget_appselect($arr) { '$system' => t('System'), '$authed' => ((local_channel()) ? true : false), '$personal' => t('Personal'), - '$new' => t('Create Personal App'), - '$edit' => t('Edit Personal App') + '$new' => t('New App'), + '$edit' => t('Edit App') )); } @@ -212,13 +212,13 @@ function widget_savedsearch($arr) { $search = ((x($_GET,'search')) ? $_GET['search'] : ''); if(x($_GET,'searchsave') && $search) { - $r = q("select * from `term` where `uid` = %d and `type` = %d and `term` = '%s' limit 1", + $r = q("select * from `term` where `uid` = %d and `ttype` = %d and `term` = '%s' limit 1", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) ); if(! $r) { - q("insert into `term` ( `uid`,`type`,`term` ) values ( %d, %d, '%s') ", + q("insert into `term` ( `uid`,`ttype`,`term` ) values ( %d, %d, '%s') ", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) @@ -227,7 +227,7 @@ function widget_savedsearch($arr) { } if(x($_GET,'searchremove') && $search) { - q("delete from `term` where `uid` = %d and `type` = %d and `term` = '%s'", + q("delete from `term` where `uid` = %d and `ttype` = %d and `term` = '%s'", intval(local_channel()), intval(TERM_SAVEDSEARCH), dbesc($search) @@ -254,7 +254,7 @@ function widget_savedsearch($arr) { $o = ''; - $r = q("select `tid`,`term` from `term` WHERE `uid` = %d and `type` = %d ", + $r = q("select `tid`,`term` from `term` WHERE `uid` = %d and `ttype` = %d ", intval(local_channel()), intval(TERM_SAVEDSEARCH) ); @@ -296,7 +296,7 @@ function widget_filer($arr) { $selected = ((x($_REQUEST,'file')) ? $_REQUEST['file'] : ''); $terms = array(); - $r = q("select distinct(term) from term where uid = %d and type = %d order by term asc", + $r = q("select distinct term from term where uid = %d and ttype = %d order by term asc", intval(local_channel()), intval(TERM_FILE) ); @@ -369,7 +369,7 @@ function widget_fullprofile($arr) { if(! App::$profile['profile_uid']) return; - $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $block = observer_prohibited(); return profile_sidebar(App::$profile, $block); } @@ -379,7 +379,7 @@ function widget_shortprofile($arr) { if(! App::$profile['profile_uid']) return; - $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $block = observer_prohibited(); return profile_sidebar(App::$profile, $block, true, true); } @@ -400,6 +400,55 @@ function widget_categories($arr) { } +function widget_appcategories($arr) { + + if(! local_channel()) + return ''; + + $cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); + $srchurl = App::$query_string; + $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + + $terms = array(); + + $r = q("select distinct(term.term) + from term join app on term.oid = app.id + where app_channel = %d + and term.uid = app_channel + and term.otype = %d + order by term.term asc", + intval(local_channel()), + intval(TERM_OBJ_APP) + ); + if($r) { + foreach($r as $rr) + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + + return replace_macros(get_markup_template('categories_widget.tpl'),array( + '$title' => t('Categories'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => $srchurl, + + )); + } + + + +} + + + +function widget_appcloud($arr) { + if(! local_channel()) + return ''; + return app_tagblock(z_root() . '/apps'); +} + + function widget_tagcloud_wall($arr) { @@ -560,6 +609,15 @@ function widget_settings_menu($arr) { 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), ); + if(! UNO) { + $tabs[] = array( + 'label' => t('Guest Access Tokens'), + 'url' => z_root() . '/settings/tokens', + 'selected' => ((argv(1) === 'tokens') ? 'active' : ''), + ); + } + + if($role === false || $role === 'custom') { $tabs[] = array( 'label' => t('Connection Default Permissions'), @@ -694,21 +752,6 @@ function widget_conversations($arr) { return $o; } -function widget_eventsmenu($arr) { - if (! local_channel()) - return; - - return replace_macros(get_markup_template('events_menu_side.tpl'), array( - '$title' => t('Events Menu'), - '$day' => t('Day View'), - '$week' => t('Week View'), - '$month' => t('Month View'), - '$export' => t('Export'), - '$upload' => t('Import'), - '$submit' => t('Submit') - )); -} - function widget_eventstools($arr) { if (! local_channel()) return; @@ -722,7 +765,6 @@ function widget_eventstools($arr) { } function widget_design_tools($arr) { - $a = get_app(); // mod menu doesn't load a profile. For any modules which load a profile, check it. // otherwise local_channel() is sufficient for permissions. @@ -751,13 +793,14 @@ function widget_photo_albums($arr) { if((! $channelx) || (! perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_storage'))) return ''; require_once('include/photos.php'); + $sortkey = ((array_key_exists('sortkey',$arr)) ? $arr['sortkey'] : 'album'); + $direction = ((array_key_exists('direction',$arr)) ? $arr['direction'] : 'asc'); - return photos_album_widget($channelx, App::get_observer()); + return photos_album_widget($channelx, App::get_observer(),$sortkey,$direction); } function widget_vcard($arr) { - require_once ('include/Contact.php'); return vcard_from_xchan('', App::get_observer()); } @@ -786,8 +829,7 @@ function widget_menu_preview($arr) { function widget_chatroom_list($arr) { - require_once("include/chat.php"); - $r = chatroom_list(App::$profile['profile_uid']); + $r = Zotlabs\Lib\Chatroom::roomlist(App::$profile['profile_uid']); if($r) { return replace_macros(get_markup_template('chatroomlist.tpl'), array( @@ -808,6 +850,78 @@ function widget_chatroom_members() { return $o; } +function widget_wiki_list($arr) { + + require_once("include/wiki.php"); + $channel = null; + if (argc() < 2 && local_channel()) { + // This should not occur because /wiki should redirect to /wiki/channel ... + $channel = \App::get_channel(); + } else { + $channel = get_channel_by_nick(argv(1)); // Channel being viewed by observer + } + if (!$channel) { + return ''; + } + $wikis = wiki_list($channel, get_observer_hash()); + if ($wikis) { + return replace_macros(get_markup_template('wikilist.tpl'), array( + '$header' => t('Wiki List'), + '$channel' => $channel['channel_address'], + '$wikis' => $wikis['wikis'], + // If the observer is the local channel owner, show the wiki controls + '$showControls' => ((local_channel() === intval($channel['channel_id'])) ? true : false) + )); + } + return ''; +} + +function widget_wiki_pages($arr) { + + require_once("include/wiki.php"); + $channelname = ((array_key_exists('channel',$arr)) ? $arr['channel'] : ''); + $wikiname = ''; + if (array_key_exists('refresh', $arr)) { + $not_refresh = (($arr['refresh']=== true) ? false : true); + } else { + $not_refresh = true; + } + $pages = array(); + if (!array_key_exists('resource_id', $arr)) { + $hide = true; + } else { + $p = wiki_page_list($arr['resource_id']); + if ($p['pages']) { + $pages = $p['pages']; + $w = $p['wiki']; + // Wiki item record is $w['wiki'] + $wikiname = $w['urlName']; + if (!$wikiname) { + $wikiname = ''; + } + } + } + return replace_macros(get_markup_template('wiki_page_list.tpl'), array( + '$hide' => $hide, + '$not_refresh' => $not_refresh, + '$header' => t('Wiki Pages'), + '$channel' => $channelname, + '$wikiname' => $wikiname, + '$pages' => $pages + )); +} + +function widget_wiki_page_history($arr) { + require_once("include/wiki.php"); + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + $pageHistory = wiki_page_history(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + + return replace_macros(get_markup_template('wiki_page_history.tpl'), array( + '$pageHistory' => $pageHistory['history'] + )); +} + function widget_bookmarkedchats($arr) { if(! feature_enabled(App::$profile['profile_uid'],'ajaxchat')) @@ -874,8 +988,9 @@ function widget_item($arr) { $sql_extra = item_permissions_sql($channel_id); if($arr['title']) { - $r = q("select item.* from item left join item_id on item.id = item_id.iid - where item.uid = %d and sid = '%s' and service = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where item.uid = %d and iconfig.cat = 'system' and iconfig.v = '%s' + and iconfig.k = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", intval($channel_id), dbesc($arr['title']), intval(ITEM_TYPE_WEBPAGE) @@ -1003,7 +1118,7 @@ function widget_photo($arr) { function widget_cover_photo($arr) { - require_once('include/identity.php'); + require_once('include/channel.php'); $o = ''; if(App::$module == 'channel' && $_REQUEST['mid']) @@ -1037,7 +1152,7 @@ function widget_cover_photo($arr) { if(array_key_exists('subtitle', $arr) && isset($arr['subtitle'])) $subtitle = $arr['subtitle']; else - $subtitle = $channel['xchan_addr']; + $subtitle = str_replace('@','@',$channel['xchan_addr']); $c = get_cover_photo($channel_id,'html'); @@ -1080,7 +1195,7 @@ function widget_photo_rand($arr) { $filtered = array(); if($ret['success'] && $ret['photos']) foreach($ret['photos'] as $p) - if($p['scale'] == $scale) + if($p['imgscale'] == $scale) $filtered[] = $p['src']; if($filtered) { @@ -1133,8 +1248,8 @@ function widget_random_block($arr) { $randfunc = db_getfunc('RAND'); - $r = q("select item.* from item left join item_id on item.id = item_id.iid - where item.uid = %d and sid like '%s' and service = 'BUILDBLOCK' and + $r = q("select item.* from item left join iconfig on item.id = iconfig.iid + where item.uid = %d and iconfig.cat = 'system' and iconfig.v like '%s' and iconfig.k = 'BUILDBLOCK' and item_type = %d $sql_options order by $randfunc limit 1", intval($channel_id), dbesc('%' . $contains . '%'), @@ -1203,12 +1318,12 @@ function widget_rating($arr) { if((($remote) || (local_channel())) && (! $self)) { if($remote) - $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="icon-pencil"></i> ' . t('Rate Me') . '</a>'; + $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="fa fa-pencil"></i> ' . t('Rate Me') . '</a>'; else - $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="icon-pencil"></i> ' . t('Rate Me') . '</div>'; + $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="fa fa-pencil"></i> ' . t('Rate Me') . '</div>'; } - $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="icon-eye-open"></i> ' . t('View Ratings') . '</a>'; + $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="fa fa-eye"></i> ' . t('View Ratings') . '</a>'; $o .= '</div>'; return $o; @@ -1241,9 +1356,14 @@ function widget_forums($arr) { $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", - intval(PERMS_W_TAGWALL), - intval(PERMS_W_STREAM), + /** + * We used to try and find public forums with custom permissions by checking to see if + * send_stream was false and tag_deliver was true. However with the newer extensible + * permissions infrastructure this makes for a very complicated query. Now we're only + * checking channels that report themselves specifically as pubforums + */ + + $r1 = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_pubforum = 1 and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", intval(local_channel()) ); if(! $r1) @@ -1255,12 +1375,34 @@ function widget_forums($arr) { // There also should be a way to update this via ajax. for($x = 0; $x < count($r1); $x ++) { - $r = q("select sum(item_unseen) as unseen from item where owner_xchan = '%s' and uid = %d $perms_sql ", + $r = q("select sum(item_unseen) as unseen from item where owner_xchan = '%s' and uid = %d and item_unseen = 1 $perms_sql ", dbesc($r1[$x]['xchan_hash']), intval(local_channel()) ); if($r) $r1[$x]['unseen'] = $r[0]['unseen']; + +/** + * @FIXME + * This SQL makes the counts correct when you get forum posts arriving from different routes/sources + * (like personal channels). However the network query for these posts doesn't yet include this + * correction and it makes the SQL for that query pretty hairy so this is left as a future exercise. + * It may make more sense in that query to look for the mention in the body rather than another join, + * but that makes it very inefficient. + * + $r = q("select sum(item_unseen) as unseen from item left join term on oid = id where otype = %d and owner_xchan != '%s' and item.uid = %d and url = '%s' and ttype = %d $perms_sql ", + intval(TERM_OBJ_POST), + dbesc($r1[$x]['xchan_hash']), + intval(local_channel()), + dbesc($r1[$x]['xchan_url']), + intval(TERM_MENTION) + ); + if($r) + $r1[$x]['unseen'] = ((array_key_exists('unseen',$r1[$x])) ? $r1[$x]['unseen'] + $r[0]['unseen'] : $r[0]['unseen']); + * + * end @FIXME + */ + } if($r1) { @@ -1332,7 +1474,7 @@ function widget_admin($arr) { $aside = array( 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), - 'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users', 'pending-update', t('Member registrations waiting for confirmation')), + 'accounts' => array(z_root() . '/admin/accounts/', t('Accounts'), 'accounts', 'pending-update', t('Member registrations waiting for confirmation')), 'channels' => array(z_root() . '/admin/channels/', t('Channels'), 'channels'), 'security' => array(z_root() . '/admin/security/', t('Security'), 'security'), 'features' => array(z_root() . '/admin/features/', t('Features'), 'features'), @@ -1351,7 +1493,7 @@ function widget_admin($arr) { $plugins = array(); if($r) { foreach ($r as $h){ - $plugin = $h['name']; + $plugin = $h['aname']; $plugins[] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin'); // temp plugins with admin App::$plugins_admin[] = $plugin; @@ -1413,9 +1555,9 @@ function widget_album($args) { $order = 'DESC'; - $r = q("SELECT p.resource_id, p.id, p.filename, p.type, p.scale, p.description, p.created FROM photo p INNER JOIN - (SELECT resource_id, max(scale) scale FROM photo WHERE uid = %d AND album = '%s' AND scale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph - ON (p.resource_id = ph.resource_id AND p.scale = ph.scale) + $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN + (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph + ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order ", intval($owner_uid), dbesc($album), @@ -1436,7 +1578,7 @@ function widget_album($args) { else $twist = 'rotright'; - $ext = $phototypes[$rr['type']]; + $ext = $phototypes[$rr['mimetype']]; $imgalt_e = $rr['filename']; $desc_e = $rr['description']; @@ -1449,7 +1591,7 @@ function widget_album($args) { 'twist' => ' ' . $twist . rand(2,4), 'link' => $imagelink, 'title' => t('View Photo'), - 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['scale'] . '.' .$ext, + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' .$ext, 'alt' => $imgalt_e, 'desc'=> $desc_e, 'ext' => $ext, diff --git a/include/wiki.php b/include/wiki.php new file mode 100644 index 000000000..424b2d9a0 --- /dev/null +++ b/include/wiki.php @@ -0,0 +1,571 @@ +<?php +/** + * @file include/wiki.php + * @brief Wiki related functions. + */ + +use \Zotlabs\Storage\GitRepo as GitRepo; +define ( 'WIKI_ITEM_RESOURCE_TYPE', 'wiki' ); + +function wiki_list($channel, $observer_hash) { + $sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash); + $wikis = q("SELECT * FROM item WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra", + dbesc(WIKI_ITEM_RESOURCE_TYPE), + intval($channel['channel_id']) + ); + foreach($wikis as &$w) { + $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); + $w['htmlName'] = get_iconfig($w, 'wiki', 'htmlName'); + $w['urlName'] = get_iconfig($w, 'wiki', 'urlName'); + $w['path'] = get_iconfig($w, 'wiki', 'path'); + } + // TODO: query db for wikis the observer can access. Return with two lists, for read and write access + return array('wikis' => $wikis); +} + +function wiki_page_list($resource_id) { + // TODO: Create item table records for pages so that metadata like title can be applied + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('pages' => null, 'wiki' => null); + } + $pages = array(); + if (is_dir($w['path']) === true) { + $files = array_diff(scandir($w['path']), array('.', '..', '.git')); + // TODO: Check that the files are all text files + + foreach($files as $file) { + // strip the .md file extension and unwrap URL encoding to leave HTML encoded name + $pages[] = array('title' => urldecode(substr($file, 0, -3)), 'url' => urlencode(substr($file, 0, -3))); + } + } + + return array('pages' => $pages, 'wiki' => $w); +} + +function wiki_init_wiki($channel, $wiki) { + // Store the path as a relative path, but pass absolute path to mkdir + $path = 'store/[data]/git/'.$channel['channel_address'].'/wiki/'.$wiki['urlName']; + if (!os_mkdir(__DIR__ . '/../' . $path, 0770, true)) { + logger('Error creating wiki path: ' . $path); + return null; + } + // Create GitRepo object + $git = new GitRepo($channel['channel_address'], null, false, $wiki['urlName'], __DIR__ . '/../' . $path); + if(!$git->initRepo()) { + logger('Error creating new git repo in ' . $git->path); + return null; + } + + return array('path' => $path); +} + +function wiki_create_wiki($channel, $observer_hash, $wiki, $acl) { + $wikiinit = wiki_init_wiki($channel, $wiki); + if (!$wikiinit['path']) { + notice('Error creating wiki'); + return array('item' => null, 'success' => false); + } + $path = $wikiinit['path']; + // Generate unique resource_id using the same method as item_message_id() + do { + $dups = false; + $resource_id = random_string(); + $r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1", + dbesc($resource_id), + dbesc(WIKI_ITEM_RESOURCE_TYPE), + intval($channel['channel_id']) + ); + if (count($r)) + $dups = true; + } while ($dups == true); + $ac = $acl->get(); + $mid = item_message_id(); + $arr = array(); // Initialize the array of parameters for the post + $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); + $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; + $arr['item_hidden'] = $item_hidden; + $arr['resource_type'] = WIKI_ITEM_RESOURCE_TYPE; + $arr['resource_id'] = $resource_id; + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $observer_hash; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['llink'] = $arr['plink']; + $arr['title'] = $wiki['htmlName']; // name of new wiki; + $arr['allow_cid'] = $ac['allow_cid']; + $arr['allow_gid'] = $ac['allow_gid']; + $arr['deny_cid'] = $ac['deny_cid']; + $arr['deny_gid'] = $ac['deny_gid']; + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['item_private'] = intval($acl->is_private()); + $arr['verb'] = ACTIVITY_CREATE; + $arr['obj_type'] = ACTIVITY_OBJ_WIKI; + $arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]'; + // Save the path using iconfig. The file path should not be shared with other hubs + if (!set_iconfig($arr, 'wiki', 'path', $path, false)) { + return array('item' => null, 'success' => false); + } + // Save the wiki name information using iconfig. This is shareable. + if (!set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) { + return array('item' => null, 'success' => false); + } + if (!set_iconfig($arr, 'wiki', 'htmlName', $wiki['htmlName'], true)) { + return array('item' => null, 'success' => false); + } + if (!set_iconfig($arr, 'wiki', 'urlName', $wiki['urlName'], true)) { + return array('item' => null, 'success' => false); + } + $post = item_store($arr); + $item_id = $post['item_id']; + + if ($item_id) { + proc_run('php', "include/notifier.php", "activity", $item_id); + return array('item' => $arr, 'success' => true); + } else { + return array('item' => null, 'success' => false); + } +} + +function wiki_delete_wiki($resource_id) { + + $w = wiki_get_wiki($resource_id); + $item = $w['wiki']; + if (!$item || !$w['path']) { + return array('item' => null, 'success' => false); + } else { + $drop = drop_item($item['id'], false, DROPITEM_NORMAL, true); + $pathdel = rrmdir($w['path']); + if ($pathdel) { + info('Wiki files deleted successfully'); + } + return array('item' => $item, 'success' => (($drop === 1 && $pathdel) ? true : false)); + } +} + +function wiki_get_wiki($resource_id) { + $item = q("SELECT * FROM item WHERE resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 limit 1", + dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) + ); + if (!$item) { + return array('wiki' => null, 'path' => null); + } else { + $w = $item[0]; // wiki item table record + // Get wiki metadata + $rawName = get_iconfig($w, 'wiki', 'rawName'); + $htmlName = get_iconfig($w, 'wiki', 'htmlName'); + $urlName = get_iconfig($w, 'wiki', 'urlName'); + $path = get_iconfig($w, 'wiki', 'path'); + if (!realpath(__DIR__ . '/../' . $path)) { + return array('wiki' => null, 'path' => null); + } + // Path to wiki exists + $abs_path = realpath(__DIR__ . '/../' . $path); + return array( 'wiki' => $w, + 'path' => $abs_path, + 'rawName' => $rawName, + 'htmlName' => $htmlName, + 'urlName' => $urlName + ); + } +} + +function wiki_exists_by_name($uid, $urlName) { + $item = q("SELECT id,resource_id FROM item WHERE resource_type = '%s' AND title = '%s' AND uid = '%s' AND item_deleted = 0 limit 1", + dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc(escape_tags(urldecode($urlName))), + dbesc($uid) + ); + if (!$item) { + return array('id' => null, 'resource_id' => null); + } else { + return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']); + } +} + +function wiki_get_permissions($resource_id, $owner_id, $observer_hash) { + // TODO: For now, only the owner can edit + $sql_extra = item_permissions_sql($owner_id, $observer_hash); + $r = q("SELECT * FROM item WHERE resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", + dbesc(WIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) + ); + + if (!$r) { + return array('read' => false, 'write' => false, 'success' => true); + } else { + $perms = get_all_perms($owner_id, $observer_hash); + // TODO: Create a new permission setting for wiki analogous to webpages. Until + // then, use webpage permissions + if (!$perms['write_pages']) { + $write = false; + } else { + $write = true; + } + return array('read' => true, 'write' => $write, 'success' => true); + } +} + +function wiki_create_page($name, $resource_id) { + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('page' => null, 'wiki' => null, 'message' => 'Wiki not found.', 'success' => false); + } + $page = array('rawName' => $name, 'htmlName' => escape_tags($name), 'urlName' => urlencode(escape_tags($name)), 'fileName' => urlencode(escape_tags($name)).'.md'); + $page_path = $w['path'] . '/' . $page['fileName']; + if (is_file($page_path)) { + return array('page' => null, 'wiki' => null, 'message' => 'Page already exists.', 'success' => false); + } + // Create the page file in the wiki repo + if(!touch($page_path)) { + return array('page' => null, 'wiki' => null, 'message' => 'Page file cannot be created.', 'success' => false); + } else { + return array('page' => $page, 'wiki' => $w, 'message' => '', 'success' => true); + } + +} + +function wiki_rename_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Wiki not found.', 'success' => false); + } + $page_path_old = $w['path'].'/'.$pageUrlName.'.md'; + if (!is_readable($page_path_old) === true) { + return array('message' => 'Cannot read wiki page: ' . $page_path_old, 'success' => false); + } + $page = array('rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), 'urlName' => urlencode(escape_tags($pageNewName)), 'fileName' => urlencode(escape_tags($pageNewName)).'.md'); + $page_path_new = $w['path'] . '/' . $page['fileName'] ; + if (is_file($page_path_new)) { + return array('message' => 'Page already exists.', 'success' => false); + } + // Rename the page file in the wiki repo + if(!rename($page_path_old, $page_path_new)) { + return array('message' => 'Error renaming page file.', 'success' => false); + } else { + return array('page' => $page, 'message' => '', 'success' => true); + } + +} + +function wiki_get_page_content($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_readable($page_path) === true) { + if(filesize($page_path) === 0) { + $content = ''; + } else { + $content = file_get_contents($page_path); + if(!$content) { + return array('content' => null, 'message' => 'Error reading page content', 'success' => false); + } + } + // TODO: Check that the files are all text files + return array('content' => json_encode($content), 'message' => '', 'success' => true); + } +} + +function wiki_page_history($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('history' => null, 'message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (!is_readable($page_path) === true) { + return array('history' => null, 'message' => 'Cannot read wiki page: ' . $page_path, 'success' => false); + } + $reponame = ((array_key_exists('title', $w['wiki'])) ? $w['wiki']['title'] : 'repo'); + if($reponame === '') { + $reponame = 'repo'; + } + $git = new GitRepo('', null, false, $w['wiki']['title'], $w['path']); + try { + $gitlog = $git->git->log('', $page_path , array('limit' => 500)); + return array('history' => $gitlog, 'message' => '', 'success' => true); + } catch (\PHPGit\Exception\GitException $e) { + return array('history' => null, 'message' => 'GitRepo error thrown', 'success' => false); + } +} + +function wiki_save_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $content = ((array_key_exists('content',$arr)) ? purify_html($arr['content']) : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_writable($page_path) === true) { + if(!file_put_contents($page_path, $content)) { + return array('message' => 'Error writing to page file', 'success' => false); + } + return array('message' => '', 'success' => true); + } else { + return array('message' => 'Page file not writable', 'success' => false); + } +} + +function wiki_delete_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_writable($page_path) === true) { + if(!unlink($page_path)) { + return array('message' => 'Error deleting page file', 'success' => false); + } + return array('message' => '', 'success' => true); + } else { + return array('message' => 'Page file not writable', 'success' => false); + } +} + +function wiki_revert_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); + if (! $commitHash) { + return array('content' => $content, 'message' => 'No commit was provided', 'success' => false); + } + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_writable($page_path) === true) { + + $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); + if($reponame === '') { + $reponame = 'repo'; + } + $git = new GitRepo($observer['xchan_addr'], null, false, $w['wiki']['title'], $w['path']); + $content = null; + try { + $git->setIdentity($observer['xchan_name'], $observer['xchan_addr']); + foreach ($git->git->tree($commitHash) as $object) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + $content = $git->git->cat->blob($object['hash']); + } + } + } catch (\PHPGit\Exception\GitException $e) { + return array('content' => $content, 'message' => 'GitRepo error thrown', 'success' => false); + } + return array('content' => $content, 'message' => '', 'success' => true); + } else { + return array('content' => $content, 'message' => 'Page file not writable', 'success' => false); + } +} + +function wiki_compare_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : 'HEAD'); + $compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : null); + if (! $compareCommit) { + return array('message' => 'No compare commit was provided', 'success' => false); + } + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Error reading wiki', 'success' => false); + } + $page_path = $w['path'].'/'.$pageUrlName.'.md'; + if (is_readable($page_path) === true) { + $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); + if($reponame === '') { + $reponame = 'repo'; + } + $git = new GitRepo('', null, false, $w['wiki']['title'], $w['path']); + $compareContent = $currentContent = ''; + try { + foreach ($git->git->tree($currentCommit) as $object) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + $currentContent = $git->git->cat->blob($object['hash']); + } + } + foreach ($git->git->tree($compareCommit) as $object) { + if ($object['type'] == 'blob' && $object['file'] === $pageUrlName.'.md' ) { + $compareContent = $git->git->cat->blob($object['hash']); + } + } + require_once('library/class.Diff.php'); + $diff = Diff::toTable(Diff::compare($currentContent, $compareContent)); + } catch (\PHPGit\Exception\GitException $e) { + return array('message' => 'GitRepo error thrown', 'success' => false); + } + return array('diff' => $diff, 'message' => '', 'success' => true); + } else { + return array('message' => 'Page file not writable', 'success' => false); + } +} + +function wiki_git_commit($arr) { + $files = ((array_key_exists('files', $arr)) ? $arr['files'] : null); + $all = ((array_key_exists('all', $arr)) ? $arr['all'] : false); + $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : 'Repo updated'); + if(array_key_exists('resource_id', $arr)) { + $resource_id = $arr['resource_id']; + } else { + return array('message' => 'Wiki resource_id required for git commit', 'success' => false); + } + if(array_key_exists('observer', $arr)) { + $observer = $arr['observer']; + } else { + return array('message' => 'Observer required for git commit', 'success' => false); + } + $w = wiki_get_wiki($resource_id); + if (!$w['path']) { + return array('message' => 'Error reading wiki', 'success' => false); + } + $reponame = ((array_key_exists('title', $w['wiki'])) ? urlencode($w['wiki']['title']) : 'repo'); + if($reponame === '') { + $reponame = 'repo'; + } + $git = new GitRepo($observer['xchan_addr'], null, false, $w['wiki']['title'], $w['path']); + try { + $git->setIdentity($observer['xchan_name'], $observer['xchan_addr']); + if ($files === null) { + $options = array('all' => true); // git commit option to include all changes + } else { + $options = array('all' => $all); // git commit options\ + foreach ($files as $file) { + if (!$git->git->add($file)) { // add specified files to the git repo stage + if (!$git->git->reset->hard()) { + return array('message' => 'Error adding file to git stage: ' . $file . '. Error resetting git repo.', 'success' => false); + } + return array('message' => 'Error adding file to git stage: ' . $file, 'success' => false); + } + } + } + if ($git->commit($commit_msg, $options)) { + return array('message' => 'Wiki repo commit succeeded', 'success' => true); + } else { + return array('message' => 'Wiki repo commit failed', 'success' => false); + } + } catch (\PHPGit\Exception\GitException $e) { + return array('message' => 'GitRepo error thrown', 'success' => false); + } +} + +function wiki_generate_page_filename($name) { + $file = urlencode(escape_tags($name)); + if( $file === '') { + return null; + } else { + return $file . '.md'; + } +} + +function wiki_convert_links($s, $wikiURL) { + + if (strpos($s,'[[') !== false) { + preg_match_all("/\[\[(.*?)\]\]/", $s, $match); + $pages = $pageURLs = array(); + foreach ($match[1] as $m) { + // TODO: Why do we need to double urlencode for this to work? + $pageURLs[] = urlencode(urlencode(escape_tags($m))); + $pages[] = $m; + } + $idx = 0; + while(strpos($s,'[[') !== false) { + $replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>'; + $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); + $idx++; + } + } + return $s; +} + +function wiki_generate_toc($s) { + + if (strpos($s,'[toc]') !== false) { + //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render + $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ + $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); + } + return $s; +} + +// This function is derived from +// http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php +function wiki_toc($content) { + // ensure using only "\n" as line-break + $source = str_replace(["\r\n", "\r"], "\n", $content); + + // look for markdown TOC items + preg_match_all( + '/^(?:=|-|#).*$/m', + $source, + $matches, + PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE + ); + + // preprocess: iterate matched lines to create an array of items + // where each item is an array(level, text) + $file_size = strlen($source); + foreach ($matches[0] as $item) { + $found_mark = substr($item[0], 0, 1); + if ($found_mark == '#') { + // text is the found item + $item_text = $item[0]; + $item_level = strrpos($item_text, '#') + 1; + $item_text = substr($item_text, $item_level); + } else { + // text is the previous line (empty if <hr>) + $item_offset = $item[1]; + $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); + $item_text = + substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); + $item_text = trim($item_text); + $item_level = $found_mark == '=' ? 1 : 2; + } + if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { + // item is an horizontal separator or a table header, don't mind + continue; + } + $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; + } + $o = ''; + foreach($raw_toc as $t) { + $level = intval($t['level']); + $text = $t['text']; + switch ($level) { + case 1: + $li = '* '; + break; + case 2: + $li = ' * '; + break; + case 3: + $li = ' * '; + break; + case 4: + $li = ' * '; + break; + default: + $li = '* '; + break; + } + $o .= $li . $text . "\n"; + } + return $o; +} diff --git a/include/zot.php b/include/zot.php index 0cdf7fc87..73d9ef950 100644 --- a/include/zot.php +++ b/include/zot.php @@ -12,6 +12,7 @@ require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/hubloc.php'); require_once('include/queue_fn.php'); +require_once('include/perm_upgrade.php'); /** @@ -329,8 +330,12 @@ function zot_refresh($them, $channel = null, $force = false) { return false; } + $token = random_string(); + $postvars = array(); + $postvars['token'] = $token; + if($channel) { $postvars['target'] = $channel['channel_guid']; $postvars['target_sig'] = $channel['channel_guid_sig']; @@ -343,9 +348,9 @@ function zot_refresh($them, $channel = null, $force = false) { $postvars['guid_hash'] = $them['xchan_hash']; if (array_key_exists('xchan_guid',$them) && $them['xchan_guid'] && array_key_exists('xchan_guid_sig',$them) && $them['xchan_guid_sig']) { - $postvars['guid'] = $them['xchan_guid']; $postvars['guid_sig'] = $them['xchan_guid_sig']; + } $rhs = '/.well-known/zot-info'; @@ -363,15 +368,28 @@ function zot_refresh($them, $channel = null, $force = false) { return false; } + $signed_token = ((is_array($j) && array_key_exists('signed_token',$j)) ? $j['signed_token'] : null); + if($signed_token) { + $valid = rsa_verify('token.' . $token,base64url_decode($signed_token),$j['key']); + if(! $valid) { + logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR); + return false; + } + } + else { + logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING); + // after 2017-01-01 this will be a hard error unless you over-ride it. + if((time() > 1483228800) && (! get_config('system','allow_unsigned_zotfinger'))) { + return false; + } + } + $x = import_xchan($j, (($force) ? UPDATE_FLAGS_FORCED : UPDATE_FLAGS_UPDATED)); if(! $x['success']) return false; - $their_perms = 0; - if($channel) { - $global_perms = get_perms(); if($j['permissions']['data']) { $permissions = crypto_unencapsulate(array( 'data' => $j['permissions']['data'], @@ -388,15 +406,10 @@ function zot_refresh($them, $channel = null, $force = false) { $connected_set = false; if($permissions && is_array($permissions)) { + $old_read_stream_perm = get_abconfig($channel['channel_id'],$x['hash'],'their_perms','view_stream'); + foreach($permissions as $k => $v) { - // The connected permission means you are in their address book - if($k === 'connected') { - $connected_set = intval($v); - continue; - } - if(($v) && (array_key_exists($k,$global_perms))) { - $their_perms = $their_perms | intval($global_perms[$k][1]); - } + set_abconfig($channel['channel_id'],$x['hash'],'their_perms',$k,$v); } } @@ -423,52 +436,49 @@ function zot_refresh($them, $channel = null, $force = false) { if(substr($r[0]['abook_dob'],5) == substr($next_birthday,5)) $next_birthday = $r[0]['abook_dob']; - $current_abook_connected = (intval($r[0]['abook_unconnected']) ? 0 : 1); - - $y = q("update abook set abook_their_perms = %d, abook_dob = '%s' + $y = q("update abook set abook_dob = '%s' where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 ", - intval($their_perms), dbescdate($next_birthday), dbesc($x['hash']), intval($channel['channel_id']) ); -// if(($connected_set === 0 || $connected_set === 1) && ($connected_set !== $current_abook_unconnected)) { - - // if they are in your address book but you aren't in theirs, and/or this does not - // match your current connected state setting, toggle it. - /** @FIXME uncoverted to postgres */ - /** @FIXME when this was enabled, all contacts became unconnected. Currently disabled intentionally */ -// $y1 = q("update abook set abook_unconnected = 1 -// where abook_xchan = '%s' and abook_channel = %d -// and abook_self = 0 limit 1", -// dbesc($x['hash']), -// intval($channel['channel_id']) -// ); -// } - if(! $y) logger('abook update failed'); else { // if we were just granted read stream permission and didn't have it before, try to pull in some posts - if((! ($r[0]['abook_their_perms'] & PERMS_R_STREAM)) && ($their_perms & PERMS_R_STREAM)) - proc_run('php','include/onepoll.php',$r[0]['abook_id']); + if((! $old_read_stream_perm) && (intval($permissions['view_stream']))) + Zotlabs\Daemon\Master::Summon(array('Onepoll',$r[0]['abook_id'])); } } else { // new connection + $my_perms = null; + $role = get_pconfig($channel['channel_id'],'system','permissions_role'); if($role) { - $xx = get_role_perms($role); - if($xx['perms_auto']) - $default_perms = $xx['perms_accept']; + $xx = \Zotlabs\Access\PermissionRoles::role_perms($role); + if($xx['perms_auto']) { + $default_perms = $xx['perms_connect']; + $my_perms = \Zotlabs\Access\Permissions::FilledPerms($default_perms); + } } - if(! $default_perms) - $default_perms = intval(get_pconfig($channel['channel_id'],'system','autoperms')); + if(! $my_perms) { + $m = \Zotlabs\Access\Permissions::FilledAutoperms($channel['channel_id']); + if($m) { + $my_perms = $m; + } + } + + if($my_perms) { + foreach($my_perms as $k => $v) { + set_abconfig($channel['channel_id'],$x['hash'],'my_perms',$k,$v); + } + } // Keep original perms to check if we need to notify them $previous_perms = get_all_perms($channel['channel_id'],$x['hash']); @@ -478,13 +488,11 @@ function zot_refresh($them, $channel = null, $force = false) { if($closeness === false) $closeness = 80; - $y = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_their_perms, abook_my_perms, abook_created, abook_updated, abook_dob, abook_pending ) values ( %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', %d )", + $y = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_created, abook_updated, abook_dob, abook_pending ) values ( %d, %d, %d, '%s', '%s', '%s', '%s', %d )", intval($channel['channel_account_id']), intval($channel['channel_id']), intval($closeness), dbesc($x['hash']), - intval($their_perms), - intval($default_perms), dbesc(datetime_convert()), dbesc(datetime_convert()), dbesc($next_birthday), @@ -503,27 +511,36 @@ function zot_refresh($them, $channel = null, $force = false) { ); if($new_connection) { - if($new_perms != $previous_perms) - proc_run('php','include/notifier.php','permission_create',$new_connection[0]['abook_id']); - require_once('include/enotify.php'); - notification(array( + if(! \Zotlabs\Access\Permissions::PermsCompare($new_perms,$previous_perms)) + Zotlabs\Daemon\Master::Summon(array('Notifier','permission_create',$new_connection[0]['abook_id'])); + Zotlabs\Lib\Enotify::submit(array( 'type' => NOTIFY_INTRO, 'from_xchan' => $x['hash'], 'to_xchan' => $channel['channel_hash'], 'link' => z_root() . '/connedit/' . $new_connection[0]['abook_id'], )); - if($their_perms & PERMS_R_STREAM) { - if(($channel['channel_w_stream'] & PERMS_PENDING) - || (! intval($new_connection[0]['abook_pending'])) ) - proc_run('php','include/onepoll.php',$new_connection[0]['abook_id']); + if(intval($permissions['view_stream'])) { + if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING) + || (! intval($new_connection[0]['abook_pending']))) + Zotlabs\Daemon\Master::Summon(array('Onepoll',$new_connection[0]['abook_id'])); + } + + + /** If there is a default group for this channel, add this connection to it */ + $default_group = $channel['channel_default_group']; + if($default_group) { + require_once('include/group.php'); + $g = group_rec_byhash($channel['channel_id'],$default_group); + if($g) + group_add_member($channel['channel_id'],'',$x['hash'],$g['id']); } unset($new_connection[0]['abook_id']); unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_channel']); - $abconfig = load_abconfig($channel['channel_hash'],$new_connection['abook_xchan']); + $abconfig = load_abconfig($channel['channel_id'],$new_connection['abook_xchan']); if($abconfig) $new_connection['abconfig'] = $abconfig; @@ -1027,8 +1044,9 @@ function zot_process_response($hub, $arr, $outq) { /** * @brief * - * We received a notification packet (in mod/post.php) that a message is waiting for us, and we've verified the sender. - * Now send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site private key. + * We received a notification packet (in mod_post) that a message is waiting for us, and we've verified the sender. + * Now send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site + * private key. * The entire pickup message is encrypted with the remote site's public key. * If everything checks out on the remote end, we will receive back a packet containing one or more messages, * which will be processed and delivered before this function ultimately returns. @@ -1102,6 +1120,7 @@ function zot_fetch($arr) { * * [1] => \e string $delivery_status * * [2] => \e string $address */ + function zot_import($arr, $sender_url) { $data = json_decode($arr['body'], true); @@ -1332,7 +1351,7 @@ function zot_import($arr, $sender_url) { */ function public_recips($msg) { - require_once('include/identity.php'); + require_once('include/channel.php'); $check_mentions = false; $include_sys = false; @@ -1340,8 +1359,8 @@ function public_recips($msg) { if($msg['message']['type'] === 'activity') { if(! get_config('system','disable_discover_tab')) $include_sys = true; - $col = 'channel_w_stream'; - $field = PERMS_W_STREAM; + $perm = 'send_stream'; + if(array_key_exists('flags',$msg['message']) && in_array('thread_parent', $msg['message']['flags'])) { // check mention recipient permissions on top level posts only $check_mentions = true; @@ -1373,65 +1392,30 @@ function public_recips($msg) { // contains the tag. we'll solve that further below. if($msg['notify']['sender']['guid_sig'] != $msg['message']['owner']['guid_sig']) { - $col = 'channel_w_comment'; - $field = PERMS_W_COMMENT; + $perm = 'post_comments'; } } } - elseif($msg['message']['type'] === 'mail') { - $col = 'channel_w_mail'; - $field = PERMS_W_MAIL; - } + elseif($msg['message']['type'] === 'mail') + $perm = 'post_mail'; - if(! $col) - return NULL; - - $col = dbesc($col); - - // First find those channels who are accepting posts from anybody, or at least - // something greater than just their connections. - - if($msg['notify']['sender']['url'] === z_root()) { - $sql = " where (( " . $col . " & " . intval(PERMS_NETWORK) . " ) > 0 - or ( " . $col . " & " . intval(PERMS_SITE) . " ) > 0 - or ( " . $col . " & " . intval(PERMS_PUBLIC) . ") > 0 - or ( " . $col . " & " . intval(PERMS_AUTHED) . ") > 0 ) "; - } else { - $sql = " where ( " . $col . " = " . intval(PERMS_NETWORK) . " - or " . $col . " = " . intval(PERMS_PUBLIC) . " - or " . $col . " = " . intval(PERMS_AUTHED) . " ) "; + $r = array(); + + $c = q("select channel_id, channel_hash from channel where channel_removed = 0"); + if($c) { + foreach($c as $cc) { + if(perm_is_allowed($cc['channel_id'],$msg['notify']['sender']['hash'],$perm)) { + $r[] = [ 'hash' => $cc['channel_hash'] ]; + } + } } - $r = q("select channel_hash as hash from channel $sql or channel_hash = '%s' - and channel_removed = 0 ", - dbesc($msg['notify']['sender']['hash']) - ); - - if(! $r) - $r = array(); - - // Now we have to get a bit dirty. Find every channel that has the sender in their connections (abook) - // and is allowing this sender at least at a high level. - - $x = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id - where abook_xchan = '%s' and channel_removed = 0 - and (( " . $col . " = " . intval(PERMS_SPECIFIC) . " and ( abook_my_perms & " . intval($field) . " ) > 0 ) - OR " . $col . " = " . intval(PERMS_PENDING) . " - OR ( " . $col . " = " . intval(PERMS_CONTACTS) . " and abook_pending = 0 )) ", - dbesc($msg['notify']['sender']['hash']) - ); - - if(! $x) - $x = array(); - - $r = array_merge($r,$x); - - //logger('message: ' . print_r($msg['message'],true)); + // logger('message: ' . print_r($msg['message'],true)); if($include_sys && array_key_exists('public_scope',$msg['message']) && $msg['message']['public_scope'] === 'public') { $sys = get_sys_channel(); if($sys) - $r[] = array('hash' => $sys['channel_hash']); + $r[] = [ 'hash' => $sys['channel_hash'] ]; } // look for any public mentions on this site @@ -1494,7 +1478,7 @@ function public_recips($msg) { /** * @brief * - * This is the second part of public_recipes(). + * This is the second part of public_recips(). * We'll find all the channels willing to accept public posts from us, then * match them against the sender privacy scope and see who in that list that * the sender is allowing. @@ -1703,7 +1687,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ if((! $relay) && (! $request) && (! $local_public) && perm_is_allowed($channel['channel_id'],$sender['hash'],'send_stream')) { - proc_run('php', 'include/notifier.php', 'request', $channel['channel_id'], $sender['hash'], $arr['parent_mid']); + Zotlabs\Daemon\Master::Summon(array('Notifier', 'request', $channel['channel_id'], $sender['hash'], $arr['parent_mid'])); } continue; } @@ -1775,7 +1759,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ if($relay && $item_id) { logger('process_delivery: invoking relay'); - proc_run('php','include/notifier.php','relay',intval($item_id)); + Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id))); $DR->update('relayed'); $result[] = $DR->get(); } @@ -1858,7 +1842,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ if($relay && $item_id) { logger('process_delivery: invoking relay'); - proc_run('php','include/notifier.php','relay',intval($item_id)); + Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id))); $DR->addto_update('relayed'); $result[] = $DR->get(); } @@ -1912,9 +1896,9 @@ function remove_community_tag($sender, $arr, $uid) { $i = $r[0]; if($i['target']) - $i['target'] = json_decode_plus($i['target']); + $i['target'] = json_decode($i['target'],true); if($i['object']) - $i['object'] = json_decode_plus($i['object']); + $i['object'] = json_decode($i['object'],true); if(! ($i['target'] && $i['object'])) { logger('remove_community_tag: no target/object'); @@ -1932,7 +1916,7 @@ function remove_community_tag($sender, $arr, $uid) { return; } - q("delete from term where uid = %d and oid = %d and otype = %d and type in ( %d, %d ) and term = '%s' and url = '%s'", + q("delete from term where uid = %d and oid = %d and otype = %d and ttype in ( %d, %d ) and term = '%s' and url = '%s'", intval($uid), intval($r[0]['id']), intval(TERM_OBJ_POST), @@ -1953,6 +1937,16 @@ function remove_community_tag($sender, $arr, $uid) { */ function update_imported_item($sender, $item, $orig, $uid) { + // If this is a comment being updated, remove any privacy information + // so that item_store_update will set it from the original. + + if($item['mid'] !== $item['parent_mid']) { + unset($item['allow_cid']); + unset($item['allow_gid']); + unset($item['deny_cid']); + unset($item['deny_gid']); + unset($item['item_private']); + } $x = item_store_update($item); @@ -2371,11 +2365,14 @@ function sync_locations($sender, $arr, $absolute = false) { $current_site = false; + $t = datetime_convert('UTC','UTC','now - 15 minutes'); + if(array_key_exists('site',$arr) && $location['url'] == $arr['site']['url']) { - q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d", + q("update hubloc set hubloc_connected = '%s', hubloc_updated = '%s' where hubloc_id = %d and hubloc_connected < '%s'", dbesc(datetime_convert()), dbesc(datetime_convert()), - intval($r[0]['hubloc_id']) + intval($r[0]['hubloc_id']), + dbesc($t) ); $current_site = true; } @@ -2935,8 +2932,6 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if(UNO) return; - $a = get_app(); - logger('build_sync_packet'); if($packet) @@ -2956,6 +2951,14 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $channel = $r[0]; + translate_channel_perms_outbound($channel); + if($packet && array_key_exists('abook',$packet) && $packet['abook']) { + for($x = 0; $x < count($packet['abook']); $x ++) { + translate_abook_perms_outbound($packet['abook'][$x]); + } + } + + if(intval($channel['channel_removed'])) return; @@ -2972,7 +2975,12 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { if($x['hubloc_host'] == App::get_hostname()) continue; - $synchubs[] = $x; + $y = q("select site_dead from site where site_url = '%s' limit 1", + dbesc($x['hubloc_url']) + ); + + if((! $y) || ($y[0]['site_dead'] == 0)) + $synchubs[] = $x; } if(! $synchubs) @@ -2989,7 +2997,8 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $info = (($packet) ? $packet : array()); $info['type'] = 'channel_sync'; - $info['encoding'] = 'red'; // note: not zot, this packet is very red specific + $info['encoding'] = 'red'; // note: not zot, this packet is very platform specific + $info['relocate'] = ['channel_address' => $channel['channel_address'], 'url' => z_root() ]; if(array_key_exists($uid,App::$config) && array_key_exists('transient',App::$config[$uid])) { $settings = App::$config[$uid]['transient']; @@ -3019,7 +3028,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { } if($groups_changed) { - $r = q("select hash as collection, visible, deleted, name from groups where uid = %d", + $r = q("select hash as collection, visible, deleted, gname as name from groups where uid = %d", intval($uid) ); if($r) @@ -3050,7 +3059,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { 'msg' => json_encode($info) )); - proc_run('php', 'include/deliver.php', $hash); + Zotlabs\Daemon\Master::Summon(array('Deliver', $hash)); $total = $total - 1; if($interval && $total) @@ -3073,7 +3082,8 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { require_once('include/import.php'); - /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */ + /** @FIXME this will sync red structures (channel, pconfig and abook). + Eventually we need to make this application agnostic. */ $result = array(); @@ -3121,16 +3131,19 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { import_conv($channel,$arr['conv']); if(array_key_exists('mail',$arr) && $arr['mail']) - import_mail($channel,$arr['mail']); + sync_mail($channel,$arr['mail']); if(array_key_exists('event',$arr) && $arr['event']) sync_events($channel,$arr['event']); if(array_key_exists('event_item',$arr) && $arr['event_item']) - sync_items($channel,$arr['event_item']); + sync_items($channel,$arr['event_item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); if(array_key_exists('item',$arr) && $arr['item']) - sync_items($channel,$arr['item']); + sync_items($channel,$arr['item'],((array_key_exists('relocate',$arr)) ? $arr['relocate'] : null)); + + // deprecated, maintaining for a few months for upward compatibility + // this should sync webpages, but the logic is a bit subtle if(array_key_exists('item_id',$arr) && $arr['item_id']) sync_items($channel,$arr['item_id']); @@ -3143,6 +3156,8 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { + translate_channel_perms_inbound($arr['channel']); + if(array_key_exists('channel_pageflags',$arr['channel']) && intval($arr['channel']['channel_pageflags'])) { // These flags cannot be sync'd. // remove the bits from the incoming flags. @@ -3156,7 +3171,15 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } - $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey', 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', 'channel_system'); + $disallowed = [ + 'channel_id', 'channel_account_id', 'channel_primary', 'channel_prvkey', + 'channel_address', 'channel_notifyflags', 'channel_removed', 'channel_deleted', + 'channel_system', 'channel_r_stream', 'channel_r_profile', 'channel_r_abook', + 'channel_r_storage', 'channel_r_pages', 'channel_w_stream', 'channel_w_wall', + 'channel_w_comment', 'channel_w_mail', 'channel_w_like', 'channel_w_tagwall', + 'channel_w_chat', 'channel_w_storage', 'channel_w_pages', 'channel_a_republish', + 'channel_a_delegate' + ]; $clean = array(); foreach($arr['channel'] as $k => $v) { @@ -3192,6 +3215,8 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($arr['abook'] as $abook) { + + $abconfig = null; if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) @@ -3212,7 +3237,6 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { $clean = array(); if($abook['abook_xchan'] && $abook['entry_deleted']) { logger('process_channel_sync_delivery: removing abook entry for ' . $abook['abook_xchan']); - require_once('include/Contact.php'); $r = q("select abook_id, abook_feed from abook where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 limit 1", dbesc($abook['abook_xchan']), @@ -3287,11 +3311,16 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } + // This will set abconfig vars if the sender is using old-style fixed permissions + // using the raw abook record as passed to us. New-style permissions will fall through + // and be set using abconfig + + translate_abook_perms_inbound($channel,$abook); + if($abconfig) { // @fixme does not handle sync of del_abconfig foreach($abconfig as $abc) { - if($abc['chan'] === $channel['channel_hash']) - set_abconfig($abc['chan'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); } } } @@ -3313,10 +3342,10 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } if($found) { - if(($y['name'] != $cl['name']) + if(($y['gname'] != $cl['name']) || ($y['visible'] != $cl['visible']) || ($y['deleted'] != $cl['deleted'])) { - q("update groups set name = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", + q("update groups set gname = '%s', visible = %d, deleted = %d where hash = '%s' and uid = %d", dbesc($cl['name']), intval($cl['visible']), intval($cl['deleted']), @@ -3332,7 +3361,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } if(! $found) { - $r = q("INSERT INTO `groups` ( hash, uid, visible, deleted, name ) + $r = q("INSERT INTO `groups` ( hash, uid, visible, deleted, gname ) VALUES( '%s', %d, %d, %d, '%s' ) ", dbesc($cl['collection']), intval($channel['channel_id']), @@ -3439,7 +3468,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('profile',$arr) && is_array($arr['profile']) && count($arr['profile'])) { - $disallowed = array('id','aid','uid'); + $disallowed = array('id','aid','uid','guid'); foreach($arr['profile'] as $profile) { $x = q("select * from profile where profile_guid = '%s' and uid = %d limit 1", @@ -3463,13 +3492,22 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($profile as $k => $v) { if(in_array($k,$disallowed)) continue; + + if($k === 'name') + $clean['fullname'] = $v; + elseif($k === 'with') + $clean['partner'] = $v; + elseif($k === 'work') + $clean['employment'] = $v; + elseif(array_key_exists($k,$x[0])) + $clean[$k] = $v; - $clean[$k] = $v; /** - * @TODO check if these are allowed, otherwise we'll error + * @TODO * We also need to import local photos if a custom photo is selected */ } + if(count($clean)) { foreach($clean as $k => $v) { $r = dbq("UPDATE profile set `" . dbesc($k) . "` = '" . dbesc($v) @@ -3479,13 +3517,6 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } - - if(array_key_exists('item',$arr) && $arr['item']) - sync_items($channel,$arr['item']); - - if(array_key_exists('item_id',$arr) && $arr['item_id']) - sync_items($channel,$arr['item_id']); - $addon = array('channel' => $channel,'data' => $arr); call_hooks('process_channel_sync_delivery',$addon); @@ -3642,7 +3673,7 @@ function zot_reply_message_request($data) { * invoke delivery to send out the notify packet */ - proc_run('php', 'include/deliver.php', $hash); + Zotlabs\Daemon\Master::Summon(array('Deliver', $hash)); } } $ret['success'] = true; @@ -3662,6 +3693,8 @@ function zotinfo($arr) { $zsig = ((x($arr,'target_sig')) ? $arr['target_sig'] : ''); $zkey = ((x($arr,'key')) ? $arr['key'] : ''); $mindate = ((x($arr,'mindate')) ? $arr['mindate'] : ''); + $token = ((x($arr,'token')) ? $arr['token'] : ''); + $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); if($ztarget) { @@ -3749,11 +3782,21 @@ function zotinfo($arr) { } else { // check if it has characteristics of a public forum based on custom permissions. - $t = q("select abook_my_perms from abook where abook_channel = %d and abook_self = 1 limit 1", - intval($e['channel_id']) + $t = q("select * from abconfig where abconfig.cat = 'my_perms' and abconfig.chan = %d and abconfig.xchan = '%s' and abconfig.k in ('tag_deliver', 'send_stream') ", + intval($e['channel_id']), + intval($e['channel_hash']) ); - if(($t) && (($t[0]['abook_my_perms'] & PERMS_W_TAGWALL) && (! ($t[0]['abook_my_perms'] & PERMS_W_STREAM)))) - $public_forum = true; + $ch = 0; + if($t) { + foreach($t as $tt) { + if($tt['k'] == 'tag_deliver' && $tt['v'] == 1) + $ch ++; + if($tt['k'] == 'send_stream' && $tt['v'] == 0) + $ch ++; + } + if($ch == 2) + $public_forum = true; + } } @@ -3806,6 +3849,10 @@ function zotinfo($arr) { // Communication details + if($token) + $ret['signed_token'] = base64url_encode(rsa_sign('token.' . $token,$e['channel_prvkey'])); + + $ret['guid'] = $e['xchan_guid']; $ret['guid_sig'] = $e['xchan_guid_sig']; $ret['key'] = $e['xchan_pubkey']; @@ -3910,16 +3957,14 @@ function zotinfo($arr) { $ret['site']['accounts'] = account_total(); - require_once('include/identity.php'); + require_once('include/channel.php'); $ret['site']['channels'] = channel_total(); - $ret['site']['version'] = Zotlabs\Project\System::get_platform_name() . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; + $ret['site']['version'] = Zotlabs\Lib\System::get_platform_name() . ' ' . STD_VERSION . '[' . DB_UPDATE_VERSION . ']'; $ret['site']['admin'] = get_config('system','admin_email'); - $a = get_app(); - $visible_plugins = array(); if(is_array(App::$plugins) && count(App::$plugins)) { $r = q("select * from addon where hidden = 0"); @@ -3934,7 +3979,7 @@ function zotinfo($arr) { $ret['site']['sellpage'] = get_config('system','sellpage'); $ret['site']['location'] = get_config('system','site_location'); $ret['site']['realm'] = get_directory_realm(); - $ret['site']['project'] = Zotlabs\Project\System::get_platform_name(); + $ret['site']['project'] = Zotlabs\Lib\System::get_platform_name() . ' ' . Zotlabs\Lib\System::get_server_role(); } @@ -4048,6 +4093,17 @@ function delivery_report_is_storable($dr) { if(($dr['location'] !== z_root()) && ($dr['sender'] === $rxchan) && ($dr['status'] === 'recipient_not_found')) return false; + // If you have a private post with a recipient list, every single site is going to report + // back a failed delivery for anybody on that list that isn't local to them. We're only + // concerned about this if we have a local hubloc record which says we expected them to + // have a channel on that site. + + $r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_url = '%s'", + dbesc($rxchan), + dbesc($dr['location']) + ); + if((! $r) && ($dr['status'] === 'recipient_not_found')) + return false; $r = q("select abook_id from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($rxchan), @@ -4082,7 +4138,7 @@ function update_hub_connected($hub,$sitekey = '') { $sitekey = $hub['sitekey']; } - // $sender['sitekey'] is a new addition to the protcol to distinguish + // $sender['sitekey'] is a new addition to the protocol to distinguish // hublocs coming from re-installed sites. Older sites will not provide // this field and we have to still mark them valid, since we can't tell // if this hubloc has the same sitekey as the packet we received. @@ -4091,10 +4147,13 @@ function update_hub_connected($hub,$sitekey = '') { // Update our DB to show when we last communicated successfully with this hub // This will allow us to prune dead hubs from using up resources - $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_sitekey = '%s' ", + $t = datetime_convert('UTC','UTC','now - 15 minutes'); + + $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_sitekey = '%s' and hubloc_connected < '%s' ", dbesc(datetime_convert()), intval($hub['hubloc_id']), - dbesc($sitekey) + dbesc($sitekey), + dbesc($t) ); // a dead hub came back to life - reset any tombstones we might have @@ -4394,7 +4453,6 @@ function zot_reply_purge($sender,$recipients) { $arr = $sender; $sender_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']); - require_once('include/Contact.php'); remove_all_xchan_resources($sender_hash); $ret['success'] = true; |