diff options
author | Mario <mario@mariovavti.com> | 2018-01-09 09:00:20 +0100 |
---|---|---|
committer | Mario <mario@mariovavti.com> | 2018-01-09 09:00:20 +0100 |
commit | 4f4d0e416eac87121898b8a27b1afa6065ff17a2 (patch) | |
tree | aae7f2582b2b9c6596dcbf87c06a836434140830 /Zotlabs | |
parent | 22c89b6c660e185d5c5c6362acf23b145d932d15 (diff) | |
parent | 8fde0f01b8472082158b38386046ed606bcfbc49 (diff) | |
download | volse-hubzilla-4f4d0e416eac87121898b8a27b1afa6065ff17a2.tar.gz volse-hubzilla-4f4d0e416eac87121898b8a27b1afa6065ff17a2.tar.bz2 volse-hubzilla-4f4d0e416eac87121898b8a27b1afa6065ff17a2.zip |
Merge branch '3.0RC'3.0
Diffstat (limited to 'Zotlabs')
99 files changed, 3039 insertions, 995 deletions
diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 49d478c5c..b335bf825 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -1,12 +1,21 @@ <?php - namespace Zotlabs\Access; -use Zotlabs\Lib as Zlib; - +/** + * @brief PermissionRoles class. + * + * @see Permissions + */ class PermissionRoles { + /** + * @brief PermissionRoles version. + * + * This must match the version in Permissions.php before permission updates can run. + * + * @return number + */ static public function version() { return 2; } @@ -23,12 +32,13 @@ class PermissionRoles { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = true; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'chat', 'post_like', 'republish' ]; - + 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'chat', 'post_like', 'republish' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); + break; case 'social_restricted': @@ -36,11 +46,11 @@ class PermissionRoles { $ret['default_collection'] = true; $ret['directory_publish'] = true; $ret['online'] = true; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'chat', 'post_like' ]; - + 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'chat', 'post_like' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); break; @@ -50,10 +60,11 @@ class PermissionRoles { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' ]; + 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); $ret['limits']['view_contacts'] = PERMS_SPECIFIC; $ret['limits']['view_storage'] = PERMS_SPECIFIC; @@ -65,12 +76,13 @@ class PermissionRoles { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver', - 'post_mail', 'post_like' , 'republish', 'chat' ]; - + 'post_mail', 'post_like' , 'republish', 'chat' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); + break; case 'forum_restricted': @@ -78,11 +90,10 @@ class PermissionRoles { $ret['default_collection'] = true; $ret['directory_publish'] = true; $ret['online'] = false; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', 'post_wall', 'post_comments', 'tag_deliver', 'post_mail', 'post_like' , 'chat' ]; - $ret['limits'] = PermissionLimits::Std_Limits(); break; @@ -92,12 +103,11 @@ class PermissionRoles { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'chat' ]; - + 'post_mail', 'post_like' , 'chat' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); $ret['limits']['view_profile'] = PERMS_SPECIFIC; $ret['limits']['view_contacts'] = PERMS_SPECIFIC; @@ -112,12 +122,11 @@ class PermissionRoles { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'republish' ]; - + 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' , 'republish' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); break; @@ -127,11 +136,11 @@ class PermissionRoles { $ret['default_collection'] = true; $ret['directory_publish'] = false; $ret['online'] = false; - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', - 'post_mail', 'post_like' , 'republish' ]; - + 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments', + 'post_mail', 'post_like' , 'republish' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); break; @@ -141,11 +150,10 @@ class PermissionRoles { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', - 'view_pages', 'view_wiki', 'post_like' , 'republish' ]; - + 'view_pages', 'view_wiki', 'post_like' , 'republish' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); break; @@ -155,13 +163,13 @@ class PermissionRoles { $ret['default_collection'] = false; $ret['directory_publish'] = true; $ret['online'] = false; - - $ret['perms_connect'] = [ + $ret['perms_connect'] = [ 'view_stream', 'view_profile', 'view_contacts', 'view_storage', 'view_pages', 'view_wiki', 'write_storage', 'write_pages', 'post_wall', 'post_comments', 'tag_deliver', - 'post_mail', 'post_like' , 'republish', 'chat', 'write_wiki' ]; - + 'post_mail', 'post_like' , 'republish', 'chat', 'write_wiki' + ]; $ret['limits'] = PermissionLimits::Std_Limits(); + break; case 'custom': @@ -170,11 +178,15 @@ class PermissionRoles { } $x = get_config('system','role_perms'); - // let system settings over-ride any or all + // let system settings over-ride any or all if($x && is_array($x) && array_key_exists($role,$x)) $ret = array_merge($ret,$x[$role]); - call_hooks('get_role_perms',$ret); + /** + * @hooks get_role_perms + * * \e array + */ + call_hooks('get_role_perms', $ret); return $ret; } @@ -187,10 +199,10 @@ class PermissionRoles { // \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1); if($perm === 'view_wiki') - \Zotlabs\Access\PermissionLimits::Set($uid,$perm,PERMS_PUBLIC); + \Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_PUBLIC); if($perm === 'write_wiki') - \Zotlabs\Access\PermissionLimits::Set($uid,$perm,PERMS_SPECIFIC); + \Zotlabs\Access\PermissionLimits::Set($uid, $perm, PERMS_SPECIFIC); // set autoperms here if applicable @@ -213,8 +225,6 @@ class PermissionRoles { if($c) { set_abconfig($uid,$c['channel_hash'],'autoperms',$perm,$value); } - - } // now set something for all existing connections. @@ -242,38 +252,44 @@ class PermissionRoles { } } - + /** + * @brief Array with translated role names and grouping. + * + * Return an associative array with grouped role names that can be used + * to create select groups like in \e field_select_grouped.tpl. + * + * @return array + */ static public function roles() { - $roles = [ + $roles = [ t('Social Networking') => [ - 'social' => t('Social - Mostly Public'), - 'social_restricted' => t('Social - Restricted'), + 'social' => t('Social - Mostly Public'), + 'social_restricted' => t('Social - Restricted'), 'social_private' => t('Social - Private') ], t('Community Forum') => [ - 'forum' => t('Forum - Mostly Public'), - 'forum_restricted' => t('Forum - Restricted'), + 'forum' => t('Forum - Mostly Public'), + 'forum_restricted' => t('Forum - Restricted'), 'forum_private' => t('Forum - Private') ], t('Feed Republish') => [ - 'feed' => t('Feed - Mostly Public'), + 'feed' => t('Feed - Mostly Public'), 'feed_restricted' => t('Feed - Restricted') ], t('Special Purpose') => [ - 'soapbox' => t('Special - Celebrity/Soapbox'), + 'soapbox' => t('Special - Celebrity/Soapbox'), 'repository' => t('Special - Group Repository') ], t('Other') => [ 'custom' => t('Custom/Expert Mode') ] - ]; - return $roles; + return $roles; } }
\ No newline at end of file diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 62c4af0ff..bca40a9c1 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -33,19 +33,22 @@ use Zotlabs\Lib as Zlib; */ class Permissions { + /** + * @brief Permissions version. + * + * This must match the version in PermissionRoles.php before permission updates can run. + * + * @return number + */ static public function version() { - // This must match the version in PermissionRoles.php before permission updates can run. return 2; } /** * @brief Return an array with Permissions. * - * @hooks permissions_list - * * \e array \b permissions - * * \e string \b filter - * @param string $filter (optional) only passed to hook permission_list - * @return Associative array with permissions and short description. + * @param string $filter (optional) only passed to hook permissions_list + * @return array Associative array with permissions and short description. */ static public function Perms($filter = '') { @@ -74,6 +77,11 @@ class Permissions { 'permissions' => $perms, 'filter' => $filter ]; + /** + * @hooks permissions_list + * * \e array \b permissions + * * \e string \b filter + */ call_hooks('permissions_list', $x); return($x['permissions']); @@ -84,9 +92,7 @@ class Permissions { * * e.g. you must be authenticated. * - * @hooks write_perms - * * \e array \b permissions - * @return Associative array with permissions and short description. + * @return array Associative array with permissions and short description. */ static public function BlockedAnonPerms() { @@ -99,6 +105,10 @@ class Permissions { } $x = ['permissions' => $res]; + /** + * @hooks write_perms + * * \e array \b permissions + */ call_hooks('write_perms', $x); return($x['permissions']); @@ -117,6 +127,7 @@ class Permissions { static public function FilledPerms($arr) { if(is_null($arr)) { btlogger('FilledPerms: null'); + $arr = []; } $everything = self::Perms(); @@ -138,7 +149,7 @@ class Permissions { * to [ 0 => ['name' => 'view_stream', 'value' => 1], ... ] * * @param array $arr associative perms array 'view_stream' => 1 - * @return Indexed array with elements that look like + * @return array Indexed array with elements that look like * * \e string \b name the perm name (e.g. view_stream) * * \e int \b value the value of the perm (e.g. 1) */ @@ -197,11 +208,10 @@ class Permissions { * @brief * * @param int $channel_id A channel id - * @return associative array + * @return array Associative array with * * \e array \b perms Permission array * * \e int \b automatic 0 or 1 */ - static public function connect_perms($channel_id) { $my_perms = []; diff --git a/Zotlabs/Daemon/Cron.php b/Zotlabs/Daemon/Cron.php index 65edbedfa..01c43262a 100644 --- a/Zotlabs/Daemon/Cron.php +++ b/Zotlabs/Daemon/Cron.php @@ -78,7 +78,7 @@ class Cron { // channels and sites that quietly vanished and prevent the directory from accumulating stale // or dead entries. - $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s", + $r = q("select channel_id from channel where channel_dirdate < %s - INTERVAL %s and channel_removed = 0", db_utcnow(), db_quoteinterval('30 DAY') ); diff --git a/Zotlabs/Daemon/Cron_weekly.php b/Zotlabs/Daemon/Cron_weekly.php index 5b185f475..d44400767 100644 --- a/Zotlabs/Daemon/Cron_weekly.php +++ b/Zotlabs/Daemon/Cron_weekly.php @@ -21,6 +21,21 @@ class Cron_weekly { mark_orphan_hubsxchans(); + // Find channels that were removed in the last three weeks, but + // haven't been finally cleaned up. These should be older than 10 + // days to ensure that "purgeall" messages have gone out or bounced + // or timed out. + + $r = q("select channel_id from channel where channel_removed = 1 and + channel_deleted > %s - INTERVAL %s and channel_deleted < %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('21 DAY'), + db_utcnow(), db_quoteinterval('10 DAY') + ); + if($r) { + foreach($r as $rv) { + channel_remove_final($rv['channel_id']); + } + } // get rid of really old poco records diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index d0175549b..ca6a7c08a 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -426,8 +426,10 @@ class Notifier { logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); stringify_array_elms($recipients); - if(! $recipients) + if(! $recipients) { + logger('no recipients'); return; + } // logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 11cbe4494..17d150250 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -12,6 +12,7 @@ class Queue { require_once('include/items.php'); require_once('include/bbcode.php'); + if(argc() > 1) $queue_id = argv(1); else diff --git a/Zotlabs/Daemon/Ratenotif.php b/Zotlabs/Daemon/Ratenotif.php index a94b89004..c7bf79854 100644 --- a/Zotlabs/Daemon/Ratenotif.php +++ b/Zotlabs/Daemon/Ratenotif.php @@ -88,6 +88,14 @@ class Ratenotif { 'msg' => json_encode($encoded_item) )); + + $x = q("select count(outq_hash) as total from outq where outq_delivered = 0"); + if(intval($x[0]['total']) > intval(get_config('system','force_queue_threshold',300))) { + logger('immediate delivery deferred.', LOGGER_DEBUG, LOG_INFO); + update_queue_item($hash); + continue; + } + $deliver[] = $hash; if(count($deliver) >= $deliveries_per_process) { diff --git a/Zotlabs/Daemon/Thumbnail.php b/Zotlabs/Daemon/Thumbnail.php new file mode 100644 index 000000000..e1f17c304 --- /dev/null +++ b/Zotlabs/Daemon/Thumbnail.php @@ -0,0 +1,78 @@ +<?php /** @file */ + +namespace Zotlabs\Daemon; + + +class Thumbnail { + + static public function run($argc,$argv) { + + if(! $argc == 2) + return; + + $c = q("select * from attach where hash = '%s' ", + dbesc($argv[1]) + ); + + if(! $c) + return; + + $attach = $c[0]; + + $preview_style = intval(get_config('system','thumbnail_security',0)); + $preview_width = intval(get_config('system','thumbnail_width',300)); + $preview_height = intval(get_config('system','thumbnail_height',300)); + + $p = [ + 'attach' => $attach, + 'preview_style' => $preview_style, + 'preview_width' => $preview_width, + 'preview_height' => $preview_height, + 'thumbnail' => null + ]; + + /** + * @hooks thumbnail + * * \e array \b attach + * * \e int \b preview_style + * * \e int \b preview_width + * * \e int \b preview_height + * * \e string \b thumbnail + */ + + call_hooks('thumbnail',$p); + if($p['thumbnail']) { + return; + } + + + $default_controller = null; + + $files = glob('Zotlabs/Thumbs/*.php'); + if($files) { + foreach($files as $f) { + $clsname = '\\Zotlabs\\Thumbs\\' . ucfirst(basename($f,'.php')); + if(class_exists($clsname)) { + $x = new $clsname(); + if(method_exists($x,'Match')) { + $matched = $x->Match($attach['filetype']); + if($matched) { + $x->Thumb($attach,$preview_style,$preview_width,$preview_height); + } + } + if(method_exists($x,'MatchDefault')) { + $default_matched = $x->MatchDefault(substr($attach['filetype'],0,strpos($attach['filetype'],'/'))); + if($default_matched) { + $default_controller = $x; + } + } + } + } + } + if(($default_controller) + && ((! file_exists(dbunescbin($attach['content']) . '.thumb')) + || (filectime(dbunescbin($attach['content']) . 'thumb') < (time() - 60)))) { + $default_controller->Thumb($attach,$preview_style,$preview_width,$preview_height); + } + } +} diff --git a/Zotlabs/Extend/Hook.php b/Zotlabs/Extend/Hook.php index c6f9ea850..81260ead6 100644 --- a/Zotlabs/Extend/Hook.php +++ b/Zotlabs/Extend/Hook.php @@ -2,7 +2,12 @@ namespace Zotlabs\Extend; +use App; +/** + * @brief Hook class. + * + */ class Hook { static public function register($hook,$file,$function,$version = 1,$priority = 0) { @@ -64,11 +69,14 @@ class Hook { return $r; } - // unregister all hooks with this file component. - // Useful for addon upgrades where you want to clean out old interfaces. - + /** + * @brief Unregister all hooks with this file component. + * + * Useful for addon upgrades where you want to clean out old interfaces. + * + * @param string $file + */ static public function unregister_by_file($file) { - $r = q("DELETE FROM hook WHERE file = '%s' ", dbesc($file) ); @@ -76,7 +84,6 @@ class Hook { return $r; } - /** * @brief Inserts a hook into a page request. * @@ -98,7 +105,6 @@ class Hook { * @param int $priority * currently not implemented in this function, would require the hook array to be resorted */ - static public function insert($hook, $fn, $version = 0, $priority = 0) { if(is_array($fn)) { $fn = serialize($fn); diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php index 379e78a59..2e9bb0703 100644 --- a/Zotlabs/Lib/ActivityStreams.php +++ b/Zotlabs/Lib/ActivityStreams.php @@ -2,6 +2,11 @@ namespace Zotlabs\Lib; +/** + * @brief ActivityStreams class. + * + * Parses an ActivityStream JSON string. + */ class ActivityStreams { public $data; @@ -19,9 +24,16 @@ class ActivityStreams { public $recips = null; public $raw_recips = null; + /** + * @brief Constructor for ActivityStreams. + * + * Takes a JSON string as parameter, decodes it and sets up this object. + * + * @param string $string + */ function __construct($string) { - $this->data = json_decode($string,true); + $this->data = json_decode($string, true); if($this->data) { $this->valid = true; } @@ -50,6 +62,11 @@ class ActivityStreams { } } + /** + * @brief Return if instantiated ActivityStream is valid. + * + * @return boolean Return true if the JSON string could be decoded. + */ function is_valid() { return $this->valid; } @@ -58,18 +75,26 @@ class ActivityStreams { $this->saved_recips = $arr; } - function collect_recips($base = '',$namespace = '') { + /** + * @brief Collects all recipients. + * + * @param string $base + * @param string $namespace (optional) default empty + * @return array + */ + function collect_recips($base = '', $namespace = '') { $x = []; - $fields = [ 'to','cc','bto','bcc','audience']; + $fields = [ 'to', 'cc', 'bto', 'bcc', 'audience']; foreach($fields as $f) { - $y = $this->get_compound_property($f,$base,$namespace); + $y = $this->get_compound_property($f, $base, $namespace); if($y) { - $x = array_merge($x,$y); + $x = array_merge($x, $y); if(! is_array($this->raw_recips)) $this->raw_recips = []; + $this->raw_recips[$f] = $x; } - } + } // not yet ready for prime time // $x = $this->expand($x,$base,$namespace); return $x; @@ -96,23 +121,30 @@ class ActivityStreams { } } - // @fixme de-duplicate + /// @fixme de-duplicate return $ret; } - function get_namespace($base,$namespace) { + /** + * @brief + * + * @param array $base + * @param string $namespace if not set return empty string + * @return string|NULL + */ + function get_namespace($base, $namespace) { if(! $namespace) return ''; $key = null; - foreach( [ $this->data, $base ] as $b ) { if(! $b) continue; - if(array_key_exists('@context',$b)) { + + if(array_key_exists('@context', $b)) { if(is_array($b['@context'])) { foreach($b['@context'] as $ns) { if(is_array($ns)) { @@ -135,19 +167,35 @@ class ActivityStreams { } } } + return $key; } - - function get_property_obj($property,$base = '',$namespace = '' ) { - $prefix = $this->get_namespace($base,$namespace); + /** + * @brief + * + * @param string $property + * @param array $base (optional) + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_property_obj($property, $base = '', $namespace = '') { + $prefix = $this->get_namespace($base, $namespace); if($prefix === null) - return null; + return null; + $base = (($base) ? $base : $this->data); $propname = (($prefix) ? $prefix . ':' : '') . $property; - return ((array_key_exists($propname,$base)) ? $base[$propname] : null); + + return ((array_key_exists($propname, $base)) ? $base[$propname] : null); } + /** + * @brief Fetches a property from an URL. + * + * @param string $url + * @return NULL|mixed + */ function fetch_property($url) { $redirects = 0; if(! check_siteallowed($url)) { @@ -155,44 +203,70 @@ class ActivityStreams { return null; } - $x = z_fetch_url($url,true,$redirects, + $x = z_fetch_url($url, true, $redirects, ['headers' => [ 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json' ]]); if($x['success']) - return json_decode($x['body'],true); + return json_decode($x['body'], true); + return null; } - function get_compound_property($property,$base = '',$namespace = '') { - $x = $this->get_property_obj($property,$base,$namespace); + /** + * @brief + * + * @param string $property + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_compound_property($property, $base = '', $namespace = '') { + $x = $this->get_property_obj($property, $base, $namespace); if($this->is_url($x)) { - $x = $this->fetch_property($x); + $x = $this->fetch_property($x); } + return $x; } + /** + * @brief Check if string starts with http. + * + * @param string $url + * @return boolean + */ function is_url($url) { - if(($url) && (! is_array($url)) && (strpos($url,'http') === 0)) { + if(($url) && (! is_array($url)) && (strpos($url, 'http') === 0)) { return true; } + return false; } - function get_primary_type($base = '',$namespace = '') { + /** + * @brief Gets the type property. + * + * @param array $base + * @param string $namespace (optional) default empty + * @return NULL|mixed + */ + function get_primary_type($base = '', $namespace = '') { if(! $base) $base = $this->data; - $x = $this->get_property_obj('type',$base,$namespace); + + $x = $this->get_property_obj('type', $base, $namespace); if(is_array($x)) { foreach($x as $y) { - if(strpos($y,':') === false) { + if(strpos($y, ':') === false) { return $y; } } } + return $x; } function debug() { - $x = var_export($this,true); + $x = var_export($this, true); return $x; } diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index f13fbe362..457b85b62 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -352,7 +352,7 @@ class Apps { break; default: if($config) - $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); + $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true); else $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); if($unset) @@ -401,11 +401,15 @@ class Apps { '$undelete' => ((local_channel() && $installed && $mode == 'edit') ? t('Undelete') : ''), '$deleted' => $papp['deleted'], '$feature' => (($papp['embed']) ? false : true), + '$pin' => (($papp['embed']) ? false : true), '$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true), + '$pinned' => ((strpos($papp['categories'], 'nav_pinned_app') === false) ? false : true), '$navapps' => (($mode == 'nav') ? true : false), '$order' => (($mode == 'nav-order') ? true : false), '$add' => t('Add to app-tray'), - '$remove' => t('Remove from app-tray') + '$remove' => t('Remove from app-tray'), + '$add_nav' => t('Pin to navbar'), + '$remove_nav' => t('Unpin from navbar') )); } @@ -498,25 +502,27 @@ class Apps { } } - static public function app_feature($uid,$app) { + static public function app_feature($uid,$app,$term) { $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) ); - $x = q("select * from term where otype = %d and oid = %d and term = 'nav_featured_app' limit 1", + $x = q("select * from term where otype = %d and oid = %d and term = '%s' limit 1", intval(TERM_OBJ_APP), - intval($r[0]['id']) + intval($r[0]['id']), + dbesc($term) ); if($x) { - q("delete from term where otype = %d and oid = %d and term = 'nav_featured_app'", + q("delete from term where otype = %d and oid = %d and term = '%s'", intval(TERM_OBJ_APP), - intval($x[0]['oid']) + intval($x[0]['oid']), + dbesc($term) ); } else { - store_item_tag($uid,$r[0]['id'],TERM_OBJ_APP,TERM_CATEGORY,'nav_featured_app',escape_tags(z_root() . '/apps/?f=&cat=nav_featured_app')); + store_item_tag($uid, $r[0]['id'], TERM_OBJ_APP, TERM_CATEGORY, $term, escape_tags(z_root() . '/apps/?f=&cat=' . $term)); } } @@ -531,16 +537,27 @@ class Apps { } - static public function app_list($uid, $deleted = false, $cat = '') { + static public function app_list($uid, $deleted = false, $cats = []) { if($deleted) $sql_extra = ""; else $sql_extra = " and app_deleted = 0 "; - if($cat) { - $r = q("select oid from term where otype = %d and term = '%s'", - intval(TERM_OBJ_APP), - dbesc($cat) + if($cats) { + + $cat_sql_extra = " and ( "; + + foreach($cats as $cat) { + if(strpos($cat_sql_extra, 'term')) + $cat_sql_extra .= "or "; + + $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; + } + + $cat_sql_extra .= ") "; + + $r = q("select oid from term where otype = %d $cat_sql_extra", + intval(TERM_OBJ_APP) ); if(! $r) return $r; @@ -616,7 +633,7 @@ class Apps { static function moveup($uid,$guid) { $syslist = array(); - $list = self::app_list($uid, false, 'nav_featured_app'); + $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = self::app_encode($li); @@ -657,7 +674,7 @@ class Apps { static function movedown($uid,$guid) { $syslist = array(); - $list = self::app_list($uid, false, 'nav_featured_app'); + $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = self::app_encode($li); diff --git a/Zotlabs/Lib/Chatroom.php b/Zotlabs/Lib/Chatroom.php index e1a9a10b3..e762620ae 100644 --- a/Zotlabs/Lib/Chatroom.php +++ b/Zotlabs/Lib/Chatroom.php @@ -2,22 +2,18 @@ namespace Zotlabs\Lib; /** - * @brief Chat related functions. + * @brief A class with chatroom related static methods. */ - - - class Chatroom { /** * @brief Creates a chatroom. * * @param array $channel * @param array $arr - * @return An associative array containing: - * - success: A boolean - * - message: (optional) A string + * @return array An associative array containing: + * * \e boolean \b success - A boolean success status + * * \e string \b message - (optional) A string */ - static public function create($channel, $arr) { $ret = array('success' => false); @@ -150,8 +146,8 @@ class Chatroom { } if(intval($x[0]['cr_expire'])) { - $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", - db_utcnow(), + $r = q("delete from chat where created < %s - INTERVAL %s and chat_room = %d", + db_utcnow(), db_quoteinterval( intval($x[0]['cr_expire']) . ' MINUTE' ), intval($x[0]['cr_id']) ); @@ -225,10 +221,16 @@ class Chatroom { } /** - * create a chat message via API. + * @brief Create a chat message via API. + * * It is the caller's responsibility to enter the room. - */ - + * + * @param int $uid + * @param int $room_id + * @param string $xchan + * @param string $text + * @return array + */ static public function message($uid, $room_id, $xchan, $text) { $ret = array('success' => false); @@ -245,12 +247,18 @@ class Chatroom { if(! $r) return $ret; - $arr = array( + $arr = [ 'chat_room' => $room_id, 'chat_xchan' => $xchan, 'chat_text' => $text - ); - + ]; + /** + * @hooks chat_message + * Called to create a chat message. + * * \e int \b chat_room + * * \e string \b chat_xchan + * * \e string \b chat_text + */ call_hooks('chat_message', $arr); $x = q("insert into chat ( chat_room, chat_xchan, created, chat_text ) diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php index 6e042feba..f9f22ba3a 100644 --- a/Zotlabs/Lib/Config.php +++ b/Zotlabs/Lib/Config.php @@ -1,4 +1,4 @@ -<?php /** @file */ +<?php namespace Zotlabs\Lib; @@ -14,7 +14,6 @@ class Config { * @param string $family * The category of the configuration value */ - static public function Load($family) { if(! array_key_exists($family, \App::$config)) \App::$config[$family] = array(); @@ -30,7 +29,7 @@ class Config { } \App::$config[$family]['config_loaded'] = true; } - } + } } /** @@ -47,8 +46,7 @@ class Config { * @return mixed * Return the set value, or false if the database update failed */ - - static public function Set($family,$key,$value) { + static public function Set($family, $key, $value) { // manage array value $dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); @@ -76,8 +74,8 @@ class Config { \App::$config[$family][$key] = $value; $ret = $value; } - return $ret; + return $ret; } /** @@ -88,25 +86,25 @@ class Config { * $key from a cached storage in App::$config[$family]. If a key is found in the * DB but does not exist in local config cache, pull it into the cache so we * do not have to hit the DB again for this item. - * + * * Returns false if not set. * * @param string $family * The category of the configuration value * @param string $key * The configuration key to query + * @param string $default (optional) default false * @return mixed Return value or false on error or if not set */ - - static public function Get($family,$key,$default = false) { + static public function Get($family, $key, $default = false) { if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family]))) self::Load($family); if(array_key_exists('config_loaded', \App::$config[$family])) { if(! array_key_exists($key, \App::$config[$family])) { - return $default; + return $default; } - return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key])) + return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key])) ? unserialize(\App::$config[$family][$key]) : \App::$config[$family][$key] ); @@ -127,17 +125,18 @@ class Config { * The configuration key to delete * @return mixed */ - - static public function Delete($family,$key) { + static public function Delete($family, $key) { $ret = false; if(array_key_exists($family, \App::$config) && array_key_exists($key, \App::$config[$family])) unset(\App::$config[$family][$key]); - $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'", + + $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s'", dbesc($family), dbesc($key) ); + return $ret; } @@ -154,12 +153,12 @@ class Config { * The configuration key to query * @return mixed */ - static private function get_from_storage($family,$key) { $ret = q("SELECT * FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1", dbesc($family), dbesc($key) ); + return $ret; } diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 21227893c..a7b4f28e8 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -778,10 +778,14 @@ class Enotify { // Call localize_item to get a one line status for activities. // This should set $item['localized'] to indicate we have a brief summary. + // and perhaps $item['shortlocalized'] for an even briefer summary localize_item($item); - if($item['localize']) { + if($item['shortlocalize']) { + $itemem_text = $item['shortlocalize']; + } + elseif($item['localize']) { $itemem_text = $item['localize']; } else { @@ -800,6 +804,8 @@ class Enotify { 'when' => relative_date($item['created']), 'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'), 'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])), + 'notify_id' => 'undefined', + 'thread_top' => (($item['item_thread_top']) ? true : false), 'message' => strip_tags(bbcode($itemem_text)) ); diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index 209a5ef3c..919c51276 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -68,6 +68,9 @@ class NativeWikiPage { return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); } + // backslashes won't work well in the javascript functions + $name = str_replace('\\','',$name); + // create an empty activity $arr = []; @@ -351,6 +354,7 @@ class NativeWikiPage { // fetch the most recently saved revision. $item = self::load_page($arr); + if(! $item) { return array('message' => t('Page not found'), 'success' => false); } diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 2a0b18aac..ec0792ce1 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -1,8 +1,21 @@ -<?php /** @file */ +<?php namespace Zotlabs\Lib; - +/** + * @brief Class for handling channel specific configurations. + * + * <b>PConfig</b> is used for channel specific configurations and takes a + * <i>channel_id</i> as identifier. It stores for example which features are + * enabled per channel. The storage is of size MEDIUMTEXT. + * + * @code{.php}$var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'key'); + * // with default value for non existent key + * $var = Zotlabs\Lib\PConfig::Get('uid', 'category', 'unsetkey', 'defaultvalue');@endcode + * + * The old (deprecated?) way to access a PConfig value is: + * @code{.php}$var = get_pconfig(local_channel(), 'category', 'key');@endcode + */ class PConfig { /** @@ -13,9 +26,8 @@ class PConfig { * * @param string $uid * The channel_id - * @return void|false Nothing or false if $uid is false + * @return void|false Nothing or false if $uid is null or false */ - static public function Load($uid) { if(is_null($uid) || $uid === false) return false; @@ -64,11 +76,11 @@ class PConfig { * The category of the configuration value * @param string $key * The configuration key to query - * @param boolean $instore (deprecated, without function) + * @param mixed $default (optional, default false) + * Default value to return if key does not exist * @return mixed Stored value or false if it does not exist */ - - static public function Get($uid,$family,$key,$default = false) { + static public function Get($uid, $family, $key, $default = false) { if(is_null($uid) || $uid === false) return $default; @@ -79,11 +91,10 @@ class PConfig { if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family]))) return $default; - return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) + return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) ? unserialize(\App::$config[$uid][$family][$key]) : \App::$config[$uid][$family][$key] ); - } /** @@ -102,12 +113,11 @@ class PConfig { * The value to store * @return mixed Stored $value or false */ - static public function Set($uid, $family, $key, $value) { - // this catches subtle errors where this function has been called + // this catches subtle errors where this function has been called // with local_channel() when not logged in (which returns false) - // and throws an error in array_key_exists below. + // and throws an error in array_key_exists below. // we provide a function backtrace in the logs so that we can find // and fix the calling function. @@ -132,7 +142,6 @@ class PConfig { dbesc($key), dbesc($dbvalue) ); - } else { @@ -142,7 +151,6 @@ class PConfig { dbesc($family), dbesc($key) ); - } // keep a separate copy for all variables which were @@ -178,7 +186,6 @@ class PConfig { * The configuration key to delete * @return mixed */ - static public function Delete($uid, $family, $key) { if(is_null($uid) || $uid === false) @@ -186,12 +193,12 @@ class PConfig { $ret = false; - if(array_key_exists($uid,\App::$config) - && is_array(\App::$config['uid']) - && array_key_exists($family,\App::$config['uid']) + if(array_key_exists($uid,\App::$config) + && is_array(\App::$config['uid']) + && array_key_exists($family,\App::$config['uid']) && array_key_exists($key, \App::$config[$uid][$family])) unset(\App::$config[$uid][$family][$key]); - + $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", intval($uid), dbesc($family), @@ -202,4 +209,3 @@ class PConfig { } } -
\ No newline at end of file diff --git a/Zotlabs/Lib/SConfig.php b/Zotlabs/Lib/SConfig.php index ca0d133b2..ab6f49025 100644 --- a/Zotlabs/Lib/SConfig.php +++ b/Zotlabs/Lib/SConfig.php @@ -2,8 +2,11 @@ namespace Zotlabs\Lib; -// account configuration storage is built on top of the under-utilised xconfig - +/** + * @brief Account configuration storage is built on top of the under-utilised xconfig. + * + * @see XConfig + */ class SConfig { static public function Load($server_id) { diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 67a507025..748edcdb7 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -38,7 +38,7 @@ class ThreadItem { $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); // Prepare the children - if(count($data['children'])) { + if($data['children']) { foreach($data['children'] as $item) { /* @@ -105,7 +105,17 @@ class ThreadItem { $mode = $conv->get_mode(); - $edlink = (($item['item_type'] == ITEM_TYPE_CARD) ? 'card_edit' : 'editpost'); + switch($item['item_type']) { + case ITEM_TYPE_CARD: + $edlink = 'card_edit'; + break; + case ITEM_TYPE_ARTICLE: + $edlink = 'article_edit'; + break; + default: + $edlink = 'editpost'; + break; + } if(local_channel() && $observer['xchan_hash'] === $item['author_xchan']) $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); @@ -186,7 +196,7 @@ class ThreadItem { $like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : ''); $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); - if (count($like_list) > MAX_LIKERS) { + if (($like_list) && (count($like_list) > MAX_LIKERS)) { $like_list_part = array_slice($like_list, 0, MAX_LIKERS); array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); } else { @@ -198,7 +208,7 @@ class ThreadItem { $dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : ''); $dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : ''); $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); - if (count($dislike_list) > MAX_LIKERS) { + if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) { $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); } else { @@ -303,7 +313,7 @@ class ThreadItem { $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : ''); - + @@ -360,6 +370,7 @@ class ThreadItem { 'unverified' => $unverified, 'forged' => $forged, 'location' => $location, + 'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'), 'attend_label' => t('Attend'), 'attend_title' => t('Attendance Options'), 'vote_label' => t('Vote'), diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php index 436723f8c..d0c964149 100644 --- a/Zotlabs/Lib/ThreadStream.php +++ b/Zotlabs/Lib/ThreadStream.php @@ -54,6 +54,14 @@ class ThreadStream { $this->profile_owner = local_channel(); $this->writable = true; break; + case 'pubstream': + $this->profile_owner = local_channel(); + $this->writable = ((local_channel()) ? true : false); + break; + case 'hq': + $this->profile_owner = local_channel(); + $this->writable = true; + break; case 'channel': $this->profile_owner = \App::$profile['profile_uid']; $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); @@ -63,6 +71,11 @@ class ThreadStream { $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); $this->reload = $_SESSION['return_url']; break; + case 'articles': + $this->profile_owner = \App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); + $this->reload = $_SESSION['return_url']; + break; case 'display': // in this mode we set profile_owner after initialisation (from conversation()) and then // pull some trickery which allows us to re-invoke this function afterward @@ -179,6 +192,10 @@ class ThreadStream { $item->set_commentable(can_comment_on_post($ob_hash,$item->data)); } } + if($this->mode === 'pubstream' && (! local_channel())) { + $item->set_commentable(false); + } + require_once('include/channel.php'); $item->set_conversation($this); diff --git a/Zotlabs/Lib/XConfig.php b/Zotlabs/Lib/XConfig.php index bf78c360f..c5a108ac9 100644 --- a/Zotlabs/Lib/XConfig.php +++ b/Zotlabs/Lib/XConfig.php @@ -2,7 +2,26 @@ namespace Zotlabs\Lib; - +/** + * @brief Class for handling observer's config. + * + * <b>XConfig</b> is comparable to <i>PConfig</i>, except that it uses <i>xchan</i> + * (an observer hash) as an identifier. + * + * <b>XConfig</b> is used for observer specific configurations and takes a + * <i>xchan</i> as identifier. + * The storage is of size MEDIUMTEXT. + * + * @code{.php}$var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'key'); + * // with default value for non existent key + * $var = Zotlabs\Lib\XConfig::Get('xchan', 'category', 'unsetkey', 'defaultvalue');@endcode + * + * The old (deprecated?) way to access a XConfig value is: + * @code{.php}$observer = App::get_observer_hash(); + * if ($observer) { + * $var = get_xconfig($observer, 'category', 'key'); + * }@endcode + */ class XConfig { /** @@ -15,7 +34,6 @@ class XConfig { * The observer's hash * @return void|false Returns false if xchan is not set */ - static public function Load($xchan) { if(! $xchan) @@ -56,9 +74,9 @@ class XConfig { * The category of the configuration value * @param string $key * The configuration key to query + * @param boolean $default (optional) default false * @return mixed Stored $value or false if it does not exist */ - static public function Get($xchan, $family, $key, $default = false) { if(! $xchan) @@ -70,7 +88,7 @@ class XConfig { if((! array_key_exists($family, \App::$config[$xchan])) || (! array_key_exists($key, \App::$config[$xchan][$family]))) return $default; - return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key])) + return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key])) ? unserialize(\App::$config[$xchan][$family][$key]) : \App::$config[$xchan][$family][$key] ); @@ -82,7 +100,6 @@ class XConfig { * Stores a config value ($value) in the category ($family) under the key ($key) * for the observer's $xchan hash. * - * * @param string $xchan * The observer's hash * @param string $family @@ -93,7 +110,6 @@ class XConfig { * The value to store * @return mixed Stored $value or false */ - static public function Set($xchan, $family, $key, $value) { // manage array value @@ -106,7 +122,7 @@ class XConfig { if(! array_key_exists($family, \App::$config[$xchan])) \App::$config[$xchan][$family] = array(); - $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ", + $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' )", dbesc($xchan), dbesc($family), dbesc($key), @@ -126,6 +142,7 @@ class XConfig { if($ret) return $value; + return $ret; } @@ -143,11 +160,11 @@ class XConfig { * The configuration key to delete * @return mixed */ - static public function Delete($xchan, $family, $key) { if(x(\App::$config[$xchan][$family], $key)) unset(\App::$config[$xchan][$family][$key]); + $ret = q("DELETE FROM xconfig WHERE xchan = '%s' AND cat = '%s' AND k = '%s'", dbesc($xchan), dbesc($family), diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index e164875e8..ad1c8b8cd 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -176,11 +176,18 @@ class Acl extends \Zotlabs\Web\Controller { $extra_channels_sql = " OR (abook_channel IN ($extra_channels_sql)) and abook_hidden = 0 "; - // Add atokens belonging to the local channel @TODO restrict by search + // Add atokens belonging to the local channel + + if($search) { + $sql_extra_atoken = "AND ( atoken_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . ") "; + } + else { + $sql_extra_atoken = ''; + } $r2 = null; - $r1 = q("select * from atoken where atoken_uid = %d", + $r1 = q("select * from atoken where atoken_uid = %d $sql_extra_atoken", intval(local_channel()) ); diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index d3d058c53..a9db1ad55 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -61,8 +61,10 @@ class Site { $maxloadavg = ((x($_POST,'maxloadavg')) ? intval(trim($_POST['maxloadavg'])) : 50); $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); $verify_email = ((x($_POST,'verify_email')) ? 1 : 0); - $techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0); - $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); + $techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0); + $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); + $thumbnail_security = ((x($_POST,'thumbnail_security')) ? intval($_POST['thumbnail_security']) : 0); + $force_queue = ((intval($_POST['force_queue']) > 0) ? intval($_POST['force_queue']) : 300); $techlevel = null; if(array_key_exists('techlevel', $_POST)) @@ -84,7 +86,7 @@ class Site { set_config('system', 'from_email', $from_email); set_config('system', 'from_email_name' , $from_email_name); set_config('system', 'imagick_convert_path' , $imagick_path); - + set_config('system', 'thumbnail_security' , $thumbnail_security); set_config('system', 'techlevel_lock', $techlevel_lock); @@ -128,6 +130,7 @@ class Site { set_config('system','allowed_sites', $allowed_sites); set_config('system','publish_all', $force_publish); set_config('system','disable_discover_tab', $disable_discover_tab); + set_config('system','force_queue_threshold', $force_queue); if ($global_directory == '') { del_config('system', 'directory_submit_url'); } else { @@ -248,6 +251,7 @@ class Site { ); $discover_tab = get_config('system','disable_discover_tab'); + // $disable public streams by default if($discover_tab === false) $discover_tab = 1; @@ -318,8 +322,10 @@ class Site { '$timeout' => array('timeout', t("Network timeout"), (x(get_config('system','curl_timeout'))?get_config('system','curl_timeout'):60), t("Value is in seconds. Set to 0 for unlimited (not recommended).")), '$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")), '$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")), + '$force_queue' => array('force_queue', t("Queue Threshold"), get_config('system','force_queue_threshold',300), t("Always defer immediate delivery if queue contains more than this number of entries.")), '$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")), '$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")), + '$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")), '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")), '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')), '$form_security_token' => get_form_security_token("admin_site"), diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php index 5c0667357..64d4628ae 100644 --- a/Zotlabs/Module/Appman.php +++ b/Zotlabs/Module/Appman.php @@ -64,7 +64,11 @@ class Appman extends \Zotlabs\Web\Controller { } if($_POST['feature']) { - Zlib\Apps::app_feature(local_channel(),$papp); + Zlib\Apps::app_feature(local_channel(), $papp, $_POST['feature']); + } + + if($_POST['pin']) { + Zlib\Apps::app_feature(local_channel(), $papp, $_POST['pin']); } if($_SESSION['return_url']) diff --git a/Zotlabs/Module/Apporder.php b/Zotlabs/Module/Apporder.php index 956548d1f..a9f66ba69 100644 --- a/Zotlabs/Module/Apporder.php +++ b/Zotlabs/Module/Apporder.php @@ -18,7 +18,7 @@ class Apporder extends \Zotlabs\Web\Controller { nav_set_selected('Order Apps'); $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), false, 'nav_featured_app'); + $list = Zlib\Apps::app_list(local_channel(), false, ['nav_featured_app', 'nav_pinned_app']); if($list) { foreach($list as $li) { $syslist[] = Zlib\Apps::app_encode($li); @@ -31,14 +31,20 @@ class Apporder extends \Zotlabs\Web\Controller { $syslist = Zlib\Apps::app_order(local_channel(),$syslist); foreach($syslist as $app) { - $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); + if(strpos($app['categories'],'nav_pinned_app') !== false) { + $navbar_apps[] = Zlib\Apps::app_render($app,'nav-order'); + } + else { + $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); + } } return replace_macros(get_markup_template('apporder.tpl'), [ - '$header' => t('Change Order of Navigation Apps'), - '$desc' => t('Use arrows to move the corresponding app up or down in the display list'), - '$nav_apps' => $nav_apps + '$header' => [t('Change Order of Pinned Navbar Apps'), t('Change Order of App Tray Apps')], + '$desc' => [t('Use arrows to move the corresponding app left (top) or right (bottom) in the navbar'), t('Use arrows to move the corresponding app up or down in the app tray')], + '$nav_apps' => $nav_apps, + '$navbar_apps' => $navbar_apps ] ); } diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 2f61f2932..c672ea467 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -22,7 +22,8 @@ class Apps extends \Zotlabs\Web\Controller { if(local_channel()) { Zlib\Apps::import_system_apps(); $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $_GET['cat']); + $cat = ((array_key_exists('cat',$_GET) && $_GET['cat']) ? [ escape_tags($_GET['cat']) ] : ''); + $list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $cat); if($list) { foreach($list as $x) { $syslist[] = Zlib\Apps::app_encode($x); @@ -43,7 +44,7 @@ class Apps extends \Zotlabs\Web\Controller { return replace_macros(get_markup_template('myapps.tpl'), array( '$sitename' => get_config('system','sitename'), - '$cat' => ((array_key_exists('cat',$_GET) && $_GET['cat']) ? escape_tags($_GET['cat']) : ''), + '$cat' => $cat, '$title' => t('Apps'), '$apps' => $apps, '$authed' => ((local_channel()) ? true : false), diff --git a/Zotlabs/Module/Article_edit.php b/Zotlabs/Module/Article_edit.php new file mode 100644 index 000000000..758c1db2e --- /dev/null +++ b/Zotlabs/Module/Article_edit.php @@ -0,0 +1,138 @@ +<?php +namespace Zotlabs\Module; + +require_once('include/channel.php'); +require_once('include/acl_selectors.php'); +require_once('include/conversation.php'); + +class Article_edit extends \Zotlabs\Web\Controller { + + + function get() { + + // Figure out which post we're editing + $post_id = ((argc() > 1) ? intval(argv(1)) : 0); + + if(! $post_id) { + notice( t('Item not found') . EOL); + return; + } + + $itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1", + intval($post_id), + intval(ITEM_TYPE_ARTICLE) + ); + if($itm) { + $item_id = q("select * from iconfig where cat = 'system' and k = 'ARTICLE' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if($item_id) + $card_title = $item_id[0]['v']; + } + else { + notice( t('Item not found') . EOL); + return; + } + + $owner = $itm[0]['uid']; + $uid = local_channel(); + + $observer = \App::get_observer(); + + $channel = channelx_by_n($owner); + if(! $channel) { + notice( t('Channel not found.') . EOL); + return; + } + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = (($uid && $uid == $owner) ? true : false); + + $o = ''; + + + + $category = ''; + $catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : ''); + + if ($catsenabled){ + $itm = fetch_post_tags($itm); + + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); + + foreach ($cats as $cat) { + if (strlen($category)) + $category .= ', '; + $category .= $cat['term']; + } + } + + if($itm[0]['attach']) { + $j = json_decode($itm[0]['attach'],true); + if($j) { + foreach($j as $jj) { + $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n"; + } + } + } + + + $mimetype = $itm[0]['mimetype']; + + $content = $itm[0]['body']; + + + + $rp = 'articles/' . $channel['channel_address']; + + $x = array( + 'nickname' => $channel['channel_address'], + 'bbco_autocomplete'=> 'bbcode', + 'return_path' => $rp, + 'webpage' => ITEM_TYPE_ARTICLE, + 'button' => t('Edit'), + 'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'), + 'weblink' => t('Insert web link'), + 'hide_voting' => false, + 'hide_future' => false, + 'hide_location' => false, + 'hide_expire' => false, + 'showacl' => true, + 'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')), + 'permissions' => $itm[0], + 'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'), + 'ptyp' => $itm[0]['type'], + 'mimeselect' => false, + 'mimetype' => $itm[0]['mimetype'], + 'body' => undo_post_tagging($content), + 'post_id' => $post_id, + 'visitor' => true, + 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), + 'placeholdertitle' => t('Title (optional)'), + 'pagetitle' => $card_title, + 'profile_uid' => (intval($channel['channel_id'])), + 'catsenabled' => $catsenabled, + 'category' => $category, + 'bbcode' => (($mimetype == 'text/bbcode') ? true : false) + ); + + $editor = status_editor($a, $x); + + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Article'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$id' => $itm[0]['id'], + '$editor' => $editor + )); + + return $o; + + } + +} diff --git a/Zotlabs/Module/Articles.php b/Zotlabs/Module/Articles.php new file mode 100644 index 000000000..25daca81d --- /dev/null +++ b/Zotlabs/Module/Articles.php @@ -0,0 +1,187 @@ +<?php +namespace Zotlabs\Module; + +require_once('include/channel.php'); +require_once('include/conversation.php'); +require_once('include/acl_selectors.php'); + + +class Articles extends \Zotlabs\Web\Controller { + + function init() { + + if(argc() > 1) + $which = argv(1); + else + return; + + profile_load($which); + + } + + function get($update = 0, $load = false) { + + if(observer_prohibited(true)) { + return login(); + } + + if(! \App::$profile) { + notice( t('Requested profile is not available.') . EOL ); + \App::$error = 404; + return; + } + + if(! feature_enabled(\App::$profile_uid,'articles')) { + return; + } + + nav_set_selected(t('Cards')); + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); + + + $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); + + if($category) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); + } + + + $which = argv(1); + + $selected_card = ((argc() > 2) ? argv(2) : ''); + + $_SESSION['return_url'] = \App::$query_string; + + $uid = local_channel(); + $owner = \App::$profile_uid; + $observer = \App::get_observer(); + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'view_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = ($uid && $uid == $owner); + + $channel = channelx_by_n($owner); + + if($channel) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + } + else { + $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + } + + + + if(perm_is_allowed($owner,$ob_hash,'write_pages')) { + + $x = [ + 'webpage' => ITEM_TYPE_ARTICLE, + 'is_owner' => true, + 'content_label' => t('Add Article'), + 'button' => t('Create'), + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] + || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, + \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? true : false), + 'visitor' => true, + 'hide_location' => false, + 'hide_voting' => false, + 'profile_uid' => intval($owner), + 'mimetype' => 'text/bbcode', + 'mimeselect' => false, + 'layoutselect' => false, + 'expanded' => false, + 'novoting' => false, + 'catsenabled' => feature_enabled($owner,'categories'), + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true + ]; + + if($_REQUEST['title']) + $x['title'] = $_REQUEST['title']; + if($_REQUEST['body']) + $x['body'] = $_REQUEST['body']; + $editor = status_editor($a,$x); + + } + else { + $editor = ''; + } + + + $sql_extra = item_permissions_sql($owner); + + if($selected_card) { + $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1", + dbesc($selected_card) + ); + if($r) { + $sql_extra .= "and item.id = " . intval($r[0]['iid']) . " "; + } + } + + $r = q("select * from item + where item.uid = %d and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_ARTICLE) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) 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 "; + + if($r) { + + $parents_str = ids_to_querystr($r,'id'); + + $items = q("SELECT item.*, item.id AS item_id + FROM item + WHERE item.uid = %d $item_normal + AND item.parent IN ( %s ) + $sql_extra $sql_extra2 ", + intval(\App::$profile['profile_uid']), + dbesc($parents_str) + ); + if($items) { + xchan_query($items); + $items = fetch_post_tags($items, true); + $items = conv_sort($items,'updated'); + } + else + $items = []; + } + + $mode = 'articles'; + + $content = conversation($items,$mode,false,'traditional'); + + $o = replace_macros(get_markup_template('cards.tpl'), [ + '$title' => t('Articles'), + '$editor' => $editor, + '$content' => $content, + '$pager' => alt_pager($a,count($items)) + ]); + + return $o; + } + +} diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index 91d279f7a..6737ac4ee 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -39,7 +39,7 @@ class Cdav extends \Zotlabs\Web\Controller { $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); if($sigblock) { - $keyId = $sigblock['keyId']; + $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", dbesc($keyId) diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index 14d02d873..7c4c900a1 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -332,6 +332,7 @@ class Channel extends \Zotlabs\Web\Controller { '$tags' => (($hashtags) ? urlencode($hashtags) : ''), '$mid' => $mid, '$verb' => '', + '$net' => '', '$dend' => $datequery, '$dbegin' => $datequery2 )); diff --git a/Zotlabs/Module/Cloud.php b/Zotlabs/Module/Cloud.php index 75191a279..2215507ca 100644 --- a/Zotlabs/Module/Cloud.php +++ b/Zotlabs/Module/Cloud.php @@ -57,12 +57,12 @@ class Cloud extends \Zotlabs\Web\Controller { $auth->observer = $ob_hash; } + // if we arrived at this path with any query parameters in the url, build a clean url without + // them and redirect. - $_SERVER['QUERY_STRING'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['QUERY_STRING']); - $_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']); - - $_SERVER['REQUEST_URI'] = str_replace(array('?f=', '&f='), array('', ''), $_SERVER['REQUEST_URI']); - $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); + $x = clean_query_string(); + if($x !== \App::$query_string) + goaway(z_root() . '/' . $x); $rootDirectory = new \Zotlabs\Storage\Directory('/', $auth); @@ -83,17 +83,42 @@ class Cloud extends \Zotlabs\Web\Controller { $server->addPlugin($browser); // Experimental QuotaPlugin - // require_once('\Zotlabs\Storage/QuotaPlugin.php'); - // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); + // require_once('\Zotlabs\Storage/QuotaPlugin.php'); + // $server->addPlugin(new \Zotlabs\Storage\\QuotaPlugin($auth)); + + + // over-ride the default XML output on thrown exceptions + + $server->on('exception', [ $this, 'DAVException' ]); -// ob_start(); // All we need to do now, is to fire up the server + $server->exec(); -// ob_end_flush(); if($browser->build_page) construct_page(); + + killme(); + } + + + function DAVException($err) { + + if($err instanceof \Sabre\DAV\Exception\NotFound) { + notice( t('Not found') . EOL); + } + elseif($err instanceof \Sabre\DAV\Exception\Forbidden) { + notice( t('Permission denied') . EOL); + } + else { + notice( t('Unknown error') . EOL); + } + + construct_page(); + killme(); } } + + diff --git a/Zotlabs/Module/Cloud_tiles.php b/Zotlabs/Module/Cloud_tiles.php new file mode 100644 index 000000000..da551904f --- /dev/null +++ b/Zotlabs/Module/Cloud_tiles.php @@ -0,0 +1,21 @@ +<?php + +namespace Zotlabs\Module; + +class Cloud_tiles extends \Zotlabs\Web\Controller { + + function init() { + + if(intval($_SESSION['cloud_tiles'])) + $_SESSION['cloud_tiles'] = 0; + else + $_SESSION['cloud_tiles'] = 1; + + if(local_channel()) { + set_pconfig(local_channel(),'system','cloud_tiles',$_SESSION['cloud_tiles']); + } + + goaway(z_root() . '/' . hex2bin(argv(1))); + + } +}
\ No newline at end of file diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index 23c5282e3..e0511b0d3 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -567,7 +567,7 @@ class Connedit extends \Zotlabs\Web\Controller { $contact_id = \App::$poi['abook_id']; $contact = \App::$poi; - $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 order by xchan_name", + $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name", intval(local_channel()) ); diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php index d506fe9f5..9f64e2fea 100644 --- a/Zotlabs/Module/Dav.php +++ b/Zotlabs/Module/Dav.php @@ -48,7 +48,7 @@ class Dav extends \Zotlabs\Web\Controller { $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); if($sigblock) { - $keyId = $sigblock['keyId']; + $keyId = str_replace('acct:','',$sigblock['keyId']); if($keyId) { $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", dbesc($keyId) diff --git a/Zotlabs/Module/Defperms.php b/Zotlabs/Module/Defperms.php new file mode 100644 index 000000000..9214331e4 --- /dev/null +++ b/Zotlabs/Module/Defperms.php @@ -0,0 +1,267 @@ +<?php +namespace Zotlabs\Module; + + +require_once('include/socgraph.php'); +require_once('include/selectors.php'); +require_once('include/group.php'); +require_once('include/photos.php'); + + +class Defperms extends \Zotlabs\Web\Controller { + + /* @brief Initialize the connection-editor + * + * + */ + + function init() { + + if(! local_channel()) + return; + + $r = q("SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_self = 1 and abook_id = %d LIMIT 1", + intval(local_channel()) + ); + if($r) { + \App::$poi = $r[0]; + } + + $channel = \App::get_channel(); + if($channel) + head_set_icon($channel['xchan_photo_s']); + } + + + /* @brief Evaluate posted values and set changes + * + */ + + function post() { + + if(! local_channel()) + return; + + $contact_id = intval(argv(1)); + if(! $contact_id) + return; + + $channel = \App::get_channel(); + + $orig_record = q("SELECT * FROM abook WHERE abook_id = %d AND abook_channel = %d LIMIT 1", + intval($contact_id), + intval(local_channel()) + ); + + if(! $orig_record) { + notice( t('Could not access contact record.') . EOL); + goaway(z_root() . '/connections'); + return; // NOTREACHED + } + + + if(intval($orig_record[0]['abook_self'])) { + $autoperms = intval($_POST['autoperms']); + $is_self = true; + } + else { + $autoperms = null; + $is_self = false; + } + + + $all_perms = \Zotlabs\Access\Permissions::Perms(); + + if($all_perms) { + foreach($all_perms as $perm => $desc) { + + $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$perm); + $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + + if(array_key_exists('perms_' . $perm, $_POST)) { + set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm, + intval($_POST['perms_' . $perm])); + if($autoperms) { + set_pconfig($channel['channel_id'],'autoperms',$perm,intval($_POST['perms_' . $perm])); + } + } + else { + set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'my_perms',$perm,0); + if($autoperms) { + set_pconfig($channel['channel_id'],'autoperms',$perm,0); + } + } + } + } + + if(! is_null($autoperms)) + set_pconfig($channel['channel_id'],'system','autoperms',$autoperms); + + + notice( t('Settings updated.') . EOL); + + + // Refresh the structure in memory with the new data + + $r = q("SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_id = %d LIMIT 1", + intval(local_channel()), + intval($contact_id) + ); + if($r) { + \App::$poi = $r[0]; + } + + + $this->defperms_clone($a); + + goaway(z_root() . '/defperms'); + + return; + + } + + /* @brief Clone connection + * + * + */ + + function defperms_clone(&$a) { + + if(! \App::$poi) + return; + + $channel = \App::get_channel(); + + $r = q("SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_id = %d LIMIT 1", + intval(local_channel()), + intval(\App::$poi['abook_id']) + ); + if($r) { + \App::$poi = array_shift($r); + } + + $clone = \App::$poi; + + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + + $abconfig = load_abconfig($channel['channel_id'],$clone['abook_xchan']); + if($abconfig) + $clone['abconfig'] = $abconfig; + + build_sync_packet(0 /* use the current local_channel */, array('abook' => array($clone))); + } + + /* @brief Generate content of connection default permissions page + * + * + */ + + function get() { + + $sort_type = 0; + $o = ''; + + if(! local_channel()) { + notice( t('Permission denied.') . EOL); + return login(); + } + + $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); + $channel = \App::get_channel(); + + $yes_no = array(t('No'),t('Yes')); + + $connect_perms = \Zotlabs\Access\Permissions::connect_perms(local_channel()); + + $o .= "<script>function connectDefaultShare() { + \$('.abook-edit-me').each(function() { + if(! $(this).is(':disabled')) + $(this).prop('checked', false); + });\n\n"; + foreach($connect_perms['perms'] as $p => $v) { + if($v) { + $o .= "\$('#me_id_perms_" . $p . "').prop('checked', true); \n"; + } + } + $o .= " }\n</script>\n"; + + if(\App::$poi) { + + $sections = []; + + $self = false; + + $tpl = get_markup_template('defperms.tpl'); + + + $perms = array(); + $channel = \App::get_channel(); + + $contact = \App::$poi; + + $global_perms = \Zotlabs\Access\Permissions::Perms(); + + $existing = get_all_perms(local_channel(),$contact['abook_xchan']); + $hidden_perms = []; + + foreach($global_perms as $k => $v) { + $thisperm = get_abconfig(local_channel(),$contact['abook_xchan'],'my_perms',$k); + + $checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); + + $inherited = (($checkinherited & PERMS_SPECIFIC) ? false : true); + + $perms[] = [ 'perms_' . $k, $v, intval($thisperm), '', $yes_no, (($inherited) ? ' disabled="disabled" ' : '') ]; + if($inherited) { + $hidden_perms[] = [ 'perms_' . $k, intval($thisperm) ]; + } + } + + $pcat = new \Zotlabs\Lib\Permcat(local_channel()); + $pcatlist = $pcat->listing(); + $permcats = []; + if($pcatlist) { + foreach($pcatlist as $pc) { + $permcats[$pc['name']] = $pc['localname']; + } + } + + $o .= replace_macros($tpl, [ + '$header' => t('Connection Default Permissions'), + '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('If enabled, connection requests will be approved without your interaction'), $yes_no), + '$permcat' => [ 'permcat', t('Permission role'), '', '',$permcats ], + '$permcat_new' => t('Add permission role'), + '$permcat_enable' => feature_enabled(local_channel(),'permcats'), + '$section' => $section, + '$sections' => $sections, + '$autolbl' => t('The permissions indicated on this page will be applied to all new connections.'), + '$autoapprove' => t('Automatic approval settings'), + '$unapproved' => $unapproved, + '$inherited' => t('inherited'), + '$submit' => t('Submit'), + '$me' => t('My Settings'), + '$perms' => $perms, + '$hidden_perms' => $hidden_perms, + '$permlbl' => t('Individual Permissions'), + '$permnote_self' => t('Some individual permissions may have been preset or locked based on your channel type and privacy settings.'), + '$contact_id' => $contact['abook_id'], + '$name' => $contact['xchan_name'], + ]); + + $arr = array('contact' => $contact,'output' => $o); + + call_hooks('contact_edit', $arr); + + return $arr['output']; + + } + } +} diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index caf0190ae..b1552a694 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -64,6 +64,11 @@ class Directory extends \Zotlabs\Web\Controller { return; } + if(get_config('system','block_public_directory',false) && (! get_observer_hash())) { + notice( t('Public access denied.') . EOL); + return; + } + $observer = get_observer_hash(); $globaldir = get_directory_setting($observer, 'globaldir'); @@ -102,7 +107,7 @@ class Directory extends \Zotlabs\Web\Controller { $common = array(); $index = 0; foreach($r as $rr) { - $common[$rr['xchan_addr']] = $rr['total']; + $common[$rr['xchan_addr']] = ((intval($rr['total']) > 0) ? intval($rr['total']) - 1 : 0); $addresses[$rr['xchan_addr']] = $index++; } @@ -334,7 +339,7 @@ class Directory extends \Zotlabs\Web\Controller { 'ignlink' => $suggest ? z_root() . '/directory?ignore=' . $rr['hash'] : '', 'ignore_label' => t('Don\'t suggest'), 'common_friends' => (($common[$rr['address']]) ? intval($common[$rr['address']]) : ''), - 'common_label' => t('Common connections:'), + 'common_label' => t('Common connections (estimated):'), 'common_count' => intval($common[$rr['address']]), 'safe' => $safe_mode ); diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index d5afdd787..6d895feb5 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -12,6 +12,15 @@ class Display extends \Zotlabs\Web\Controller { function get($update = 0, $load = false) { + $module_format = 'html'; + + + if(argc() > 1) { + $module_format = substr(argv(1),strrpos(argv(1),'.') + 1); + if(! in_array($module_format,['atom','zot','json'])) + $module_format = 'html'; + } + $checkjs = new \Zotlabs\Web\CheckJS(1); if($load) @@ -22,8 +31,12 @@ class Display extends \Zotlabs\Web\Controller { return; } - if(argc() > 1 && argv(1) !== 'load') + if(argc() > 1) { $item_hash = argv(1); + if($module_format !== 'html') { + $item_hash = substr($item_hash,0,strrpos($item_hash,'.')); + } + } if($_REQUEST['mid']) $item_hash = $_REQUEST['mid']; @@ -44,28 +57,28 @@ class Display extends \Zotlabs\Web\Controller { $channel_acl = array( 'allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], - 'deny_cid' => $channel['channel_deny_cid'], - 'deny_gid' => $channel['channel_deny_gid'] + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] ); $x = array( - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'return_path' => 'channel/' . $channel['channel_address'], - 'expanded' => true, + 'acl' => populate_acl($channel_acl), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'channel/' . $channel['channel_address'], + 'expanded' => true, 'editor_autocomplete' => true, - 'bbco_autocomplete' => 'bbcode', - 'bbcode' => true, - 'jotnets' => true + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true ); $o = '<div id="jot-popup">'; @@ -139,10 +152,11 @@ class Display extends \Zotlabs\Web\Controller { $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); - //if the target item is not a post (eg a like) we want to address its thread parent + // if the target item is not a post (eg a like) we want to address its thread parent + $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); - //if we got a decoded hash we must encode it again before handing to javascript + // if we got a decoded hash we must encode it again before handing to javascript if($decoded) $mid = 'b64.' . base64url_encode($mid); @@ -152,32 +166,33 @@ class Display extends \Zotlabs\Web\Controller { \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( '$baseurl' => z_root(), - '$pgtype' => 'display', - '$uid' => '0', - '$gid' => '0', - '$cid' => '0', - '$cmin' => '0', - '$cmax' => '99', - '$star' => '0', - '$liked' => '0', - '$conv' => '0', - '$spam' => '0', - '$fh' => '0', + '$pgtype' => 'display', + '$uid' => '0', + '$gid' => '0', + '$cid' => '0', + '$cmin' => '0', + '$cmax' => '99', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', '$nouveau' => '0', - '$wall' => '0', - '$static' => $static, - '$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1), - '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), - '$search' => '', - '$xchan' => '', - '$order' => '', - '$file' => '', - '$cats' => '', - '$tags' => '', - '$dend' => '', - '$dbegin' => '', - '$verb' => '', - '$mid' => $mid + '$wall' => '0', + '$static' => $static, + '$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1), + '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), + '$search' => '', + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$dend' => '', + '$dbegin' => '', + '$verb' => '', + '$net' => '', + '$mid' => $mid )); head_add_link([ @@ -195,11 +210,11 @@ class Display extends \Zotlabs\Web\Controller { $sql_extra = public_permissions_sql($observer_hash); - if(($update && $load) || ($checkjs->disabled())) { + if(($update && $load) || ($checkjs->disabled()) || ($module_format !== 'html')) { $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']),intval(\App::$pager['start'])); - if($load || ($checkjs->disabled())) { + if($load || ($checkjs->disabled()) || ($module_format !== 'html')) { $r = null; require_once('include/channel.php'); @@ -246,7 +261,7 @@ class Display extends \Zotlabs\Web\Controller { elseif($update && !$load) { $r = null; - + require_once('include/channel.php'); $sys = get_sys_channel(); $sysid = $sys['channel_id']; @@ -271,7 +286,6 @@ class Display extends \Zotlabs\Web\Controller { // make that content unsearchable by ensuring the owner_xchan can't match if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) $sysid = 0; - $r = q("SELECT item.parent AS item_id from item WHERE parent_mid = '%s' AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' @@ -301,7 +315,6 @@ class Display extends \Zotlabs\Web\Controller { WHERE parent in ( %s ) $item_normal ", dbesc($parents_str) ); - xchan_query($items); $items = fetch_post_tags($items,true); $items = conv_sort($items,'created'); @@ -311,13 +324,61 @@ class Display extends \Zotlabs\Web\Controller { $items = array(); } - if ($checkjs->disabled()) { - $o .= conversation($items, 'display', $update, 'traditional'); - if ($items[0]['title']) - \App::$page['title'] = $items[0]['title'] . " - " . \App::$page['title']; - } - else { - $o .= conversation($items, 'display', $update, 'client'); + + switch($module_format) { + + case 'html': + + if ($checkjs->disabled()) { + $o .= conversation($items, 'display', $update, 'traditional'); + if ($items[0]['title']) + \App::$page['title'] = $items[0]['title'] . " - " . \App::$page['title']; + } + else { + $o .= conversation($items, 'display', $update, 'client'); + } + + break; + + case 'atom': + + $atom = replace_macros(get_markup_template('atom_feed.tpl'), array( + '$version' => xmlify(\Zotlabs\Lib\System::get_project_version()), + '$red' => xmlify(\Zotlabs\Lib\System::get_platform_name()), + '$feed_id' => xmlify(\App::$cmd), + '$feed_title' => xmlify(t('Article')), + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now', ATOM_TIME)), + '$author' => '', + '$owner' => '', + '$profile_page' => xmlify(z_root() . '/display/' . $target_item['mid']), + )); + + $x = [ 'xml' => $atom, 'channel' => $channel, 'observer_hash' => $observer_hash, 'params' => $params ]; + call_hooks('atom_feed_top',$x); + + $atom = $x['xml']; + + // a much simpler interface + call_hooks('atom_feed', $atom); + + + if($items) { + $type = 'html'; + foreach($items as $item) { + if($item['item_private']) + continue; + $atom .= atom_entry($item, $type, null, '', true, '', false); + } + } + + call_hooks('atom_feed_end', $atom); + + $atom .= '</feed>' . "\r\n"; + + header('Content-type: application/atom+xml'); + echo $atom; + killme(); + } if($updateable) { diff --git a/Zotlabs/Module/Embedphotos.php b/Zotlabs/Module/Embedphotos.php index c92af27d6..15cc68d7f 100644 --- a/Zotlabs/Module/Embedphotos.php +++ b/Zotlabs/Module/Embedphotos.php @@ -83,7 +83,7 @@ class Embedphotos extends \Zotlabs\Web\Controller { return ''; if($args['album']) - $album = $args['album']; + $album = (($args['album'] === '/') ? '' : $args['album']); if($args['title']) $title = $args['title']; diff --git a/Zotlabs/Module/Feed.php b/Zotlabs/Module/Feed.php index 06637b6d2..36869abbe 100644 --- a/Zotlabs/Module/Feed.php +++ b/Zotlabs/Module/Feed.php @@ -16,12 +16,15 @@ class Feed extends \Zotlabs\Web\Controller { $params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml'); $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); - $params['start'] = ((x($params,'start')) ? intval($params['start']) : 0); - $params['records'] = ((x($params,'records')) ? intval($params['records']) : 40); - $params['direction'] = ((x($params,'direction')) ? dbesc($params['direction']) : 'desc'); + $params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0); + $params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 40); + $params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); $params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 0); + if(! in_array($params['direction'],['asc','desc'])) { + $params['direction'] = 'desc'; + } if(argc() > 1) { diff --git a/Zotlabs/Module/File_upload.php b/Zotlabs/Module/File_upload.php index 5c4b9a502..4d1cc4cda 100644 --- a/Zotlabs/Module/File_upload.php +++ b/Zotlabs/Module/File_upload.php @@ -10,7 +10,8 @@ class File_upload extends \Zotlabs\Web\Controller { function post() { - // logger('file upload: ' . print_r($_REQUEST,true)); + logger('file upload: ' . print_r($_REQUEST,true)); + logger('file upload: ' . print_r($_FILES,true)); $channel = (($_REQUEST['channick']) ? channelx_by_nick($_REQUEST['channick']) : null); @@ -30,8 +31,8 @@ class File_upload extends \Zotlabs\Web\Controller { $_REQUEST['allow_cid'] = perms2str($_REQUEST['contact_allow']); $_REQUEST['allow_gid'] = perms2str($_REQUEST['group_allow']); - $_REQUEST['deny_cid'] = perms2str($_REQUEST['contact_deny']); - $_REQUEST['deny_gid'] = perms2str($_REQUEST['group_deny']); + $_REQUEST['deny_cid'] = perms2str($_REQUEST['contact_deny']); + $_REQUEST['deny_gid'] = perms2str($_REQUEST['group_deny']); if($_REQUEST['filename']) { $r = attach_mkdir($channel, get_observer_hash(), $_REQUEST); @@ -47,6 +48,51 @@ class File_upload extends \Zotlabs\Web\Controller { } } else { + + $matches = []; + $partial = false; + + + + if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); + if($pm) { + logger('Content-Range: ' . print_r($matches,true)); + $partial = true; + } + } + + if($partial) { + $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + + if($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($result); + } + else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } + else { + if(! array_key_exists('userfile',$_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + $r = attach_store($channel, get_observer_hash(), '', $_REQUEST); if($r['success']) { $sync = attach_export_data($channel,$r['data']['hash']); diff --git a/Zotlabs/Module/Filestorage.php b/Zotlabs/Module/Filestorage.php index 55713027a..5c8557e5a 100644 --- a/Zotlabs/Module/Filestorage.php +++ b/Zotlabs/Module/Filestorage.php @@ -103,6 +103,11 @@ class Filestorage extends \Zotlabs\Web\Controller { attach_delete($owner, $f['hash']); + $sync = attach_export_data($channel, $f['hash'], true); + if($sync) { + build_sync_packet($channel['channel_id'], array('file' => array($sync))); + } + goaway(dirname($url)); } diff --git a/Zotlabs/Module/Getfile.php b/Zotlabs/Module/Getfile.php index 413a68e0c..abc9f50d9 100644 --- a/Zotlabs/Module/Getfile.php +++ b/Zotlabs/Module/Getfile.php @@ -28,17 +28,51 @@ class Getfile extends \Zotlabs\Web\Controller { function post() { - logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO); - + $header_verified = false; + $hash = $_POST['hash']; $time = $_POST['time']; $sig = $_POST['signature']; $resource = $_POST['resource']; $revision = intval($_POST['revision']); $resolution = (-1); - + if(! $hash) killme(); + + foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { + if($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } + + $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); + if($sigblock) { + $keyId = $sigblock['keyId']; + + if($keyId) { + $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash + where hubloc_addr = '%s' limit 1", + dbesc(str_replace('acct:','',$keyId)) + ); + if($r) { + $hubloc = $r[0]; + $verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']); + if($verified && $verified['header_signed'] && $verified['header_valid'] && $hash == $hubloc['hubloc_hash']) { + $header_verified = true; + } + } + } + } + } + } + + + logger('post: ' . print_r($_POST,true),LOGGER_DEBUG,LOG_INFO); + if($header_verified) { + logger('HTTPSig verified'); + } $channel = channelx_by_hash($hash); @@ -59,16 +93,17 @@ class Getfile extends \Zotlabs\Web\Controller { $d1 = datetime_convert('UTC','UTC',"now + $slop minutes"); $d2 = datetime_convert('UTC','UTC',"now - $slop minutes"); - if(($time > $d1) || ($time < $d2)) { - logger('time outside allowable range'); - killme(); - } + if(! $header_verified) { + if(($time > $d1) || ($time < $d2)) { + logger('time outside allowable range'); + killme(); + } - if(! rsa_verify($hash . '.' . $time,base64url_decode($sig),$channel['channel_pubkey'])) { - logger('verify failed.'); - killme(); + if(! rsa_verify($hash . '.' . $time,base64url_decode($sig),$channel['channel_pubkey'])) { + logger('verify failed.'); + killme(); + } } - if($resolution > 0) { $r = q("select * from photo where resource_id = '%s' and uid = %d limit 1", diff --git a/Zotlabs/Module/Hq.php b/Zotlabs/Module/Hq.php new file mode 100644 index 000000000..1e46a6353 --- /dev/null +++ b/Zotlabs/Module/Hq.php @@ -0,0 +1,301 @@ +<?php +namespace Zotlabs\Module; + +require_once("include/bbcode.php"); +require_once('include/security.php'); +require_once('include/conversation.php'); +require_once('include/acl_selectors.php'); +require_once('include/items.php'); + + +class Hq extends \Zotlabs\Web\Controller { + + function init() { + if(! local_channel()) + return; + + \App::$profile_uid = local_channel(); + } + + function post() { + + if(!local_channel()) + return; + + if($_REQUEST['notify_id']) { + q("update notify set seen = 1 where id = %d and uid = %d", + intval($_REQUEST['notify_id']), + intval(local_channel()) + ); + } + + } + + function get($update = 0, $load = false) { + + if(!local_channel()) + return; + + if($load) + $_SESSION['loadtime'] = datetime_convert(); + + if(argc() > 1 && argv(1) !== 'load') { + $item_hash = argv(1); + } + + if($_REQUEST['mid']) + $item_hash = $_REQUEST['mid']; + + $item_normal = item_normal(); + $item_normal_update = item_normal_update(); + + if(! $item_hash) { + $r = q("SELECT mid FROM item + WHERE uid = %d + AND mid = parent_mid + ORDER BY created DESC LIMIT 1", + intval(local_channel()) + ); + + if($r[0]['mid']) { + $item_hash = 'b64.' . base64url_encode($r[0]['mid']); + } + } + + if($item_hash) { + + if(strpos($item_hash,'b64.') === 0) + $decoded = @base64url_decode(substr($item_hash,4)); + + if($decoded) + $item_hash = $decoded; + + $target_item = null; + + $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", + dbesc($item_hash . '%') + ); + + if($r) { + $target_item = $r[0]; + } + + //if the item is to be moderated redirect to /moderate + if($target_item['item_blocked'] == ITEM_MODERATED) { + goaway(z_root() . '/moderate/' . $target_item['id']); + } + + $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); + + $simple_update = (($update) ? " AND item_unseen = 1 " : ''); + + if($update && $_SESSION['loadtime']) + $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; + + if($static && $simple_update) + $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + + $sys = get_sys_channel(); + $sql_extra = item_permissions_sql($sys['channel_id']); + + $sys_item = false; + + } + + if(! $update) { + $channel = \App::get_channel(); + + $channel_acl = [ + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ]; + + $x = [ + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + + 'acl' => populate_acl($channel_acl), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'hq', + 'expanded' => true, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true + ]; + + $o = replace_macros(get_markup_template("hq.tpl"), + [ + '$no_messages' => (($target_item) ? false : true), + '$no_messages_label' => t('Welcome to hubzilla!') + ] + ); + + $o = '<div id="jot-popup">'; + $o .= status_editor($a,$x); + $o .= '</div>'; + } + + if(! $update && ! $load) { + + nav_set_selected('HQ'); + + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); + + if($target_item) { + // if the target item is not a post (eg a like) we want to address its thread parent + $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); + + // if we got a decoded hash we must encode it again before handing to javascript + if($decoded) + $mid = 'b64.' . base64url_encode($mid); + } + else { + $mid = ''; + } + + $o .= '<div id="live-hq"></div>' . "\r\n"; + $o .= "<script> var profile_uid = " . local_channel() + . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . ";</script>\r\n"; + + \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),[ + '$baseurl' => z_root(), + '$pgtype' => 'hq', + '$uid' => local_channel(), + '$gid' => '0', + '$cid' => '0', + '$cmin' => '0', + '$cmax' => '99', + '$star' => '0', + '$liked' => '0', + '$conv' => '0', + '$spam' => '0', + '$fh' => '0', + '$nouveau' => '0', + '$wall' => '0', + '$static' => $static, + '$page' => 1, + '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), + '$search' => '', + '$xchan' => '', + '$order' => '', + '$file' => '', + '$cats' => '', + '$tags' => '', + '$dend' => '', + '$dbegin' => '', + '$verb' => '', + '$net' => '', + '$mid' => $mid + ]); + } + + $updateable = false; + + if($load && $target_item) { + $r = null; + + $r = q("SELECT item.id AS item_id FROM item + WHERE uid = %d + AND mid = '%s' + $item_normal + LIMIT 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + + if($r) { + $updateable = true; + } + + if(!$r) { + $sys_item = true; + + $r = q("SELECT item.id AS item_id FROM item + LEFT JOIN abook ON item.author_xchan = abook.abook_xchan + WHERE mid = '%s' AND item.uid = %d $item_normal + AND (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra LIMIT 1", + dbesc($target_item['parent_mid']), + intval($sys['channel_id']) + ); + } + } + elseif($update && $target_item) { + $r = null; + + $r = q("SELECT item.parent AS item_id FROM item + WHERE uid = %d + AND parent_mid = '%s' + $item_normal_update + $simple_update + LIMIT 1", + intval(local_channel()), + dbesc($target_item['parent_mid']) + ); + + if($r) { + $updateable = true; + } + + if(!$r) { + $sys_item = true; + + $r = q("SELECT item.parent AS item_id FROM item + LEFT JOIN abook ON item.author_xchan = abook.abook_xchan + WHERE mid = '%s' AND item.uid = %d $item_normal_update $simple_update + AND (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra LIMIT 1", + dbesc($target_item['parent_mid']), + intval($sys['channel_id']) + ); + } + + $_SESSION['loadtime'] = datetime_convert(); + } + else { + $r = []; + } + + if($r) { + $parents_str = ids_to_querystr($r,'item_id'); + if($parents_str) { + $items = q("SELECT item.*, item.id AS item_id + FROM item + WHERE parent IN ( %s ) $item_normal ", + dbesc($parents_str) + ); + + xchan_query($items,true,(($sys_item) ? local_channel() : 0)); + $items = fetch_post_tags($items,true); + $items = conv_sort($items,'created'); + } + } + else { + $items = []; + } + + $o .= conversation($items, 'hq', $update, 'client'); + + if($updateable) { + $x = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d AND parent = %d ", + intval(local_channel()), + intval($r[0]['item_id']) + ); + } + + $o .= '<div id="content-complete"></div>'; + + return $o; + + } + +} diff --git a/Zotlabs/Module/Impel.php b/Zotlabs/Module/Impel.php index 77f488d26..0c372bd96 100644 --- a/Zotlabs/Module/Impel.php +++ b/Zotlabs/Module/Impel.php @@ -26,6 +26,8 @@ class Impel extends \Zotlabs\Web\Controller { if(! $j) json_return_and_die($ret); + // logger('element: ' . print_r($j,true)); + $channel = \App::get_channel(); $arr = array(); diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 9e5dcfaff..6365230f8 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -59,6 +59,7 @@ class Item extends \Zotlabs\Web\Controller { $profile_uid = ((x($_REQUEST,'profile_uid')) ? intval($_REQUEST['profile_uid']) : 0); require_once('include/channel.php'); + $sys = get_sys_channel(); if($sys && $profile_uid && ($sys['channel_id'] == $profile_uid) && is_site_admin()) { $uid = intval($sys['channel_id']); @@ -155,7 +156,7 @@ class Item extends \Zotlabs\Web\Controller { if(! x($_REQUEST,'type')) $_REQUEST['type'] = 'net-comment'; - if($obj_type == ACTIVITY_OBJ_POST) + if($obj_type == ACTIVITY_OBJ_NOTE) $obj_type = ACTIVITY_OBJ_COMMENT; if($parent) { @@ -171,7 +172,7 @@ class Item extends \Zotlabs\Web\Controller { ); } // if this isn't the real parent of the conversation, find it - if($r !== false && count($r)) { + if($r) { $parid = $r[0]['parent']; $parent_mid = $r[0]['mid']; if($r[0]['id'] != $r[0]['parent']) { @@ -179,9 +180,16 @@ class Item extends \Zotlabs\Web\Controller { intval($parid) ); } + + // if interacting with a pubstream item, + // create a copy of the parent in your stream + + if($r[0]['uid'] === $sys['channel_id'] && local_channel()) { + $r = [ copy_of_pubitem(\App::get_channel(), $r[0]['mid']) ]; + } } - - if(($r === false) || (! count($r))) { + + if(! $r) { notice( t('Unable to locate original post.') . EOL); if($api_source) return ( [ 'success' => false, 'message' => 'invalid post id' ] ); @@ -189,15 +197,12 @@ class Item extends \Zotlabs\Web\Controller { goaway(z_root() . "/" . $return_path ); killme(); } - - // can_comment_on_post() needs info from the following xchan_query - // This may be from the discover tab which means we need to correct the effective uid - xchan_query($r,true,(($r[0]['uid'] == local_channel()) ? 0 : local_channel())); - + xchan_query($r,true); + $parent_item = $r[0]; $parent = $r[0]['id']; - + // multi-level threading - preserve the info but re-parent to our single level threading $thr_parent = $parent_mid; @@ -511,48 +516,20 @@ class Item extends \Zotlabs\Web\Controller { require_once('include/text.php'); - // Markdown doesn't work correctly. Do not re-enable unless you're willing to fix it and support it. - - // Sample that will probably give you grief - you must preserve the linebreaks - // and provide the correct markdown interpretation and you cannot allow unfiltered HTML - - // Markdown - // ======== - // - // **bold** abcde - // fghijkl - // *italic* - // <img src="javascript:alert('hacked');" /> - - // if($uid && $uid == $profile_uid && feature_enabled($uid,'markdown')) { - // require_once('include/markdown.php'); - // $body = escape_tags(trim($body)); - // $body = str_replace("\n",'<br />', $body); - // $body = preg_replace_callback('/\[share(.*?)\]/ism','\share_shield',$body); - // $body = markdown_to_bb($body,true); - // $body = preg_replace_callback('/\[share(.*?)\]/ism','\share_unshield',$body); - // } + if($uid && $uid == $profile_uid && feature_enabled($uid,'markdown')) { + require_once('include/markdown.php'); + $body = preg_replace_callback('/\[share(.*?)\]/ism','\share_shield',$body); + $body = markdown_to_bb($body,true,['preserve_lf' => true]); + $body = preg_replace_callback('/\[share(.*?)\]/ism','\share_unshield',$body); + + } // BBCODE alert: the following functions assume bbcode input // and will require alternatives for alternative content-types (text/html, text/markdown, text/plain, etc.) // we may need virtual or template classes to implement the possible alternatives - - // Work around doubled linefeeds in Tinymce 3.5b2 - // First figure out if it's a status post that would've been - // created using tinymce. Otherwise leave it alone. - - $plaintext = true; - - // $plaintext = ((feature_enabled($profile_uid,'richtext')) ? false : true); - // if((! $parent) && (! $api_source) && (! $plaintext)) { - // $body = fix_mce_lf($body); - // } - - - + // If we're sending a private top-level message with a single @-taggable channel as a recipient, @-tag it, if our pconfig is set. - - + if((! $parent) && (get_pconfig($profile_uid,'system','tagifonlyrecip')) && (substr_count($str_contact_allow,'<') == 1) && ($str_group_allow == '') && ($str_contact_deny == '') && ($str_group_deny == '')) { $x = q("select abook_id, abconfig.v from abook left join abconfig on abook_xchan = abconfig.xchan and abook_channel = abconfig.chan and cat= 'their_perms' and abconfig.k = 'tag_deliver' and abconfig.v = 1 and abook_xchan = '%s' and abook_channel = %d limit 1", dbesc(str_replace(array('<','>'),array('',''),$str_contact_allow)), @@ -605,15 +582,6 @@ class Item extends \Zotlabs\Web\Controller { * so we'll set the permissions regardless and realise that the media may not be * referenced in the post. * - * What is preventing us from being able to upload photos into comments is dealing with - * the photo and attachment permissions, since we don't always know who was in the - * distribution for the top level post. - * - * We might be able to provide this functionality with a lot of fiddling: - * - if the top level post is public (make the photo public) - * - if the top level post was written by us or a wall post that belongs to us (match the top level post) - * - if the top level post has privacy mentions, add those to the permissions. - * - otherwise disallow the photo *or* make the photo public. This is the part that gets messy. */ if(! $preview) { @@ -666,6 +634,9 @@ class Item extends \Zotlabs\Web\Controller { if($webpage == ITEM_TYPE_CARD) { $catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); } + elseif($webpage == ITEM_TYPE_ARTICLE) { + $catlink = z_root() . '/articles/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); + } else { $catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)); } @@ -770,6 +741,18 @@ class Item extends \Zotlabs\Web\Controller { } } + if($webpage == ITEM_TYPE_ARTICLE) { + $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16)); + } + if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) { + $r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1", + intval($parent_item['id']) + ); + if($r) { + $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . $r[0]['v']; + } + } + if ((! $plink) && ($item_thread_top)) { $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid; } @@ -1156,7 +1139,29 @@ class Item extends \Zotlabs\Web\Controller { $ret['message'] = t('Unable to obtain post information from database.'); return $ret; } - + + // auto-upgrade beginner (techlevel 0) accounts - if they have at least two friends and ten posts + // and have uploaded something (like a profile photo), promote them to level 1. + + $a = q("select account_id, account_level from account where account_id = (select channel_account_id from channel where channel_id = %d limit 1)", + intval($channel_id) + ); + if((! intval($a[0]['account_level'])) && intval($r[0]['total']) > 10) { + $x = q("select count(abook_id) as total from abook where abook_channel = %d", + intval($channel_id) + ); + if($x && intval($x[0]['total']) > 2) { + $y = q("select count(id) as total from attach where uid = %d", + intval($channel_id) + ); + if($y && intval($y[0]['total']) > 1) { + q("update account set account_level = 1 where account_id = %d limit 1", + intval($a[0]['account_id']) + ); + } + } + } + if (!$iswebpage) { $max = engr_units_to_bytes(service_class_fetch($channel_id,'total_items')); if(! service_class_allows($channel_id,'total_items',$r[0]['total'])) { diff --git a/Zotlabs/Module/Layouts.php b/Zotlabs/Module/Layouts.php index 34d754029..19efb37fd 100644 --- a/Zotlabs/Module/Layouts.php +++ b/Zotlabs/Module/Layouts.php @@ -162,12 +162,12 @@ class Layouts extends \Zotlabs\Web\Controller { 'created' => $rr['created'], 'edited' => $rr['edited'], 'mimetype' => $rr['mimetype'], - 'pagetitle' => $rr['sid'], + 'pagetitle' => urldecode($rr['v']), 'mid' => $rr['mid'] ); $pages[$rr['iid']][] = array( 'url' => $rr['iid'], - 'title' => $rr['v'], + 'title' => urldecode($rr['v']), 'descr' => $rr['title'], 'mid' => $rr['mid'], 'created' => $rr['created'], diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index b104a5f5f..b07824363 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -12,7 +12,10 @@ class Like extends \Zotlabs\Web\Controller { function get() { $o = ''; - + + $sys_channel = get_sys_channel(); + $sys_channel_id = (($sys_channel) ? $sys_channel['channel_id'] : 0); + $observer = \App::get_observer(); $interactive = $_REQUEST['interactive']; if($interactive) { @@ -253,20 +256,29 @@ class Like extends \Zotlabs\Web\Controller { logger('like: verb ' . $verb . ' item ' . $item_id, LOGGER_DEBUG); // get the item. Allow linked photos (which are normally hidden) to be liked - + $r = q("SELECT * FROM item WHERE id = %d - and (item_type = 0 or item_type = 6) and item_deleted = 0 and item_unpublished = 0 + and item_type in (0,6,7) and item_deleted = 0 and item_unpublished = 0 and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", intval($item_id) ); - + + // if interacting with a pubstream item, + // create a copy of the parent in your stream. If not the conversation + // parent, copy that as well. + + if($r) { + if($r[0]['uid'] === $sys_channel['channel_id'] && local_channel()) { + $r = [ copy_of_pubitem(\App::get_channel(), $r[0]['mid']) ]; + } + } + if(! $item_id || (! $r)) { logger('like: no item ' . $item_id); killme(); } - - xchan_query($r,true,(($r[0]['uid'] == local_channel()) ? 0 : local_channel())); + xchan_query($r,true); $item = $r[0]; @@ -464,6 +476,8 @@ class Like extends \Zotlabs\Web\Controller { $arr['mid'] = $mid; $arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid); $arr['uid'] = $owner_uid; + + $arr['item_flags'] = $item_flags; $arr['item_wall'] = $item_wall; $arr['parent_mid'] = (($extended_like) ? $mid : $item['mid']); diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index ee736ff42..a3e1eee88 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -35,9 +35,12 @@ class Network extends \Zotlabs\Web\Controller { return login(false); } - if($load) + $o = ''; + + if($load) { $_SESSION['loadtime'] = datetime_convert(); - + } + $arr = array('query' => \App::$query_string); call_hooks('network_content_init', $arr); @@ -104,7 +107,6 @@ class Network extends \Zotlabs\Web\Controller { $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); } - $o = ''; // if no tabs are selected, defaults to comments @@ -119,6 +121,7 @@ class Network extends \Zotlabs\Web\Controller { $cmax = ((x($_GET,'cmax')) ? intval($_GET['cmax']) : 99); $file = ((x($_GET,'file')) ? $_GET['file'] : ''); $xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : ''); + $net = ((x($_GET,'net')) ? $_GET['net'] : ''); $deftag = ''; @@ -326,7 +329,8 @@ class Network extends \Zotlabs\Web\Controller { '$tags' => urlencode($hashtags), '$dend' => $datequery, '$mid' => '', - '$verb' => $verb, + '$verb' => $verb, + '$net' => $net, '$dbegin' => $datequery2 )); } @@ -404,7 +408,10 @@ class Network extends \Zotlabs\Web\Controller { } - + + $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); + $abook_uids = " and abook.abook_channel = " . local_channel() . " "; $uids = " and item.uid = " . local_channel() . " "; @@ -441,10 +448,12 @@ class Network extends \Zotlabs\Web\Controller { $items = q("SELECT item.*, item.id AS item_id, received FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) + $net_query WHERE true $uids $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) $simple_update $sql_extra $sql_nets + $net_query2 ORDER BY item.received DESC $pager_sql " ); @@ -469,10 +478,12 @@ class Network extends \Zotlabs\Web\Controller { $r = q("SELECT distinct item.id AS item_id, $ordering FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) + $net_query WHERE true $uids $item_normal AND item.parent = item.id and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra3 $sql_extra $sql_nets + $net_query2 ORDER BY $ordering DESC $pager_sql " ); @@ -482,9 +493,10 @@ class Network extends \Zotlabs\Web\Controller { // this is an update $r = q("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) + $net_query WHERE true $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets " + $sql_extra3 $sql_extra $sql_nets $net_query2" ); $_SESSION['loadtime'] = datetime_convert(); } @@ -523,13 +535,12 @@ class Network extends \Zotlabs\Web\Controller { if($parents_str) { $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; - $update_unseen .= " AND obj_type != '" . dbesc(ACTIVITY_OBJ_FILE) . "'"; $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; } } else { if($parents_str) { - $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " ) AND obj_type != '" . dbesc(ACTIVITY_OBJ_FILE) . "'"; + $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; } } } diff --git a/Zotlabs/Module/Notify.php b/Zotlabs/Module/Notify.php index 3d6e1c2e7..cffcc8099 100644 --- a/Zotlabs/Module/Notify.php +++ b/Zotlabs/Module/Notify.php @@ -34,7 +34,7 @@ class Notify extends \Zotlabs\Web\Controller { } - function get() { + function get() { if(! local_channel()) return login(); diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 5e06d3540..bb3a13b56 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -45,6 +45,8 @@ class Oep extends \Zotlabs\Web\Controller { $arr = $this->oep_profile_reply($_REQUEST); elseif(fnmatch('*/cards/*',$url)) $arr = $this->oep_cards_reply($_REQUEST); + elseif(fnmatch('*/articles/*',$url)) + $arr = $this->oep_articles_reply($_REQUEST); if($arr) { if($html) { @@ -232,6 +234,89 @@ class Oep extends \Zotlabs\Web\Controller { } + function oep_articles_reply($args) { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if(preg_match('#//(.*?)/articles/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) { + $nick = $matches[2]; + $res = $matches[3]; + } + if(! ($nick && $res)) + return $ret; + + $channel = channelx_by_nick($nick); + + if(! $channel) + return $ret; + + + if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) + return $ret; + + $sql_extra = item_permissions_sql($channel['channel_id'],get_observer_hash()); + + $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.v = '%s' limit 1", + dbesc($res) + ); + if($r) { + $sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; + } + else { + return $ret; + } + + $r = q("select * from item + where item.uid = %d and item_type = %d + $sql_extra order by item.created desc", + intval($channel['channel_id']), + intval(ITEM_TYPE_ARTICLE) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) 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 "; + + if($r) { + xchan_query($r); + $p = fetch_post_tags($r, true); + } + + $x = '2eGriplW^*Jmf4'; + + + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; + if($p[0]['title']) + $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); + + $o = str_replace($x,bbcode($p[0]['body']),$o); + + $ret['type'] = 'rich'; + + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); + + $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>'; + + $ret['width'] = $w; + $ret['height'] = $h; + + return $ret; + + } + function oep_mid_reply($args) { diff --git a/Zotlabs/Module/Ofeed.php b/Zotlabs/Module/Ofeed.php index 58488d4af..d18a43ae5 100644 --- a/Zotlabs/Module/Ofeed.php +++ b/Zotlabs/Module/Ofeed.php @@ -17,12 +17,15 @@ class Ofeed extends \Zotlabs\Web\Controller { $params['type'] = ((stristr(argv(0),'json')) ? 'json' : 'xml'); $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); - $params['start'] = ((x($params,'start')) ? intval($params['start']) : 0); - $params['records'] = ((x($params,'records')) ? intval($params['records']) : 10); - $params['direction'] = ((x($params,'direction')) ? dbesc($params['direction']) : 'desc'); + $params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0); + $params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 10); + $params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); $params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1); + if(! in_array($params['direction'],['asc','desc'])) { + $params['direction'] = 'desc'; + } if(argc() > 1) { diff --git a/Zotlabs/Module/Page.php b/Zotlabs/Module/Page.php index c142afe77..5fdd32825 100644 --- a/Zotlabs/Module/Page.php +++ b/Zotlabs/Module/Page.php @@ -89,22 +89,30 @@ class Page extends \Zotlabs\Web\Controller { if(! $ignore_language) { $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 item.item_delayed = 0 - and (( iconfig.k = 'WEBPAGE' and item_type = %d ) - OR ( iconfig.k = 'PDL' AND item_type = %d )) $sql_options $revision limit 1", + and iconfig.k = 'WEBPAGE' and item_type = %d + $sql_options $revision limit 1", intval($u[0]['channel_id']), dbesc($lang_page_id), - intval(ITEM_TYPE_WEBPAGE), - intval(ITEM_TYPE_PDL) + intval(ITEM_TYPE_WEBPAGE) + ); + } + if(! $r) { + $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 item.item_delayed = 0 + and iconfig.k = 'WEBPAGE' and item_type = %d + $sql_options $revision limit 1", + intval($u[0]['channel_id']), + dbesc($page_id), + intval(ITEM_TYPE_WEBPAGE) ); } if(! $r) { + // no webpage by that name, but we do allow you to load/preview a layout using this module. Try that. $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 item.item_delayed = 0 - and (( iconfig.k = 'WEBPAGE' and item_type = %d ) - OR ( iconfig.k = 'PDL' AND item_type = %d )) $sql_options $revision limit 1", + and iconfig.k = 'PDL' AND item_type = %d $sql_options $revision limit 1", intval($u[0]['channel_id']), dbesc($page_id), - intval(ITEM_TYPE_WEBPAGE), intval(ITEM_TYPE_PDL) ); } @@ -129,7 +137,7 @@ class Page extends \Zotlabs\Web\Controller { } return; } - + if($r[0]['title']) \App::$page['title'] = escape_tags($r[0]['title']); diff --git a/Zotlabs/Module/Pdledit.php b/Zotlabs/Module/Pdledit.php index f8af470ac..9b86b599b 100644 --- a/Zotlabs/Module/Pdledit.php +++ b/Zotlabs/Module/Pdledit.php @@ -77,8 +77,10 @@ class Pdledit extends \Zotlabs\Web\Controller { } $t = get_pconfig(local_channel(),'system',$module); - if(! $t) - $t = file_get_contents(theme_include($module)); + $s = file_get_contents(theme_include($module)); + if(! $t) { + $t = $s; + } if(! $t) { notice( t('Layout not found.') . EOL); return ''; @@ -89,7 +91,9 @@ class Pdledit extends \Zotlabs\Web\Controller { '$mname' => t('Module Name:'), '$help' => t('Layout Help'), '$another' => t('Edit another layout'), + '$original' => t('System layout'), '$module' => argv(1), + '$src' => $s, '$content' => htmlspecialchars($t,ENT_COMPAT,'UTF-8'), '$submit' => t('Submit') )); diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index caef45d98..81af607ec 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -202,6 +202,11 @@ class Photos extends \Zotlabs\Web\Controller { ); if(($m) && ($m[0]['folder'] != $_POST['move_to_album'])) { attach_move($page_owner_uid,argv(2),$_POST['move_to_album']); + + $sync = attach_export_data(\App::$data['channel'],argv(2),true); + if($sync) + build_sync_packet($page_owner_uid,array('file' => array($sync))); + if(! ($_POST['desc'] && $_POST['newtag'])) goaway(z_root() . '/' . $_SESSION['photo_return']); } @@ -465,6 +470,51 @@ class Photos extends \Zotlabs\Web\Controller { $_REQUEST['group_deny'] = expand_acl($channel['channel_deny_gid']); } + + $matches = []; + $partial = false; + + + + if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); + if($pm) { + logger('Content-Range: ' . print_r($matches,true)); + $partial = true; + } + } + + if($partial) { + $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]); + + if($x['partial']) { + header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0)); + json_return_and_die($result); + } + else { + header('Range: bytes=0-' . (($x['size']) ? $x['size'] - 1 : 0)); + + $_FILES['userfile'] = [ + 'name' => $x['name'], + 'type' => $x['type'], + 'tmp_name' => $x['tmp_name'], + 'error' => $x['error'], + 'size' => $x['size'] + ]; + } + } + else { + if(! array_key_exists('userfile',$_FILES)) { + $_FILES['userfile'] = [ + 'name' => $_FILES['files']['name'], + 'type' => $_FILES['files']['type'], + 'tmp_name' => $_FILES['files']['tmp_name'], + 'error' => $_FILES['files']['error'], + 'size' => $_FILES['files']['size'] + ]; + } + } + $r = attach_store($channel,get_observer_hash(), '', $_REQUEST); if(! $r['success']) { @@ -557,8 +607,11 @@ class Photos extends \Zotlabs\Web\Controller { nav_set_selected('Photos'); - $o = ""; - + $o = '<script src="library/blueimp_upload/js/vendor/jquery.ui.widget.js"></script> + <script src="library/blueimp_upload/js/jquery.iframe-transport.js"></script> + <script src="library/blueimp_upload/js/jquery.fileupload.js"></script>'; + + $o .= "<script> var profile_uid = " . \App::$profile['profile_uid'] . "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; @@ -656,7 +709,7 @@ class Photos extends \Zotlabs\Web\Controller { '$uploader' => $ret['addon_text'], '$default' => (($ret['default_upload']) ? true : false), '$uploadurl' => $ret['post_url'], - '$submit' => t('Submit') + '$submit' => t('Upload') )); @@ -1052,7 +1105,7 @@ class Photos extends \Zotlabs\Web\Controller { } $comments = ''; - if(! count($r)) { + if(! $r) { if($observer && ($can_post || $can_comment)) { $commentbox = replace_macros($cmnt_tpl,array( '$return_path' => '', diff --git a/Zotlabs/Module/Ping.php b/Zotlabs/Module/Ping.php index cda42f426..a3f6cdfec 100644 --- a/Zotlabs/Module/Ping.php +++ b/Zotlabs/Module/Ping.php @@ -140,7 +140,7 @@ class Ping extends \Zotlabs\Web\Controller { db_utcnow(), db_quoteinterval('3 MINUTE') ); - $discover_tab_on = ((get_config('system','disable_discover_tab') != 1) ? true : false); + $discover_tab_on = ((get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false) ? false : true); $notify_pubs = ((local_channel()) ? ($vnotify & VNOTIFY_PUBS) && $discover_tab_on : $discover_tab_on); if($notify_pubs) { @@ -169,15 +169,13 @@ class Ping extends \Zotlabs\Web\Controller { $r = q("SELECT * FROM item WHERE uid = %d AND author_xchan != '%s' - AND obj_type != '%s' AND item_unseen = 1 AND created > '" . datetime_convert('UTC','UTC',$_SESSION['static_loadtime']) . "' $item_normal ORDER BY created DESC LIMIT 300", intval($sys['channel_id']), - dbesc(get_observer_hash()), - dbesc(ACTIVITY_OBJ_FILE) + dbesc(get_observer_hash()) ); if($r) { @@ -264,6 +262,16 @@ class Ping extends \Zotlabs\Web\Controller { if($t) { foreach($t as $tt) { + $message = trim(strip_tags(bbcode($tt['msg']))); + + if(strpos($message, $tt['xname']) === 0) + $message = substr($message, strlen($tt['xname']) + 1); + + + $mid = basename($tt['link']); + + $b64mid = ((strpos($mid, 'b64.' === 0)) ? $mid : 'b64.' . base64url_encode($mid)); + $notifs[] = array( 'notify_link' => z_root() . '/notify/view/' . $tt['id'], 'name' => $tt['xname'], @@ -271,7 +279,9 @@ class Ping extends \Zotlabs\Web\Controller { 'photo' => $tt['photo'], 'when' => relative_date($tt['created']), 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), - 'message' => strip_tags(bbcode($tt['msg'])) + 'b64mid' => (($tt['otype'] == 'item') ? $b64mid : 'undefined'), + 'notify_id' => (($tt['otype'] == 'item') ? $tt['id'] : 'undefined'), + 'message' => $message ); } } @@ -313,11 +323,9 @@ class Ping extends \Zotlabs\Web\Controller { $r = q("SELECT * FROM item WHERE item_unseen = 1 and uid = %d $item_normal AND author_xchan != '%s' - AND obj_type != '%s' ORDER BY created DESC limit 300", intval(local_channel()), - dbesc($ob_hash), - dbesc(ACTIVITY_OBJ_FILE) + dbesc($ob_hash) ); if($r) { @@ -484,14 +492,13 @@ class Ping extends \Zotlabs\Web\Controller { $t3 = dba_timer(); if($vnotify & (VNOTIFY_NETWORK|VNOTIFY_CHANNEL)) { + $r = q("SELECT id, item_wall FROM item WHERE item_unseen = 1 and uid = %d $item_normal - AND author_xchan != '%s' - AND obj_type != '%s'", + AND author_xchan != '%s'", intval(local_channel()), - dbesc($ob_hash), - dbesc(ACTIVITY_OBJ_FILE) + dbesc($ob_hash) ); if($r) { diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php index 43106e3af..4235f0b97 100644 --- a/Zotlabs/Module/Profile.php +++ b/Zotlabs/Module/Profile.php @@ -109,7 +109,7 @@ class Profile extends \Zotlabs\Web\Controller { 'title' => 'oembed' ]); - $o .= advanced_profile($a); + $o .= advanced_profile(); call_hooks('profile_advanced',$o); return $o; diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php index 411518c61..45a606d5f 100644 --- a/Zotlabs/Module/Profile_photo.php +++ b/Zotlabs/Module/Profile_photo.php @@ -179,7 +179,10 @@ class Profile_photo extends \Zotlabs\Web\Controller { ); } - profiles_build_sync(local_channel()); + // set $send to false in profiles_build_sync() to return the data + // so that we only send one sync packet. + + $sync_profiles = profiles_build_sync(local_channel(),false); // We'll set the updated profile-photo timestamp even if it isn't the default profile, // so that browsers will do a cache update unconditionally @@ -201,7 +204,7 @@ class Profile_photo extends \Zotlabs\Web\Controller { $sync = attach_export_data($channel,$base_image['resource_id']); if($sync) - build_sync_packet($channel['channel_id'],array('file' => array($sync))); + build_sync_packet($channel['channel_id'],array('file' => array($sync), 'profile' => $sync_profiles)); // Similarly, tell the nav bar to bypass the cache and update the avatar image. diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 15e2d8a74..c469a0eca 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -2,6 +2,7 @@ namespace Zotlabs\Module; require_once('include/conversation.php'); +require_once('include/acl_selectors.php'); class Pubstream extends \Zotlabs\Web\Controller { @@ -31,6 +32,48 @@ class Pubstream extends \Zotlabs\Web\Controller { $item_normal_update = item_normal_update(); $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); + $net = ((array_key_exists('net',$_REQUEST)) ? escape_tags($_REQUEST['net']) : ''); + + + if(local_channel() && (! $update)) { + + $channel = \App::get_channel(); + + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + + $x = array( + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($group || $cid || $channel['channel_allow_cid'] || $channel['channel_allow_gid'] || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + + 'acl' => populate_acl($channel_acl), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'return_path' => 'channel/' . $channel['channel_address'], + 'expanded' => true, + 'editor_autocomplete' => true, + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true, + 'jotnets' => true + ); + + $o = '<div id="jot-popup">'; + $o .= status_editor($a,$x); + $o .= '</div>'; + } + + + + if(! $update && !$load) { @@ -81,7 +124,8 @@ class Pubstream extends \Zotlabs\Web\Controller { '$tags' => '', '$dend' => '', '$mid' => $mid, - '$verb' => '', + '$verb' => '', + '$net' => $net, '$dbegin' => '' )); } @@ -112,20 +156,22 @@ class Pubstream extends \Zotlabs\Web\Controller { $page_mode = 'list'; else $page_mode = 'client'; + + + $net_query = (($net) ? " left join xchan on xchan_hash = author_xchan " : ''); + $net_query2 = (($net) ? " and xchan_network = '" . protect_sprintf(dbesc($net)) . "' " : ''); - $simple_update = (($update) ? " and item.item_unseen = 1 " : ''); + $simple_update = (($_SESSION['loadtime']) ? " AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' " : ''); - if($update && $_SESSION['loadtime']) - $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; if($load) $simple_update = ''; if($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; //logger('update: ' . $update . ' load: ' . $load); - + if($update) { $ordering = "commented"; @@ -134,9 +180,10 @@ class Pubstream extends \Zotlabs\Web\Controller { if($mid) { $r = q("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan + $net_query WHERE mid like '%s' $uids $item_normal and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets LIMIT 1", + $sql_extra3 $sql_extra $sql_nets $net_query2 LIMIT 1", dbesc($mid . '%') ); } @@ -144,10 +191,11 @@ class Pubstream extends \Zotlabs\Web\Controller { // Fetch a page full of parent items for this page $r = q("SELECT distinct item.id AS item_id, $ordering FROM item left join abook on item.author_xchan = abook.abook_xchan + $net_query WHERE true $uids $item_normal AND item.parent = item.id and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets + $sql_extra3 $sql_extra $sql_nets $net_query2 ORDER BY $ordering DESC $pager_sql " ); } @@ -156,23 +204,26 @@ class Pubstream extends \Zotlabs\Web\Controller { if($mid) { $r = q("SELECT parent AS item_id FROM item left join abook on item.author_xchan = abook.abook_xchan + $net_query WHERE mid like '%s' $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets LIMIT 1", + $sql_extra3 $sql_extra $sql_nets $net_query2 LIMIT 1", dbesc($mid . '%') ); } else { - $r = q("SELECT distinct item.id AS item_id, $ordering FROM item + $r = q("SELECT distinct parent AS item_id, $ordering FROM item left join abook on item.author_xchan = abook.abook_xchan + $net_query WHERE true $uids $item_normal_update - AND item.parent = item.id $simple_update + $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets" + $sql_extra3 $sql_extra $sql_nets $net_query2" ); } $_SESSION['loadtime'] = datetime_convert(); } + // Then fetch all the children of the parents that are on this page $parents_str = ''; $update_unseen = ''; @@ -188,7 +239,10 @@ class Pubstream extends \Zotlabs\Web\Controller { dbesc($parents_str) ); - xchan_query($items,true,(-1)); + // use effective_uid param of xchan_query to help sort out comment permission + // for sys_channel owned items. + + xchan_query($items,true,(($sys) ? local_channel() : 0)); $items = fetch_post_tags($items,true); $items = conv_sort($items,$ordering); } @@ -199,7 +253,7 @@ class Pubstream extends \Zotlabs\Web\Controller { } // fake it - $mode = ('network'); + $mode = ('pubstream'); $o .= conversation($items,$mode,$update,$page_mode); diff --git a/Zotlabs/Module/React.php b/Zotlabs/Module/React.php index 6cd79c952..6473317c7 100644 --- a/Zotlabs/Module/React.php +++ b/Zotlabs/Module/React.php @@ -6,15 +6,21 @@ namespace Zotlabs\Module; class React extends \Zotlabs\Web\Controller { function get() { + if(! local_channel()) return; + $sys = get_sys_channel(); + $channel = \App::get_channel(); + $postid = $_REQUEST['postid']; if(! $postid) return; $emoji = $_REQUEST['emoji']; + + if($_REQUEST['emoji']) { $i = q("select * from item where id = %d and uid = %d", @@ -22,10 +28,22 @@ class React extends \Zotlabs\Web\Controller { intval(local_channel()) ); - if(! $i) + if(! $i) { + $i = q("select * from item where id = %d and uid = %d", + intval($postid), + intval($sys['channel_id']) + ); + + if($i) { + $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; + $postid = (($i) ? $i[0]['id'] : 0); + } + } + + if(! $i) { return; + } - $channel = \App::get_channel(); $n = array(); $n['aid'] = $channel['channel_account_id']; @@ -40,8 +58,7 @@ class React extends \Zotlabs\Web\Controller { $x = item_store($n); - if(local_channel()) - retain_item($postid); + retain_item($postid); if($x['success']) { $nid = $x['item_id']; diff --git a/Zotlabs/Module/Register.php b/Zotlabs/Module/Register.php index 95e3ca96f..deaee31bf 100644 --- a/Zotlabs/Module/Register.php +++ b/Zotlabs/Module/Register.php @@ -234,7 +234,11 @@ class Register extends \Zotlabs\Web\Controller { if(get_config('system','no_age_restriction')) $label_tos = sprintf( t('I accept the %s for this website'), $toslink); else - $label_tos = sprintf( t('I am over 13 years of age and accept the %s for this website'), $toslink); + $age = get_config('system','minimum_age'); + if(!$age) { + $age = 13; + } + $label_tos = sprintf( t('I am over %s years of age and accept the %s for this website'), $age, $toslink); $enable_tos = 1 - intval(get_config('system','no_termsofservice')); diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index 37e9a336f..a572a5a42 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -56,8 +56,7 @@ class Search extends \Zotlabs\Web\Controller { $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); - if((! local_channel()) || (! feature_enabled(local_channel(),'savedsearch'))) - $o .= search($search,'search-box','/search',((local_channel()) ? true : false)); + $o .= search($search,'search-box','/search',((local_channel()) ? true : false)); if(strpos($search,'#') === 0) { $tag = true; @@ -138,6 +137,7 @@ class Search extends \Zotlabs\Web\Controller { '$tags' => '', '$mid' => '', '$verb' => '', + '$net' => '', '$dend' => '', '$dbegin' => '' )); diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 41e23b717..5e9e88a6d 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -2,6 +2,8 @@ namespace Zotlabs\Module\Settings; +require_once('include/selectors.php'); + class Channel { @@ -148,6 +150,9 @@ class Channel { $defpermcat = ((x($_POST,'defpermcat')) ? notags(trim($_POST['defpermcat'])) : 'default'); $cal_first_day = (((x($_POST,'first_day')) && (intval($_POST['first_day']) == 1)) ? 1: 0); + $mailhost = ((array_key_exists('mailhost',$_POST)) ? notags(trim($_POST['mailhost'])) : ''); + $profile_assign = ((x($_POST,'profile_assign')) ? notags(trim($_POST['profile_assign'])) : ''); + $pageflags = $channel['channel_pageflags']; $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); @@ -201,7 +206,7 @@ class Channel { $vnotify += intval($_POST['vnotify11']); if(x($_POST,'vnotify12')) $vnotify += intval($_POST['vnotify12']); - if(x($_POST,'vnotify13') && (get_config('system', 'disable_discover_tab') != 1)) + if(x($_POST,'vnotify13')) $vnotify += intval($_POST['vnotify13']); $always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0; @@ -239,6 +244,8 @@ class Channel { set_pconfig(local_channel(),'system','attach_path',$attach_path); set_pconfig(local_channel(),'system','cal_first_day',$cal_first_day); set_pconfig(local_channel(),'system','default_permcat',$defpermcat); + set_pconfig(local_channel(),'system','email_notify_host',$mailhost); + set_pconfig(local_channel(),'system','profile_assign',$profile_assign); $r = q("update channel set channel_name = '%s', channel_pageflags = %d, channel_timezone = '%s', channel_location = '%s', channel_notifyflags = %d, channel_max_anon_mail = %d, channel_max_friend_req = %d, channel_expire_days = %d $set_perms where channel_id = %d", dbesc($username), @@ -474,6 +481,8 @@ class Channel { $plugin = [ 'basic' => '', 'security' => '', 'notify' => '', 'misc' => '' ]; call_hooks('channel_settings',$plugin); + $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; + $o .= replace_macros($stpl,array( '$ptitle' => t('Channel Settings'), @@ -512,6 +521,9 @@ class Channel { '$permissions' => t('Default Privacy Group'), '$permdesc' => t("\x28click to open/close\x29"), '$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), + '$profseltxt' => t('Profile to assign new connections'), + '$profselect' => ((feature_enabled(local_channel(),'multi_profiles')) ? contact_profile_assign(get_pconfig(local_channel(),'system','profile_assign','')) : ''), + '$allow_cid' => acl2json($perm_defaults['allow_cid']), '$allow_gid' => acl2json($perm_defaults['allow_gid']), '$deny_cid' => acl2json($perm_defaults['deny_cid']), @@ -560,7 +572,8 @@ class Channel { '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), '$vnotify11' => array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no), '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), - '$vnotify13' => ((get_config('system', 'disable_discover_tab') != 1) ? array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no) : array()), + '$vnotify13' => (($disable_discover_tab) ? array() : array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no)), + '$mailhost' => [ 'mailhost', t('Email notification hub (hostname)'), get_pconfig(local_channel(),'system','email_notify_host',\App::get_hostname()), sprintf( t('If your channel is mirrored to multiple hubs, set this to your preferred location. This will prevent duplicate email notifications. Example: %s'),\App::get_hostname()) ], '$always_show_in_notices' => array('always_show_in_notices', t('Also show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), diff --git a/Zotlabs/Module/Settings/Display.php b/Zotlabs/Module/Settings/Display.php index a444d28a2..e1ea0e3e5 100644 --- a/Zotlabs/Module/Settings/Display.php +++ b/Zotlabs/Module/Settings/Display.php @@ -23,6 +23,7 @@ class Display { $mobile_theme = ((x($_POST,'mobile_theme')) ? notags(trim($_POST['mobile_theme'])) : ''); $preload_images = ((x($_POST,'preload_images')) ? intval($_POST['preload_images']) : 0); + $channel_menu = ((x($_POST,'channel_menu')) ? intval($_POST['channel_menu']) : 0); $user_scalable = ((x($_POST,'user_scalable')) ? intval($_POST['user_scalable']) : 0); $nosmile = ((x($_POST,'nosmile')) ? intval($_POST['nosmile']) : 0); $title_tosource = ((x($_POST,'title_tosource')) ? intval($_POST['title_tosource']) : 0); @@ -63,6 +64,7 @@ class Display { set_pconfig(local_channel(),'system','channel_divmore_height', $channel_divmore_height); set_pconfig(local_channel(),'system','network_divmore_height', $network_divmore_height); set_pconfig(local_channel(),'system','manual_conversation_update', $manual_update); + set_pconfig(local_channel(),'system','channel_menu', $channel_menu); $newschema = ''; if($theme){ @@ -217,6 +219,7 @@ class Display { '$ajaxint' => array('browser_update', t("Update browser every xx seconds"), $browser_update, t('Minimum of 10 seconds, no maximum')), '$itemspage' => array('itemspage', t("Maximum number of conversations to load at any time:"), $itemspage, t('Maximum of 100 items')), '$nosmile' => array('nosmile', t("Show emoticons (smilies) as images"), 1-intval($nosmile), '', $yes_no), + '$channel_menu' => [ 'channel_menu', t('Provide channel menu in navigation bar'), get_pconfig(local_channel(),'system','channel_menu',get_config('system','channel_menu',0)), t('Default: channel menu located in app menu'),$yes_no ], '$manual_update' => array('manual_update', t('Manual conversation updates'), channel_manual_conv_update(local_channel()), t('Default is on, turning this off may increase screen jumping'), $yes_no), '$title_tosource' => array('title_tosource', t("Link post titles to source"), $title_tosource, '', $yes_no), '$layout_editor' => t('System Page Layout Editor - (advanced)'), diff --git a/Zotlabs/Module/Settings/Featured.php b/Zotlabs/Module/Settings/Featured.php index ebe2996d3..6a8d10b2d 100644 --- a/Zotlabs/Module/Settings/Featured.php +++ b/Zotlabs/Module/Settings/Featured.php @@ -11,15 +11,21 @@ class Featured { call_hooks('feature_settings_post', $_POST); if($_POST['affinity_slider-submit']) { - if(intval($_POST['affinity_cmax'])) { - set_pconfig(local_channel(),'affinity','cmax',intval($_POST['affinity_cmax'])); + $cmax = intval($_POST['affinity_cmax']); + if($cmax < 0 || $cmax > 99) + $cmax = 99; + $cmin = intval($_POST['affinity_cmin']); + if($cmin < 0 || $cmin > 99) + $cmin = 0; + if($cmin !== 0 || $cmax !== 99) { + set_pconfig(local_channel(),'system','network_page_default','cmin=' . $cmin . '&cmax=' . $cmax); } - if(intval($_POST['affinity_cmin'])) { - set_pconfig(local_channel(),'affinity','cmin',intval($_POST['affinity_cmin'])); - } - if(intval($_POST['affinity_cmax']) || intval($_POST['affinity_cmin'])) { - info( t('Affinity Slider settings updated.') . EOL); + else { + set_pconfig(local_channel(),'system','network_page_default',''); } + + info( t('Affinity Slider settings updated.') . EOL); + } build_sync_packet(); diff --git a/Zotlabs/Module/Starred.php b/Zotlabs/Module/Starred.php index 4f1d99ec6..8349ae25c 100644 --- a/Zotlabs/Module/Starred.php +++ b/Zotlabs/Module/Starred.php @@ -16,7 +16,7 @@ class Starred extends \Zotlabs\Web\Controller { if(! $message_id) killme(); - $r = q("SELECT item_flags FROM item WHERE uid = %d AND id = %d LIMIT 1", + $r = q("SELECT item_starred FROM item WHERE uid = %d AND id = %d LIMIT 1", intval(local_channel()), intval($message_id) ); diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php index dae8bf020..1a9caff6c 100644 --- a/Zotlabs/Module/Subthread.php +++ b/Zotlabs/Module/Subthread.php @@ -11,10 +11,13 @@ class Subthread extends \Zotlabs\Web\Controller { function get() { - if((! local_channel()) && (! remote_channel())) { + if(! local_channel()) { return; } + $sys = get_sys_channel(); + $channel = \App::get_channel(); + $item_id = ((argc() > 2) ? notags(trim(argv(2))) : 0); if(argv(1) === 'sub') @@ -23,10 +26,31 @@ class Subthread extends \Zotlabs\Web\Controller { $activity = ACTIVITY_UNFOLLOW; - $r = q("SELECT parent FROM item WHERE id = '%s'", - dbesc($item_id) + $i = q("select * from item where id = %d and uid = %d", + intval($item_id), + intval(local_channel()) ); - + + if(! $i) { + $i = q("select * from item where id = %d and uid = %d", + intval($postid), + intval($sys['channel_id']) + ); + + if($i) { + $i = [ copy_of_pubitem($channel, $i[0]['mid']) ]; + $item_id = (($i) ? $i[0]['id'] : 0); + } + } + + if(! $i) { + return; + } + + $r = q("SELECT parent FROM item WHERE id = %d", + intval($item_id) + ); + if($r) { $r = q("select * from item where id = parent and id = %d limit 1", dbesc($r[0]['parent']) diff --git a/Zotlabs/Module/Tagger.php b/Zotlabs/Module/Tagger.php index 98e901965..603a95f2b 100644 --- a/Zotlabs/Module/Tagger.php +++ b/Zotlabs/Module/Tagger.php @@ -11,10 +11,12 @@ class Tagger extends \Zotlabs\Web\Controller { function get() { - if(! local_channel() && ! remote_channel()) { + if(! local_channel()) { return; } + $sys = get_sys_channel(); + $observer_hash = get_observer_hash(); //strip html-tags $term = notags(trim($_GET['term'])); @@ -26,9 +28,29 @@ class Tagger extends \Zotlabs\Web\Controller { logger('tagger: tag ' . $term . ' item ' . $item_id); - - $r = q("SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = '%s' and uid = %d LIMIT 1", - dbesc($item_id), + $r = q("select * from item where id = %d and uid = %d limit 1", + intval($item_id), + intval(local_channel()) + ); + + if(! $r) { + $r = q("select * from item where id = %d and uid = %d limit 1", + intval($item_id), + intval($sys['channel_id']) + ); + if($r) { + $r = [ copy_of_pubitem($channel, $i[0]['mid']) ]; + $item_id = (($r) ? $r[0]['id'] : 0); + } + } + + if(! $r) { + notice( t('Post not found.') . EOL); + return; + } + + $r = q("SELECT * FROM item left join xchan on xchan_hash = author_xchan WHERE id = %d and uid = %d LIMIT 1", + intval($item_id), intval(local_channel()) ); diff --git a/Zotlabs/Module/Update.php b/Zotlabs/Module/Update.php new file mode 100644 index 000000000..b3252f8b9 --- /dev/null +++ b/Zotlabs/Module/Update.php @@ -0,0 +1,43 @@ +<?php +namespace Zotlabs\Module; + + +class Update extends \Zotlabs\Web\Controller { + + function get() { + + $profile_uid = intval($_GET['p']); + + // it's probably safe to do this for all modules and not just a limited subset, + // but it needs to be verified. + + if((! $profile_uid) && in_array(argv(1),['display','search','pubstream','home'])) + $profile_uid = (-1); + + if(argc() < 2) { + killme(); + } + + // These modules don't have a completely working liveUpdate implementation currently + + if(in_array(strtolower(argv(1)),['articles','cards'])) + killme(); + + $module = "\\Zotlabs\\Module\\" . ucfirst(argv(1)); + $load = (((argc() > 2) && (argv(2) == 'load')) ? 1 : 0); + + $mod = new $module; + + header("Content-type: text/html"); + + \App::$argv = [ argv(1) ]; + \App::$argc = 1; + + echo "<!DOCTYPE html><html><body><section>\r\n"; + echo $mod->get($profile_uid, $load); + echo "</section></body></html>\r\n"; + + killme(); + + } +} diff --git a/Zotlabs/Module/Update_cards.php b/Zotlabs/Module/Update_cards.php deleted file mode 100644 index bb87357e8..000000000 --- a/Zotlabs/Module/Update_cards.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -namespace Zotlabs\Module; - -/** - * Module: update_profile - * Purpose: AJAX synchronisation of profile page - * - */ - - -class Update_cards extends \Zotlabs\Web\Controller { - -function get() { - - $profile_uid = intval($_GET['p']); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body><section></section></body></html>\r\n"; - - killme(); - - - $mod = new Cards(); - - $text = $mod->get($profile_uid,$load); - - /** - * reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well - */ - - echo str_replace("\t",' ',$text); - echo (($_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - killme(); - -} -} diff --git a/Zotlabs/Module/Update_channel.php b/Zotlabs/Module/Update_channel.php deleted file mode 100644 index 46ad19805..000000000 --- a/Zotlabs/Module/Update_channel.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -namespace Zotlabs\Module; - -/** - * Module: update_profile - * Purpose: AJAX synchronisation of profile page - * - */ - - -class Update_channel extends \Zotlabs\Web\Controller { - -function get() { - - $profile_uid = intval($_GET['p']); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - - /** - * We can remove this hack once Internet Explorer recognises HTML5 natively - */ - - echo (($_GET['msie'] == 1) ? '<div>' : '<section>'); - - /** - * - * Grab the page inner contents by calling the content function from the profile module directly, - * but move any image src attributes to another attribute name. This is because - * some browsers will prefetch all the images for the page even if we don't need them. - * The only ones we need to fetch are those for new page additions, which we'll discover - * on the client side and then swap the image back. - * - */ - - $mod = new Channel(); - - $text = $mod->get($profile_uid,$load); - - $pattern = "/<img([^>]*) src=\"([^\"]*)\"/"; - $replace = "<img\${1} dst=\"\${2}\""; -// $text = preg_replace($pattern, $replace, $text); - -/* - if(! $load) { - $replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />'; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } -*/ - - /** - * reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well - */ - - echo str_replace("\t",' ',$text); - echo (($_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - killme(); - -} -} diff --git a/Zotlabs/Module/Update_display.php b/Zotlabs/Module/Update_display.php deleted file mode 100644 index b2c6a56f5..000000000 --- a/Zotlabs/Module/Update_display.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -namespace Zotlabs\Module; - -// See update_profile.php for documentation - -require_once('include/group.php'); - - -class Update_display extends \Zotlabs\Web\Controller { - - function get() { - - $profile_uid = intval($_GET['p']); - if(! $profile_uid) - $profile_uid = (-1); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - echo (($_GET['msie'] == 1) ? '<div>' : '<section>'); - - $mod = new Display(); - $text = $mod->get($profile_uid, $load); - - echo str_replace("\t",' ',$text); - echo (($_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - - killme(); - - } - -} diff --git a/Zotlabs/Module/Update_home.php b/Zotlabs/Module/Update_home.php deleted file mode 100644 index 0f699482e..000000000 --- a/Zotlabs/Module/Update_home.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -namespace Zotlabs\Module; - -// See update_profile.php for documentation - -class Update_home extends \Zotlabs\Web\Controller { - - function get() { - - $profile_uid = ((intval($_GET['p'])) ? intval($_GET['p']) : (-1)); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>'); - - $mod = new Home(); - $text = $mod->get($profile_uid, $load); - - $pattern = "/<img([^>]*) src=\"([^\"]*)\"/"; - $replace = "<img\${1} dst=\"\${2}\""; - // $text = preg_replace($pattern, $replace, $text); - /* - if(! $load) { - $replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />'; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - */ - echo str_replace("\t",' ',$text); - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - // logger('update_home: ' . $text); - killme(); - - } -} diff --git a/Zotlabs/Module/Update_network.php b/Zotlabs/Module/Update_network.php deleted file mode 100644 index c27b7614a..000000000 --- a/Zotlabs/Module/Update_network.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -namespace Zotlabs\Module; - -// See update_profile.php for documentation - -require_once('include/group.php'); - -class Update_network extends \Zotlabs\Web\Controller { - - function get() { - - $profile_uid = intval($_GET['p']); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>'); - - $mod = new Network(); - $text = $mod->get($profile_uid, $load); - - $pattern = "/<img([^>]*) src=\"([^\"]*)\"/"; - $replace = "<img\${1} dst=\"\${2}\""; - // $text = preg_replace($pattern, $replace, $text); - /* - if(! $load) { - $replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />'; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - */ - echo str_replace("\t",' ',$text); - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - // logger('update_network: ' . $text); - killme(); - - } -} diff --git a/Zotlabs/Module/Update_pubstream.php b/Zotlabs/Module/Update_pubstream.php deleted file mode 100644 index 952b48df3..000000000 --- a/Zotlabs/Module/Update_pubstream.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -namespace Zotlabs\Module; - -// See update_profile.php for documentation - - -class Update_pubstream extends \Zotlabs\Web\Controller { - - function get() { - - $profile_uid = ((intval($_GET['p'])) ? intval($_GET['p']) : (-1)); - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '<div>' : '<section>'); - - $mod = new Pubstream(); - $text = $mod->get($profile_uid, $load); - - $pattern = "/<img([^>]*) src=\"([^\"]*)\"/"; - $replace = "<img\${1} dst=\"\${2}\""; - // $text = preg_replace($pattern, $replace, $text); - /* - if(! $load) { - $replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />'; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - */ - echo str_replace("\t",' ',$text); - echo ((array_key_exists('msie',$_GET) && $_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - killme(); - - } -} diff --git a/Zotlabs/Module/Update_search.php b/Zotlabs/Module/Update_search.php deleted file mode 100644 index 4491f40f4..000000000 --- a/Zotlabs/Module/Update_search.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -namespace Zotlabs\Module; - -/** - * Module: update_profile - * Purpose: AJAX synchronisation of search page - * - */ - - -class Update_search extends \Zotlabs\Web\Controller { - - function get() { - - $profile_uid = intval($_GET['p']); - if(! $profile_uid) - $profile_uid = (-1); - - $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0); - - header("Content-type: text/html"); - echo "<!DOCTYPE html><html><body>\r\n"; - - /** - * We can remove this hack once Internet Explorer recognises HTML5 natively - */ - - echo (($_GET['msie'] == 1) ? '<div>' : '<section>'); - - /** - * - * Grab the page inner contents by calling the content function from the profile module directly, - * but move any image src attributes to another attribute name. This is because - * some browsers will prefetch all the images for the page even if we don't need them. - * The only ones we need to fetch are those for new page additions, which we'll discover - * on the client side and then swap the image back. - * - */ - - $mod = new Search(); - $text = $mod->get($profile_uid,$load); - - $pattern = "/<img([^>]*) src=\"([^\"]*)\"/"; - $replace = "<img\${1} dst=\"\${2}\""; - // $text = preg_replace($pattern, $replace, $text); - /* - if(! $load) { - $replace = '<br />' . t('[Embedded content - reload page to view]') . '<br />'; - $pattern = "/<\s*audio[^>]*>(.*?)<\s*\/\s*audio>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*video[^>]*>(.*?)<\s*\/\s*video>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*embed[^>]*>(.*?)<\s*\/\s*embed>/i"; - $text = preg_replace($pattern, $replace, $text); - $pattern = "/<\s*iframe[^>]*>(.*?)<\s*\/\s*iframe>/i"; - $text = preg_replace($pattern, $replace, $text); - } - */ - /** - * reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well - */ - - echo str_replace("\t",' ',$text); - echo (($_GET['msie'] == 1) ? '</div>' : '</section>'); - echo "</body></html>\r\n"; - killme(); - - } -} diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index e001ad929..2250e6e44 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -41,10 +41,12 @@ class Wall_attach extends \Zotlabs\Web\Controller { $matches = []; $partial = false; - $x = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); - if($x) { - // logger('Content-Range: ' . print_r($matches,true)); - $partial = true; + if(array_key_exists('HTTP_CONTENT_RANGE',$_SERVER)) { + $pm = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches); + if($pm) { + // logger('Content-Range: ' . print_r($matches,true)); + $partial = true; + } } if($partial) { diff --git a/Zotlabs/Module/Wfinger.php b/Zotlabs/Module/Wfinger.php index 2e9307196..753721d27 100644 --- a/Zotlabs/Module/Wfinger.php +++ b/Zotlabs/Module/Wfinger.php @@ -32,6 +32,7 @@ class Wfinger extends \Zotlabs\Web\Controller { $root_resource = false; + $pchan = false; if(strcasecmp(rtrim($resource,'/'),z_root()) === 0) $root_resource = true; @@ -57,11 +58,24 @@ class Wfinger extends \Zotlabs\Web\Controller { $channel = str_replace('~','',basename($resource)); } - $r = q("select * from channel left join xchan on channel_hash = xchan_hash - where channel_address = '%s' limit 1", - dbesc($channel) - ); - + if(substr($channel,0,1) === '[' ) { + $channel = substr($channel,1); + $channel = substr($channel,0,-1); + $pchan = true; + $r = q("select * from pchan left join xchan on pchan_hash = xchan_hash + where pchan_guid = '%s' limit 1", + dbesc($channel) + ); + if($r) { + $r[0] = pchan_to_chan($r[0]); + } + } + else { + $r = q("select * from channel left join xchan on channel_hash = xchan_hash + where channel_address = '%s' limit 1", + dbesc($channel) + ); + } } header('Access-Control-Allow-Origin: *'); @@ -94,7 +108,7 @@ class Wfinger extends \Zotlabs\Web\Controller { $result['subject'] = $resource; $aliases = array( - z_root() . '/channel/' . $r[0]['channel_address'], + z_root() . (($pchan) ? '/pchan/' : '/channel/') . $r[0]['channel_address'], z_root() . '/~' . $r[0]['channel_address'] ); @@ -116,59 +130,87 @@ class Wfinger extends \Zotlabs\Web\Controller { if($alias != $resource) $result['aliases'][] = $alias; - $result['links'] = [ + + if($pchan) { + $result['links'] = [ - [ - 'rel' => 'http://webfinger.net/rel/avatar', - 'type' => $r[0]['xchan_photo_mimetype'], - 'href' => $r[0]['xchan_photo_l'] - ], + [ + 'rel' => 'http://webfinger.net/rel/avatar', + 'type' => $r[0]['xchan_photo_mimetype'], + 'href' => $r[0]['xchan_photo_l'] + ], + + [ + 'rel' => 'http://webfinger.net/rel/profile-page', + 'href' => $r[0]['xchan_url'], + ], - [ - 'rel' => 'http://microformats.org/profile/hcard', - 'type' => 'text/html', - 'href' => z_root() . '/hcard/' . $r[0]['channel_address'] - ], + [ + 'rel' => 'magic-public-key', + 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']), + ] - [ - 'rel' => 'http://webfinger.net/rel/profile-page', - 'href' => z_root() . '/profile/' . $r[0]['channel_address'], - ], - - [ - 'rel' => 'http://schemas.google.com/g/2010#updates-from', - 'type' => 'application/atom+xml', - 'href' => z_root() . '/ofeed/' . $r[0]['channel_address'] - ], + ]; - [ - 'rel' => 'http://webfinger.net/rel/blog', - 'href' => z_root() . '/channel/' . $r[0]['channel_address'], - ], - - [ - 'rel' => 'http://ostatus.org/schema/1.0/subscribe', - 'template' => z_root() . '/follow?f=&url={uri}', - ], - - [ - 'rel' => 'http://purl.org/zot/protocol', - 'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'], - ], - [ - 'rel' => 'http://purl.org/openwebauth/v1', - 'type' => 'application/x-zot+json', - 'href' => z_root() . '/owa', - ], + } + else { + + $result['links'] = [ + + [ + 'rel' => 'http://webfinger.net/rel/avatar', + 'type' => $r[0]['xchan_photo_mimetype'], + 'href' => $r[0]['xchan_photo_l'] + ], + + [ + 'rel' => 'http://microformats.org/profile/hcard', + 'type' => 'text/html', + 'href' => z_root() . '/hcard/' . $r[0]['channel_address'] + ], + + + [ + 'rel' => 'http://webfinger.net/rel/profile-page', + 'href' => z_root() . '/profile/' . $r[0]['channel_address'], + ], + + [ + 'rel' => 'http://schemas.google.com/g/2010#updates-from', + 'type' => 'application/atom+xml', + 'href' => z_root() . '/ofeed/' . $r[0]['channel_address'] + ], + + [ + 'rel' => 'http://webfinger.net/rel/blog', + 'href' => z_root() . '/channel/' . $r[0]['channel_address'], + ], + + [ + 'rel' => 'http://ostatus.org/schema/1.0/subscribe', + 'template' => z_root() . '/follow?f=&url={uri}', + ], + + [ + 'rel' => 'http://purl.org/zot/protocol', + 'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'], + ], + + [ + 'rel' => 'http://purl.org/openwebauth/v1', + 'type' => 'application/x-zot+json', + 'href' => z_root() . '/owa', + ], + + + [ + 'rel' => 'magic-public-key', + 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']), + ] + ]; + } - - [ - 'rel' => 'magic-public-key', - 'href' => 'data:application/magic-public-key,' . salmon_key($r[0]['channel_pubkey']), - ] - ]; - if($zot) { // get a zotinfo packet and return it with webfinger $result['zot'] = zotinfo( [ 'address' => $r[0]['xchan_addr'] ]); @@ -180,7 +222,7 @@ class Wfinger extends \Zotlabs\Web\Controller { killme(); } - $arr = [ 'channel' => $r[0], 'request' => $_REQUEST, 'result' => $result ]; + $arr = [ 'channel' => $r[0], 'pchan' => $pchan, 'request' => $_REQUEST, 'result' => $result ]; call_hooks('webfinger',$arr); diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index 2d2d8e2b7..ae543eb98 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -293,31 +293,43 @@ class Wiki extends \Zotlabs\Web\Controller { $p = Zlib\NativeWikiPage::get_page_content(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); } if(! ($p && $p['success'])) { + $x = new \Zotlabs\Widget\Wiki_pages(); + + $html = $x->create_missing_page([ + 'resource_id' => $resource_id, + 'channel_id' => $owner['channel_id'], + 'channel_address' => $owner['channel_address'], + 'refresh' => true + ]); + //json_return_and_die(array('pages' => $page_list_html, 'message' => '', 'success' => true)); notice( t('Error retrieving page content') . EOL); - goaway(z_root() . '/' . argv(0) . '/' . argv(1) ); + //goaway(z_root() . '/' . argv(0) . '/' . argv(1) ); + $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); + $showPageControls = $wiki_editor; } - - $mimeType = $p['pageMimeType']; - - $sampleContent = (($mimeType == 'text/bbcode') ? '[h3]' . t('New page') . '[/h3]' : '### ' . t('New page')); - if($mimeType === 'text/plain') - $sampleContent = t('New page'); - - $content = (($p['content'] == '') ? $sampleContent : $p['content']); - - // Render the Markdown-formatted page content in HTML - if($mimeType == 'text/bbcode') { - $renderedContent = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))), argv(0) . '/' . argv(1) . '/' . $wikiUrlName); - } - elseif($mimeType === 'text/plain') { - $renderedContent = str_replace(["\n",' ',"\t"],[EOL,' ',' '],htmlentities($content,ENT_COMPAT,'UTF-8',false)); - } - elseif($mimeType === 'text/markdown') { - $content = Zlib\MarkdownSoap::unescape($content); - $html = Zlib\NativeWikiPage::generate_toc(zidify_text(MarkdownExtra::defaultTransform(Zlib\NativeWikiPage::bbcode($content)))); - $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); - } - $showPageControls = $wiki_editor; + else { + $mimeType = $p['pageMimeType']; + + $sampleContent = (($mimeType == 'text/bbcode') ? '[h3]' . t('New page') . '[/h3]' : '### ' . t('New page')); + if($mimeType === 'text/plain') + $sampleContent = t('New page'); + + $content = (($p['content'] == '') ? $sampleContent : $p['content']); + + // Render the Markdown-formatted page content in HTML + if($mimeType == 'text/bbcode') { + $renderedContent = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))), argv(0) . '/' . argv(1) . '/' . $wikiUrlName); + } + elseif($mimeType === 'text/plain') { + $renderedContent = str_replace(["\n",' ',"\t"],[EOL,' ',' '],htmlentities($content,ENT_COMPAT,'UTF-8',false)); + } + elseif($mimeType === 'text/markdown') { + $content = Zlib\MarkdownSoap::unescape($content); + $html = Zlib\NativeWikiPage::generate_toc(zidify_text(MarkdownExtra::defaultTransform(Zlib\NativeWikiPage::bbcode($content)))); + $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); + } + $showPageControls = $wiki_editor; + } break; // default: // Strip the extraneous URL components // goaway('/' . argv(0) . '/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName); @@ -430,11 +442,15 @@ class Wiki extends \Zotlabs\Web\Controller { goaway('/' . argv(0) . '/' . $nick . '/'); } $wiki = array(); + + // backslashes won't work well in the javascript functions + $name = str_replace('\\','',$_POST['wikiName']); + // Generate new wiki info from input name $wiki['postVisible'] = ((intval($_POST['postVisible'])) ? 1 : 0); - $wiki['rawName'] = $_POST['wikiName']; - $wiki['htmlName'] = escape_tags($_POST['wikiName']); - $wiki['urlName'] = urlencode(urlencode($_POST['wikiName'])); + $wiki['rawName'] = $name; + $wiki['htmlName'] = escape_tags($name); + $wiki['urlName'] = urlencode(urlencode($name)); $wiki['mimeType'] = $_POST['mimeType']; $wiki['typelock'] = $_POST['typelock']; @@ -555,9 +571,14 @@ class Wiki extends \Zotlabs\Web\Controller { } $name = $_POST['pageName']; //Get new page name - if(urlencode(escape_tags($_POST['pageName'])) === '') { - json_return_and_die(array('message' => 'Error creating page. Invalid name.', 'success' => false)); + + // backslashes won't work well in the javascript functions + $name = str_replace('\\','',$name); + + if(urlencode(escape_tags($name)) === '') { + json_return_and_die(array('message' => 'Error creating page. Invalid name (' . print_r($_POST,true) . ').', 'success' => false)); } + $page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype); if($page['item_id']) { @@ -626,7 +647,7 @@ class Wiki extends \Zotlabs\Web\Controller { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); } - + $saved = Zlib\NativeWikiPage::save_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'content' => $content)); if($saved['success']) { @@ -758,7 +779,7 @@ class Wiki extends \Zotlabs\Web\Controller { if ((argc() === 4) && (argv(2) === 'rename') && (argv(3) === 'page')) { $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['oldName']; - $pageNewName = $_POST['newName']; + $pageNewName = str_replace('\\','',$_POST['newName']); if ($pageUrlName === 'Home') { json_return_and_die(array('message' => 'Cannot rename Home','success' => false)); } diff --git a/Zotlabs/Render/Comanche.php b/Zotlabs/Render/Comanche.php index 8831bd117..fb400b6fe 100644 --- a/Zotlabs/Render/Comanche.php +++ b/Zotlabs/Render/Comanche.php @@ -5,10 +5,20 @@ namespace Zotlabs\Render; require_once('include/security.php'); require_once('include/menu.php'); - +/** + * @brief Comanche Page Description Language. + * + * Comanche is a markup language similar to bbcode with which to create elaborate + * and complex web pages by assembling them from a series of components - some of + * which are pre-built and others which can be defined on the fly. Comanche uses + * a Page Decription Language to create these pages. + * + * Comanche primarily chooses what content will appear in various regions of the + * page. The various regions have names and these names can change depending on + * what layout template you choose. + */ class Comanche { - function parse($s, $pass = 0) { $matches = array(); @@ -18,13 +28,13 @@ class Comanche { $s = str_replace($mtch[0], '', $s); } } - + /* - * This section supports the "switch" statement of the form given by the following - * example. The [default][/default] block must be the last in the arbitrary + * This section supports the "switch" statement of the form given by the following + * example. The [default][/default] block must be the last in the arbitrary * list of cases. The first case that matches the switch variable is used * and the rest are not evaluated. - * + * * [switch observer.language] * [case de] * [block]german-content[/block] @@ -37,7 +47,7 @@ class Comanche { * [/default] * [/switch] */ - + $cnt = preg_match_all("/\[switch (.*?)\](.*?)\[default\](.*?)\[\/default\]\s*\[\/switch\]/ism", $s, $matches, PREG_SET_ORDER); if($cnt) { foreach($matches as $mtch) { @@ -60,7 +70,7 @@ class Comanche { } } } - + $cnt = preg_match_all("/\[if (.*?)\](.*?)\[else\](.*?)\[\/if\]/ism", $s, $matches, PREG_SET_ORDER); if($cnt) { foreach($matches as $mtch) { @@ -89,7 +99,6 @@ class Comanche { $this->parse_pass0($s); else $this->parse_pass1($s); - } function parse_pass0($s) { @@ -103,7 +112,7 @@ class Comanche { $cnt = preg_match("/\[template=(.*?)\](.*?)\[\/template\]/ism", $s, $matches); if($cnt) { \App::$page['template'] = trim($matches[2]); - \App::$page['template_style'] = trim($matches[2]) . '_' . $matches[1]; + \App::$page['template_style'] = trim($matches[2]) . '_' . $matches[1]; } $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $s, $matches); @@ -145,20 +154,23 @@ class Comanche { } /** + * @brief Replace conditional variables with real values. + * * Currently supported condition variables: + * * $config.xxx.yyy - get_config with cat = xxx and k = yyy + * * $request - request uri for this page + * * $observer.language - viewer's preferred language (closest match) + * * $observer.address - xchan_addr or false + * * $observer.name - xchan_name or false + * * $observer - xchan_hash of observer or empty string + * * $local_channel - logged in channel_id or false * - * $config.xxx.yyy - get_config with cat = xxx and k = yyy - * $request - request uri for this page - * $observer.language - viewer's preferred language (closest match) - * $observer.address - xchan_addr or false - * $observer.name - xchan_name or false - * $observer - xchan_hash of observer or empty string - * $local_channel - logged in channel_id or false + * @param string $v The conditional variable name + * @return string|boolean */ - function get_condition_var($v) { if($v) { - $x = explode('.',$v); + $x = explode('.', $v); if($x[0] == 'config') return get_config($x[1],$x[2]); elseif($x[0] === 'request') @@ -179,6 +191,7 @@ class Comanche { return $y['xchan_name']; elseif($x[1] == 'webname') return substr($y['xchan_addr'],0,strpos($y['xchan_addr'],'@')); + return false; } return get_observer_hash(); @@ -186,30 +199,39 @@ class Comanche { else return false; } + return false; } + /** + * @brief Test for Conditional Execution conditions. + * + * This is extensible. The first version of variable testing supports tests of the forms: + * + * - [if $config.system.foo ~= baz] which will check if get_config('system','foo') contains the string 'baz'; + * - [if $config.system.foo == baz] which will check if get_config('system','foo') is the string 'baz'; + * - [if $config.system.foo != baz] which will check if get_config('system','foo') is not the string 'baz'; + * - [if $config.system.foo >= 3] which will check if get_config('system','foo') is greater than or equal to 3; + * - [if $config.system.foo > 3] which will check if get_config('system','foo') is greater than 3; + * - [if $config.system.foo <= 3] which will check if get_config('system','foo') is less than or equal to 3; + * - [if $config.system.foo < 3] which will check if get_config('system','foo') is less than 3; + * + * - [if $config.system.foo {} baz] which will check if 'baz' is an array element in get_config('system','foo') + * - [if $config.system.foo {*} baz] which will check if 'baz' is an array key in get_config('system','foo') + * - [if $config.system.foo] which will check for a return of a true condition for get_config('system','foo'); + * + * The values 0, '', an empty array, and an unset value will all evaluate to false. + * + * @param int|string $s + * @return boolean + */ function test_condition($s) { - // This is extensible. The first version of variable testing supports tests of the forms: - - // [if $config.system.foo ~= baz] which will check if get_config('system','foo') contains the string 'baz'; - // [if $config.system.foo == baz] which will check if get_config('system','foo') is the string 'baz'; - // [if $config.system.foo != baz] which will check if get_config('system','foo') is not the string 'baz'; - // [if $config.system.foo >= 3] which will check if get_config('system','foo') is greater than or equal to 3; - // [if $config.system.foo > 3] which will check if get_config('system','foo') is greater than 3; - - // [if $config.system.foo <= 3] which will check if get_config('system','foo') is less than or equal to 3; - // [if $config.system.foo < 3] which will check if get_config('system','foo') is less than 3; - - // [if $config.system.foo {} baz] which will check if 'baz' is an array element in get_config('system','foo') - // [if $config.system.foo {*} baz] which will check if 'baz' is an array key in get_config('system','foo') - // [if $config.system.foo] which will check for a return of a true condition for get_config('system','foo'); - // The values 0, '', an empty array, and an unset value will all evaluate to false. if(preg_match('/[\$](.*?)\s\~\=\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if(stripos($x,trim($matches[2])) !== false) return true; + return false; } @@ -217,6 +239,7 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if($x == trim($matches[2])) return true; + return false; } @@ -224,6 +247,7 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if($x != trim($matches[2])) return true; + return false; } @@ -231,24 +255,31 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if($x >= trim($matches[2])) return true; + return false; } + if(preg_match('/[\$](.*?)\s\<\=\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if($x <= trim($matches[2])) return true; + return false; } + if(preg_match('/[\$](.*?)\s\>\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if($x > trim($matches[2])) return true; + return false; } + if(preg_match('/[\$](.*?)\s\>\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if($x < trim($matches[2])) return true; + return false; } @@ -256,6 +287,7 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if(is_array($x) && in_array(trim($matches[2]),$x)) return true; + return false; } @@ -263,6 +295,7 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if(is_array($x) && array_key_exists(trim($matches[2]),$x)) return true; + return false; } @@ -270,13 +303,21 @@ class Comanche { $x = $this->get_condition_var($matches[1]); if($x) return true; + return false; } - return false; + return false; } - + /** + * @brief Return rendered menu for current channel_id. + * + * @see menu_render() + * @param string $s + * @param string $class (optional) default empty + * @return string + */ function menu($s, $class = '') { $channel_id = $this->get_channel_id(); @@ -291,7 +332,7 @@ class Comanche { } if($channel_id) { - $m = menu_fetch($name,$channel_id, get_observer_hash()); + $m = menu_fetch($name, $channel_id, get_observer_hash()); return menu_render($m, $class, $edit = false, $var); } } @@ -309,9 +350,8 @@ class Comanche { * Returns the channel_id of the profile owner of the page, or the local_channel * if there is no profile owner. Otherwise returns 0. * - * @return channel_id + * @return int channel_id */ - function get_channel_id() { $channel_id = ((is_array(\App::$profile)) ? \App::$profile['profile_uid'] : 0); @@ -321,6 +361,13 @@ class Comanche { return $channel_id; } + /** + * @brief Returns a parsed block. + * + * @param string $s + * @param string $class (optional) default empty + * @return string parsed HTML of block + */ function block($s, $class = '') { $var = array(); $matches = array(); @@ -339,7 +386,7 @@ class Comanche { $channel_id = $this->get_channel_id(); if($channel_id) { - $r = q("select * from item inner join iconfig on iconfig.iid = item.id and item.uid = %d + $r = q("select * from item inner join iconfig on iconfig.iid = item.id and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' and iconfig.v = '%s' limit 1", intval($channel_id), dbesc($name) @@ -381,6 +428,12 @@ class Comanche { return $o; } + /** + * @brief Include JS depending on framework. + * + * @param string $s + * @return string + */ function js($s) { switch($s) { @@ -401,9 +454,14 @@ class Comanche { $ret .= $init; return $ret; - } + /** + * @brief Include CSS depending on framework. + * + * @param string $s + * @return string + */ function css($s) { switch($s) { @@ -418,17 +476,22 @@ class Comanche { $ret = '<link rel="stylesheet" href="' . z_root() . '/' . $path . '" type="text/css" media="screen">'; return $ret; - } - // This doesn't really belong in Comanche, but it could also be argued that it is the perfect place. - // We need to be able to select what kind of template and decoration to use for the webpage at the heart of our content. - // For now we'll allow an '[authored]' element which defaults to name and date, or 'none' to remove these, and perhaps - // 'full' to provide a social network style profile photo. - // But leave it open to have richer templating options and perhaps ultimately discard this one, once we have a better idea - // of what template and webpage options we might desire. - - function webpage(&$a,$s) { + /** + * This doesn't really belong in Comanche, but it could also be argued that it is the perfect place. + * We need to be able to select what kind of template and decoration to use for the webpage at the heart of our content. + * For now we'll allow an '[authored]' element which defaults to name and date, or 'none' to remove these, and perhaps + * 'full' to provide a social network style profile photo. + * + * But leave it open to have richer templating options and perhaps ultimately discard this one, once we have a better idea + * of what template and webpage options we might desire. + * + * @param[in,out] array $a + * @param string $s + * @return array + */ + function webpage(&$a, $s) { $ret = array(); $matches = array(); @@ -438,22 +501,20 @@ class Comanche { $ret['authored'] = $mtch[1]; } } + return $ret; } - /** - * Render a widget + * @brief Render a widget. * * @param string $name * @param string $text */ - function widget($name, $text) { $vars = array(); $matches = array(); - $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); if ($cnt) { foreach ($matches as $mtch) { @@ -469,15 +530,23 @@ class Comanche { if(file_exists('Zotlabs/SiteWidget/' . $clsname . '.php')) require_once('Zotlabs/SiteWidget/' . $clsname . '.php'); + elseif(file_exists('widget/' . $clsname . '/' . $clsname . '.php')) + require_once('widget/' . $clsname . '/' . $clsname . '.php'); elseif(file_exists('Zotlabs/Widget/' . $clsname . '.php')) require_once('Zotlabs/Widget/' . $clsname . '.php'); + else { + $pth = theme_include($clsname . '.php'); + if($pth) { + require_once($pth); + } + } if(class_exists($nsname)) { $x = new $nsname; $f = 'widget'; if(method_exists($x,$f)) { return $x->$f($vars); } - } + } $func = 'widget_' . trim($name); @@ -486,11 +555,13 @@ class Comanche { require_once('widget/' . trim($name) . '.php'); elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); - } - else { - $theme_widget = $func . '.php'; - if((! function_exists($func)) && theme_include($theme_widget)) - require_once(theme_include($theme_widget)); + + if(! function_exists($func)) { + $theme_widget = $func . '.php'; + if(theme_include($theme_widget)) { + require_once(theme_include($theme_widget)); + } + } } if(function_exists($func)) @@ -560,9 +631,9 @@ class Comanche { } - /* - * @function register_page_template($arr) - * Registers a page template/variant for use by Comanche selectors + /** + * @brief Registers a page template/variant for use by Comanche selectors. + * * @param array $arr * 'template' => template name * 'variant' => array( @@ -574,8 +645,6 @@ class Comanche { * ) * ) */ - - function register_page_template($arr) { \App::$page_layouts[$arr['template']] = array($arr['variant']); return; diff --git a/Zotlabs/Render/Theme.php b/Zotlabs/Render/Theme.php index 3a0116abe..09cc7a4d4 100644 --- a/Zotlabs/Render/Theme.php +++ b/Zotlabs/Render/Theme.php @@ -2,6 +2,8 @@ namespace Zotlabs\Render; +use App; + class Theme { @@ -11,17 +13,28 @@ class Theme { static $session_theme = null; static $session_mobile_theme = null; - static $base_themes = array('redbasic'); + /** + * @brief Array with base or fallback themes. + */ + static $base_themes = array('redbasic'); + + /** + * @brief Figure out the best matching theme and return it. + * + * The theme will depend on channel settings, mobile, session, core compatibility, etc. + * + * @return array + */ static public function current(){ - self::$system_theme = ((isset(\App::$config['system']['theme'])) + self::$system_theme = ((isset(\App::$config['system']['theme'])) ? \App::$config['system']['theme'] : ''); - self::$session_theme = ((isset($_SESSION) && x($_SESSION,'theme')) + self::$session_theme = ((isset($_SESSION) && x($_SESSION, 'theme')) ? $_SESSION['theme'] : self::$system_theme); - self::$system_mobile_theme = ((isset(\App::$config['system']['mobile_theme'])) + self::$system_mobile_theme = ((isset(\App::$config['system']['mobile_theme'])) ? \App::$config['system']['mobile_theme'] : ''); - self::$session_mobile_theme = ((isset($_SESSION) && x($_SESSION,'mobile_theme')) + self::$session_mobile_theme = ((isset($_SESSION) && x($_SESSION, 'mobile_theme')) ? $_SESSION['mobile_theme'] : self::$system_mobile_theme); $page_theme = null; @@ -66,7 +79,7 @@ class Theme { $chosen_theme = $page_theme; } } - if(array_key_exists('theme_preview',$_GET)) + if(array_key_exists('theme_preview', $_GET)) $chosen_theme = $_GET['theme_preview']; // Allow theme selection of the form 'theme_name:schema_name' @@ -91,14 +104,12 @@ class Theme { } // Worst case scenario, the default base theme or themes don't exist; perhaps somebody renamed it/them. - - // Find any theme at all and use it. - - $fallback = array_merge(glob('view/theme/*/css/style.css'),glob('view/theme/*/php/style.php')); - if(count($fallback)) - return(array(str_replace('view/theme/','', substr($fallback[0],0,-14)))); + // Find any theme at all and use it. + $fallback = array_merge(glob('view/theme/*/css/style.css'), glob('view/theme/*/php/style.php')); + if(count($fallback)) + return(array(str_replace('view/theme/', '', substr($fallback[0], 0, -14)))); } @@ -107,12 +118,11 @@ class Theme { * * Provide a sane default if nothing is chosen or the specified theme does not exist. * - * @param bool $installing default false + * @param bool $installing (optional) default false, if true return the name of the first base theme * * @return string */ - - function url($installing = false) { + static public function url($installing = false) { if($installing) return self::$base_themes[0]; @@ -125,9 +135,10 @@ class Theme { $opts = ''; $opts = ((\App::$profile_uid) ? '?f=&puid=' . \App::$profile_uid : ''); - $schema_str = ((x(\App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : ''); + $schema_str = ((x(\App::$layout,'schema')) ? '&schema=' . App::$layout['schema'] : ''); if(($s) && (! $schema_str)) $schema_str = '&schema=' . $s; + $opts .= $schema_str; if(file_exists('view/theme/' . $t . '/php/style.php')) @@ -139,7 +150,6 @@ class Theme { function debug() { logger('system_theme: ' . self::$system_theme); logger('session_theme: ' . self::$session_theme); - } } diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index 6f6f4a292..c21b68971 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -12,7 +12,7 @@ use Sabre\DAV; * * @extends \\Sabre\\DAV\\Browser\\Plugin * - * @link http://github.com/friendica/red + * @link http://github.com/redmatrix/hubzilla * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) */ class Browser extends DAV\Browser\Plugin { @@ -173,6 +173,7 @@ class Browser extends DAV\Browser\Plugin { $displayName = $this->escapeHTML($displayName); $type = $this->escapeHTML($type); + $icon = ''; if ($this->enableAssets) { @@ -196,12 +197,53 @@ class Browser extends DAV\Browser\Plugin { } } + + // generate preview icons for tile view. + // Currently we only handle images, but this could potentially be extended with plugins + // to provide document and video thumbnails. SVG, PDF and office documents have some + // security concerns and should only be allowed on single-user sites with tightly controlled + // upload access. system.thumbnail_security should be set to 1 if you want to include these + // types + + $photo_icon = ''; + $preview_style = intval(get_config('system','thumbnail_security',0)); + + $r = q("select content from attach where hash = '%s' and uid = %d limit 1", + dbesc($attachHash), + intval($owner) + ); + + if($r && file_exists(dbunescbin($r[0]['content']) . '.thumb')) { + $photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb')); +// logger('found thumb: ' . $photo_icon); + } + + if(strpos($type,'image/') === 0 && $attachHash) { + $r = q("select resource_id, imgscale from photo where resource_id = '%s' and imgscale in ( %d, %d ) order by imgscale asc limit 1", + dbesc($attachHash), + intval(PHOTO_RES_320), + intval(PHOTO_RES_PROFILE_80) + ); + if($r) { + $photo_icon = 'photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale']; + } + if($type === 'image/svg+xml' && $preview_style > 0) { + $photo_icon = $fullPath; + } + } + + $g = [ 'resource_id' => $attachHash, 'thumbnail' => $photo_icon, 'security' => $preview_style ]; + call_hooks('file_thumbnail', $g); + $photo_icon = $g['thumbnail']; + + $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"fa fa-arrow-circle-o-down\"></i></a>"; // put the array for this file together $ft['attachId'] = $this->findAttachIdByHash($attachHash); $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); $ft['icon'] = $icon; + $ft['photo_icon'] = $photo_icon; $ft['attachIcon'] = (($size) ? $attachIcon : ''); // @todo Should this be an item value, not a global one? $ft['is_owner'] = $is_owner; @@ -216,11 +258,13 @@ class Browser extends DAV\Browser\Plugin { $f[] = $ft; } + $output = ''; if ($this->enablePost) { $this->server->emit('onHTMLActionsPanel', array($parent, &$output, $path)); } + $html .= replace_macros(get_markup_template('cloud.tpl'), array( '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", '$total' => t('Total'), @@ -230,6 +274,8 @@ class Browser extends DAV\Browser\Plugin { '$upload' => t('Upload'), '$is_owner' => $is_owner, '$parentpath' => $parentpath, + '$cpath' => bin2hex(\App::$query_string), + '$tiles' => intval($_SESSION['cloud_tiles']), '$entries' => $f, '$name' => t('Name'), '$type' => t('Type'), @@ -327,8 +373,6 @@ class Browser extends DAV\Browser\Plugin { if(strpos($path,$special) === 0) $path = trim(substr($path,$count),'/'); - $info = t('Please use DAV to upload large (video, audio) files.<br>See <a class="zrl" href="help/member/member_guide#Cloud_Desktop_Clients">Cloud Desktop Clients</a>'); - $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( '$folder_header' => t('Create new folder'), @@ -336,7 +380,6 @@ class Browser extends DAV\Browser\Plugin { '$upload_header' => t('Upload file'), '$upload_submit' => t('Upload'), '$quota' => $quota, - '$info' => $info, '$channick' => $this->auth->owner_nick, '$aclselect' => $aclselect, '$allow_cid' => acl2json($channel_acl['allow_cid']), diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index 0ed7a3c68..510d463c1 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -16,7 +16,7 @@ use Sabre\DAV; * @link http://github.com/friendica/red * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) */ -class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { +class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMoveTarget { /** * @brief The path inside /cloud @@ -299,6 +299,17 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { $is_photo = 1; } + // If we know it's a photo, over-ride the type in case the source system could not determine what it was + + if($is_photo) { + q("update attach set filetype = '%s' where hash = '%s' and uid = %d", + dbesc($gis['mime']), + dbesc($hash), + intval($c[0]['channel_id']) + ); + } + + // updates entry with filesize and timestamp $d = q("UPDATE attach SET filesize = '%s', os_path = '%s', display_path = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", dbesc($size), @@ -351,6 +362,8 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { $args = array( 'resource_id' => $hash, 'album' => $album, 'os_syspath' => $f, 'os_path' => $xpath['os_path'], 'display_path' => $xpath['path'], 'filename' => $name, 'getimagesize' => $gis, 'directory' => $direct); $p = photo_upload($c[0], \App::get_observer(), $args); } + + \Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $this->folder_hash ]); $sync = attach_export_data($c[0], $hash); @@ -444,6 +457,22 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { return false; } + + public function moveInto($targetName,$sourcePath, DAV\INode $sourceNode) { + + if(! $this->auth->owner_id) { + return false; + } + + if(! ($sourceNode->data && $sourceNode->data->hash)) { + return false; + } + + return attach_move($this->auth->owner_id, $sourceNode->data->hash, $this->folder_hash); + + } + + /** * @todo add description of what this function does. * @@ -662,7 +691,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota { } $prefix = ''; - $suffix = ''; + $suffix = ' order by is_dir desc, filename asc '; $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", dbesc($folder), diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php index 947a9fde3..53d5d3476 100644 --- a/Zotlabs/Storage/File.php +++ b/Zotlabs/Storage/File.php @@ -129,12 +129,16 @@ class File extends DAV\Node implements DAV\IFile { $album = ''; $os_path = ''; - $r = q("SELECT flags, folder, os_storage, os_path, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + $r = q("SELECT flags, folder, os_storage, os_path, display_path, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", dbesc($this->data['hash']), intval($c[0]['channel_id']) ); if ($r) { + $os_path = $r[0]['os_path']; + $display_path = $r[0]['display_path']; + $filename = $r[0]['filename']; + if (intval($r[0]['os_storage'])) { $d = q("select folder, content from attach where hash = '%s' and uid = %d limit 1", @@ -168,6 +172,17 @@ class File extends DAV\Node implements DAV\IFile { if(($gis) && ($gis[2] === IMAGETYPE_GIF || $gis[2] === IMAGETYPE_JPEG || $gis[2] === IMAGETYPE_PNG)) { $is_photo = 1; } + + // If we know it's a photo, over-ride the type in case the source system could not determine what it was + + if($is_photo) { + q("update attach set filetype = '%s' where hash = '%s' and uid = %d", + dbesc($gis['mime']), + dbesc($this->data['hash']), + intval($this->data['uid']) + ); + } + } else { // this shouldn't happen any more @@ -199,7 +214,7 @@ class File extends DAV\Node implements DAV\IFile { if($is_photo) { require_once('include/photos.php'); - $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_syspath' => $f, 'os_path' => $os_path, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); + $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_syspath' => $f, 'os_path' => $os_path, 'display_path' => $display_path, 'filename' => $filename, 'getimagesize' => $gis, 'directory' => $direct ); $p = photo_upload($c[0],\App::get_observer(),$args); } @@ -233,6 +248,9 @@ class File extends DAV\Node implements DAV\IFile { } } + \Zotlabs\Daemon\Master::Summon([ 'Thumbnail' , $this->data['hash'] ]); + + $sync = attach_export_data($c[0],$this->data['hash']); if($sync) diff --git a/Zotlabs/Thumbs/Epubthumb.php b/Zotlabs/Thumbs/Epubthumb.php new file mode 100644 index 000000000..4213b5267 --- /dev/null +++ b/Zotlabs/Thumbs/Epubthumb.php @@ -0,0 +1,38 @@ +<?php + +namespace Zotlabs\Thumbs; + +require_once('library/epub-meta/epub.php'); + +class Epubthumb { + + function Match($type) { + return(($type === 'application/epub+zip') ? true : false ); + } + + function Thumb($attach,$preview_style,$height = 300, $width = 300) { + + $photo = false; + + $ep = new \Epub(dbunescbin($attach['content'])); + $data = $ep->Cover(); + + if($data['found']) { + $photo = $data['data']; + } + + if($photo) { + $image = imagecreatefromstring($photo); + $dest = imagecreatetruecolor( $width, $height ); + $srcwidth = imagesx($image); + $srcheight = imagesy($image); + + imagealphablending($dest, false); + imagesavealpha($dest, true); + imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); + imagedestroy($image); + imagejpeg($dest,dbunescbin($attach['content']) . '.thumb'); + } + } +} + diff --git a/Zotlabs/Thumbs/Mp3audio.php b/Zotlabs/Thumbs/Mp3audio.php new file mode 100644 index 000000000..000d65b22 --- /dev/null +++ b/Zotlabs/Thumbs/Mp3audio.php @@ -0,0 +1,37 @@ +<?php + +namespace Zotlabs\Thumbs; + +use \ID3Parser\ID3Parser; + +class Mp3audio { + + function Match($type) { + return(($type === 'audio/mpeg') ? true : false ); + } + + function Thumb($attach,$preview_style,$height = 300, $width = 300) { + $p = new ID3Parser(); + + $id = $p->analyze(dbunescbin($attach['content'])); + + $photo = isset($id['id3v2']['APIC'][0]['data']) ? $id['id3v2']['APIC'][0]['data'] : null; + if(is_null($photo) && isset($id['id3v2']['PIC'][0]['data'])) { + $photo = $id['id3v2']['PIC'][0]['data']; + } + + if($photo) { + $image = imagecreatefromstring($photo); + $dest = imagecreatetruecolor( $width, $height ); + $srcwidth = imagesx($image); + $srcheight = imagesy($image); + + imagealphablending($dest, false); + imagesavealpha($dest, true); + imagecopyresampled($dest, $image, 0, 0, 0, 0, $width, $height, $srcwidth, $srcheight); + imagedestroy($image); + imagejpeg($dest,dbunescbin($attach['content']) . '.thumb'); + } + } +} + diff --git a/Zotlabs/Thumbs/Pdf.php b/Zotlabs/Thumbs/Pdf.php new file mode 100644 index 000000000..98bcf11b5 --- /dev/null +++ b/Zotlabs/Thumbs/Pdf.php @@ -0,0 +1,49 @@ +<?php + +namespace Zotlabs\Thumbs; + + +class Pdf { + + function Match($type) { + return(($type === 'application/pdf') ? true : false ); + } + + function Thumb($attach,$preview_style,$height = 300, $width = 300) { + + $photo = false; + + $file = dbunescbin($attach['content']); + $tmpfile = $file . '.pdf'; + $outfile = $file . '.jpg'; + + $istream = fopen($file,'rb'); + $ostream = fopen($tmpfile,'wb'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + + $imagick_path = get_config('system','imagick_convert_path'); + if($imagick_path && @file_exists($imagick_path)) { + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); + // logger('imagick thumbnail command: ' . $cmd); + for($x = 0; $x < 4; $x ++) { + exec($cmd); + if(! file_exists($outfile)) { + logger('imagick scale failed. Retrying.'); + continue; + } + } + if(! file_exists($outfile)) { + logger('imagick scale failed.'); + } + else { + @rename($outfile,$file . '.thumb'); + } + } + @unlink($tmpfile); + } +} + diff --git a/Zotlabs/Thumbs/Text.php b/Zotlabs/Thumbs/Text.php new file mode 100644 index 000000000..3ee7819bd --- /dev/null +++ b/Zotlabs/Thumbs/Text.php @@ -0,0 +1,49 @@ +<?php + +namespace Zotlabs\Thumbs; + + +class Text { + + function MatchDefault($type) { + return(($type === 'text') ? true : false ); + } + + function Thumb($attach,$preview_style,$height = 300, $width = 300) { + + $stream = @fopen(dbunescbin($attach['content']),'rb'); + if($stream) { + $content = trim(stream_get_contents($stream,4096)); + $content = str_replace("\r",'',$content); + $content_a = explode("\n",$content); + } + if($content_a) { + $fsize = 4; + $lsize = 8; + $image = imagecreate($width,$height); + imagecolorallocate($image,255,255,255); + $colour = imagecolorallocate($image,0,0,0); + $border = imagecolorallocate($image,208,208,208); + + $x1 = 0; + $y1 = 0; + $x2 = ImageSX($image) - 1; + $y2 = ImageSY($image) - 1; + + for($i = 0; $i < 2; $i++) { + ImageRectangle($image, $x1++, $y1++, $x2--, $y2--, $border); + } + + foreach($content_a as $l => $t) { + $l = $l + 1; + $x = 3; + $y = ($l * $lsize) + 3 - $fsize; + imagestring($image,1,$x,$y,$t,$colour); + if(($l * $lsize) >= $height) { + break; + } + } + imagejpeg($image,dbunescbin($attach['content']) . '.thumb'); + } + } +}
\ No newline at end of file diff --git a/Zotlabs/Thumbs/Video.php b/Zotlabs/Thumbs/Video.php new file mode 100644 index 000000000..05127355e --- /dev/null +++ b/Zotlabs/Thumbs/Video.php @@ -0,0 +1,65 @@ +<?php + +namespace Zotlabs\Thumbs; + + +class Video { + + function MatchDefault($type) { + return(($type === 'video') ? true : false ); + } + + function Thumb($attach,$preview_style,$height = 300, $width = 300) { + + $photo = false; + + $t = explode('/',$attach['filetype']); + if($t[1]) + $extension = '.' . $t[1]; + else + return; + + + $file = dbunescbin($attach['content']); + $tmpfile = $file . $extension; + $outfile = $file . '.jpg'; + + $istream = fopen($file,'rb'); + $ostream = fopen($tmpfile,'wb'); + if($istream && $ostream) { + pipe_streams($istream,$ostream); + fclose($istream); + fclose($ostream); + } + + /* + * Note: imagick convert may try to call 'ffmpeg' (or other conversion utilities) under + * the covers for this particular operation. If this is not installed or not in the path + * for the web server user, errors may be reported in the web server logs. + */ + + + $ffmpeg = trim(shell_exec('which ffmpeg')); + if($ffmpeg) { + logger('ffmpeg not found in path. Video thumbnails may fail.'); + } + + $imagick_path = get_config('system','imagick_convert_path'); + if($imagick_path && @file_exists($imagick_path)) { + $cmd = $imagick_path . ' ' . escapeshellarg(PROJECT_BASE . '/' . $tmpfile . '[0]') . ' -thumbnail ' . $width . 'x' . $height . ' ' . escapeshellarg(PROJECT_BASE . '/' . $outfile); + // logger('imagick thumbnail command: ' . $cmd); + + @exec($cmd); + + if(! file_exists($outfile)) { + logger('imagick scale failed.'); + } + else { + @rename($outfile,$file . '.thumb'); + } + } + + @unlink($tmpfile); + } +} + diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php index 1c66b8cf4..9a8c23a9b 100644 --- a/Zotlabs/Web/HTTPSig.php +++ b/Zotlabs/Web/HTTPSig.php @@ -237,7 +237,7 @@ class HTTPSig { $fields = '(request-target)'; } - if(head) { + if($head) { foreach($head as $k => $v) { $headers .= strtolower($k) . ': ' . trim($v) . "\n"; if($fields) diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index 9486130cb..a6b780cdc 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -2,6 +2,8 @@ namespace Zotlabs\Web; +use Exception; + /** * * We have already parsed the server path into App::$argc and App::$argv @@ -34,7 +36,7 @@ class Router { private $controller = null; /** - * @brief Router constructor + * @brief Router constructor. * * @param[in,out] App &$a * @throws Exception module not found @@ -98,15 +100,23 @@ class Router { } } - /* - * This provides a place for plugins to register module handlers which don't otherwise exist - * on the system, or to completely over-ride an existing module. - * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if - * there is no specific module file or matching plugin name. - * The plugin should catch at least one of the module hooks for this URL. + $x = [ + 'module' => $module, + 'installed' => \App::$module_loaded, + 'controller' => $this->controller + ]; + /** + * @hooks module_loaded + * Called when a module has been successfully locate to server a URL request. + * This provides a place for plugins to register module handlers which don't otherwise exist + * on the system, or to completely over-ride an existing module. + * If the plugin sets 'installed' to true we won't throw a 404 error for the specified module even if + * there is no specific module file or matching plugin name. + * The plugin should catch at least one of the module hooks for this URL. + * * \e string \b module + * * \e boolean \b installed + * * \e mixed \b controller - The initialized module object */ - - $x = array('module' => $module, 'installed' => \App::$module_loaded, 'controller' => $this->controller); call_hooks('module_loaded', $x); if($x['installed']) { \App::$module_loaded = true; @@ -131,14 +141,14 @@ class Router { } } - $x = [ - 'module' => $module, - 'installed' => \App::$module_loaded, + $x = [ + 'module' => $module, + 'installed' => \App::$module_loaded, 'controller' => $this->controller ]; call_hooks('page_not_found',$x); - // Stupid browser tried to pre-fetch our Javascript img template. + // Stupid browser tried to pre-fetch our Javascript img template. // Don't log the event or return anything - just quietly exit. if((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { @@ -147,8 +157,8 @@ class Router { if(get_config('system','log_404',true)) { logger("Module {$module} not found.", LOGGER_DEBUG, LOG_WARNING); - logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] - . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' + logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] + . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); } @@ -255,7 +265,7 @@ class Router { if(! \App::$error) { $arr = array('content' => \App::$page['content'], 'replace' => false); call_hooks(\App::$module . '_mod_content', $arr); - \App::$page['content'] = $arr['content']; + if(! $arr['replace']) { if($this->controller && method_exists($this->controller,'get')) { $arr = array('content' => $this->controller->get()); @@ -266,8 +276,8 @@ class Router { } } call_hooks(\App::$module . '_mod_aftercontent', $arr); - \App::$page['content'] .= $arr['content']; + \App::$page['content'] = (($arr['replace']) ? $arr['content'] : \App::$page['content'] . $arr['content']); } } } -}
\ No newline at end of file +} diff --git a/Zotlabs/Widget/Affinity.php b/Zotlabs/Widget/Affinity.php index 439ba1f33..a3e5b5c07 100644 --- a/Zotlabs/Widget/Affinity.php +++ b/Zotlabs/Widget/Affinity.php @@ -9,15 +9,9 @@ class Affinity { 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); + + $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : 0); + $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : 99); if(feature_enabled(local_channel(),'affinity')) { diff --git a/Zotlabs/Widget/Appcategories.php b/Zotlabs/Widget/Appcategories.php index 490ec1abc..8ff14230f 100644 --- a/Zotlabs/Widget/Appcategories.php +++ b/Zotlabs/Widget/Appcategories.php @@ -26,6 +26,7 @@ class Appcategories { and term.uid = app_channel and term.otype = %d and term.term != 'nav_featured_app' + and term.term != 'nav_pinned_app' order by term.term asc", intval(local_channel()), intval(TERM_OBJ_APP) diff --git a/Zotlabs/Widget/Categories.php b/Zotlabs/Widget/Categories.php index 305869706..9bfa9742a 100644 --- a/Zotlabs/Widget/Categories.php +++ b/Zotlabs/Widget/Categories.php @@ -13,8 +13,14 @@ class Categories { if(($cards) && (! feature_enabled(\App::$profile['profile_uid'],'cards'))) return ''; + $articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false); + + if(($articles) && (! feature_enabled(\App::$profile['profile_uid'],'articles'))) + return ''; + + if((! \App::$profile['profile_uid']) - || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards) ? 'view_pages' : 'view_stream')))) { + || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) { return ''; } @@ -25,6 +31,8 @@ class Categories { if($cards) return cardcategories_widget($srchurl, $cat); + elseif($articles) + return articlecategories_widget($srchurl, $cat); else return categories_widget($srchurl, $cat); diff --git a/Zotlabs/Widget/Conversations.php b/Zotlabs/Widget/Conversations.php index 56510750f..267d50fa0 100644 --- a/Zotlabs/Widget/Conversations.php +++ b/Zotlabs/Widget/Conversations.php @@ -28,6 +28,8 @@ class Conversations { require_once('include/message.php'); + $o = ''; + // 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']); @@ -36,13 +38,13 @@ class Conversations { return $o; } - $messages = array(); + $messages = []; foreach($r as $rr) { $selected = ((argc() == 3) ? intval(argv(2)) == intval($rr['id']) : $r[0]['id'] == $rr['id']); - $messages[] = array( + $messages[] = [ 'mailbox' => $mailbox, 'id' => $rr['id'], 'from_name' => $rr['from']['xchan_name'], @@ -57,14 +59,14 @@ class Conversations { 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], 'c'), 'seen' => $rr['seen'], 'selected' => ((argv(1) != 'new') ? $selected : '') - ); + ]; } $tpl = get_markup_template('mail_head.tpl'); - $o .= replace_macros($tpl, array( + $o .= replace_macros($tpl, [ '$header' => $header, '$messages' => $messages - )); + ]); } return $o; diff --git a/Zotlabs/Widget/Forums.php b/Zotlabs/Widget/Forums.php index 002c0ee21..91b987746 100644 --- a/Zotlabs/Widget/Forums.php +++ b/Zotlabs/Widget/Forums.php @@ -29,18 +29,32 @@ class Forums { ); 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) + + if($x2) { $xf = ids_to_querystr($x2,'xchan',true); + + // private forums + $x3 = q("select xchan from abconfig where chan = %d and cat = 'their_perms' and k = 'post_wall' and v = '1' and xchan in (" . $xc . ") and not xchan in (" . $xf . ") ", + intval(local_channel()) + ); + if($x3) { + $xf = ids_to_querystr(array_merge($x2,$x3),'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 ", + + + $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 and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 $sql_extra order by xchan_name $limit ", intval(local_channel()) ); + if(! $r1) return $o; @@ -85,9 +99,21 @@ class Forums { $o .= '<h3>' . t('Forums') . '</h3><ul class="nav nav-pills flex-column">'; foreach($r1 as $rr) { + + $link = 'network?f=&pf=1&cid=' . $rr['abook_id']; + if($x3) { + foreach($x3 as $xx) { + if($rr['xchan_hash'] == $xx['xchan']) { + $link = zid($rr['xchan_url']); + } + } + } + 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-secondary 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 .= '<li class="nav-item"><a class="nav-link" href="' . $link . '" ><span class="badge badge-secondary 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>'; } diff --git a/Zotlabs/Widget/Hq_controls.php b/Zotlabs/Widget/Hq_controls.php new file mode 100644 index 000000000..0caa54a1a --- /dev/null +++ b/Zotlabs/Widget/Hq_controls.php @@ -0,0 +1,26 @@ +<?php + +namespace Zotlabs\Widget; + +class Hq_controls { + + function widget($arr) { + + if (! local_channel()) + return; + + return replace_macros(get_markup_template('hq_controls.tpl'), + [ + '$title' => t('HQ Control Panel'), + '$menu' => [ + 'create' => [ + 'label' => t('Create a new post'), + 'id' => 'jot-toggle', + 'href' => '#', + 'class' => '' + ] + ] + ] + ); + } +} diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php index 191f2afb6..5a0c1f3d5 100644 --- a/Zotlabs/Widget/Notifications.php +++ b/Zotlabs/Widget/Notifications.php @@ -20,8 +20,10 @@ class Notifications { 'label' => t('View your network activity') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all notifications read') + ], + 'filter' => [ + 'label' => t('Show new posts only') ] ]; @@ -36,8 +38,10 @@ class Notifications { 'label' => t('View your home activity') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all notifications seen') + ], + 'filter' => [ + 'label' => t('Show new posts only') ] ]; @@ -52,7 +56,6 @@ class Notifications { 'label' => t('View your private mails') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all messages seen') ] ]; @@ -68,7 +71,6 @@ class Notifications { 'label' => t('View events') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all events seen') ] ]; @@ -104,7 +106,6 @@ class Notifications { 'label' => t('View all notices') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all notices seen') ] ]; @@ -132,8 +133,10 @@ class Notifications { 'label' => t('View the public stream') ], 'markall' => [ - 'url' => '#', 'label' => t('Mark all notifications seen') + ], + 'filter' => [ + 'label' => t('Show new posts only') ] ]; } @@ -141,7 +144,8 @@ class Notifications { $o = replace_macros(get_markup_template('notifications_widget.tpl'), array( '$module' => \App::$module, '$notifications' => $notifications, - '$loading' => t('Loading...') + '$no_notifications' => t('Sorry, you have got no notifications at the moment'), + '$loading' => t('Loading') )); return $o; diff --git a/Zotlabs/Widget/Settings_menu.php b/Zotlabs/Widget/Settings_menu.php index c15ad0980..e15ed96a5 100644 --- a/Zotlabs/Widget/Settings_menu.php +++ b/Zotlabs/Widget/Settings_menu.php @@ -107,7 +107,7 @@ class Settings_menu { if($role === false || $role === 'custom') { $tabs[] = array( 'label' => t('Connection Default Permissions'), - 'url' => z_root() . '/connedit/' . $abook_self_id, + 'url' => z_root() . '/defperms', 'selected' => '' ); } diff --git a/Zotlabs/Widget/Wiki_pages.php b/Zotlabs/Widget/Wiki_pages.php index 2aad8008f..06b32b5f5 100644 --- a/Zotlabs/Widget/Wiki_pages.php +++ b/Zotlabs/Widget/Wiki_pages.php @@ -5,6 +5,42 @@ namespace Zotlabs\Widget; class Wiki_pages { + function create_missing_page($arr) { + if(argc() < 4) + return; + + $c = channelx_by_nick(argv(1)); + $w = \Zotlabs\Lib\NativeWiki::exists_by_name($c['channel_id'],urldecode(argv(2))); + $arr = array( + 'resource_id' => $w['resource_id'], + 'channel_id' => $c['channel_id'], + 'channel_address' => $c['channel_address'], + 'refresh' => false + ); + + $can_create = perm_is_allowed(\App::$profile['uid'],get_observer_hash(),'write_wiki'); + + $can_delete = ((local_channel() && (local_channel() == \App::$profile['uid'])) ? true : false); + $pageName = addslashes(escape_tags(urldecode(argv(3)))); + + return replace_macros(get_markup_template('wiki_page_not_found.tpl'), array( + '$resource_id' => $arr['resource_id'], + '$channel_address' => $arr['channel_address'], + '$wikiname' => $wikiname, + '$canadd' => $can_create, + '$candel' => $can_delete, + '$addnew' => t('Add new page'), + '$typelock' => $typelock, + '$lockedtype' => $w['mimeType'], + '$mimetype' => mimetype_select(0,$w['mimeType'], + [ 'text/markdown' => t('Markdown'), 'text/bbcode' => t('BBcode'), 'text/plain' => t('Text') ]), + '$pageName' => array('missingPageName', 'Create Page' , $pageName), + '$refresh' => $arr['refresh'], + '$options' => t('Options'), + '$submit' => t('Submit') + )); + } + function widget($arr) { if(argc() < 3) diff --git a/Zotlabs/Zot/Verify.php b/Zotlabs/Zot/Verify.php index 1d9e6de3f..7abe38d17 100644 --- a/Zotlabs/Zot/Verify.php +++ b/Zotlabs/Zot/Verify.php @@ -26,12 +26,11 @@ class Verify { q("delete from verify where id = %d", intval($r[0]['id']) ); - return true; + return true; } return false; } - function get_meta($type,$channel_id,$token) { $r = q("select id, meta from verify where vtype = '%s' and channel = %d and token = '%s' limit 1", dbesc($type), @@ -42,12 +41,18 @@ class Verify { q("delete from verify where id = %d", intval($r[0]['id']) ); - return $r[0]['meta']; + return $r[0]['meta']; } return false; } - function purge($type,$interval) { + /** + * @brief Purge entries of a verify-type older than interval. + * + * @param string $type Verify type + * @param string $interval SQL compatible time interval + */ + function purge($type, $interval) { q("delete from verify where vtype = '%s' and created < %s - INTERVAL %s", dbesc($type), db_utcnow(), |