','[',']'),array('%3c','%3e','%5b','%5d'),$s); } function file_tag_decode($s) { return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s); } function file_tag_file_query($table,$s,$type = 'file') { if($type == 'file') $termtype = TERM_FILE; else $termtype = TERM_CATEGORY; 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)) ); } 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.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.ttype = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", intval($type), protect_sprintf(dbesc($s)) ); } } function term_item_parent_query($uid,$table,$s,$type = TERM_UNKNOWN, $type2 = '') { // Allow asterisks for wildcard search // In theory this means '%' will also do a wildcard search, but there appear to be multiple escape // issues with '%' in term names and trying to fix this with '\\%' here did not help. // Ideally I think we want '*' to indicate wildcards and allow '%' literally in names, but that is being // left for another developer on another day. $s = str_replace('*','%',$s); if($type2) { $r = q("select parent from item left join term on term.oid = item.id where term.ttype in (%d, %d) and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), intval($type2), dbesc($s), intval($uid), dbesc(ACTIVITY_UPDATE) ); } else { $r = q("select parent from item left join term on term.oid = item.id where term.ttype = %d and term.term like '%s' and term.uid = %d and term.otype = 1 and item.verb NOT IN ('Update', '%s')", intval($type), dbesc($s), intval($uid), dbesc(ACTIVITY_UPDATE) ); } if($r) { $str = ''; foreach($r as $rv) { if($str) $str .= ','; $str .= intval($rv['parent']); } return " AND " . (($table) ? dbesc($table) . '.' : '') . "id in ( $str ) "; } return " AND false "; } function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { if(! $term) return false; $r = q("select * from term where uid = %d and oid = %d and otype = %d and ttype = %d and term = '%s' and url = '%s' ", intval($uid), intval($iid), intval($otype), intval($type), dbesc($term), dbesc($url) ); if($r) return false; $r = q("insert into term (uid, oid, otype, ttype, term, url) values( %d, %d, %d, %d, '%s', '%s') ", intval($uid), intval($iid), intval($otype), intval($type), dbesc($term), dbesc($url) ); return $r; } function get_terms_oftype($arr, $type) { $ret = []; if(!is_array($arr)) return $ret; if(! is_array($type)) $type = [$type]; foreach($type as $t) foreach($arr as $x) if($x['ttype'] == $t) $ret[] = $x; return $ret; } function format_term_for_display($term) { $s = ''; if(($term['ttype'] == TERM_HASHTAG) || ($term['ttype'] == TERM_COMMUNITYTAG)) $s .= '#'; elseif($term['ttype'] == TERM_FORUM) $s .= '!'; elseif($term['ttype'] == TERM_MENTION) $s .= '@'; else return $s; if($term['url']) $s .= '' . htmlspecialchars($term['term'], ENT_COMPAT,'UTF-8') . ''; else $s .= htmlspecialchars($term['term'], ENT_COMPAT,'UTF-8'); return $s; } // Tag cloud functions - need to be adpated to this database format function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_HASHTAG) { require_once('include/security.php'); if(! perm_is_allowed($uid,get_observer_hash(),'view_stream')) return array(); $item_normal = item_normal(); $sql_options = item_permissions_sql($uid); $count = intval($count); if($flags) { if($flags === 'wall') $sql_options .= " and item_wall = 1 "; } if($authors) { if(! is_array($authors)) $authors = array($authors); $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; } if($owner) { $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; } // 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.ttype = %d and otype = %d and item_type = %d $sql_options $item_normal group by term order by total desc %s", intval($uid), intval($type), intval(TERM_OBJ_POST), intval($restrict), ((intval($count)) ? "limit $count" : '') ); if(! $r) return array(); return Zotlabs\Text\Tagadelic::calc($r); } function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) { require_once('include/security.php'); if(! perm_is_allowed($uid,get_observer_hash(),'view_pages')) return array(); $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; $sql_options = item_permissions_sql($uid); $count = intval($count); if($flags) { if($flags === 'wall') $sql_options .= " and item_wall = 1 "; } if($authors) { if(! is_array($authors)) $authors = array($authors); $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; } if($owner) { $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; } // 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.ttype = %d and otype = %d and item_type = %d $sql_options $item_normal group by term order by total desc %s", intval($uid), intval($type), intval(TERM_OBJ_POST), intval(ITEM_TYPE_CARD), ((intval($count)) ? "limit $count" : '') ); if(! $r) return array(); return Zotlabs\Text\Tagadelic::calc($r); } function article_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) { require_once('include/security.php'); if(! perm_is_allowed($uid,get_observer_hash(),'view_pages')) return array(); $item_normal = " and item.item_hidden = 0 and item.item_deleted = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_blocked = 0 and item.obj_type != 'http://purl.org/zot/activity/file' "; $sql_options = item_permissions_sql($uid); $count = intval($count); if($flags) { if($flags === 'wall') $sql_options .= " and item_wall = 1 "; } if($authors) { if(! is_array($authors)) $authors = array($authors); $sql_options .= " and author_xchan in (" . stringify_array($authors,true) . ") "; } if($owner) { $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; } // 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.ttype = %d and otype = %d and item_type = %d $sql_options $item_normal group by term order by total desc %s", intval($uid), intval($type), intval(TERM_OBJ_POST), intval(ITEM_TYPE_ARTICLE), ((intval($count)) ? "limit $count" : '') ); if(! $r) return array(); return Zotlabs\Text\Tagadelic::calc($r); } function pubtagblock($net,$site,$limit,$recent = 0,$safemode = 1, $type = TERM_HASHTAG) { $o = ''; $r = pub_tagadelic($net,$site,$limit,$recent,$safemode,$type); $link = z_root() . '/pubstream'; if($r) { $o = '

