diff options
author | zotlabs <mike@macgirvin.com> | 2017-03-25 14:40:31 -0700 |
---|---|---|
committer | zotlabs <mike@macgirvin.com> | 2017-03-25 14:40:31 -0700 |
commit | e5e0a6b31f7870a46766c1e8ff87886fdb14b6de (patch) | |
tree | 7035d84f2edbc6b6efa435527ffdc3cd38b8027a /include | |
parent | 5c63f7dd58c5b3b88efcc47120c791f92527f720 (diff) | |
parent | 2115eb26a7fd2ca937286bd4e98ab74c7d6e9525 (diff) | |
download | volse-hubzilla-e5e0a6b31f7870a46766c1e8ff87886fdb14b6de.tar.gz volse-hubzilla-e5e0a6b31f7870a46766c1e8ff87886fdb14b6de.tar.bz2 volse-hubzilla-e5e0a6b31f7870a46766c1e8ff87886fdb14b6de.zip |
Merge branch 'dev' of https://github.com/redmatrix/hubzilla into xdev_merge
Diffstat (limited to 'include')
-rw-r--r-- | include/conversation.php | 64 | ||||
-rw-r--r-- | include/nav.php | 68 | ||||
-rw-r--r-- | include/text.php | 11 | ||||
-rw-r--r-- | include/widgets.php | 1778 |
4 files changed, 1845 insertions, 76 deletions
diff --git a/include/conversation.php b/include/conversation.php index 20ed4f216..b4f959afa 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -611,6 +611,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ $owner_photo = ''; $owner_name = ''; $sparkle = ''; + $is_new = false; if($mode === 'search' || $mode === 'community') { if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE))) @@ -679,6 +680,9 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ $has_tags = (($body['tags'] || $body['categories'] || $body['mentions'] || $body['attachments'] || $body['folders']) ? true : false); + if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) + $is_new = true; + $tmp_item = array( 'template' => $tpl, 'toplevel' => 'toplevel_item', @@ -735,6 +739,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional', $ 'wait' => t('Please wait'), 'thread_level' => 1, 'has_tags' => $has_tags, + 'is_new' => $is_new ); $arr = array('item' => $item, 'output' => $tmp_item); @@ -1082,8 +1087,8 @@ function builtin_activity_puller($item, &$conv_responses) { if((activity_match($item['verb'], $verb)) && ($item['id'] != $item['parent'])) { $name = (($item['author']['xchan_name']) ? $item['author']['xchan_name'] : t('Unknown')); $url = (($item['author_xchan'] && $item['author']['xchan_photo_s']) - ? '<a href="' . chanlink_hash($item['author_xchan']) . '">' . '<img class="dropdown-menu-img-xs" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" />' . $name . '</a>' - : '<a href="#" class="disabled">' . $name . '</a>' + ? '<a class="dropdown-item" href="' . chanlink_hash($item['author_xchan']) . '">' . '<img class="menu-img-1" src="' . zid($item['author']['xchan_photo_s']) . '" alt="' . urlencode($name) . '" />' . $name . '</a>' + : '<a class="dropdown-item" href="#" class="disabled">' . $name . '</a>' ); if(! $item['thr_parent']) @@ -1676,6 +1681,9 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ if (App::$is_sys) return; + if (get_pconfig($uid, 'system', 'noprofiletabs')) + return; + $channel = App::get_channel(); if (is_null($nickname)) @@ -1685,6 +1693,9 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ $uid = ((App::$profile['profile_uid']) ? App::$profile['profile_uid'] : local_channel()); $account_id = ((App::$profile['profile_uid']) ? App::$profile['channel_account_id'] : App::$channel['channel_account_id']); + if ($uid == local_channel()) + return; + if($uid == local_channel()) { $cal_link = ''; } @@ -1707,9 +1718,6 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ $has_webpages = (($r) ? true : false); - if (get_pconfig($uid, 'system', 'noprofiletabs')) - return; - if (x($_GET, 'tab')) $tab = notags(trim($_GET['tab'])); @@ -1723,6 +1731,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'channel') ? 'active' : ''), 'title' => t('Status Messages and Posts'), 'id' => 'status-tab', + 'icon' => 'home' ), ); @@ -1735,6 +1744,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'profile') ? 'active' : ''), 'title' => t('Profile Details'), 'id' => 'profile-tab', + 'icon' => 'user' ); } if ($p['view_storage']) { @@ -1744,6 +1754,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'photos') ? 'active' : ''), 'title' => t('Photo Albums'), 'id' => 'photo-tab', + 'icon' => 'photo' ); $tabs[] = array( 'label' => t('Files'), @@ -1751,6 +1762,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'cloud' || argv(0) == 'sharedwithme') ? 'active' : ''), 'title' => t('Files and Storage'), 'id' => 'files-tab', + 'icon' => 'folder-open' ); } @@ -1761,6 +1773,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'cal' || argv(0) == 'events') ? 'active' : ''), 'title' => t('Events'), 'id' => 'event-tab', + 'icon' => 'calendar' ); } @@ -1774,6 +1787,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'chat') ? 'active' : '' ), 'title' => t('Chatrooms'), 'id' => 'chat-tab', + 'icon' => 'comments-o' ); } } @@ -1787,6 +1801,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'bookmarks') ? 'active' : ''), 'title' => t('Saved Bookmarks'), 'id' => 'bookmarks-tab', + 'icon' => 'bookmark' ); } @@ -1797,27 +1812,34 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ 'sel' => ((argv(0) == 'webpages') ? 'active' : ''), 'title' => t('View Webpages'), 'id' => 'webpages-tab', + 'icon' => 'newspaper-o' ); } - if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) { - $tabs[] = array( - 'label' => t('Wikis'), - 'url' => z_root() . '/wiki/' . $nickname, - 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), - 'title' => t('Wiki'), - 'id' => 'wiki-tab', - ); + if ($p['view_wiki']) { + if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) { + $tabs[] = array( + 'label' => t('Wikis'), + 'url' => z_root() . '/wiki/' . $nickname, + 'sel' => ((argv(0) == 'wiki') ? 'active' : ''), + 'title' => t('Wiki'), + 'id' => 'wiki-tab', + 'icon' => 'pencil-square-o' + ); + } } - $arr = array('is_owner' => $is_owner, 'nickname' => $nickname, 'tab' => (($tab) ? $tab : false), 'tabs' => $tabs); call_hooks('profile_tabs', $arr); - $tpl = get_markup_template('common_tabs.tpl'); + $tpl = get_markup_template('profile_tabs.tpl'); - return replace_macros($tpl,array('$tabs' => $arr['tabs'])); + return replace_macros($tpl, array( + '$tabs' => $arr['tabs'], + '$name' => App::$profile['channel_name'], + '$thumb' => App::$profile['thumb'] + )); } @@ -1828,15 +1850,11 @@ function get_responses($conv_responses,$response_verbs,$ob,$item) { $ret[$v] = array(); $ret[$v]['count'] = ((x($conv_responses[$v],$item['mid'])) ? $conv_responses[$v][$item['mid']] : ''); $ret[$v]['list'] = ((x($conv_responses[$v],$item['mid'])) ? $conv_responses[$v][$item['mid'] . '-l'] : ''); - if(count($ret[$v]['list']) > MAX_LIKERS) { - $ret[$v]['list_part'] = array_slice($ret[$v]['list'], 0, MAX_LIKERS); - array_push($ret[$v]['list_part'], '<a href="#" data-toggle="modal" data-target="#' . $v . 'Modal-' - . (($ob) ? $ob->get_id() : $item['id']) . '"><b>' . t('View all') . '</b></a>'); - } else { - $ret[$v]['list_part'] = ''; - } $ret[$v]['button'] = get_response_button_text($v,$ret[$v]['count']); $ret[$v]['title'] = $conv_responses[$v]['title']; + if($ret[$v]['count'] > MAX_LIKERS) { + $ret[$v]['modal'] = true; + } } $count = 0; diff --git a/include/nav.php b/include/nav.php index a48419c45..2d12f2950 100644 --- a/include/nav.php +++ b/include/nav.php @@ -36,14 +36,15 @@ EOT; } elseif(remote_channel()) $observer = App::get_observer(); - + + require_once('include/conversation.php'); + $is_owner = (((local_channel()) && (\App::$profile['profile_uid'] == local_channel())) ? true : false); + $navapps[] = profile_tabs($a, $is_owner, \App::$profile['channel_address']); $myident = (($channel) ? $channel['xchan_addr'] : ''); $sitelocation = (($myident) ? $myident : App::get_hostname()); - - /** * * Provide a banner/logo/whatever @@ -55,10 +56,9 @@ EOT; if($banner === false) $banner = get_config('system','sitename'); + //the notifications template is in hdr.tpl App::$page['header'] .= replace_macros(get_markup_template('hdr.tpl'), array( - '$baseurl' => z_root(), - '$sitelocation' => $sitelocation, - '$banner' => $banner + //we could additionally use this to display important system notifications e.g. for updates )); $server_role = get_config('system','server_role'); @@ -66,21 +66,21 @@ EOT; $techlevel = get_account_techlevel(); // nav links: array of array('href', 'text', 'extra css classes', 'title') - $nav = Array(); + $nav = []; /** * Display login or logout */ - $nav['usermenu']=array(); + $nav['usermenu'] = []; $userinfo = null; - $nav['loginmenu']=array(); + $nav['loginmenu'] = []; if($observer) { - $userinfo = array( + $userinfo = [ 'icon' => $observer['xchan_photo_m'], 'name' => $observer['xchan_addr'], - ); + ]; } elseif(! $_SESSION['authenticated']) { @@ -96,38 +96,21 @@ EOT; if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select') && (! $basic)) $nav['channels'] = $chans; - $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); + $nav['logout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; // user menu - //$nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'),'channel_nav_btn'); - $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn'); + $nav['usermenu'][] = ['profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn']; + if(feature_enabled(local_channel(),'multi_profiles') && (! $basic)) - $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn'); + $nav['usermenu'][] = ['profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn']; else - $nav['usermenu'][] = Array('profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn'); - - //$nav['usermenu'][] = Array('photos/' . $channel['channel_address'], t('Photos'), "", t('Your photos'),'photos_nav_btn'); - //$nav['usermenu'][] = Array('cloud/' . $channel['channel_address'],t('Files'),"",t('Your files'),'cloud_nav_btn'); + $nav['usermenu'][] = ['profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn']; - //if((! $basic) && feature_enabled(local_channel(),'ajaxchat')) - // $nav['usermenu'][] = Array('chat/' . $channel['channel_address'], t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); - - - //require_once('include/menu.php'); - //$has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - //if(($has_bookmarks) && (! $basic)) { - // $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn'); - //} - - //if(feature_enabled($channel['channel_id'],'webpages') && (! $basic)) - // $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); - //if(feature_enabled($channel['channel_id'],'wiki') && (! $basic)) - // $nav['usermenu'][] = Array('wiki/' . $channel['channel_address'],t('Wikis'),"",t('Your wikis'),'wiki_nav_btn'); } else { if(! get_account_id()) { $nav['login'] = login(true,'main-login',false,false); - $nav['loginmenu'][] = Array('login',t('Login'),'',t('Sign in'),'login_nav_btn'); + $nav['loginmenu'][] = ['login',t('Login'),'',t('Sign in'),'login_nav_btn']; App::$page['content'] .= replace_macros(get_markup_template('nav_login.tpl'), [ '$nav' => $nav, @@ -137,7 +120,7 @@ EOT; } else - $nav['alogout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); + $nav['alogout'] = ['logout',t('Logout'), "", t('End this session'),'logout_nav_btn']; } @@ -159,7 +142,7 @@ EOT; } if(((get_config('system','register_policy') == REGISTER_OPEN) || (get_config('system','register_policy') == REGISTER_APPROVE)) && (! $_SESSION['authenticated'])) - $nav['register'] = array('register',t('Register'), "", t('Create an account'),'register_nav_btn'); + $nav['register'] = ['register',t('Register'), "", t('Create an account'),'register_nav_btn']; if(! get_config('system','hide_help')) { $help_url = z_root() . '/help?f=&cmd=' . App::$cmd; @@ -171,15 +154,10 @@ EOT; //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); + $nav['help'] = [$help_url, t('Help'), "", t('Help and documentation'), 'help_nav_btn', $context_help, $enable_context_help]; } - if(! $basic) - $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn'); - - $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'); + $nav['search'] = ['search', t('Search'), "", t('Search site @name, #tag, ?docs, content')]; /** @@ -246,15 +224,13 @@ EOT; $banner = get_config('system','sitename'); $x = array('nav' => $nav, 'usermenu' => $userinfo ); + call_hooks('nav', $x); // Not sure the best place to put this on the page. So I'm implementing it but leaving it // turned off until somebody discovers this and figures out a good location for it. $powered_by = ''; - // $powered_by = '<strong>red<img class="smiley" src="' . z_root() . '/images/rm-16.png" alt="r#" />matrix</strong>'; - - //app bin if(local_channel()) { if(get_pconfig(local_channel(), 'system','initial_import_system_apps') === false) { diff --git a/include/text.php b/include/text.php index 49b588f13..2b22df2ea 100644 --- a/include/text.php +++ b/include/text.php @@ -1456,11 +1456,10 @@ function format_hashtags(&$item) { continue; if(strpos($item['body'], $t['url'])) continue; - if($s) - $s .= ' '; + $s .= ' '; - $s .= '#<a href="' . zid($t['url']) . '" >' . $term . '</a>'; + $s .= '<span class="badge badge-pill badge-info"><i class="fa fa-hashtag"></i> <a class="text-white" href="' . zid($t['url']) . '" >' . $term . '</a></span>'; } } @@ -1480,11 +1479,9 @@ function format_mentions(&$item) { continue; if(strpos($item['body'], $t['url'])) continue; - if($s) - $s .= ' '; - - $s .= '@<a href="' . zid($t['url']) . '" >' . $term . '</a>'; + $s .= ' '; + $s .= '<span class="badge badge-pill badge-success"><i class="fa fa-at"></i> <a class="text-white" href="' . zid($t['url']) . '" >' . $term . '</a></span>'; } } diff --git a/include/widgets.php b/include/widgets.php new file mode 100644 index 000000000..c88c03f7e --- /dev/null +++ b/include/widgets.php @@ -0,0 +1,1778 @@ +<?php +/** + * @file include/widgets.php + * + * @brief This file contains the widgets. + */ + +require_once('include/dir_fns.php'); +require_once('include/contact_widgets.php'); +require_once('include/attach.php'); + + +function widget_profile($args) { + + $block = observer_prohibited(); + return profile_sidebar(App::$profile, $block, true); +} + +function widget_zcard($args) { + + $block = observer_prohibited(); + $channel = channelx_by_n(App::$profile_uid); + return get_zcard($channel,get_observer_hash(),array('width' => 875)); +} + + + + +// FIXME The problem with the next widget is that we don't have a search function for webpages that we can send the links to. +// Then we should also provide an option to search webpages and conversations. + +function widget_tagcloud($args) { + + $o = ''; + //$tab = 0; + + $uid = App::$profile_uid; + $count = ((x($args,'count')) ? intval($args['count']) : 24); + $flags = 0; + $type = TERM_CATEGORY; + + // FIXME there exists no $authors variable + $r = tagadelic($uid, $count, $authors, $owner, $flags, ITEM_TYPE_WEBPAGE, $type); + + if($r) { + $o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">'; + foreach($r as $rr) { + $o .= '<span class="tag'.$rr[2].'">'.$rr[0].'</span> ' . "\r\n"; + } + $o .= '</div></div>'; + } + return $o; +} + +function widget_collections($args) { + require_once('include/group.php'); + + $mode = ((array_key_exists('mode',$args)) ? $args['mode'] : 'conversation'); + switch($mode) { + case 'conversation': + $every = argv(0); + $each = argv(0); + $edit = true; + $current = $_REQUEST['gid']; + $abook_id = 0; + $wmode = 0; + break; + case 'connections': + $every = 'connections'; + $each = 'group'; + $edit = true; + $current = $_REQUEST['gid']; + $abook_id = 0; + $wmode = 0; + case 'groups': + $every = 'connections'; + $each = argv(0); + $edit = false; + $current = intval(argv(1)); + $abook_id = 0; + $wmode = 1; + break; + case 'abook': + $every = 'connections'; + $each = 'group'; + $edit = false; + $current = 0; + $abook_id = App::$poi['abook_xchan']; + $wmode = 1; + break; + default: + return ''; + break; + } + + return group_side($every, $each, $edit, $current, $abook_id, $wmode); +} + +function widget_suggestions($arr) { + + if((! local_channel()) || (! feature_enabled(local_channel(),'suggest'))) + return ''; + + require_once('include/socgraph.php'); + + $r = suggestion_query(local_channel(),get_observer_hash(),0,20); + + if(! $r) { + return; + } + + $arr = array(); + + // Get two random entries from the top 20 returned. + // We'll grab the first one and the one immediately following. + // This will throw some entropy intot he situation so you won't + // be looking at the same two mug shots every time the widget runs + + $index = ((count($r) > 2) ? mt_rand(0,count($r) - 2) : 0); + + for($x = $index; $x <= ($index+1); $x ++) { + $rr = $r[$x]; + if(! $rr['xchan_url']) + break; + + $connlnk = z_root() . '/follow/?url=' . $rr['xchan_addr']; + + $arr[] = array( + 'url' => chanlink_url($rr['xchan_url']), + 'profile' => $rr['xchan_url'], + 'name' => $rr['xchan_name'], + 'photo' => $rr['xchan_photo_m'], + 'ignlnk' => z_root() . '/directory?ignore=' . $rr['xchan_hash'], + 'conntxt' => t('Connect'), + 'connlnk' => $connlnk, + 'ignore' => t('Ignore/Hide') + ); + } + + $o = replace_macros(get_markup_template('suggest_widget.tpl'),array( + '$title' => t('Suggestions'), + '$more' => t('See more...'), + '$entries' => $arr + )); + + return $o; +} + + +function widget_follow($args) { + if(! local_channel()) + return ''; + + $uid = App::$channel['channel_id']; + $r = q("select count(*) as total from abook where abook_channel = %d and abook_self = 0 ", + intval($uid) + ); + if($r) + $total_channels = $r[0]['total']; + $limit = service_class_fetch($uid,'total_channels'); + if($limit !== false) { + $abook_usage_message = sprintf( t("You have %1$.0f of %2$.0f allowed connections."), $total_channels, $limit); + } + else { + $abook_usage_message = ''; + } + return replace_macros(get_markup_template('follow.tpl'),array( + '$connect' => t('Add New Connection'), + '$desc' => t('Enter channel address'), + '$hint' => t('Examples: bob@example.com, https://example.com/barbara'), + '$follow' => t('Connect'), + '$abook_usage_message' => $abook_usage_message + )); +} + + +function widget_notes($arr) { + if(! local_channel()) + return ''; + if(! feature_enabled(local_channel(),'private_notes')) + return ''; + + $text = get_pconfig(local_channel(),'notes','text'); + + $o = replace_macros(get_markup_template('notes.tpl'), array( + '$banner' => t('Notes'), + '$text' => $text, + '$save' => t('Save'), + )); + + return $o; +} + + +function widget_savedsearch($arr) { + if((! local_channel()) || (! feature_enabled(local_channel(),'savedsearch'))) + return ''; + + $search = ((x($_GET,'netsearch')) ? $_GET['netsearch'] : ''); + if(! $search) + $search = ((x($_GET,'search')) ? $_GET['search'] : ''); + + if(x($_GET,'searchsave') && $search) { + $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,ttype,term ) values ( %d, %d, '%s') ", + intval(local_channel()), + intval(TERM_SAVEDSEARCH), + dbesc($search) + ); + } + } + + if(x($_GET,'searchremove') && $search) { + q("delete from term where uid = %d and ttype = %d and term = '%s'", + intval(local_channel()), + intval(TERM_SAVEDSEARCH), + dbesc($search) + ); + $search = ''; + } + + $srchurl = App::$query_string; + + $srchurl = rtrim(preg_replace('/searchsave\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $hasq = ((strpos($srchurl,'?') !== false) ? true : false); + $srchurl = rtrim(preg_replace('/searchremove\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + + $srchurl = rtrim(preg_replace('/search\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = rtrim(preg_replace('/submit\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + + + $hasq = ((strpos($srchurl,'?') !== false) ? true : false); + $hasamp = ((strpos($srchurl,'&') !== false) ? true : false); + + if(($hasamp) && (! $hasq)) + $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); + + $o = ''; + + $r = q("select tid,term from term WHERE uid = %d and ttype = %d ", + intval(local_channel()), + intval(TERM_SAVEDSEARCH) + ); + + $saved = array(); + + if(count($r)) { + foreach($r as $rr) { + $saved[] = array( + 'id' => $rr['tid'], + 'term' => $rr['term'], + 'dellink' => z_root() . '/' . $srchurl . (($hasq || $hasamp) ? '' : '?f=') . '&searchremove=1&search=' . urlencode($rr['term']), + 'srchlink' => z_root() . '/' . $srchurl . (($hasq || $hasamp) ? '' : '?f=') . '&search=' . urlencode($rr['term']), + 'displayterm' => htmlspecialchars($rr['term'], ENT_COMPAT,'UTF-8'), + 'encodedterm' => urlencode($rr['term']), + 'delete' => t('Remove term'), + 'selected' => ($search==$rr['term']), + ); + } + } + + $tpl = get_markup_template("saved_searches.tpl"); + $o = replace_macros($tpl, array( + '$title' => t('Saved Searches'), + '$add' => t('add'), + '$searchbox' => searchbox($search, 'netsearch-box', $srchurl . (($hasq) ? '' : '?f='), true), + '$saved' => $saved, + )); + + return $o; +} + +function widget_sitesearch($arr) { + + $search = ((x($_GET,'search')) ? $_GET['search'] : ''); + + $srchurl = App::$query_string; + + $srchurl = rtrim(preg_replace('/search\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = rtrim(preg_replace('/submit\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + + + $hasq = ((strpos($srchurl,'?') !== false) ? true : false); + $hasamp = ((strpos($srchurl,'&') !== false) ? true : false); + + if(($hasamp) && (! $hasq)) + $srchurl = substr($srchurl,0,strpos($srchurl,'&')) . '?f=&' . substr($srchurl,strpos($srchurl,'&')+1); + + $o = ''; + + $saved = array(); + + $tpl = get_markup_template("sitesearch.tpl"); + $o = replace_macros($tpl, array( + '$title' => t('Search'), + '$searchbox' => searchbox($search, 'netsearch-box', $srchurl . (($hasq) ? '' : '?f='), false), + '$saved' => $saved, + )); + + return $o; +} + + + + + +function widget_filer($arr) { + if(! local_channel()) + return ''; + + + $selected = ((x($_REQUEST,'file')) ? $_REQUEST['file'] : ''); + + $terms = array(); + $r = q("select distinct term from term where uid = %d and ttype = %d order by term asc", + intval(local_channel()), + intval(TERM_FILE) + ); + if(! $r) + return; + + foreach($r as $rr) + $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : '')); + + return replace_macros(get_markup_template('fileas_widget.tpl'),array( + '$title' => t('Saved Folders'), + '$desc' => '', + '$sel_all' => (($selected == '') ? 'selected' : ''), + '$all' => t('Everything'), + '$terms' => $terms, + '$base' => z_root() . '/' . App::$cmd + )); +} + +function widget_archive($arr) { + + $o = ''; + + if(! App::$profile_uid) { + return ''; + } + + $uid = App::$profile_uid; + + if(! feature_enabled($uid,'archives')) + return ''; + + if(! perm_is_allowed($uid,get_observer_hash(),'view_stream')) + return ''; + + $wall = ((array_key_exists('wall', $arr)) ? intval($arr['wall']) : 0); + $style = ((array_key_exists('style', $arr)) ? $arr['style'] : 'select'); + $showend = ((get_pconfig($uid,'system','archive_show_end_date')) ? true : false); + $mindate = get_pconfig($uid,'system','archive_mindate'); + $visible_years = get_pconfig($uid,'system','archive_visible_years'); + if(! $visible_years) + $visible_years = 5; + + $url = z_root() . '/' . App::$cmd; + + $ret = list_post_dates($uid,$wall,$mindate); + + if(! count($ret)) + return ''; + + $cutoff_year = intval(datetime_convert('',date_default_timezone_get(),'now','Y')) - $visible_years; + $cutoff = ((array_key_exists($cutoff_year,$ret))? true : false); + + $o = replace_macros(get_markup_template('posted_date_widget.tpl'),array( + '$title' => t('Archives'), + '$size' => $visible_years, + '$cutoff_year' => $cutoff_year, + '$cutoff' => $cutoff, + '$url' => $url, + '$style' => $style, + '$showend' => $showend, + '$dates' => $ret + )); + return $o; +} + + +function widget_fullprofile($arr) { + + if(! App::$profile['profile_uid']) + return; + + $block = observer_prohibited(); + + return profile_sidebar(App::$profile, $block); +} + +function widget_shortprofile($arr) { + + if(! App::$profile['profile_uid']) + return; + + $block = observer_prohibited(); + + return profile_sidebar(App::$profile, $block, true, true); +} + + +function widget_categories($arr) { + + + if(App::$profile['profile_uid'] && (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_stream'))) + 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); + + return categories_widget($srchurl, $cat); + +} + +function widget_appcategories($arr) { + + if(! local_channel()) + return ''; + + $selected = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); + + $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); + $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); + + $srchurl = z_root() . '/apps'; + + $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 + and term.term != 'nav_featured_app' + 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) { + + + if((! App::$profile['profile_uid']) || (! App::$profile['channel_hash'])) + return ''; + if(! perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) + return ''; + + $limit = ((array_key_exists('limit', $arr)) ? intval($arr['limit']) : 50); + if(feature_enabled(App::$profile['profile_uid'], 'tagadelic')) + return wtagblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash'], 'wall'); + + return ''; +} + +function widget_catcloud_wall($arr) { + + + if((! App::$profile['profile_uid']) || (! App::$profile['channel_hash'])) + return ''; + if(! perm_is_allowed(App::$profile['profile_uid'], get_observer_hash(), 'view_stream')) + return ''; + + $limit = ((array_key_exists('limit',$arr)) ? intval($arr['limit']) : 50); + + return catblock(App::$profile['profile_uid'], $limit, '', App::$profile['channel_hash'], 'wall'); +} + + +function widget_affinity($arr) { + + if(! local_channel()) + return ''; + + // Get default cmin value from pconfig, but allow GET parameter to override + $cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); + $cmin = (($cmin) ? $cmin : 0); + $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $cmin); + + // Get default cmax value from pconfig, but allow GET parameter to override + $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); + $cmax = (($cmax) ? $cmax : 99); + $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $cmax); + + + if(feature_enabled(local_channel(),'affinity')) { + + $labels = array( + t('Me'), + t('Family'), + t('Friends'), + t('Acquaintances'), + t('All') + ); + call_hooks('affinity_labels',$labels); + $label_str = ''; + + if($labels) { + foreach($labels as $l) { + if($label_str) { + $label_str .= ", '|'"; + $label_str .= ", '" . $l . "'"; + } + else + $label_str .= "'" . $l . "'"; + } + } + + $tpl = get_markup_template('main_slider.tpl'); + $x = replace_macros($tpl,array( + '$val' => $cmin . ',' . $cmax, + '$refresh' => t('Refresh'), + '$labels' => $label_str, + )); + $arr = array('html' => $x); + call_hooks('main_slider',$arr); + return $arr['html']; + } + + return ''; +} + + +function widget_settings_menu($arr) { + + if(! local_channel()) + return; + + + $channel = App::get_channel(); + + $abook_self_id = 0; + + // Retrieve the 'self' address book entry for use in the auto-permissions link + + $role = get_pconfig(local_channel(),'system','permissions_role'); + + $abk = q("select abook_id from abook where abook_channel = %d and abook_self = 1 limit 1", + intval(local_channel()) + ); + if($abk) + $abook_self_id = $abk[0]['abook_id']; + + $x = q("select count(*) as total from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0 ", + dbesc($channel['channel_hash']) + ); + + $hublocs = (($x && $x[0]['total'] > 1) ? true : false); + + $tabs = array( + array( + 'label' => t('Account settings'), + 'url' => z_root().'/settings/account', + 'selected' => ((argv(1) === 'account') ? 'active' : ''), + ), + + array( + 'label' => t('Channel settings'), + 'url' => z_root().'/settings/channel', + 'selected' => ((argv(1) === 'channel') ? 'active' : ''), + ), + + ); + + if(get_account_techlevel() > 0 && get_features()) { + $tabs[] = array( + 'label' => t('Additional features'), + 'url' => z_root().'/settings/features', + 'selected' => ((argv(1) === 'features') ? 'active' : ''), + ); + } + + $tabs[] = array( + 'label' => t('Feature/Addon settings'), + 'url' => z_root().'/settings/featured', + 'selected' => ((argv(1) === 'featured') ? 'active' : ''), + ); + + $tabs[] = array( + 'label' => t('Display settings'), + 'url' => z_root().'/settings/display', + 'selected' => ((argv(1) === 'display') ? 'active' : ''), + ); + + if($hublocs) { + $tabs[] = array( + 'label' => t('Manage locations'), + 'url' => z_root() . '/locs', + 'selected' => ((argv(1) === 'locs') ? 'active' : ''), + ); + } + + $tabs[] = array( + 'label' => t('Export channel'), + 'url' => z_root() . '/uexport', + 'selected' => '' + ); + + $tabs[] = array( + 'label' => t('Connected apps'), + 'url' => z_root() . '/settings/oauth', + 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), + ); + + if(get_account_techlevel() > 2) { + $tabs[] = array( + 'label' => t('Guest Access Tokens'), + 'url' => z_root() . '/settings/tokens', + 'selected' => ((argv(1) === 'tokens') ? 'active' : ''), + ); + } + + if(feature_enabled(local_channel(),'permcats')) { + $tabs[] = array( + 'label' => t('Permission Groups'), + 'url' => z_root() . '/settings/permcats', + 'selected' => ((argv(1) === 'permcats') ? 'active' : ''), + ); + } + + + if($role === false || $role === 'custom') { + $tabs[] = array( + 'label' => t('Connection Default Permissions'), + 'url' => z_root() . '/connedit/' . $abook_self_id, + 'selected' => '' + ); + } + + if(feature_enabled(local_channel(),'premium_channel')) { + $tabs[] = array( + 'label' => t('Premium Channel Settings'), + 'url' => z_root() . '/connect/' . $channel['channel_address'], + 'selected' => '' + ); + } + + if(feature_enabled(local_channel(),'channel_sources')) { + $tabs[] = array( + 'label' => t('Channel Sources'), + 'url' => z_root() . '/sources', + 'selected' => '' + ); + } + + $tabtpl = get_markup_template("generic_links_widget.tpl"); + return replace_macros($tabtpl, array( + '$title' => t('Settings'), + '$class' => 'settings-widget', + '$items' => $tabs, + )); +} + + +function widget_mailmenu($arr) { + if (! local_channel()) + return; + + + return replace_macros(get_markup_template('message_side.tpl'), array( + '$title' => t('Private Mail Menu'), + '$combined'=>array( + 'label' => t('Combined View'), + 'url' => z_root() . '/mail/combined', + 'sel' => (argv(1) == 'combined'), + ), + '$inbox'=>array( + 'label' => t('Inbox'), + 'url' => z_root() . '/mail/inbox', + 'sel' => (argv(1) == 'inbox'), + ), + '$outbox'=>array( + 'label' => t('Outbox'), + 'url' => z_root() . '/mail/outbox', + 'sel' => (argv(1) == 'outbox'), + ), + '$new'=>array( + 'label' => t('New Message'), + 'url' => z_root() . '/mail/new', + 'sel'=> (argv(1) == 'new'), + ) + )); +} + + +function widget_conversations($arr) { + if (! local_channel()) + return; + + if(argc() > 1) { + + switch(argv(1)) { + case 'combined': + $mailbox = 'combined'; + $header = t('Conversations'); + break; + case 'inbox': + $mailbox = 'inbox'; + $header = t('Received Messages'); + break; + case 'outbox': + $mailbox = 'outbox'; + $header = t('Sent Messages'); + break; + default: + $mailbox = 'combined'; + $header = t('Conversations'); + break; + } + + require_once('include/message.php'); + + // private_messages_list() can do other more complicated stuff, for now keep it simple + $r = private_messages_list(local_channel(), $mailbox, App::$pager['start'], App::$pager['itemspage']); + + if(! $r) { + info( t('No messages.') . EOL); + return $o; + } + + $messages = array(); + + foreach($r as $rr) { + + $messages[] = array( + 'mailbox' => $mailbox, + 'id' => $rr['id'], + 'from_name' => $rr['from']['xchan_name'], + 'from_url' => chanlink_hash($rr['from_xchan']), + 'from_photo' => $rr['from']['xchan_photo_s'], + 'to_name' => $rr['to']['xchan_name'], + 'to_url' => chanlink_hash($rr['to_xchan']), + 'to_photo' => $rr['to']['xchan_photo_s'], + 'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'), + 'delete' => t('Delete conversation'), + 'body' => $rr['body'], + 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'), + 'seen' => $rr['seen'], + 'selected' => ((argv(2)) ? (argv(2) == $rr['id']) : ($r[0]['id'] == $rr['id'])) + ); + } + + $tpl = get_markup_template('mail_head.tpl'); + $o .= replace_macros($tpl, array( + '$header' => $header, + '$messages' => $messages + )); + + //$o .= alt_pager($a,count($r)); + + } + + return $o; +} + +function widget_eventstools($arr) { + if (! local_channel()) + return; + + return replace_macros(get_markup_template('events_tools_side.tpl'), array( + '$title' => t('Events Tools'), + '$export' => t('Export Calendar'), + '$import' => t('Import Calendar'), + '$submit' => t('Submit') + )); +} + +function widget_design_tools($arr) { + + // mod menu doesn't load a profile. For any modules which load a profile, check it. + // otherwise local_channel() is sufficient for permissions. + + if(App::$profile['profile_uid']) + if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) + return ''; + + if(! local_channel()) + return ''; + + return design_tools(); +} + +function widget_website_portation_tools($arr) { + + // mod menu doesn't load a profile. For any modules which load a profile, check it. + // otherwise local_channel() is sufficient for permissions. + + if(App::$profile['profile_uid']) + if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) + return ''; + + if(! local_channel()) + return ''; + + return website_portation_tools(); +} + +function widget_findpeople($arr) { + return findpeople_widget(); +} + + +function widget_photo_albums($arr) { + + if(! App::$profile['profile_uid']) + return ''; + $channelx = channelx_by_n(App::$profile['profile_uid']); + 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(),$sortkey,$direction); +} + + +function widget_vcard($arr) { + return vcard_from_xchan('', App::get_observer()); +} + + +/* + * The following directory widgets are only useful on the directory page + */ + + +function widget_dirsort($arr) { + return dir_sort_links(); +} + +function widget_dirtags($arr) { + return dir_tagblock(z_root() . '/directory', null); +} + +function widget_menu_preview($arr) { + if(! App::$data['menu_item']) + return; + require_once('include/menu.php'); + + return menu_render(App::$data['menu_item']); +} + +function widget_chatroom_list($arr) { + + + $r = Zotlabs\Lib\Chatroom::roomlist(App::$profile['profile_uid']); + + if($r) { + return replace_macros(get_markup_template('chatroomlist.tpl'), array( + '$header' => t('Chatrooms'), + '$baseurl' => z_root(), + '$nickname' => App::$profile['channel_address'], + '$items' => $r, + '$overview' => t('Overview') + )); + } +} + +function widget_chatroom_members() { + $o = replace_macros(get_markup_template('chatroom_members.tpl'), array( + '$header' => t('Chat Members') + )); + + return $o; +} + +function widget_wiki_list($arr) { + + $channel = channelx_by_n(App::$profile_uid); + + $wikis = Zotlabs\Lib\NativeWiki::listwikis($channel,get_observer_hash()); + + if($wikis) { + return replace_macros(get_markup_template('wikilist_widget.tpl'), array( + '$header' => t('Wiki List'), + '$channel' => $channel['channel_address'], + '$wikis' => $wikis['wikis'] + )); + } + return ''; +} + +function widget_wiki_pages($arr) { + + $channelname = ((array_key_exists('channel',$arr)) ? $arr['channel'] : ''); + $c = channelx_by_nick($channelname); + + $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 = Zotlabs\Lib\NativeWikiPage::page_list($c['channel_id'],get_observer_hash(),$arr['resource_id']); + + if($p['pages']) { + $pages = $p['pages']; + $w = $p['wiki']; + // Wiki item record is $w['wiki'] + $wikiname = $w['urlName']; + if (!$wikiname) { + $wikiname = ''; + } + } + } + $can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_wiki'); + + $can_delete = ((local_channel() && (local_channel() == \App::$profile['uid'])) ? true : false); + + return replace_macros(get_markup_template('wiki_page_list.tpl'), array( + '$hide' => $hide, + '$resource_id' => $arr['resource_id'], + '$not_refresh' => $not_refresh, + '$header' => t('Wiki Pages'), + '$channel' => $channelname, + '$wikiname' => $wikiname, + '$pages' => $pages, + '$canadd' => $can_create, + '$candel' => $can_delete, + '$addnew' => t('Add new page'), + '$pageName' => array('pageName', t('Page name')), + )); +} + +function widget_wiki_page_history($arr) { + + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + + $pageHistory = Zotlabs\Lib\NativeWikiPage::page_history(array('channel_id' => App::$profile_uid, 'observer_hash' => get_observer_hash(), 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + return replace_macros(get_markup_template('nwiki_page_history.tpl'), array( + '$pageHistory' => $pageHistory['history'], + '$permsWrite' => $arr['permsWrite'], + '$name_lbl' => t('Name'), + '$msg_label' => t('Message','wiki_history') + )); + +} + +function widget_bookmarkedchats($arr) { + + if(! feature_enabled(App::$profile['profile_uid'],'ajaxchat')) + return ''; + + $h = get_observer_hash(); + if(! $h) + return; + $r = q("select xchat_url, xchat_desc from xchat where xchat_xchan = '%s' order by xchat_desc", + dbesc($h) + ); + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']); + } + } + return replace_macros(get_markup_template('bookmarkedchats.tpl'),array( + '$header' => t('Bookmarked Chatrooms'), + '$rooms' => $r + )); +} + +function widget_suggestedchats($arr) { + + if(! feature_enabled(App::$profile['profile_uid'],'ajaxchat')) + return ''; + + // There are reports that this tool does not ever remove chatrooms on dead sites, + // and also will happily link to private chats which you cannot enter. + // For those reasons, it will be disabled until somebody decides it's worth + // fixing and comes up with a plan for doing so. + + return ''; + + + // probably should restrict this to your friends, but then the widget will only work + // if you are logged in locally. + + $h = get_observer_hash(); + if(! $h) + return; + $r = q("select xchat_url, xchat_desc, count(xchat_xchan) as total from xchat group by xchat_url, xchat_desc order by total desc, xchat_desc limit 24"); + if($r) { + for($x = 0; $x < count($r); $x ++) { + $r[$x]['xchat_url'] = zid($r[$x]['xchat_url']); + } + } + return replace_macros(get_markup_template('bookmarkedchats.tpl'),array( + '$header' => t('Suggested Chatrooms'), + '$rooms' => $r + )); +} + +function widget_item($arr) { + + $channel_id = 0; + if(array_key_exists('channel_id',$arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = App::$profile_uid; + if(! $channel_id) + return ''; + + + if((! $arr['mid']) && (! $arr['title'])) + return ''; + + if(! perm_is_allowed($channel_id, get_observer_hash(), 'view_pages')) + return ''; + + require_once('include/security.php'); + $sql_extra = item_permissions_sql($channel_id); + + if($arr['title']) { + $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) + ); + } + else { + $r = q("select * from item where mid = '%s' and uid = %d and item_type = " . intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1", + dbesc($arr['mid']), + intval($channel_id) + ); + } + + if(! $r) + return ''; + + xchan_query($r); + $r = fetch_post_tags($r, true); + + $o = prepare_page($r[0]); + return $o; +} + +function widget_clock($arr) { + + $miltime = 0; + if(isset($arr['military']) && $arr['military']) + $miltime = 1; + +$o = <<< EOT +<div class="widget"> +<h3 class="clockface"></h3> +<script> + +var timerID = null +var timerRunning = false + +function stopclock(){ + if(timerRunning) + clearTimeout(timerID) + timerRunning = false +} + +function startclock(){ + stopclock() + showtime() +} + +function showtime(){ + var now = new Date() + var hours = now.getHours() + var minutes = now.getMinutes() + var seconds = now.getSeconds() + var military = $miltime + var timeValue = "" + if(military) + timeValue = hours + else + timeValue = ((hours > 12) ? hours - 12 : hours) + timeValue += ((minutes < 10) ? ":0" : ":") + minutes +// timeValue += ((seconds < 10) ? ":0" : ":") + seconds + if(! military) + timeValue += (hours >= 12) ? " P.M." : " A.M." + $('.clockface').html(timeValue) + timerID = setTimeout("showtime()",1000) + timerRunning = true +} + +$(document).ready(function() { + startclock(); +}); + +</script> +</div> +EOT; +return $o; + +} + +/** + * @brief Widget to display a single photo. + * + * @param array $arr associative array with + * * \e string \b src URL of photo; URL must be an http or https URL + * * \e boolean \b zrl use zid in URL + * * \e string \b style CSS string + * + * @return string with parsed HTML + */ +function widget_photo($arr) { + + $style = $zrl = false; + + if(array_key_exists('src', $arr) && isset($arr['src'])) + $url = $arr['src']; + + if(strpos($url, 'http') !== 0) + return ''; + + if(array_key_exists('style', $arr) && isset($arr['style'])) + $style = $arr['style']; + + // ensure they can't sneak in an eval(js) function + + if(strpbrk($style, '(\'"<>') !== false) + $style = ''; + + if(array_key_exists('zrl', $arr) && isset($arr['zrl'])) + $zrl = (($arr['zrl']) ? true : false); + + if($zrl) + $url = zid($url); + + $o = '<div class="widget">'; + + $o .= '<img ' . (($zrl) ? ' class="zrl" ' : '') + . (($style) ? ' style="' . $style . '"' : '') + . ' src="' . $url . '" alt="' . t('photo/image') . '">'; + + $o .= '</div>'; + + return $o; +} + + +function widget_cover_photo($arr) { + + require_once('include/channel.php'); + $o = ''; + + if(App::$module == 'channel' && $_REQUEST['mid']) + return ''; + + $channel_id = 0; + if(array_key_exists('channel_id', $arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = App::$profile_uid; + if(! $channel_id) + return ''; + + $channel = channelx_by_n($channel_id); + + if(array_key_exists('style', $arr) && isset($arr['style'])) + $style = $arr['style']; + else + $style = 'width:100%; height: auto;'; + + // ensure they can't sneak in an eval(js) function + + if(strpbrk($style,'(\'"<>') !== false) + $style = ''; + + if(array_key_exists('title', $arr) && isset($arr['title'])) + $title = $arr['title']; + else + $title = $channel['channel_name']; + + if(array_key_exists('subtitle', $arr) && isset($arr['subtitle'])) + $subtitle = $arr['subtitle']; + else + $subtitle = str_replace('@','@',$channel['xchan_addr']); + + $c = get_cover_photo($channel_id,'html'); + + if($c) { + $photo_html = (($style) ? str_replace('alt=',' style="' . $style . '" alt=',$c) : $c); + + $o = replace_macros(get_markup_template('cover_photo_widget.tpl'),array( + '$photo_html' => $photo_html, + '$title' => $title, + '$subtitle' => $subtitle, + '$hovertitle' => t('Click to show more'), + )); + } + return $o; +} + + +function widget_photo_rand($arr) { + + require_once('include/photos.php'); + $style = false; + + if(array_key_exists('album', $arr) && isset($arr['album'])) + $album = $arr['album']; + else + $album = ''; + + $channel_id = 0; + if(array_key_exists('channel_id', $arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = App::$profile_uid; + if(! $channel_id) + return ''; + + $scale = ((array_key_exists('scale',$arr)) ? intval($arr['scale']) : 0); + + $ret = photos_list_photos(array('channel_id' => $channel_id),App::get_observer(),$album); + + $filtered = array(); + if($ret['success'] && $ret['photos']) + foreach($ret['photos'] as $p) + if($p['imgscale'] == $scale) + $filtered[] = $p['src']; + + if($filtered) { + $e = mt_rand(0, count($filtered) - 1); + $url = $filtered[$e]; + } + + if(strpos($url, 'http') !== 0) + return ''; + + if(array_key_exists('style', $arr) && isset($arr['style'])) + $style = $arr['style']; + + // ensure they can't sneak in an eval(js) function + + if(strpos($style,'(') !== false) + return ''; + + $url = zid($url); + + $o = '<div class="widget">'; + + $o .= '<img class="zrl" ' + . (($style) ? ' style="' . $style . '"' : '') + . ' src="' . $url . '" alt="' . t('photo/image') . '">'; + + $o .= '</div>'; + + return $o; +} + + +function widget_random_block($arr) { + + $channel_id = 0; + if(array_key_exists('channel_id',$arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = App::$profile_uid; + if(! $channel_id) + return ''; + + if(array_key_exists('contains',$arr)) + $contains = $arr['contains']; + + $o = ''; + + require_once('include/security.php'); + $sql_options = item_permissions_sql($channel_id); + + $randfunc = db_getfunc('RAND'); + + $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 . '%'), + intval(ITEM_TYPE_BLOCK) + ); + + if($r) { + $o = '<div class="widget bblock">'; + if($r[0]['title']) + $o .= '<h3>' . $r[0]['title'] . '</h3>'; + + $o .= prepare_text($r[0]['body'],$r[0]['mimetype']); + $o .= '</div>'; + } + + return $o; +} + + +function widget_rating($arr) { + + + $rating_enabled = get_config('system','rating_enabled'); + if(! $rating_enabled) { + return; + } + + if($arr['target']) + $hash = $arr['target']; + else + $hash = App::$poi['xchan_hash']; + + if(! $hash) + return; + + $url = ''; + $remote = false; + + if(remote_channel() && ! local_channel()) { + $ob = App::get_observer(); + if($ob && $ob['xchan_url']) { + $p = parse_url($ob['xchan_url']); + if($p) { + $url = $p['scheme'] . '://' . $p['host'] . (($p['port']) ? ':' . $p['port'] : ''); + $url .= '/rate?f=&target=' . urlencode($hash); + } + $remote = true; + } + } + + $self = false; + + if(local_channel()) { + $channel = App::get_channel(); + + if($hash == $channel['channel_hash']) + $self = true; + + head_add_js('ratings.js'); + + } + + + $o = '<div class="widget">'; + $o .= '<h3>' . t('Rating Tools') . '</h3>'; + + if((($remote) || (local_channel())) && (! $self)) { + if($remote) + $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="fa fa-pencil"></i> ' . t('Rate Me') . '</div>'; + } + + $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; + +} + +// used by site ratings pages to provide a return link +function widget_pubsites($arr) { + if(App::$poi) + return; + return '<div class="widget"><ul class="nav nav-pills"><li><a href="pubsites">' . t('Public Hubs') . '</a></li></ul></div>'; +} + + +function widget_forums($arr) { + + if(! local_channel()) + return ''; + + $o = ''; + + if(is_array($arr) && array_key_exists('limit',$arr)) + $limit = " limit " . intval($limit) . " "; + else + $limit = ''; + + $unseen = 0; + if(is_array($arr) && array_key_exists('unseen',$arr) && intval($arr['unseen'])) + $unseen = 1; + + $perms_sql = item_permissions_sql(local_channel()) . item_normal(); + + $xf = false; + + $x1 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'send_stream' and v = '0'", + intval(local_channel()) + ); + if($x1) { + $xc = ids_to_querystr($x1,'xchan',true); + $x2 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'tag_deliver' and v = '1' and xchan in (" . $xc . ") ", + intval(local_channel()) + ); + if($x2) + $xf = ids_to_querystr($x2,'xchan',true); + } + + $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); + + $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_deleted = 0 and abook_channel = %d $sql_extra order by xchan_name $limit ", + intval(local_channel()) + ); + if(! $r1) + return $o; + + $str = ''; + + // Trying to cram all this into a single query with joins and the proper group by's is tough. + // 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 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) { + $o .= '<div class="widget">'; + $o .= '<h3>' . t('Forums') . '</h3><ul class="nav nav-pills flex-column">'; + + foreach($r1 as $rr) { + if($unseen && (! intval($rr['unseen']))) + continue; + $o .= '<li class="nav-item"><a class="nav-link" href="network?f=&pf=1&cid=' . $rr['abook_id'] . '" ><span class="badge badge-default float-right">' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . '</span><img class ="menu-img-1" src="' . $rr['xchan_photo_s'] . '" /> ' . $rr['xchan_name'] . '</a></li>'; + } + $o .= '</ul></div>'; + } + return $o; + +} + + +function widget_activity($arr) { + + if(! local_channel()) + return ''; + + $o = ''; + + if(is_array($arr) && array_key_exists('limit',$arr)) + $limit = " limit " . intval($limit) . " "; + else + $limit = ''; + + $perms_sql = item_permissions_sql(local_channel()) . item_normal(); + + $r = q("select author_xchan from item where item_unseen = 1 and uid = %d $perms_sql", + intval(local_channel()) + ); + + $contributors = []; + $arr = []; + + if($r) { + foreach($r as $rv) { + if(array_key_exists($rv['author_xchan'],$contributors)) { + $contributors[$rv['author_xchan']] ++; + } + else { + $contributors[$rv['author_xchan']] = 1; + } + } + foreach($contributors as $k => $v) { + $arr[] = [ 'author_xchan' => $k, 'total' => $v ]; + } + usort($arr,'total_sort'); + xchan_query($arr); + } + + $x = [ 'entries' => $arr ]; + call_hooks('activity_widget',$x); + $arr = $x['entries']; + + if($arr) { + $o .= '<div class="widget">'; + $o .= '<h3>' . t('Activity','widget') . '</h3><ul class="nav nav-pills flex-column">'; + + foreach($arr as $rv) { + $o .= '<li class="nav-item"><a class="nav-link" href="network?f=&xchan=' . urlencode($rv['author_xchan']) . '" ><span class="badge badge-default float-right">' . ((intval($rv['total'])) ? intval($rv['total']) : '') . '</span><img src="' . $rv['author']['xchan_photo_s'] . '" class="menu-img-1" /> ' . $rv['author']['xchan_name'] . '</a></li>'; + } + $o .= '</ul></div>'; + } + return $o; + +} + + + + +function widget_tasklist($arr) { + + if (! local_channel()) + return; + + require_once('include/event.php'); + $o .= '<script>var tasksShowAll = 0; $(document).ready(function() { tasksFetch(); $("#tasklist-new-form").submit(function(event) { event.preventDefault(); $.post( "tasks/new", $("#tasklist-new-form").serialize(), function(data) { tasksFetch(); $("#tasklist-new-summary").val(""); } ); return false; } )});</script>'; + $o .= '<script>function taskComplete(id) { $.post("tasks/complete/"+id, function(data) { tasksFetch();}); } + function tasksFetch() { + $.get("tasks/fetch" + ((tasksShowAll) ? "/all" : ""), function(data) { + $(".tasklist-tasks").html(data.html); + }); + } + </script>'; + + $o .= '<div class="widget">' . '<h3>' . t('Tasks') . '</h3><div class="tasklist-tasks">'; + $o .= '</div><form id="tasklist-new-form" action="" ><input class="form-control" id="tasklist-new-summary" type="text" name="summary" value="" /></form>'; + $o .= '</div>'; + return $o; + +} + + +function widget_helpindex($arr) { + + $o .= '<div class="widget">'; + + $level_0 = get_help_content('sitetoc'); + if(! $level_0) + $level_0 = get_help_content('toc'); + + $level_0 = preg_replace('/\<ul(.*?)\>/','<ul class="nav nav-pills flex-column">',$level_0); + + $levels = array(); + + + if(argc() > 2) { + $path = ''; + for($x = 1; $x < argc(); $x ++) { + $path .= argv($x) . '/'; + $y = get_help_content($path . 'sitetoc'); + if(! $y) + $y = get_help_content($path . 'toc'); + if($y) + $levels[] = preg_replace('/\<ul(.*?)\>/','<ul class="nav nav-pills flex-column">',$y); + } + } + + if($level_0) + $o .= $level_0; + if($levels) { + foreach($levels as $l) { + $o .= '<br /><br />'; + $o .= $l; + } + } + + $o .= '</div>'; + + return $o; + +} + + + +function widget_admin($arr) { + + /* + * Side bar links + */ + + if(! is_site_admin()) { + return ''; + } + + $o = ''; + + // array( url, name, extra css classes ) + + $aside = array( + 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), + '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'), + 'plugins' => array(z_root() . '/admin/plugins/', t('Plugins'), 'plugins'), + 'themes' => array(z_root() . '/admin/themes/', t('Themes'), 'themes'), + 'queue' => array(z_root() . '/admin/queue', t('Inspect queue'), 'queue'), + 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'), + 'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync') + + ); + + /* get plugins admin page */ + + $r = q("SELECT * FROM addon WHERE plugin_admin = 1"); + + $plugins = array(); + if($r) { + foreach ($r as $h){ + $plugin = $h['aname']; + $plugins[] = array(z_root() . '/admin/plugins/' . $plugin, $plugin, 'plugin'); + // temp plugins with admin + App::$plugins_admin[] = $plugin; + } + } + + $logs = array(z_root() . '/admin/logs/', t('Logs'), 'logs'); + + $arr = array('links' => $aside,'plugins' => $plugins,'logs' => $logs); + call_hooks('admin_aside',$arr); + + $o .= replace_macros(get_markup_template('admin_aside.tpl'), array( + '$admin' => $aside, + '$admtxt' => t('Admin'), + '$plugadmtxt' => t('Plugin Features'), + '$plugins' => $plugins, + '$logtxt' => t('Logs'), + '$logs' => $logs, + '$h_pending' => t('Member registrations waiting for confirmation'), + '$admurl'=> z_root() . '/admin/' + )); + + return $o; + +} + + + +function widget_album($args) { + + $owner_uid = App::$profile_uid; + $sql_extra = permissions_sql($owner_uid); + + + if(! perm_is_allowed($owner_uid,get_observer_hash(),'view_storage')) + return ''; + + if($args['album']) + $album = $args['album']; + if($args['title']) + $title = $args['title']; + + /** + * This may return incorrect permissions if you have multiple directories of the same name. + * It is a limitation of the photo table using a name for a photo album instead of a folder hash + */ + + if($album) { + $x = q("select hash from attach where filename = '%s' and uid = %d limit 1", + dbesc($album), + intval($owner_uid) + ); + if($x) { + $y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); + if(! $y) + return ''; + } + } + + $order = 'DESC'; + + $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), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); + + //edit album name + $album_edit = null; + + $photos = array(); + if($r) { + $twist = 'rotright'; + foreach($r as $rr) { + + if($twist == 'rotright') + $twist = 'rotleft'; + else + $twist = 'rotright'; + + $ext = $phototypes[$rr['mimetype']]; + + $imgalt_e = $rr['filename']; + $desc_e = $rr['description']; + + $imagelink = (z_root() . '/photos/' . App::$profile['channel_address'] . '/image/' . $rr['resource_id']); + + + $photos[] = array( + 'id' => $rr['id'], + 'twist' => ' ' . $twist . rand(2,4), + 'link' => $imagelink, + 'title' => t('View Photo'), + 'src' => z_root() . '/photo/' . $rr['resource_id'] . '-' . $rr['imgscale'] . '.' .$ext, + 'alt' => $imgalt_e, + 'desc'=> $desc_e, + 'ext' => $ext, + 'hash'=> $rr['resource_id'], + 'unknown' => t('Unknown') + ); + } + } + + + $tpl = get_markup_template('photo_album.tpl'); + $o .= replace_macros($tpl, array( + '$photos' => $photos, + '$album' => (($title) ? $title : $album), + '$album_id' => rand(), + '$album_edit' => array(t('Edit Album'), $album_edit), + '$can_post' => false, + '$upload' => array(t('Upload'), z_root() . '/photos/' . App::$profile['channel_address'] . '/upload/' . bin2hex($album)), + '$order' => false, + '$upload_form' => $upload_form, + '$usage' => $usage_message + )); + + return $o; +} + |