','[',']'], ['%3c','%3e','%5b','%5d'], $s); } function file_tag_decode($s) { return str_replace(['%3c','%3e','%5b','%5d'], ['<','>','[',']'], $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 != '%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 != '%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) && count($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'); $channel = Channel::from_id($uid); if (! $channel) { return []; } if (! perm_is_allowed($uid, get_observer_hash(), 'view_stream')) { return []; } $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 = [$authors]; } $sql_options .= " and author_xchan in (" . stringify_array($authors, true) . ") "; } if ($owner) { $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' "; } // This needs fixing so that cloned groups will show categories originating // from other group clone instances - not just this one. For now, this is needed // to prevent a group from displaying personal categories of the post author; // when posted remotely. $urlFilter = ($type === TERM_CATEGORY && intval($channel['xchan_type']) === XCHAN_TYPE_GROUP) ? " and url like '" . Channel::url($channel) . "%%'" : ''; // 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 $urlFilter 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 []; } return Code\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 []; } $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 = [$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 []; } return Code\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 []; } $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 = [$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 []; } return Code\Text\Tagadelic::calc($r); } function pubtagblock($mode, $limit, $recent = 0, $safemode = 1, $type = TERM_HASHTAG) { $o = ''; $r = pub_tagadelic($mode, $limit, $recent, $safemode, $type); $link = z_root() . '/pubstream'; if ($r) { $o = '

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

'; foreach ($r as $rr) { $o .= '#' . $rr[0] . ' ' . "\r\n"; } $o .= '
'; } return $o; } function pub_tagadelic($mode, $limit, $recent, $safemode, $type) { $item_normal = item_normal(); $count = intval($limit); if (intval($mode) === PUBLIC_STREAM_SITE) { $uids = " and item_private = 0 and item_wall = 1 "; } else { $sys = Channel::get_system(); $uids = " and item.uid = " . intval($sys['channel_id']) . " "; $sql_extra = " and item_private = 0 "; } if ($recent) { $sql_extra .= " and item.created > '" . Time::convert('UTC', 'UTC', 'now - ' . intval($recent) . ' days ') . "' "; } if ($safemode) { $unsafetags = get_config('system', 'unsafepubtags', [ 'boobs', 'bot', 'rss', 'girl','girls', 'nsfw', 'sexy', 'nude' ]); if ($unsafetags) { $sql_extra .= " and not term.term in ( " . stringify_array($unsafetags, true) . ") "; } } // Fetch tags $r = q( "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 $sql_extra $uids $item_normal group by term order by total desc %s", intval($type), intval(TERM_OBJ_POST), intval(ITEM_TYPE_POST), ((intval($count)) ? "limit $count" : '') ); if (! $r) { return []; } return Code\Text\Tagadelic::calc($r); } function dir_tagadelic($count = 0, $hub = '', $type = 0, $safe = '') { $count = intval($count); $sql_extra = EMPTY_STR; if ($type) { return []; } 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' and hubloc_deleted = 0) $sql_extra 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 left join xchan on xtag_hash = xchan_hash where xtag_flags = 0 $sql_extra $safe group by xtag_term order by total desc %s", ((intval($count)) ? "limit $count" : '') ); } if (! $r) { return []; } return Code\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 []; } return Code\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 = ''; if (! Apps::system_app_installed($uid, 'Categories')) { return $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 = ''; $observer = get_observer_hash(); 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 = [ 'has' => [t('have'), t('has')], 'wants' => [t('want'), t('wants')], 'likes' => [t('like'), t('likes')], 'dislikes' => [t('dislike'), t('dislikes')], ]; $arr = ['verbs' => $verbs]; Hook::call('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 = (($profile_hash) ? " and obj_page = '" . $profile_hash . "' " : ''); $r = q( "select * from obj where obj_channel = %d and obj_type = %d $sql_extra order by obj_verb, obj_term", intval($uid), intval(TERM_OBJ_THING) ); $things = $sorted_things = null; $profile_hashes = []; 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 = []; // 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']] = []; } $things[$rr['obj_verb']][] = ['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 = []; 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; }