' . (($recent) ? t('Trending') : t('Tags')) . '

'; foreach($r as $rr) { $o .= '#'.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function pub_tagadelic($net, $site, $limit, $recent, $safemode, $type) { $item_normal = item_normal(); $count = intval($limit); $sys = get_sys_channel(); $uids = " and item.uid = " . intval($sys['channel_id']) . " "; $sql_extra = item_permissions_sql($sys['channel_id']); $site_firehose_sql = ''; if ($site) { $site_firehose_sql = " and owner_xchan in (select channel_hash from channel where channel_system = 0 and channel_removed = 0) "; } if($recent) { $sql_extra .= " and item.created > NOW() - INTERVAL " . db_quoteinterval(intval($recent) . ' DAY') . " "; } if($safemode) { $unsafetags = Config::Get('system','unsafepubtags', [ 'boobs', 'bot', 'rss', 'girl','girls', 'nsfw', 'sexy', 'nude' ]); if($unsafetags) { $sql_extra .= " and not term.term in ( " . stringify_array($unsafetags,true) . ") "; } } $key = __FUNCTION__ . "-" . md5($site . $recent . $safemode . $limit . $type); $content = Cache::get($key, '5 MINUTE'); if(! $content) { $content = Cache::get($key, '1 MONTH'); $arr = [ "SELECT term, count(term) AS total FROM term LEFT JOIN item ON term.oid = item.id WHERE term.ttype = %d AND otype = %d AND item_type = %d AND item_private = 0 $uids $item_normal $site_firehose_sql $sql_extra GROUP BY term ORDER BY total DESC %s", intval($type), intval(TERM_OBJ_POST), intval(ITEM_TYPE_POST), (intval($count) ? "LIMIT $count" : '') ]; \Zotlabs\Daemon\Master::Summon([ 'Cache_query', $key, base64_encode(json_encode($arr)) ]); } $r = unserialize($content); if(! $r) return []; return Zotlabs\Text\Tagadelic::calc($r); } function dir_tagadelic($count = 0, $hub = '') { $count = intval($count); if($hub) { $r = q("select xtag_term as term, count(xtag_term) as total from xtag left join hubloc on xtag_hash = hubloc_hash where xtag_flags = 0 and xtag_hash in (select hubloc_hash from hubloc where hubloc_host = '%s' ) group by xtag_term order by total desc %s", dbesc($hub), ((intval($count)) ? "limit $count" : '') ); } else { $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 []; return Zotlabs\Text\Tagadelic::calc($r); } function app_tagblock($link,$count = 0) { $o = ''; $r = app_tagadelic($count); if($r) { $o = '

' . t('Categories') . '

'; foreach($r as $rr) { $o .= ''.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function app_tagadelic($count = 0) { if(! local_channel()) return ''; $count = intval($count); // Fetch tags $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(); return Zotlabs\Text\Tagadelic::calc($r); } function tagblock($link,$uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_HASHTAG) { $o = ''; $r = tagadelic($uid,$count,$authors,$owner, $flags,$restrict,$type); if($r) { $o = '

' . t('Tags') . '

'; foreach($r as $rr) { $o .= '#'.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function wtagblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_HASHTAG) { $o = ''; $r = tagadelic($uid,$count,$authors,$owner, $flags,$restrict,$type); if($r) { $c = q("select channel_address from channel where channel_id = %d limit 1", intval($uid) ); $o = '

' . t('Tags') . '

'; foreach($r as $rr) { $o .= '#'.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { $o = ''; $r = tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); if($r) { $c = q("select channel_address from channel where channel_id = %d limit 1", intval($uid) ); $o = '

' . t('Categories') . '

'; foreach($r as $rr) { $o .= ''.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function card_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { $o = ''; $r = card_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); if($r) { $c = q("select channel_address from channel where channel_id = %d limit 1", intval($uid) ); $o = '

' . t('Categories') . '

'; foreach($r as $rr) { $o .= ''.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function article_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) { $o = ''; $r = article_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type); if($r) { $c = q("select channel_address from channel where channel_id = %d limit 1", intval($uid) ); $o = '

' . t('Categories') . '

'; foreach($r as $rr) { $o .= ''.$rr[0].' ' . "\r\n"; } $o .= '
'; } return $o; } function dir_tagblock($link,$r) { $o = ''; if(! $r) $r = App::$data['directory_keywords'] ?? []; if($r) { $o = '

' . t('Keywords') . '

'; foreach($r as $rr) { $o .= ''.$rr['term'].' ' . "\r\n"; } $o .= '
'; } return $o; } /** * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" * We use the first person form when creating an activity, but the third person for use in activities * FIXME: There is no accounting for verb gender for languages where this is significant. We may eventually * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. */ function obj_verbs() { $verbs = array( 'has' => array( t('have'), t('has')), 'wants' => array( t('want'), t('wants')), 'likes' => array( t('like'), t('likes')), 'dislikes' => array( t('dislike'), t('dislikes')), ); $arr = array('verbs' => $verbs); call_hooks('obj_verbs', $arr); return $arr['verbs']; } function obj_verb_selector($current = '') { $verbs = obj_verbs(); $o = ''; return $o; } function get_things($profile_hash,$uid) { $sql_extra = permissions_sql($uid); $sql_extra_profile = (($profile_hash) ? " and obj_page = '" . $profile_hash . "' " : ''); $r = q("select * from obj where obj_channel = %d and obj_type = %d $sql_extra $sql_extra_profile order by obj_verb, obj_term", intval($uid), intval(TERM_OBJ_THING) ); $things = $sorted_things = null; $profile_hashes = array(); if($r) { // if no profile_hash was specified (display on profile page mode), match each of the things to a profile name // (list all my things mode). This is harder than it sounds. foreach($r as $rr) { $rr['profile_name'] = ''; if(! in_array($rr['obj_obj'],$profile_hashes)) $profile_hashes[] = $rr['obj_obj']; } if(! $profile_hash) { $exp = stringify_array($profile_hashes,true); $p = q("select profile_guid as hash, profile_name as name from profile where profile_guid in ( $exp ) "); if($p) { foreach($r as $rr) { foreach($p as $pp) { if($rr['obj_page'] == $pp['hash']) { $rr['profile_name'] == $pp['name']; } } } } } $things = array(); // Use the system obj_verbs array as a sort key, since we don't really // want an alphabetic sort. To change the order, use a plugin to // alter the obj_verbs() array or alter it in code. Unknown verbs come // after the known ones - in no particular order. $v = obj_verbs(); foreach($v as $k => $foo) $things[$k] = null; foreach($r as $rr) { $l = q("select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on liker = xchan_hash where target_type = '%s' and target_id = '%s' and channel_id = %d", dbesc(ACTIVITY_OBJ_THING), dbesc($rr['obj_obj']), intval($uid) ); for($x = 0; $x < count($l); $x ++) { $l[$x]['xchan_url'] = zid($l[$x]['xchan_url']); $l[$x]['xchan_photo_s'] = zid($l[$x]['xchan_photo_s']); } 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'], '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) { foreach($things as $k => $v) { if(is_array($things[$k])) { $sorted_things[$k] = $v; } } } } //logger('things: ' . print_r($sorted_things,true)); return $sorted_things; }