diff options
111 files changed, 2621 insertions, 1295 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b8e0430f..bf03c1763 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,10 @@ # Select image from https://hub.docker.com/_/php/ image: php:7.1 +stages: + - test + - deploy + # Select what we should cache cache: paths: @@ -11,13 +15,11 @@ variables: MYSQL_DATABASE: hello_world_test MYSQL_ROOT_PASSWORD: mysql - -services: -- mysql:5.7 - before_script: +# prevent error installing buggy postgresql-client package +- mkdir -p /usr/share/man/man1 /usr/share/man/man7 - apt-get update -yqq -- apt-get install -yqq git mysql-server mysql-client libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev +- apt-get install -yqq --no-install-recommends git mysql-client postgresql-client libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libaspell-dev libpcre3-dev libtidy-dev # Install PHP extensions - docker-php-ext-install mbstring mcrypt pdo_mysql pdo_pgsql curl json intl gd xml zip bz2 opcache # Install & enable Xdebug for code coverage reports @@ -25,12 +27,69 @@ before_script: - docker-php-ext-enable xdebug # Install and run Composer - curl -sS https://getcomposer.org/installer | php -- php composer.phar install +# Install dev libraries from composer +- php composer.phar install --no-progress +# Configure PHP values, needed for phpunit code coverage HTML generation +- echo "memory_limit = 256M" > /usr/local/etc/php/conf.d/hubzilla.ini -# We test PHP7 with MySQL, but we allow it to fail + +# We test PHP7 with MySQL test:php:mysql: + stage: test + services: + - mysql:5.7 script: - echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text + +# test PHP7 with PostgreSQL +test:php:postgres: + stage: test + services: + - postgres:latest + variables: + POSTGRES_DB: ci-db + POSTGRES_USER: ci-user + POSTGRES_PASSWORD: ci-pass + script: + - export PGPASSWORD=$POSTGRES_PASSWORD + - psql --version + - psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT VERSION();" + # Import hubzilla's DB schema + - psql -h "postgres" -U "$POSTGRES_USER" -v ON_ERROR_STOP=1 --quiet "$POSTGRES_DB" < ./install/schema_postgres.sql + # Show databases and relations/tables of hubzilla's database + #- psql -h "postgres" -U "$POSTGRES_USER" -l + #- psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt;" + # Run the actual tests + - vendor/bin/phpunit --configuration tests/phpunit-pgsql.xml --testdox + artifacts: + expire_in: 1 week + # Gitlab should show the results, but has problems parsing PHPUnit's junit file. + reports: + junit: tests/results/junit.xml + # Archive test results (coverage, testdox, junit) + name: "$CI_COMMIT_REF_SLUG-$CI_JOB_NAME" + paths: + - tests/results/ + + +# Generate Doxygen API Documentation and deploy it at GitLab pages +pages: + stage: deploy + cache: {} + image: php:7-cli-alpine + before_script: + - apk update + - apk add doxygen ttf-freefont graphviz + script: + - doxygen util/Doxyfile + - mv doc/html/ public/ + - echo "API documentation should be accessible at https://hubzilla.frama.io/core/ soon" + artifacts: + paths: + - public + only: + # Only generate it on main repo's master branch + - master@hubzilla/core @@ -1,3 +1,31 @@ +Hubzilla 3.8.9 (2018-02-03) + - Fix typos in mod oep + - Fix page jumping when liking collapsed/expanded post + - Fix failure to import mail in mod import + - Fix wrong channel count in mod new_channel + - Fix diaspora addon regression + - Remove deprecated diaspora addon endpoint + - Fix wrong function call in gallery addon + + +Hubzilla 3.8.8 (2018-12-22) + - Fix issue with linkinfo + - Fix cURL with HTTP/2 + - Remove scale_external_images() + - Style highlight bbcode via css + - Make mod channel deal with b64 encoded mid's + - Fix email retrieval in OAuth2Storage + - Add reinstall option to util/addons + - Remove deprecated caching protection from mod photo + - Add missing check for observer in mod like + - Articles: default to logged in channel if channel name is not passed + - Wiki: fix preview issue with hyperlinks + - Cart: backport fixes from osada + - Gallery: provide file extensions for better compatibility + - Hsse: fix issue when linkinfo data was inserted + - Diaspora: remove deprecated scale_external_images() + + Hubzilla 3.8.7 (2018-12-14) - Fix issue with linkdropper in comment area - Fix regression wit app ordering diff --git a/Zotlabs/Daemon/Cache_embeds.php b/Zotlabs/Daemon/Cache_embeds.php new file mode 100644 index 000000000..08088abd6 --- /dev/null +++ b/Zotlabs/Daemon/Cache_embeds.php @@ -0,0 +1,27 @@ +<?php /** @file */ + +namespace Zotlabs\Daemon; + + +class Cache_embeds { + + static public function run($argc,$argv) { + + if(! $argc == 2) + return; + + $c = q("select body from item where id = %d ", + dbesc(intval($argv[1])) + ); + + if(! $c) + return; + + $item = $c[0]; + + // bbcode conversion by default processes embeds that aren't already cached. + // Ignore the returned html output. + + bbcode($item['body']); + } +} diff --git a/Zotlabs/Daemon/Master.php b/Zotlabs/Daemon/Master.php index 0656ca05b..857d47243 100644 --- a/Zotlabs/Daemon/Master.php +++ b/Zotlabs/Daemon/Master.php @@ -16,8 +16,6 @@ if(array_search( __file__ , get_included_files()) === 0) { class Master { - static public $queueworker = null; - static public function Summon($arr) { proc_run('php','Zotlabs/Daemon/Master.php',$arr); } @@ -25,126 +23,21 @@ class Master { static public function Release($argc,$argv) { cli_startup(); - $maxworkers = get_config('system','max_queue_workers'); - - if (!$maxworkers || $maxworkers == 0) { - logger('Master: release: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG); - $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; - $cls::run($argc,$argv); - self::ClearQueue(); - } else { - logger('Master: enqueue: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG); - $workinfo = ['argc'=>$argc,'argv'=>$argv]; - q("insert into config (cat,k,v) values ('queuework','%s','%s')", - dbesc(uniqid('workitem:',true)), - dbesc(serialize($workinfo))); - self::Process(); - } - } - - static public function GetWorkerID() { - $maxworkers = get_config('system','max_queue_workers'); - $maxworkers = ($maxworkers) ? $maxworkers : 3; - - $workermaxage = get_config('system','max_queue_worker_age'); - $workermaxage = ($workermaxage) ? $workermaxage : 300; - - $workers = q("select * from config where cat='queueworkers' and k like '%s'", 'workerstarted_%'); - - if (count($workers) > $maxworkers) { - foreach ($workers as $idx => $worker) { - $curtime = time(); - $age = (intval($curtime) - intval($worker['v'])); - if ( $age > $workermaxage) { - logger("Prune worker: ".$worker['k'], LOGGER_ALL, LOGGER_DEBUG); - $k = explode('_',$worker['k']); - q("delete from config where cat='queueworkers' and k='%s'", - 'workerstarted_'.$k[1]); - q("update config set k='%s' where cat='queuework' and k='%s'", - dbesc(uniqid('workitem:',true)), - 'workitem_'.$k[1]); - unset($workers[$idx]); - } - } - if (count($workers) > $maxworkers) { - return false; - } - } - return uniqid('',true); - - } - - static public function Process() { - - self::$queueworker = self::GetWorkerID(); - - if (!self::$queueworker) { - logger('Master: unable to obtain worker ID.'); - killme(); - } - - set_config('queueworkers','workerstarted_'.self::$queueworker,time()); - - $workersleep = get_config('system','queue_worker_sleep'); - $workersleep = ($workersleep) ? $workersleep : 5; - cli_startup(); - - $work = q("update config set k='%s' where cat='queuework' and k like '%s' limit 1", - 'workitem_'.self::$queueworker, - dbesc('workitem:%')); - $jobs = 0; - while ($work) { - $workitem = q("select * from config where cat='queuework' and k='%s'", - 'workitem_'.self::$queueworker); - - if (isset($workitem[0])) { - $jobs++; - $workinfo = unserialize($workitem[0]['v']); - $argc = $workinfo['argc']; - $argv = $workinfo['argv']; - logger('Master: process: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG); - - //Delete unclaimed duplicate workitems. - q("delete from config where cat='queuework' and k='workitem' and v='%s'", - serialize($argv)); - - $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; - $cls::run($argc,$argv); + $hookinfo = [ + 'argv'=>$argv + ]; - //Right now we assume that if we get a return, everything is OK. - //At some point we may want to test whether the run returns true/false - // and requeue the work to be tried again. But we probably want - // to implement some sort of "retry interval" first. + call_hooks ('daemon_master_release',$hookinfo); - q("delete from config where cat='queuework' and k='%s'", - 'workitem_'.self::$queueworker); - } else { - break; - } - sleep ($workersleep); - $work = q("update config set k='%s' where cat='queuework' and k like '%s' limit 1", - 'workitem_'.self::$queueworker, - dbesc('workitem:%')); + $argv = $hookinfo['argv']; + $argc = count($argv); + if ((!is_array($argv) || (count($argv) < 1))) { + return; } - logger('Master: Worker Thread: queue items processed:' . $jobs); - q("delete from config where cat='queueworkers' and k='%s'", - 'workerstarted_'.self::$queueworker); - } - static public function ClearQueue() { - $work = q("select * from config where cat='queuework' and k like '%s'", - dbesc('workitem%')); - foreach ($work as $workitem) { - $workinfo = unserialize($workitem['v']); - $argc = $workinfo['argc']; - $argv = $workinfo['argv']; - logger('Master: process: ' . print_r($argv,true), LOGGER_ALL,LOG_DEBUG); - $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; - $cls::run($argc,$argv); - } - $work = q("delete from config where cat='queuework' and k like '%s'", - dbesc('workitem%')); + logger('Master: release: ' . json_encode($argv), LOGGER_ALL,LOG_DEBUG); + $cls = '\\Zotlabs\\Daemon\\' . $argv[0]; + $cls::run($argc,$argv); } - } diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index beb30ed96..df73d977d 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -434,6 +434,8 @@ class Notifier { $x['body'] = 'private'; logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); + //logger('notifier: encoded activity: ' . print_r($activity,true), LOGGER_DATA, LOG_DEBUG); + stringify_array_elms($recipients); if(! $recipients) { logger('no recipients'); @@ -677,6 +679,17 @@ class Notifier { } $packet_type = (($upstream || $uplink) ? 'response' : 'activity'); + + // block zot private reshares from zot6, as this could cause a number of privacy issues + // due to parenting differences between the reshare implementations. In zot a reshare is + // a standalone parent activity and in zot6 it is a followup/child of the original activity. + // For public reshares, some comments to the reshare on the zot fork will not make it to zot6 + // due to these different message models. This cannot be prevented at this time. + + if($packet_type === 'activity' && $activity['type'] === 'Announce' && intval($target_item['item_private'])) { + continue; + } + $packet = Libzot::build_packet($channel,$packet_type,$zenv,$activity,'activitystreams',(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto']); } else { diff --git a/Zotlabs/Daemon/Onepoll.php b/Zotlabs/Daemon/Onepoll.php index 920916828..1d9fd5f72 100644 --- a/Zotlabs/Daemon/Onepoll.php +++ b/Zotlabs/Daemon/Onepoll.php @@ -69,7 +69,7 @@ class Onepoll { return; } - if($contact['xchan_network'] !== 'zot') + if(! in_array($contact['xchan_network'],['zot','zot6'])) return; // update permissions diff --git a/Zotlabs/Daemon/Poller.php b/Zotlabs/Daemon/Poller.php index 49151437c..a8cf34ce2 100644 --- a/Zotlabs/Daemon/Poller.php +++ b/Zotlabs/Daemon/Poller.php @@ -110,7 +110,7 @@ class Poller { } - if($contact['xchan_network'] !== 'zot') + if(! in_array($contact['xchan_network'],['zot','zot6'])) continue; if($c == $t) { diff --git a/Zotlabs/Daemon/Queue.php b/Zotlabs/Daemon/Queue.php index 8f529ff13..814148404 100644 --- a/Zotlabs/Daemon/Queue.php +++ b/Zotlabs/Daemon/Queue.php @@ -12,7 +12,6 @@ class Queue { require_once('include/items.php'); require_once('include/bbcode.php'); - if($argc > 1) $queue_id = $argv[1]; else @@ -61,10 +60,20 @@ class Queue { // or just prior to this query based on recent and long-term delivery history. If we have good reason to believe // the site is permanently down, there's no reason to attempt delivery at all, or at most not more than once // or twice a day. - - $r = q("SELECT * FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s ", + + $sqlrandfunc = db_getfunc('rand'); + + $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", db_utcnow() ); + while ($r) { + foreach($r as $rv) { + queue_deliver($rv); + } + $r = q("SELECT *,$sqlrandfunc as rn FROM outq WHERE outq_delivered = 0 and outq_scheduled < %s order by rn limit 1", + db_utcnow() + ); + } } if(! $r) return; diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index ef6ee6c3e..bcbe53df7 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -32,6 +32,12 @@ class Activity { if($x['type'] === ACTIVITY_OBJ_THING) { return self::fetch_thing($x); } + if($x['type'] === ACTIVITY_OBJ_EVENT) { + return self::fetch_event($x); + } + if($x['type'] === ACTIVITY_OBJ_PHOTO) { + return self::fetch_image($x); + } return $x; @@ -99,6 +105,63 @@ class Activity { } } + + static function fetch_image($x) { + + + $ret = [ + 'type' => 'Image', + 'id' => $x['id'], + 'name' => $x['title'], + 'content' => bbcode($x['body']), + 'source' => [ 'mediaType' => 'text/bbcode', 'content' => $x['body'] ], + 'published' => datetime_convert('UTC','UTC',$x['created'],ATOM_TIME), + 'updated' => datetime_convert('UTC','UTC', $x['edited'],ATOM_TIME), + 'url' => [ + 'type' => 'Link', + 'mediaType' => $x['link'][0]['type'], + 'href' => $x['link'][0]['href'], + 'width' => $x['link'][0]['width'], + 'height' => $x['link'][0]['height'] + ] + ]; + return $ret; + } + + static function fetch_event($x) { + + // convert old Zot event objects to ActivityStreams Event objects + + if (array_key_exists('content',$x) && array_key_exists('dtstart',$x)) { + $ev = bbtoevent($x['content']); + if($ev) { + + $actor = null; + if(array_key_exists('author',$x) && array_key_exists('link',$x['author'])) { + $actor = $x['author']['link'][0]['href']; + } + $y = [ + 'type' => 'Event', + 'id' => z_root() . '/event/' . $ev['event_hash'], + 'summary' => bbcode($ev['summary']), + // RFC3339 Section 4.3 + 'startTime' => (($ev['adjust']) ? datetime_convert('UTC','UTC',$ev['dtstart'], ATOM_TIME) : datetime_convert('UTC','UTC',$ev['dtstart'],'Y-m-d\\TH:i:s-00:00')), + 'content' => bbcode($ev['description']), + 'location' => [ 'type' => 'Place', 'content' => bbcode($ev['location']) ], + 'source' => [ 'content' => format_event_bbcode($ev), 'mediaType' => 'text/bbcode' ], + 'actor' => $actor, + ]; + if($actor) { + return $y; + } + } + } + + return $x; + + } + + static function encode_item_collection($items,$id,$type,$extra = null) { $ret = [ @@ -173,7 +236,7 @@ class Activity { $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); if($i['title']) - $ret['title'] = bbcode($i['title']); + $ret['name'] = $i['title']; $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); if($i['created'] !== $i['edited']) @@ -348,10 +411,17 @@ class Activity { $ret['type'] = 'Tombstone'; $ret['formerType'] = self::activity_obj_mapper($i['obj_type']); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); + $actor = self::encode_person($i['author'],false); + if($actor) + $ret['actor'] = $actor; + else + return []; return $ret; } $ret['type'] = self::activity_mapper($i['verb']); + + $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); if($i['title']) @@ -423,6 +493,10 @@ class Activity { if(! is_array($i['obj'])) { $i['obj'] = json_decode($i['obj'],true); } + if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) { + $i['obj']['id'] = $i['id']; + } + $obj = self::encode_object($i['obj']); if($obj) $ret['object'] = $obj; @@ -568,6 +642,9 @@ class Activity { 'http://activitystrea.ms/schema/1.0/tag' => 'Add', 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', 'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept' ]; @@ -607,6 +684,9 @@ class Activity { 'http://activitystrea.ms/schema/1.0/tag' => 'Add', 'http://activitystrea.ms/schema/1.0/follow' => 'Follow', 'http://activitystrea.ms/schema/1.0/unfollow' => 'Unfollow', + 'http://purl.org/zot/activity/attendyes' => 'Accept', + 'http://purl.org/zot/activity/attendno' => 'Reject', + 'http://purl.org/zot/activity/attendmaybe' => 'TentativeAccept' ]; @@ -1331,6 +1411,9 @@ class Activity { $s['owner_xchan'] = $act->actor['id']; $s['author_xchan'] = $act->actor['id']; + // ensure we store the original actor + self::actor_store($act->actor['id'],$act->actor); + $s['mid'] = $act->obj['id']; $s['parent_mid'] = $act->parent_id; @@ -1403,7 +1486,8 @@ class Activity { $s['verb'] = self::activity_decode_mapper($act->type); - if($act->type === 'Tombstone') { + + if($act->type === 'Tombstone' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) { $s['item_deleted'] = 1; } @@ -1412,7 +1496,32 @@ class Activity { $s['obj_type'] = ACTIVITY_OBJ_COMMENT; } - $s['obj'] = $act->obj; + if($act->obj['type'] === 'Event') { + $s['obj'] = []; + $s['obj']['asld'] = $act->obj; + $s['obj']['type'] = ACTIVITY_OBJ_EVENT; + $s['obj']['id'] = $act->obj['id']; + $s['obj']['title'] = $act->obj['summary']; + + if(strpos($act->obj['startTime'],'Z')) + $s['obj']['adjust'] = true; + else + $s['obj']['adjust'] = false; + + $s['obj']['dtstart'] = datetime_convert('UTC','UTC',$act->obj['startTime']); + if($act->obj['endTime']) + $s['obj']['dtend'] = datetime_convert('UTC','UTC',$act->obj['endTime']); + else + $s['obj']['nofinish'] = true; + $s['obj']['description'] = $act->obj['content']; + + if(array_path_exists('location/content',$act->obj)) + $s['obj']['location'] = $act->obj['location']['content']; + + } + else { + $s['obj'] = $act->obj; + } $instrument = $act->get_property_obj('instrument'); if((! $instrument) && (! $response_activity)) { @@ -1540,7 +1649,9 @@ class Activity { } - if($act->obj['type'] === 'Image') { + // avoid double images from hubzilla to zap/osada + + if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) { $ptr = null; diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 8cf62c01a..56283ff76 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -1,32 +1,34 @@ -<?php /** @file */ +<?php namespace Zotlabs\Lib; -/** - * Apps - * - */ - require_once('include/plugin.php'); require_once('include/channel.php'); - +/** + * @brief Apps class. + * + */ class Apps { static public $available_apps = null; static public $installed_apps = null; - static public $base_apps = null; - - + /** + * @brief + * + * @param boolean $translate (optional) default true + * @return array + */ static public function get_system_apps($translate = true) { + $ret = []; - $ret = array(); if(is_dir('apps')) $files = glob('apps/*.apd'); else $files = glob('app/*.apd'); + if($files) { foreach($files as $f) { $x = self::parse_app_description($f,$translate); @@ -50,14 +52,17 @@ class Apps { } } - call_hooks('get_system_apps',$ret); + /** + * @hooks get_system_apps + * Hook to manipulate the system apps array. + */ + call_hooks('get_system_apps', $ret); return $ret; - } static public function get_base_apps() { - return get_config('system','base_apps',[ + $x = get_config('system','base_apps',[ 'Connections', 'Network', 'Settings', @@ -72,6 +77,14 @@ class Apps { 'Mail', 'Profile Photo' ]); + + /** + * @hooks get_base_apps + * Hook to manipulate the base apps array. + */ + call_hooks('get_base_apps', $x); + + return $x; } static public function import_system_apps() { @@ -79,7 +92,7 @@ class Apps { return; self::$base_apps = self::get_base_apps(); - + $apps = self::get_system_apps(false); self::$available_apps = q("select * from app where app_channel = 0"); @@ -104,6 +117,7 @@ class Apps { // $id will be boolean true or false to install an app, or an integer id to update an existing app if($id === false) continue; + if($id !== true) { // if we already installed this app, but it changed, preserve any categories we created $s = EMPTY_STR; @@ -124,16 +138,17 @@ class Apps { $app['guid'] = hash('whirlpool',$app['name']); $app['system'] = 1; self::app_install(local_channel(),$app); - } - } + } } /** - * Install the system app if no system apps have been installed, or if a new system app + * Install the system app if no system apps have been installed, or if a new system app * is discovered, or if the version of a system app changes. + * + * @param array $app + * @return boolean|int */ - static public function check_install_system_app($app) { if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) { return true; @@ -157,17 +172,16 @@ class Apps { return $notfound; } - /** - * Install the system app if no system apps have been installed, or if a new system app - * is discovered, or if the version of a system app changes. + * Install the personal app if no personal apps have been installed, or if a new personal app + * is discovered, or if the version of a personal app changes. + * + * @param array $app + * @return boolean|int */ - - - static public function check_install_personal_app($app) { $installed = false; - foreach(self::$installed_apps as $iapp) { + foreach(self::$installed_apps as $iapp) { if($iapp['app_id'] == hash('whirlpool',$app['name'])) { $installed = true; if(($iapp['app_version'] != $app['version']) @@ -187,19 +201,24 @@ class Apps { return strcasecmp($a['name'],$b['name']); } - - static public function parse_app_description($f,$translate = true) { - - $ret = array(); + /** + * @brief Parse app description. + * + * @param string $f filename + * @param boolean $translate (optional) default true + * @return boolean|array + */ + static public function parse_app_description($f, $translate = true) { + $ret = []; + $matches = []; $baseurl = z_root(); - $channel = \App::get_channel(); - $address = (($channel) ? $channel['channel_address'] : ''); - + //$channel = \App::get_channel(); + //$address = (($channel) ? $channel['channel_address'] : ''); + //future expansion $observer = \App::get_observer(); - $lines = @file($f); if($lines) { @@ -208,7 +227,7 @@ class Apps { $ret[$matches[1]] = trim($matches[2]); } } - } + } if(! $ret['photo']) $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); @@ -290,15 +309,18 @@ class Apps { if($ret) { if($translate) self::translate_system_apps($ret); + return $ret; } + return false; - } + } static public function translate_system_apps(&$arr) { $apps = array( 'Apps' => t('Apps'), + 'Affinity Tool' => t('Affinity Tool'), 'Articles' => t('Articles'), 'Cards' => t('Cards'), 'Admin' => t('Site Admin'), @@ -309,17 +331,17 @@ class Apps { 'Remote Diagnostics' => t('Remote Diagnostics'), 'Suggest Channels' => t('Suggest Channels'), 'Login' => t('Login'), - 'Channel Manager' => t('Channel Manager'), + 'Channel Manager' => t('Channel Manager'), 'Network' => t('Stream'), 'Settings' => t('Settings'), 'Files' => t('Files'), 'Webpages' => t('Webpages'), 'Wiki' => t('Wiki'), - 'Channel Home' => t('Channel Home'), + 'Channel Home' => t('Channel Home'), 'View Profile' => t('View Profile'), - 'Photos' => t('Photos'), - 'Events' => t('Events'), - 'Directory' => t('Directory'), + 'Photos' => t('Photos'), + 'Events' => t('Events'), + 'Directory' => t('Directory'), 'Help' => t('Help'), 'Mail' => t('Mail'), 'Mood' => t('Mood'), @@ -364,30 +386,31 @@ class Apps { if(array_key_exists($arr[$x]['name'],$apps)) { $arr[$x]['name'] = $apps[$arr[$x]['name']]; } else { - // Try to guess by app name if not in list - $arr[$x]['name'] = t(trim($arr[$x]['name'])); + // Try to guess by app name if not in list + $arr[$x]['name'] = t(trim($arr[$x]['name'])); } } } - } - - // papp is a portable app - - static public function app_render($papp,$mode = 'view') { - - /** - * modes: - * view: normal mode for viewing an app via bbcode from a conversation or page - * provides install/update button if you're logged in locally - * install: like view but does not display app-bin options if they are present - * list: normal mode for viewing an app on the app page - * no buttons are shown - * edit: viewing the app page in editing mode provides a delete button - * nav: render apps for app-bin - */ - + /** + * @brief + * + * @param array $papp + * papp is a portable app + * @param string $mode (optional) default 'view' + * Render modes: + * * \b view: normal mode for viewing an app via bbcode from a conversation or page + * provides install/update button if you're logged in locally + * * \b install: like view but does not display app-bin options if they are present + * * \b list: normal mode for viewing an app on the app page + * no buttons are shown + * * \b edit: viewing the app page in editing mode provides a delete button + * * \b nav: render apps for app-bin + * + * @return void|string Parsed HTML + */ + static public function app_render($papp, $mode = 'view') { $installed = false; if(! $papp) @@ -412,7 +435,7 @@ class Apps { $sys = get_sys_channel(); $view_channel = $sys['channel_id']; } - self::app_macros($view_channel,$papp); + self::app_macros($view_channel,$papp); } if(strpos($papp['url'], ',')) { @@ -425,7 +448,6 @@ class Apps { $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; - foreach($papp as $k => $v) { if(strpos($v,'http') === 0 && $k != 'papp') { if(! (local_channel() && strpos($v,z_root()) === 0)) { @@ -507,7 +529,7 @@ class Apps { if($x) { $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; } - } + } } $install_action = (($installed) ? t('Update') : t('Install')); @@ -590,8 +612,14 @@ class Apps { return false; } - - static public function can_delete($uid,$app) { + /** + * @brief + * + * @param mixed $uid If not set return false, otherwise no influence + * @param array $app + * @return boolean + */ + static public function can_delete($uid, $app) { if(! $uid) { return false; } @@ -599,7 +627,7 @@ class Apps { $base_apps = self::get_base_apps(); if($base_apps) { foreach($base_apps as $b) { - if($app['guid'] === hash('whirlpool',$b)) { + if($app['guid'] === hash('whirlpool', $b)) { return false; } } @@ -611,7 +639,6 @@ class Apps { static public function app_destroy($uid,$app) { if($uid && $app['guid']) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) @@ -620,7 +647,7 @@ class Apps { if(! intval($x[0]['app_deleted'])) { $x[0]['app_deleted'] = 1; if(self::can_delete($uid,$app)) { - $r = q("delete from app where app_id = '%s' and app_channel = %d", + q("delete from app where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -628,10 +655,15 @@ class Apps { intval(TERM_OBJ_APP), intval($x[0]['id']) ); + /** + * @hooks app_destroy + * Called after app entry got removed from database + * and provide app array from database. + */ call_hooks('app_destroy', $x[0]); } else { - $r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -645,22 +677,23 @@ class Apps { } } } - } - static public function app_undestroy($uid,$app) { - - // undelete a system app - + /** + * @brief Undelete a system app. + * + * @param int $uid + * @param array $app + */ + static public function app_undestroy($uid, $app) { if($uid && $app['guid']) { - $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", dbesc($app['guid']), intval($uid) ); if($x) { if($x[0]['app_system']) { - $r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", + q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); @@ -669,7 +702,15 @@ class Apps { } } - static public function app_feature($uid,$app,$term) { + /** + * @brief + * + * @param int $uid + * @param array $app + * @param string $term + * @return void + */ + 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) @@ -693,23 +734,37 @@ class Apps { } } - static public function app_installed($uid,$app,$bypass_filter=false) { + /** + * @brief + * + * @param int $uid + * @param array $app + * @param boolean $bypass_filter (optional) default false + * @return boolean + */ + static public function app_installed($uid, $app, $bypass_filter = false) { $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", - dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''), + dbesc((array_key_exists('guid', $app)) ? $app['guid'] : ''), intval($uid) ); - if (!$bypass_filter) { + if(!$bypass_filter) { $filter_arr = [ - 'uid'=>$uid, - 'app'=>$app, - 'installed'=>$r + 'uid' => $uid, + 'app' => $app, + 'installed' => $r ]; - call_hooks('app_installed_filter',$filter_arr); + /** + * @hooks app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } @@ -725,11 +780,17 @@ class Apps { 'app'=>$app, 'installed'=>$r ]; - call_hooks('addon_app_installed_filter',$filter_arr); + /** + * @hooks addon_app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('addon_app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } static public function system_app_installed($uid,$app,$bypass_filter=false) { @@ -744,28 +805,39 @@ class Apps { 'app'=>$app, 'installed'=>$r ]; - call_hooks('system_app_installed_filter',$filter_arr); + /** + * @hooks system_app_installed_filter + * * \e int \b uid + * * \e array \b app + * * \e mixed \b installed - return value + */ + call_hooks('system_app_installed_filter', $filter_arr); $r = $filter_arr['installed']; } - return(($r) ? true : false); + return(($r) ? true : false); } - - + /** + * @brief + * + * @param int $uid + * @param boolean $deleted + * @param array $cats + * @return boolean|array + */ static public function app_list($uid, $deleted = false, $cats = []) { - if($deleted) - $sql_extra = ""; + if($deleted) + $sql_extra = ''; else - $sql_extra = " and app_deleted = 0 "; + $sql_extra = ' and app_deleted = 0 '; if($cats) { - - $cat_sql_extra = " and ( "; + $cat_sql_extra = ' and ( '; foreach($cats as $cat) { if(strpos($cat_sql_extra, 'term')) - $cat_sql_extra .= "or "; + $cat_sql_extra .= 'or '; $cat_sql_extra .= "term = '" . dbesc($cat) . "' "; } @@ -777,11 +849,13 @@ class Apps { ); if(! $r) return $r; - $sql_extra .= " and app.id in ( "; + + $sql_extra .= ' and app.id in ( '; $s = ''; foreach($r as $rr) { if($s) $s .= ','; + $s .= intval($rr['oid']); } $sql_extra .= $s . ') '; @@ -792,12 +866,26 @@ class Apps { ); if($r) { - $hookinfo = Array('uid'=>$uid,'deleted'=>$deleted,'cats'=>$cats,'apps'=>$r); - call_hooks('app_list',$hookinfo); + $hookinfo = [ + 'uid' => $uid, + 'deleted' => $deleted, + 'cats' => $cats, + 'apps' => $r, + ]; + /** + * @hooks app_list + * * \e int \b uid + * * \e boolean \b deleted + * * \e array \b cats + * * \e array \b apps - return value + */ + call_hooks('app_list', $hookinfo); $r = $hookinfo['apps']; - for($x = 0; $x < count($r); $x ++) { + + for($x = 0; $x < count($r); $x++) { if(! $r[$x]['app_system']) $r[$x]['type'] = 'personal'; + $r[$x]['term'] = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($r[$x]['id']) @@ -805,7 +893,7 @@ class Apps { } } - return($r); + return $r; } static public function app_order($uid,$apps,$menu) { @@ -837,13 +925,14 @@ class Apps { $ret[] = $ap; } } - return $ret; + return $ret; } static function find_app_in_array($name,$arr) { if(! $arr) return false; + foreach($arr as $x) { if($x['name'] === $name) { return $x; @@ -852,8 +941,16 @@ class Apps { return false; } - static function moveup($uid,$guid,$menu) { - $syslist = array(); + /** + * @brief + * + * @param int $uid + * @param int $guid + * @param string $menu + * @return void + */ + static function moveup($uid, $guid, $menu) { + $syslist = []; $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); @@ -863,6 +960,7 @@ class Apps { $papp = self::app_encode($li); if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) continue; + $syslist[] = $papp; } } @@ -875,8 +973,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -885,6 +981,7 @@ class Apps { } if(! $position) return; + $dest_position = $position - 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -896,11 +993,18 @@ class Apps { } set_pconfig($uid,'system',$conf,implode(',',$narr)); - } - static function movedown($uid,$guid,$menu) { - $syslist = array(); + /** + * @brief + * + * @param int $uid + * @param int $guid + * @param string $menu + * @return void + */ + static function movedown($uid, $guid, $menu) { + $syslist = []; $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); @@ -910,6 +1014,7 @@ class Apps { $papp = self::app_encode($li); if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) continue; + $syslist[] = $papp; } } @@ -922,8 +1027,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -932,6 +1035,7 @@ class Apps { } if($position >= count($syslist) - 1) return; + $dest_position = $position + 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -943,7 +1047,6 @@ class Apps { } set_pconfig($uid,'system',$conf,implode(',',$narr)); - } static public function app_decode($s) { @@ -951,8 +1054,14 @@ class Apps { return json_decode($x,true); } - - static public function app_macros($uid,&$arr) { + /** + * @brief + * + * @param int $uid + * @param[in,out] array $arr + * @return void + */ + static public function app_macros($uid, &$arr) { if(! intval($uid)) return; @@ -960,21 +1069,17 @@ class Apps { $baseurl = z_root(); $channel = channelx_by_n($uid); $address = (($channel) ? $channel['channel_address'] : ''); - + //future expansion - $observer = \App::get_observer(); - + //$observer = \App::get_observer(); + $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); - } - - - static public function app_store($arr) { //logger('app_store: ' . print_r($arr,true)); @@ -1158,16 +1263,20 @@ class Apps { } return $ret; - } - - static public function app_encode($app,$embed = false) { - - $ret = array(); + /** + * @brief + * + * @param array $app + * @param boolean $embed (optional) default false + * @return array|string + */ + static public function app_encode($app, $embed = false) { + $ret = []; $ret['type'] = 'personal'; - + if($app['app_id']) $ret['guid'] = $app['app_id']; @@ -1200,7 +1309,7 @@ class Apps { if($app['app_price']) $ret['price'] = $app['app_price']; - + if($app['app_page']) $ret['page'] = $app['app_page']; @@ -1224,12 +1333,12 @@ class Apps { foreach($app['term'] as $t) { if($s) $s .= ','; + $s .= $t['term']; } $ret['categories'] = $s; } - if(! $embed) return $ret; @@ -1237,18 +1346,15 @@ class Apps { if(array_key_exists('categories',$ret)) unset($ret['categories']); - + $j = json_encode($ret); - return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; + return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; } static public function papp_encode($papp) { return chunk_split(base64_encode(json_encode($papp)),72,"\n"); - } } - - diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php index 21b320cac..18087e29f 100644 --- a/Zotlabs/Lib/DReport.php +++ b/Zotlabs/Lib/DReport.php @@ -87,17 +87,29 @@ class DReport { // Is the sender one of our channels? - $c = q("select channel_id from channel where channel_hash = '%s' limit 1", + $c = q("select channel_id from channel where channel_hash = '%s' or channel_portable_id = '%s' limit 1", + dbesc($dr['sender']), dbesc($dr['sender']) ); + if(! $c) return false; + // legacy zot recipients add a space and their name to the xchan. remove it if true. + + $legacy_recipient = strpos($dr['recipient'], ' '); + if($legacy_recipient !== false) { + $legacy_recipient_parts = explode(' ', $dr['recipient'], 2); + $rxchan = $legacy_recipient_parts[0]; + } + else { + $rxchan = $dr['recipient']; + } - // is the recipient one of our connections, or do we want to store every report? - $rxchan = $dr['recipient']; + // is the recipient one of our connections, or do we want to store every report? + $pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all'); if($pcf) return true; diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php index 87a5126f4..019237568 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -2,11 +2,6 @@ namespace Zotlabs\Lib; -/** - * @brief lowlevel implementation of Zot6 protocol. - * - */ - use Zotlabs\Zot6\HTTPSig; use Zotlabs\Access\Permissions; use Zotlabs\Access\PermissionLimits; @@ -14,14 +9,17 @@ use Zotlabs\Daemon\Master; require_once('include/crypto.php'); - +/** + * @brief Lowlevel implementation of Zot6 protocol. + * + */ class Libzot { /** * @brief Generates a unique string for use as a zot guid. * - * Generates a unique string for use as a zot guid using our DNS-based url, the - * channel nickname and some entropy. + * Generates a unique string for use as a zot guid using our DNS-based url, + * the channel nickname and some entropy. * The entropy ensures uniqueness against re-installs where the same URL and * nickname are chosen. * @@ -32,9 +30,8 @@ class Libzot { * immediate universe. * * @param string $channel_nick a unique nickname of controlling entity - * @returns string + * @return string */ - static function new_uid($channel_nick) { $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); return(base64url_encode(hash('whirlpool', $rawstr, true), true)); @@ -52,8 +49,8 @@ class Libzot { * * @param string $guid * @param string $pubkey + * @return string */ - static function make_xchan_hash($guid, $pubkey) { return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); } @@ -65,10 +62,8 @@ class Libzot { * should only be used by channels which are defined on this hub. * * @param string $hash - xchan_hash - * @returns array of hubloc (hub location structures) - * + * @return array of hubloc (hub location structures) */ - static function get_hublocs($hash) { /* Only search for active hublocs - e.g. those that haven't been marked deleted */ @@ -92,16 +87,17 @@ class Libzot { * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * @param array $recipients * envelope recipients, array of portable_id's; empty for public posts - * @param string msg + * @param string $msg * optional message + * @param string $encoding + * optional encoding, default 'activitystreams' * @param string $remote_key * optional public site key of target hub used to encrypt entire packet * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others * @param string $methods - * optional comma separated list of encryption methods @ref self::best_algorithm() + * optional comma separated list of encryption methods @ref best_algorithm() * @returns string json encoded zot packet */ - static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') { $sig_method = get_config('system','signature_algorithm','sha256'); @@ -146,11 +142,10 @@ class Libzot { * @brief Choose best encryption function from those available on both sites. * * @param string $methods - * comma separated list of encryption methods + * Comma separated list of encryption methods * @return string first match from our site method preferences crypto_methods() array - * of a method which is common to both sites; or 'aes256cbc' if no matches are found. + * of a method which is common to both sites; or 'aes256cbc' if no matches are found. */ - static function best_algorithm($methods) { $x = [ @@ -164,7 +159,6 @@ class Libzot { * * \e string \b methods - comma separated list of encryption methods * * \e string \b result - the algorithm to return */ - call_hooks('zot_best_algorithm', $x); if($x['result']) @@ -190,7 +184,7 @@ class Libzot { /** - * @brief send a zot message + * @brief Send a zot message. * * @see z_post_url() * @@ -200,18 +194,17 @@ class Libzot { * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) * @return array see z_post_url() for returned data format */ - static function zot($url, $data, $channel = null,$crypto = null) { if($channel) { - $headers = [ - 'X-Zot-Token' => random_string(), - 'Digest' => HTTPSig::generate_digest_header($data), + $headers = [ + 'X-Zot-Token' => random_string(), + 'Digest' => HTTPSig::generate_digest_header($data), 'Content-type' => 'application/x-zot+json', '(request-target)' => 'post ' . get_request_string($url) ]; - $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false,'sha512', + $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false,'sha512', (($crypto) ? [ 'key' => $crypto['hubloc_sitekey'], 'algorithm' => self::best_algorithm($crypto['site_crypto']) ] : false)); } else { @@ -227,7 +220,6 @@ class Libzot { /** * @brief Refreshes after permission changed or friending, etc. * - * * refresh is typically invoked when somebody has changed permissions of a channel and they are notified * to fetch new permissions via a finger/discovery operation. This may result in a new connection * (abook entry) being added to a local channel and it may result in auto-permissions being granted. @@ -251,7 +243,6 @@ class Libzot { * * \b true if successful * * otherwise \b false */ - static function refresh($them, $channel = null, $force = false) { logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); @@ -265,13 +256,13 @@ class Libzot { } else { $r = null; - + // if they re-installed the server we could end up with the wrong record - pointing to the old install. // We'll order by reverse id to try and pick off the newest one first and hopefully end up with the // correct hubloc. If this doesn't work we may have to re-write this section to try them all. if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) { - $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", + $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' order by hubloc_id desc", dbesc($them['xchan_addr']) ); } @@ -317,7 +308,7 @@ class Libzot { if(! $hsig_valid) { logger('http signature not valid: ' . print_r($hsig,true)); - return $result; + return false; } @@ -356,7 +347,7 @@ class Libzot { ); if($r) { -logger('4'); + // connection exists // if the dob is the same as what we have stored (disregarding the year), keep the one @@ -416,7 +407,7 @@ logger('4'); if($y) { logger("New introduction received for {$channel['channel_name']}"); $new_perms = get_all_perms($channel['channel_id'],$x['hash'],false); - + // Send a clone sync packet and a permissions update if permissions have changed $new_connection = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_xchan = '%s' and abook_channel = %d and abook_self = 0 order by abook_created desc limit 1", @@ -524,10 +515,14 @@ logger('4'); return false; } - - - - static function valid_hub($sender,$site_id) { + /** + * @brief + * + * @param string $sender + * @param string $site_id + * @return null|array + */ + static function valid_hub($sender, $site_id) { $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", dbesc($sender), @@ -548,7 +543,6 @@ logger('4'); } return $r[0]; - } /** @@ -559,21 +553,14 @@ logger('4'); * origination address. This will fetch the discovery packet of the sender, * which contains the public key we need to verify our guid and url signatures. * - * @param array $arr an associative array which must contain: - * * \e string \b guid => guid of conversant - * * \e string \b guid_sig => guid signed with conversant's private key - * * \e string \b url => URL of the origination hub of this communication - * * \e string \b url_sig => URL signed with conversant's private key + * @param string $id * * @return array An associative array with - * * \b success boolean true or false - * * \b message (optional) error string only if success is false + * * \e boolean \b success + * * \e string \b message (optional, unused) error string only if success is false */ - static function register_hub($id) { - $id_hash = false; - $valid = false; $hsig_valid = false; $result = [ 'success' => false ]; @@ -807,7 +794,7 @@ logger('4'); // If setting for the default profile, unset the profile photo flag from any other photos I own if($is_default_profile) { - q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", + q("UPDATE photo SET photo_usage = %d WHERE photo_usage = %d AND resource_id != '%s' AND aid = %d AND uid = %d", intval(PHOTO_NORMAL), intval(PHOTO_PROFILE), dbesc($hash), @@ -954,8 +941,8 @@ logger('4'); * @param string $hub - url of site we just contacted * @param array $arr - output of z_post_url() * @param array $outq - The queue structure attached to this request + * @return void */ - static function process_response($hub, $arr, $outq) { logger('remote: ' . print_r($arr,true),LOGGER_DATA); @@ -986,7 +973,7 @@ logger('4'); if(! $x['success']) { // handle remote validation issues - + $b = q("update dreport set dreport_result = '%s', dreport_time = '%s' where dreport_queue = '%s'", dbesc(($x['message']) ? $x['message'] : 'unknown delivery error'), dbesc(datetime_convert()), @@ -994,10 +981,20 @@ logger('4'); ); } - if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { + if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { + foreach($x['delivery_report'] as $xx) { call_hooks('dreport_process',$xx); if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) { + + // legacy recipients add a space and their name to the xchan. split those if true. + $legacy_recipient = strpos($xx['recipient'], ' '); + if($legacy_recipient !== false) { + $legacy_recipient_parts = explode(' ', $xx['recipient'], 2); + $xx['recipient'] = $legacy_recipient_parts[0]; + $xx['name'] = $legacy_recipient_parts[1]; + } + q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ", dbesc($xx['message_id']), dbesc($xx['location']), @@ -1073,11 +1070,6 @@ logger('4'); * * @param array $arr * 'pickup' structure returned from remote site - * @param string $sender_url - * the url specified by the sender in the initial communication. - * We will verify the sender and url in each returned message structure and - * also verify that all the messages returned match the site url that we are - * currently processing. * * @returns array * Suitable for logging remotely, enumerating the processing results of each message/recipient combination @@ -1085,7 +1077,6 @@ logger('4'); * * [1] => \e string $delivery_status * * [2] => \e string $address */ - static function import($arr) { $env = $arr; @@ -1107,7 +1098,7 @@ logger('4'); $has_data = array_key_exists('data',$env) && $env['data']; $data = (($has_data) ? $env['data'] : false); - $AS = null; + $AS = null; if($env['encoding'] === 'activitystreams') { @@ -1165,7 +1156,6 @@ logger('4'); $deliveries = self::public_recips($env,$AS); - } $deliveries = array_unique($deliveries); @@ -1184,31 +1174,31 @@ logger('4'); //logger($AS->debug()); - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($AS->actor['id']) - ); + ); if($r) { $arr['author_xchan'] = $r[0]['hubloc_hash']; } - $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($env['sender']) - ); + ); // in individual delivery, change owner if needed if($s) { $arr['owner_xchan'] = $s[0]['hubloc_hash']; } else { - $arr['owner_xchan'] = $env['sender']; + $arr['owner_xchan'] = $env['sender']; } if($private) { $arr['item_private'] = true; } - // @fixme - spoofable + /// @FIXME - spoofable if($AS->data['hubloc']) { $arr['item_verified'] = true; } @@ -1237,12 +1227,19 @@ logger('4'); } if ($result) { $return = array_merge($return, $result); - } + } return $return; } - static function is_top_level($env,$act) { + /** + * @brief + * + * @param array $env + * @param object $act + * @return boolean + */ + static function is_top_level($env, $act) { if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { return true; } @@ -1285,9 +1282,9 @@ logger('4'); * Some of these will be rejected, but this gives us a place to start. * * @param array $msg - * @return NULL|array + * @param object $act + * @return array */ - static function public_recips($msg, $act) { require_once('include/channel.php'); @@ -1432,7 +1429,7 @@ logger('4'); * will normally arrive first via sync delivery, but this isn't guaranteed. * There's a chance the current delivery could take place before the cloned copy arrives * hence the item could have the wrong ACL and *could* be used in subsequent deliveries or - * access checks. + * access checks. */ if($sender === $channel['channel_portable_id'] && $arr['author_xchan'] === $channel['channel_portable_id'] && $arr['mid'] === $arr['parent_mid']) { @@ -1487,14 +1484,37 @@ logger('4'); intval($channel['channel_id']) ); if ($parent) { - $allowed = can_comment_on_post($d,$parent[0]); + $allowed = can_comment_on_post($sender,$parent[0]); } } - if($request) { - $allowed = true; + + if ($request) { + + // Conversation fetches (e.g. $request == true) take place for + // a) new comments on expired posts + // b) hyperdrive (friend-of-friend) conversations + // c) Repeats of posts by others + + + // over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations + // (if hyperdrive is enabled) and repeated posts by a friend. + // If $allowed is already true, this is probably the conversation of a direct friend or a + // conversation fetch for a new comment on an expired post + // Comments of all these activities are allowed and will only be rejected (later) if the parent + // doesn't exist. + + if ($perm === 'send_stream') { + if (get_pconfig($channel['channel_id'],'system','hyperdrive',false) || $arr['verb'] === ACTIVITY_SHARE) { + $allowed = true; + } + } + else { + $allowed = true; + } + $friendofriend = true; } - + if (! $allowed) { logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); $DR->update('permission denied'); @@ -1503,16 +1523,18 @@ logger('4'); } } -logger('item: ' . print_r($arr,true), LOGGER_DATA); + // logger('item: ' . print_r($arr,true), LOGGER_DATA); if($arr['mid'] !== $arr['parent_mid']) { -logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); + + logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); + // check source route. // We are only going to accept comments from this sender if the comment has the same route as the top-level-post, // this is so that permissions mismatches between senders apply to the entire conversation // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise // processing it is pointless. - + $r = q("select route, id, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1", dbesc($arr['parent_mid']), intval($channel['channel_id']) @@ -1527,10 +1549,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"') // have the copy and we don't want the request to loop. // Also don't do this if this comment came from a conversation request packet. // It's possible that comments are allowed but posting isn't and that could - // cause a conversation fetch loop. We can detect these packets since they are - // delivered via a 'notify' packet type that has a message_id element in the - // initial zot packet (just like the corresponding 'request' packet type which - // makes the request). + // cause a conversation fetch loop. // We'll also check the send_stream permission - because if it isn't allowed, // the top level post is unlikely to be imported and // this is just an exercise in futility. @@ -1541,14 +1560,14 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"') } continue; } - + if($relay || $friendofriend || (intval($r[0]['item_private']) === 0 && intval($arr['item_private']) === 0)) { // reset the route in case it travelled a great distance upstream // use our parent's route so when we go back downstream we'll match // with whatever route our parent has. // Also friend-of-friend conversations may have been imported without a route, // but we are now getting comments via listener delivery - // and if there is no privacy on this or the parent, we don't care about the route, + // and if there is no privacy on this or the parent, we don't care about the route, // so just set the owner and route accordingly. $arr['route'] = $r[0]['route']; $arr['owner_xchan'] = $r[0]['owner_xchan']; @@ -1591,7 +1610,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"') $arr['route'] = $last_prior_route; } } -logger('hey'); + $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", intval($channel['channel_id']), dbesc($arr['owner_xchan']) @@ -1602,13 +1621,13 @@ logger('hey'); // remove_community_tag is a no-op if this isn't a community tag activity self::remove_community_tag($sender,$arr,$channel['channel_id']); - + // set these just in case we need to store a fresh copy of the deleted post. // This could happen if the delete got here before the original post did. $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; - + $item_id = self::delete_imported_item($sender,$arr,$channel['channel_id'],$relay); $DR->update(($item_id) ? 'deleted' : 'delete_failed'); $result[] = $DR->get(); @@ -1704,7 +1723,7 @@ logger('hey'); * * \e array \b item * * \e array \b sender * * \e array \b channel - */ + */ call_hooks('activity_received', $parr); // don't add a source route if it's a relay or later recipients will get a route mismatch if(! $relay) @@ -1769,17 +1788,17 @@ logger('hey'); logger($AS->debug()); - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($AS->actor['id']) - ); + ); if(! $r) { $y = import_author_xchan([ 'url' => $AS->actor['id'] ]); if($y) { - $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($AS->actor['id']) ); - } + } if(! $r) { logger('FOF Activity: no actor'); continue; @@ -1799,9 +1818,9 @@ logger('hey'); $arr['author_xchan'] = $r[0]['hubloc_hash']; } - $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", + $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($a['signature']['signer']) - ); + ); if($s) { $arr['owner_xchan'] = $s[0]['hubloc_hash']; @@ -1810,7 +1829,8 @@ logger('hey'); $arr['owner_xchan'] = $a['signature']['signer']; } - // @fixme - spoofable + + /// @FIXME - spoofable if($AS->data['hubloc']) { $arr['item_verified'] = true; } @@ -1824,7 +1844,7 @@ logger('hey'); $result = self::process_delivery($arr['owner_xchan'],$arr, [ $channel['channel_portable_id'] ],false,false,true); if ($result) { $ret = array_merge($ret, $result); - } + } } return $ret; @@ -1841,8 +1861,8 @@ logger('hey'); * * \e int \b obj_type * * \e int \b mid * @param int $uid + * @return void */ - static function remove_community_tag($sender, $arr, $uid) { if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) @@ -1870,7 +1890,7 @@ logger('hey'); } $i = $r[0]; - + if($i['target']) $i['target'] = json_decode($i['target'],true); if($i['object']) @@ -1913,8 +1933,8 @@ logger('hey'); * @param array $orig * @param int $uid * @param boolean $tag_delivery + * @return void|array */ - static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { // If this is a comment being updated, remove any privacy information @@ -2054,7 +2074,7 @@ logger('hey'); } foreach($deliveries as $d) { - + $DR = new DReport(z_root(),$sender,$d,$arr['mid']); $r = q("select * from channel where channel_portable_id = '%s' limit 1", @@ -2073,7 +2093,7 @@ logger('hey'); if(! perm_is_allowed($channel['channel_id'],$sender,'post_mail')) { - /* + /* * Always allow somebody to reply if you initiated the conversation. It's anti-social * and a bit rude to send a private message to somebody and block their ability to respond. * If you are being harrassed and want to put an end to it, delete the conversation. @@ -2133,12 +2153,13 @@ logger('hey'); * @brief Processes delivery of profile. * * @see import_directory_profile() + * * @param array $sender an associative array * * \e string \b hash a xchan_hash * @param array $arr * @param array $deliveries (unused) + * @return void */ - static function process_profile_delivery($sender, $arr, $deliveries) { logger('process_profile_delivery', LOGGER_DEBUG); @@ -2159,6 +2180,7 @@ logger('hey'); * * \e string \b hash a xchan_hash * @param array $arr * @param array $deliveries (unused) deliveries is irrelevant + * @return void */ static function process_location_delivery($sender, $arr, $deliveries) { @@ -2176,7 +2198,7 @@ logger('hey'); $x = Libsync::sync_locations($xchan,$arr,true); logger('results: ' . print_r($x,true), LOGGER_DEBUG); if($x['changed']) { - $guid = random_string() . '@' . App::get_hostname(); + //$guid = random_string() . '@' . App::get_hostname(); Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED); } } @@ -2200,8 +2222,8 @@ logger('hey'); * * @param string $sender_hash A channel hash * @param array $locations + * @return void */ - static function check_location_move($sender_hash, $locations) { if(! $locations) @@ -2243,7 +2265,6 @@ logger('hey'); } - /** * @brief Returns an array with all known distinct hubs for this channel. * @@ -2252,7 +2273,6 @@ logger('hey'); * * \e string \b channel_hash the hash of the channel * @return array an array with associative arrays */ - static function encode_locations($channel) { $ret = []; @@ -2293,7 +2313,7 @@ logger('hey'); if(! $z['site_id']) { $z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']); } - + $ret[] = $z; } } @@ -2306,10 +2326,8 @@ logger('hey'); * @brief * * @param array $arr - * @param string $pubkey * @return boolean true if updated or inserted */ - static function import_site($arr) { if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) @@ -2584,20 +2602,20 @@ logger('hey'); $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); if($ztarget) { - $t = q("select * from hubloc where hubloc_id_url = '%s' limit 1", + $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1", dbesc($ztarget) ); if($t) { - + $ztarget_hash = $t[0]['hubloc_hash']; } else { - + // should probably perform discovery of the requestor (target) but if they actually had - // permissions we would know about them and we only want to know who they are to + // permissions we would know about them and we only want to know who they are to // enumerate their specific permissions - + $ztarget_hash = EMPTY_STR; } } @@ -2744,7 +2762,7 @@ logger('hey'); $ret['id'] = $e['xchan_guid']; $ret['id_sig'] = self::sign($e['xchan_guid'], $e['channel_prvkey']); - $ret['primary_location'] = [ + $ret['primary_location'] = [ 'address' => $e['xchan_addr'], 'url' => $e['xchan_url'], 'connections_url' => $e['xchan_connurl'], @@ -2766,7 +2784,7 @@ logger('hey'); $ret['searchable'] = $searchable; $ret['adult_content'] = $adult_channel; $ret['public_forum'] = $public_forum; - + $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments')); $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_mail')); @@ -2824,14 +2842,20 @@ logger('hey'); $ret['locations'] = $x; $ret['site'] = self::site_info(); + /** + * @hooks zotinfo + * Hook to manipulate the zotinfo array before it is returned. + */ + call_hooks('zotinfo', $ret); - call_hooks('zotinfo',$ret); - - return($ret); - + return $ret; } - + /** + * @brief Get siteinfo. + * + * @return array + */ static function site_info() { $signing_key = get_config('system','prvkey'); @@ -2868,7 +2892,7 @@ logger('hey'); if($dirmode != DIRECTORY_MODE_STANDALONE) { $register_policy = intval(get_config('system','register_policy')); - + if($register_policy == REGISTER_CLOSED) $ret['site']['register_policy'] = 'closed'; if($register_policy == REGISTER_APPROVE) @@ -2915,18 +2939,16 @@ logger('hey'); } return $ret['site']; - } /** * @brief * * @param array $hub - * @param string $sitekey (optional, default empty) + * @param string $site_id (optional, default empty) * * @return string hubloc_url */ - static function update_hub_connected($hub, $site_id = '') { if ($site_id) { @@ -2985,12 +3007,21 @@ logger('hey'); return $hub['hubloc_url']; } - + /** + * @brief + * + * @param string $data + * @param string $key + * @param string $alg (optional) default 'sha256' + * @return string + */ static function sign($data,$key,$alg = 'sha256') { if(! $key) return 'no key'; + $sig = ''; openssl_sign($data,$sig,$key,$alg); + return $alg . '.' . base64url_encode($sig); } @@ -3003,24 +3034,27 @@ logger('hey'); if ($key && count($x) === 2) { $alg = $x[0]; $signature = base64url_decode($x[1]); - + $verify = @openssl_verify($data,$signature,$key,$alg); if ($verify === (-1)) { while ($msg = openssl_error_string()) { logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR); } - btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); } } return(($verify > 0) ? true : false); } - - + /** + * @brief + * + * @return boolean + */ static function is_zot_request() { - $x = getBestSupportedMimeType([ 'application/x-zot+json' ]); + return(($x) ? true : false); } diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 5e5954c95..c08c11e75 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -112,9 +112,11 @@ class PConfig { * The configuration key to set * @param string $value * The value to store + * @param string $updated (optional) + * The datetime to store * @return mixed Stored $value or false */ - static public function Set($uid, $family, $key, $value, $updated=NULL) { + static public function Set($uid, $family, $key, $value, $updated = NULL) { // this catches subtle errors where this function has been called // with local_channel() when not logged in (which returns false) @@ -131,14 +133,19 @@ class PConfig { $dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + $now = datetime_convert(); if (! $updated) { - $updated = datetime_convert(); + //Sometimes things happen fast... very fast. + //To make sure legitimate updates aren't rejected + //because not enough time has passed. We say our updates + //happened just a short time in the past rather than right now. + $updated = datetime_convert('UTC','UTC','-2 seconds'); } $hash = hash('sha256',$family.':'.$key); if (self::Get($uid, 'hz_delpconfig', $hash) !== false) { - if (self::Get($uid, 'hz_delpconfig', $hash) > $updated) { + if (self::Get($uid, 'hz_delpconfig', $hash) > $now) { logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR); return self::Get($uid,$family,$key); } else { @@ -173,7 +180,7 @@ class PConfig { } else { - $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated); + $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); if ($new) { @@ -234,16 +241,18 @@ class PConfig { * The category of the configuration value * @param string $key * The configuration key to delete - * @return mixed + * @param string $updated (optional) + * The datetime to store + * @return boolean */ static public function Delete($uid, $family, $key, $updated = NULL) { if(is_null($uid) || $uid === false) return false; - $updated = ($updated) ? $updated : datetime_convert(); - - $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated); + $updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds'); + $now = datetime_convert(); + $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now); if (! $newer) { logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR); @@ -266,22 +275,13 @@ class PConfig { dbesc($key) ); + // Synchronize delete with clones. + if ($family != 'hz_delpconfig') { $hash = hash('sha256',$family.':'.$key); set_pconfig($uid,'hz_delpconfig',$hash,$updated); } - // Synchronize delete with clones. - - if(! array_key_exists('transient', \App::$config[$uid])) - \App::$config[$uid]['transient'] = array(); - if(! array_key_exists($family, \App::$config[$uid]['transient'])) - \App::$config[$uid]['transient'][$family] = array(); - - if ($new) { - \App::$config[$uid]['transient'][$family]['pcfgdel:'.$key] = $updated; - } - return $ret; } diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php index d3ecbf7fa..3a2ab1783 100644 --- a/Zotlabs/Lib/Share.php +++ b/Zotlabs/Lib/Share.php @@ -54,6 +54,7 @@ class Share { if(! $this->item) return $obj; + $obj['asld'] = $this->item['mid']; $obj['type'] = $this->item['obj_type']; $obj['id'] = $this->item['mid']; $obj['content'] = $this->item['body']; diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 83d243177..40c0fca4b 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -76,7 +76,7 @@ class ThreadItem { * _ false on failure */ - public function get_template_data($conv_responses, $thread_level=1) { + public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) { $result = array(); @@ -101,6 +101,7 @@ class ThreadItem { || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) ? t('Private Message') : false); + $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); // allow an exemption for sharing stuff from your private feeds @@ -115,6 +116,19 @@ class ThreadItem { $privacy_warning = true; } + if ($lock) { + if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) { + $privacy_warning = true; + $conv_flags['parent_privacy_warning'] = true; + } + } + + $privacy_warning = (isset($conv_flags['parent_privacy_warning'])) ? $conv_flags['parent_privacy_warning'] : $privacy_warning; + + if ($lock && $privacy_warning) { + $lock = t('Privacy conflict. Discretion advised.'); + } + $mode = $conv->get_mode(); switch($item['item_type']) { @@ -293,8 +307,15 @@ class ThreadItem { $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike")); } - if ($shareable) - $share = array( t('Share This'), t('share')); + if ($shareable) { + // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues. + // Will allow it only for uri resolvable sources. + if(strpos($item['mid'],'http') === 0) { + $share = []; //Not yet ready for primetime + //$share = array( t('Repeat This'), t('repeat')); + } + $embed = array( t('Share This'), t('share')); + } $dreport = ''; @@ -408,12 +429,13 @@ class ThreadItem { 'like' => $like, 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''), 'share' => $share, + 'embed' => $embed, 'rawmid' => $item['mid'], 'plink' => get_plink($item), 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), - 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''), + 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''), 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), - 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), + 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''), 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), 'addtocal' => (($has_event) ? t('Add to Calendar') : ''), 'drop' => $drop, @@ -470,7 +492,7 @@ class ThreadItem { if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { foreach($children as $child) { - $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1); + $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags); } // Collapse if(($nb_children > $visible_comments) || ($thread_level > 1)) { diff --git a/Zotlabs/Lib/ThreadListener.php b/Zotlabs/Lib/ThreadListener.php new file mode 100644 index 000000000..308e02255 --- /dev/null +++ b/Zotlabs/Lib/ThreadListener.php @@ -0,0 +1,53 @@ +<?php + +namespace Zotlabs\Lib; + +class ThreadListener { + + static public function store($target_id,$portable_id,$ltype = 0) { + $x = self::fetch($target_id,$portable_id,$ltype = 0); + if(! $x) { + $r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ", + dbesc($target_id), + dbesc($portable_id), + intval($ltype) + ); + } + } + + static public function fetch($target_id,$portable_id,$ltype = 0) { + $x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1", + dbesc($target_id), + dbesc($portable_id), + intval($ltype) + ); + if($x) { + return $x[0]; + } + return false; + } + + static public function fetch_by_target($target_id,$ltype = 0) { + $x = q("select * from listeners where target_id = '%s' and ltype = %d", + dbesc($target_id), + intval($ltype) + ); + + return $x; + } + + static public function delete_by_target($target_id, $ltype = 0) { + return q("delete from listeners where target_id = '%s' and ltype = %d", + dbesc($target_id), + intval($ltype) + ); + } + + static public function delete_by_pid($portable_id, $ltype = 0) { + return q("delete from listeners where portable_id = '%s' and ltype = %d", + dbesc($portable_id), + intval($ltype) + ); + } + +} diff --git a/Zotlabs/Module/Admin/Addons.php b/Zotlabs/Module/Admin/Addons.php index b35922aef..b8e3e3a2e 100644 --- a/Zotlabs/Module/Admin/Addons.php +++ b/Zotlabs/Module/Admin/Addons.php @@ -375,6 +375,9 @@ class Addons { if($files) { foreach($files as $file) { if (is_dir($file)){ + if($file == 'addon/addon_common/') + continue; + list($tmp, $id) = array_map('trim', explode('/', $file)); $info = get_plugin_info($id); $enabled = in_array($id,\App::$plugins); @@ -476,4 +479,4 @@ class Addons { return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name']))); } -}
\ No newline at end of file +} diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 09b038729..55c8ca928 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -119,7 +119,7 @@ class Site { del_config('system', 'admininfo'); } else { require_once('include/text.php'); - linkify_tags($a, $admininfo, local_channel()); + linkify_tags($admininfo, local_channel()); set_config('system', 'admininfo', $admininfo); } set_config('system','siteinfo',$siteinfo); diff --git a/Zotlabs/Module/Affinity.php b/Zotlabs/Module/Affinity.php new file mode 100644 index 000000000..f0d99f1e7 --- /dev/null +++ b/Zotlabs/Module/Affinity.php @@ -0,0 +1,94 @@ +<?php + +namespace Zotlabs\Module; + +use App; +use Zotlabs\Lib\Apps; +use Zotlabs\Lib\Libsync; + +class Affinity extends \Zotlabs\Web\Controller { + + function post() { + + if(! local_channel()) + return; + + if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) + return; + + check_form_security_token_redirectOnErr('affinity', 'affinity'); + + $cmax = intval($_POST['affinity_cmax']); + if($cmax < 0 || $cmax > 99) + $cmax = 99; + + $cmin = intval($_POST['affinity_cmin']); + if($cmin < 0 || $cmin > 99) + $cmin = 0; + + $lock = intval($_POST['affinity_lock']); + + set_pconfig(local_channel(),'affinity','cmin',$cmin); + set_pconfig(local_channel(),'affinity','cmax',$cmax); + set_pconfig(local_channel(),'affinity','lock',$lock); + + info( t('Affinity Tool settings updated.') . EOL); + + Libsync::build_sync_packet(); + + } + + + function get() { + + if(! local_channel()) + return; + + $desc = t('This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.'); + if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) { + //Do not display any associated widgets at this point + App::$pdl = ''; + + $o = '<b>' . t('Affinity Tool App') . ' (' . t('Not Installed') . '):</b><br>'; + $o .= $desc; + return $o; + } + + $text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.'); + + $content = '<div class="section-content-info-wrapper">' . $text . '</div>'; + + $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); + $cmax = (($cmax) ? $cmax : 99); + $content .= replace_macros(get_markup_template('field_input.tpl'), array( + '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) + )); + + $cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); + $cmin = (($cmin) ? $cmin : 0); + $content .= replace_macros(get_markup_template('field_input.tpl'), array( + '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0')) + )); + + $lock = intval(get_pconfig(local_channel(),'affinity','lock',1)); + + $content .= replace_macros(get_markup_template('field_checkbox.tpl'), array( + '$field' => array('affinity_lock', t('Persistent affinity levels'), $lock, t('If disabled the max and min levels will be reset to default after page reload'), ['No','Yes']) + )); + + $tpl = get_markup_template("settings_addon.tpl"); + + $o = replace_macros($tpl, array( + '$action_url' => 'affinity', + '$form_security_token' => get_form_security_token("affinity"), + '$title' => t('Affinity Tool Settings'), + '$content' => $content, + '$baseurl' => z_root(), + '$submit' => t('Submit'), + )); + + return $o; + } + + +} diff --git a/Zotlabs/Module/Cards.php b/Zotlabs/Module/Cards.php index b66de158b..3f0e93de5 100644 --- a/Zotlabs/Module/Cards.php +++ b/Zotlabs/Module/Cards.php @@ -10,9 +10,13 @@ require_once('include/channel.php'); require_once('include/conversation.php'); require_once('include/acl_selectors.php'); +/** + * @brief Provides the Cards module. + * + */ class Cards extends Controller { - function init() { + public function init() { if(argc() > 1) $which = argv(1); @@ -20,14 +24,15 @@ class Cards extends Controller { return; profile_load($which); - } /** * {@inheritDoc} - * @see \Zotlabs\Web\Controller::get() + * @see \\Zotlabs\\Web\\Controller::get() + * + * @return string Parsed HTML from template 'cards.tpl' */ - function get($update = 0, $load = false) { + public function get($update = 0, $load = false) { if(observer_prohibited(true)) { return login(); @@ -99,7 +104,6 @@ class Cards extends Controller { } - if(perm_is_allowed($owner, $ob_hash, 'write_pages')) { $x = [ @@ -110,7 +114,7 @@ class Cards extends Controller { '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, + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, PermissionDescription::fromGlobalPermission('view_pages')) : ''), 'permissions' => $channel_acl, 'showacl' => (($is_owner) ? true : false), diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php index 779c7e646..2e653d030 100644 --- a/Zotlabs/Module/Chanview.php +++ b/Zotlabs/Module/Chanview.php @@ -106,7 +106,7 @@ class Chanview extends \Zotlabs\Web\Controller { if (\App::$poi) { $url = \App::$poi['xchan_url']; - if(\App::$poi['xchan_network'] === 'zot') { + if(in_array(\App::$poi['xchan_network'], ['zot', 'zot6'])) { $is_zot = true; } if(local_channel()) { diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index c14bcd0dd..a587324df 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -710,7 +710,7 @@ class Connedit extends \Zotlabs\Web\Controller { $tpl = get_markup_template("abook_edit.tpl"); - if(feature_enabled(local_channel(),'affinity')) { + if(Apps::system_app_installed(local_channel(),'Affinity Tool')) { $sections['affinity'] = [ 'label' => t('Affinity'), diff --git a/Zotlabs/Module/Dreport.php b/Zotlabs/Module/Dreport.php index 16ae7941f..2c125b7a9 100644 --- a/Zotlabs/Module/Dreport.php +++ b/Zotlabs/Module/Dreport.php @@ -80,8 +80,9 @@ class Dreport extends \Zotlabs\Web\Controller { return; } - $r = q("select * from dreport where dreport_xchan = '%s' and dreport_mid = '%s'", + $r = q("select * from dreport where (dreport_xchan = '%s' or dreport_xchan = '%s') and dreport_mid = '%s'", dbesc($channel['channel_hash']), + dbesc($channel['channel_portable_id']), dbesc($mid) ); diff --git a/Zotlabs/Module/Embed.php b/Zotlabs/Module/Embed.php new file mode 100644 index 000000000..77b9254dd --- /dev/null +++ b/Zotlabs/Module/Embed.php @@ -0,0 +1,22 @@ +<?php +namespace Zotlabs\Module; + +require_once('include/security.php'); +require_once('include/bbcode.php'); + + +class Embed extends \Zotlabs\Web\Controller { + + function init() { + + $post_id = ((argc() > 1) ? intval(argv(1)) : 0); + + if(! $post_id) + killme(); + + echo '[share=' . $post_id . '][/share]'; + killme(); + + } + +} diff --git a/Zotlabs/Module/Embedphotos.php b/Zotlabs/Module/Embedphotos.php index bcbb0e116..2df14c239 100644 --- a/Zotlabs/Module/Embedphotos.php +++ b/Zotlabs/Module/Embedphotos.php @@ -3,8 +3,10 @@ namespace Zotlabs\Module; /** - * @brief + * @brief Embedphoto endpoint. * + * Provide an AJAX endpoint to fill the embedPhotoModal with folders and photos + * selection. */ class Embedphotos extends \Zotlabs\Web\Controller { @@ -13,42 +15,42 @@ class Embedphotos extends \Zotlabs\Web\Controller { } /** + * @brief This is the POST destination for the embedphotos button. * - * This is the POST destination for the embedphotos button - * + * @return string A JSON string. */ - function post() { + public function post() { if (argc() > 1 && argv(1) === 'album') { // API: /embedphotos/album - $name = (x($_POST,'name') ? $_POST['name'] : null ); - if(!$name) { + $name = (x($_POST, 'name') ? $_POST['name'] : null ); + if (!$name) { json_return_and_die(array('errormsg' => 'Error retrieving album', 'status' => false)); } $album = $this->embedphotos_widget_album(array('channel' => \App::get_channel(), 'album' => $name)); json_return_and_die(array('status' => true, 'content' => $album)); } - if(argc() > 1 && argv(1) === 'albumlist') { + if (argc() > 1 && argv(1) === 'albumlist') { // API: /embedphotos/albumlist - $album_list = $this->embedphotos_album_list($a); + $album_list = $this->embedphotos_album_list(); json_return_and_die(array('status' => true, 'albumlist' => $album_list)); } - if(argc() > 1 && argv(1) === 'photolink') { + if (argc() > 1 && argv(1) === 'photolink') { // API: /embedphotos/photolink - $href = (x($_POST,'href') ? $_POST['href'] : null ); - if(!$href) { + $href = (x($_POST, 'href') ? $_POST['href'] : null ); + if (!$href) { json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); } - $resource_id = array_pop(explode("/", $href)); + $resource_id = array_pop(explode('/', $href)); $r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1", dbesc($resource_id) ); - if(!$r) { + if (!$r) { json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); } $obj = json_decode($r[0]['obj'], true); - if(x($obj,'body')) { + if (x($obj, 'body')) { $photolink = $obj['body']; - } elseif (x($obj,'bbcode')) { + } elseif (x($obj, 'bbcode')) { $photolink = $obj['bbcode']; } else { json_return_and_die(array('errormsg' => 'Error retrieving resource ' . $resource_id, 'status' => false)); @@ -58,48 +60,51 @@ class Embedphotos extends \Zotlabs\Web\Controller { } /** - * Copied from include/widgets.php::widget_album() with a modification to get the profile_uid from - * the input array as in widget_item() + * @brief Get photos from an album. + * + * @see \\Zotlabs\\Widget\\Album::widget() * - * @param array $args - * @return string with HTML + * @param array $args associative array with + * * \e array \b channel + * * \e string \b album + * @return string with HTML code from 'photo_album.tpl' */ - function embedphotos_widget_album($args) { - + protected function embedphotos_widget_album($args) { $channel_id = 0; - if(array_key_exists('channel', $args)) + + if (array_key_exists('channel', $args)) { $channel = $args['channel']; - $channel_id = intval($channel['channel_id']); - if(! $channel_id) + $channel_id = intval($channel['channel_id']); + } + if (! $channel_id) $channel_id = \App::$profile_uid; - if(! $channel_id) + if (! $channel_id) return ''; - $owner_uid = $channel_id; require_once('include/security.php'); $sql_extra = permissions_sql($channel_id); - if(! perm_is_allowed($channel_id,get_observer_hash(),'view_storage')) + if (! perm_is_allowed($channel_id, get_observer_hash(), 'view_storage')) return ''; - if($args['album']) + if (isset($args['album'])) $album = (($args['album'] === '/') ? '' : $args['album']); - if($args['title']) + if (isset($args['title'])) $title = $args['title']; /** - * This may return incorrect permissions if you have multiple directories of the same name. + * @note This may return incorrect permissions if you have multiple directories of the same name. * It is a limitation of the photo table using a name for a photo album instead of a folder hash */ - if($album) { + if ($album) { require_once('include/attach.php'); $x = q("select hash from attach where filename = '%s' and uid = %d limit 1", dbesc($album), - intval($owner_uid) + intval($channel_id) ); - if($x) { - $y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); - if(! $y) + if ($x) { + $y = attach_can_view_folder($channel_id, get_observer_hash(), $x[0]['hash']); + if (! $y) return ''; } } @@ -110,30 +115,33 @@ class Embedphotos extends \Zotlabs\Web\Controller { (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ORDER BY created $order", - intval($owner_uid), + intval($channel_id), dbesc($album), intval(PHOTO_NORMAL), intval(PHOTO_PROFILE) ); - $photos = array(); - if(count($r)) { + $photos = []; + if (count($r)) { $twist = 'rotright'; - foreach($r as $rr) { - if($twist == 'rotright') + foreach ($r as $rr) { + if ($twist == 'rotright') $twist = 'rotleft'; else $twist = 'rotright'; + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + $ext = $phototypes[$rr['mimetype']]; $imgalt_e = $rr['filename']; $desc_e = $rr['description']; - $imagelink = (z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] + $imagelink = (z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $rr['resource_id'] . (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); - $photos[] = array( + $photos[] = [ 'id' => $rr['id'], 'twist' => ' ' . $twist . rand(2,4), 'link' => $imagelink, @@ -143,35 +151,43 @@ class Embedphotos extends \Zotlabs\Web\Controller { 'desc'=> $desc_e, 'ext' => $ext, 'hash'=> $rr['resource_id'], - 'unknown' => t('Unknown') - ); + 'unknown' => t('Unknown'), + ]; } } $tpl = get_markup_template('photo_album.tpl'); - $o .= replace_macros($tpl, array( + $o = replace_macros($tpl, [ '$photos' => $photos, '$album' => (($title) ? $title : $album), '$album_id' => rand(), - '$album_edit' => array(t('Edit Album'), $album_edit), + '$album_edit' => array(t('Edit Album'), false), '$can_post' => false, '$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)), '$order' => false, - '$upload_form' => $upload_form, - '$no_fullscreen_btn' => true - )); + '$upload_form' => '', + '$no_fullscreen_btn' => true, + ]); return $o; } - function embedphotos_album_list($a) { + /** + * @brief Get albums observer is allowed to see. + * + * @see photos_albums_list() + * + * @return NULL|array + */ + protected function embedphotos_album_list() { require_once('include/photos.php'); $p = photos_albums_list(\App::get_channel(), \App::get_observer()); - if($p['success']) { + + if ($p['success']) { return $p['albums']; - } else { - return null; } + + return null; } } diff --git a/Zotlabs/Module/Events.php b/Zotlabs/Module/Events.php index 7e5204e62..e883db49f 100644 --- a/Zotlabs/Module/Events.php +++ b/Zotlabs/Module/Events.php @@ -97,8 +97,8 @@ class Events extends \Zotlabs\Web\Controller { $type = escape_tags(trim($_POST['type'])); require_once('include/text.php'); - linkify_tags($a, $desc, local_channel()); - linkify_tags($a, $location, local_channel()); + linkify_tags($desc, local_channel()); + linkify_tags($location, local_channel()); //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; diff --git a/Zotlabs/Module/Group.php b/Zotlabs/Module/Group.php index c8ccaa2cb..12edf8428 100644 --- a/Zotlabs/Module/Group.php +++ b/Zotlabs/Module/Group.php @@ -66,6 +66,9 @@ class Group extends Controller { $groupname = notags(trim($_POST['groupname'])); $public = intval($_POST['public']); + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ]; + call_hooks ('privacygroup_extras_post',$hookinfo); + if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { $r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", dbesc($groupname), @@ -75,6 +78,8 @@ class Group extends Controller { ); if($r) info( t('Privacy group updated.') . EOL ); + + build_sync_packet(local_channel(),null,true); } @@ -127,6 +132,10 @@ class Group extends Controller { $i++; } + $hookinfo = [ 'pgrp_extras' => '', 'group'=>argv(1) ]; + call_hooks ('privacygroup_extras',$hookinfo); + $pgrp_extras = $hookinfo['pgrp_extras']; + $tpl = get_markup_template('privacy_groups.tpl'); $o = replace_macros($tpl, [ '$title' => t('Privacy Groups'), @@ -136,6 +145,7 @@ class Group extends Controller { // new group form '$gname' => array('groupname',t('Privacy group name')), '$public' => array('public',t('Members are visible to other channels'), false), + '$pgrp_extras' => $pgrp_extras, '$form_security_token' => get_form_security_token("group_edit"), '$submit' => t('Submit'), @@ -166,8 +176,11 @@ class Group extends Controller { ); if($r) $result = group_rmv(local_channel(),$r[0]['gname']); - if($result) + if($result) { + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(2) ]; + call_hooks ('privacygroup_extras_drop',$hookinfo); info( t('Privacy group removed.') . EOL); + } else notice( t('Unable to remove privacy group.') . EOL); } @@ -230,6 +243,10 @@ class Group extends Controller { } } + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ]; + call_hooks ('privacygroup_extras',$hookinfo); + $pgrp_extras = $hookinfo['pgrp_extras']; + $context = $context + array( '$title' => sprintf(t('Privacy Group: %s'), $group['gname']), '$details_label' => t('Edit'), @@ -240,6 +257,7 @@ class Group extends Controller { '$form_security_token_edit' => get_form_security_token('group_edit'), '$delete' => t('Delete Group'), '$form_security_token_drop' => get_form_security_token("group_drop"), + '$pgrp_extras' => $pgrp_extras, ); } @@ -283,6 +301,7 @@ class Group extends Controller { $context['$groupeditor'] = $groupeditor; $context['$desc'] = t('Click a channel to toggle membership'); + $context['$pgrp_extras'] = $pgrp_extras; if($change) { $tpl = get_markup_template('groupeditor.tpl'); diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 24949c626..ebcf632ef 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -6,6 +6,13 @@ use Zotlabs\Lib\IConfig; use Zotlabs\Lib\Enotify; use Zotlabs\Web\Controller; use Zotlabs\Daemon\Master; +use Zotlabs\Lib\Activity; +use Zotlabs\Lib\ActivityStreams; +use Zotlabs\Lib\LDSignatures; +use Zotlabs\Zot6\HTTPSig; +use Zotlabs\Lib\Libzot; +use Zotlabs\Lib\ThreadListener; +use App; require_once('include/crypto.php'); require_once('include/items.php'); @@ -30,6 +37,144 @@ require_once('include/security.php'); class Item extends Controller { + + function init() { + + if(Libzot::is_zot_request()) { + + $conversation = false; + + $item_id = argv(1); + + if(! $item_id) + http_status_exit(404, 'Not found'); + + + $portable_id = EMPTY_STR; + + $sigdata = HTTPSig::verify(EMPTY_STR); + if($sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + } + + $item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 "; + + $sql_extra = item_permissions_sql(0); + + $r = q("select * from item where mid = '%s' $item_normal $sql_extra limit 1", + dbesc(z_root() . '/item/' . $item_id) + ); + if(! $r) { + + + $r = q("select * from item where mid = '%s' $item_normal limit 1", + dbesc(z_root() . '/item/' . $item_id) + ); + if($r) { + http_status_exit(403, 'Forbidden'); + } + http_status_exit(404, 'Not found'); + } + + + $items = q("select parent as item_id from item where mid = '%s' and uid = %d $item_normal $sql_extra ", + dbesc($r[0]['parent_mid']), + intval($r[0]['uid']) + ); + if(! $items) { + http_status_exit(404, 'Not found'); + } + + $r = $items; + + $parents_str = ids_to_querystr($r,'item_id'); + + $items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal $sql_extra ", + dbesc($parents_str) + ); + + if(! $items) { + http_status_exit(404, 'Not found'); + } + + $r = $items; + xchan_query($r,true); + $items = fetch_post_tags($r,true); + + $observer = App::get_observer(); + $parent = $items[0]; + $recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []); + $to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null); + $nitems = []; + foreach($items as $i) { + + $mids = []; + + if(intval($i['item_private'])) { + if(! $observer) { + continue; + } + // ignore private reshare, possibly from hubzilla + if($i['verb'] === 'Announce') { + if(! in_array($i['thr_parent'],$mids)) { + $mids[] = $i['thr_parent']; + } + continue; + } + // also ignore any children of the private reshares + if(in_array($i['thr_parent'],$mids)) { + continue; + } + + if((! $to) || (! in_array($observer['xchan_url'],$to))) { + continue; + } + + } + $nitems[] = $i; + } + + if(! $nitems) + http_status_exit(404, 'Not found'); + + $chan = channelx_by_n($nitems[0]['uid']); + + if(! $chan) + http_status_exit(404, 'Not found'); + + if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream')) + http_status_exit(403, 'Forbidden'); + + $i = Activity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection',( defined('NOMADIC') ? false : true)); + if($portable_id) { + ThreadListener::store(z_root() . '/item/' . $item_id,$portable_id); + } + + if(! $i) + http_status_exit(404, 'Not found'); + + $x = array_merge(['@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1', + z_root() . ZOT_APSCHEMA_REV + ]], $i); + + $headers = []; + $headers['Content-Type'] = 'application/x-zot+json' ; + $x['signature'] = LDSignatures::sign($x,$chan); + $ret = json_encode($x, JSON_UNESCAPED_SLASHES); + $headers['Digest'] = HTTPSig::generate_digest_header($ret); + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan)); + HTTPSig::set_headers($h); + echo $ret; + killme(); + + } + } + + + function post() { // This will change. Figure out who the observer is and whether or not @@ -553,8 +698,8 @@ class Item extends Controller { // Look for tags and linkify them - $results = linkify_tags($a, $summary, ($uid) ? $uid : $profile_uid); - $results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid); + $results = linkify_tags($summary, ($uid) ? $uid : $profile_uid); + $results = linkify_tags($body, ($uid) ? $uid : $profile_uid); if($results) { @@ -639,9 +784,9 @@ class Item extends Controller { if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) { + // process share by id - $verb = ACTIVITY_SHARE; $i = 0; foreach($match[2] as $mtch) { $reshare = new \Zotlabs\Lib\Share($mtch); diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 0455c5265..3d1f503b6 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -52,7 +52,7 @@ class Like extends \Zotlabs\Web\Controller { $observer = \App::get_observer(); $interactive = $_REQUEST['interactive']; - if($interactive) { + if((! $observer) || ($interactive)) { $o .= '<h1>' . t('Like/Dislike') . '</h1>'; $o .= EOL . EOL; diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php index 7c7dc0e88..32b4c0281 100644 --- a/Zotlabs/Module/Linkinfo.php +++ b/Zotlabs/Module/Linkinfo.php @@ -138,8 +138,8 @@ class Linkinfo extends \Zotlabs\Web\Controller { } $image = ""; - - if(sizeof($siteinfo["images"]) > 0){ + + if(is_array($siteinfo["images"]) && count($siteinfo["images"])){ /* Execute below code only if image is present in siteinfo */ $total_images = 0; @@ -161,7 +161,7 @@ class Linkinfo extends \Zotlabs\Web\Controller { $total_images ++; if($max_images && $max_images >= $total_images) break; - } + } } if(strlen($text)) { diff --git a/Zotlabs/Module/Mail.php b/Zotlabs/Module/Mail.php index d38c1d88c..3202d38a5 100644 --- a/Zotlabs/Module/Mail.php +++ b/Zotlabs/Module/Mail.php @@ -34,7 +34,7 @@ class Mail extends \Zotlabs\Web\Controller { } else { $body = cleanup_bbcode($body); - $results = linkify_tags($a, $body, local_channel()); + $results = linkify_tags($body, local_channel()); if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { $attachments = array(); @@ -111,7 +111,7 @@ class Mail extends \Zotlabs\Web\Controller { } require_once('include/text.php'); - linkify_tags($a, $body, local_channel()); + linkify_tags($body, local_channel()); if(! $recipient) { diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index d5cc06d09..2019082ed 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -1,6 +1,8 @@ <?php namespace Zotlabs\Module; +use Zotlabs\Lib\Group; +use Zotlabs\Lib\Apps; use App; require_once('include/items.php'); @@ -114,8 +116,8 @@ class Network extends \Zotlabs\Web\Controller { $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); } - $default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); - $default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); + $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); + $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); $cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0); $star = ((x($_GET,'star')) ? intval($_GET['star']) : 0); @@ -132,7 +134,7 @@ class Network extends \Zotlabs\Web\Controller { $deftag = ''; - if (feature_enabled(local_channel(),'affinity')) { + if (Apps::system_app_installed(local_channel(),'Affinity Tool')) { $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); if ($affinity_locked) { set_pconfig(local_channel(),'affinity','cmin',$cmin); diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php index 73cdf4c8c..98aa480fe 100644 --- a/Zotlabs/Module/New_channel.php +++ b/Zotlabs/Module/New_channel.php @@ -134,7 +134,7 @@ class New_channel extends \Zotlabs\Web\Controller { $default_role = ''; $aid = get_account_id(); if($aid) { - $r = q("select count(channel_id) as total from channel where channel_account_id = %d", + $r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0", intval($aid) ); if($r && (! intval($r[0]['total']))) { diff --git a/Zotlabs/Module/Notes.php b/Zotlabs/Module/Notes.php index 178a6bce0..7572f7420 100644 --- a/Zotlabs/Module/Notes.php +++ b/Zotlabs/Module/Notes.php @@ -1,28 +1,31 @@ <?php -namespace Zotlabs\Module; /** @file */ +namespace Zotlabs\Module; use App; use Zotlabs\Web\Controller; use Zotlabs\Lib\Apps; +/** + * @brief Notes Module controller. + */ class Notes extends Controller { function post() { - + if(! local_channel()) return EMPTY_STR; if(! Apps::system_app_installed(local_channel(), 'Notes')) return EMPTY_STR; - + $ret = array('success' => true); if(array_key_exists('note_text',$_REQUEST)) { $body = escape_tags($_REQUEST['note_text']); - + // I've had my notes vanish into thin air twice in four years. - // Provide a backup copy if there were contents previously + // Provide a backup copy if there were contents previously // and there are none being saved now. - + if(! $body) { $old_text = get_pconfig(local_channel(),'notes','text'); if($old_text) @@ -40,11 +43,9 @@ class Notes extends Controller { logger('notes saved.', LOGGER_DEBUG); json_return_and_die($ret); - } function get() { - if(! local_channel()) return EMPTY_STR; @@ -61,7 +62,6 @@ class Notes extends Controller { $arr = ['app' => true]; return $w->widget($arr); - } - + } diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 0f20a5f9a..c0d8e15e5 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -181,7 +181,7 @@ class Oep extends \Zotlabs\Web\Controller { dbesc($res) ); if($r) { - $sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; + $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; } else { return $ret; @@ -194,6 +194,9 @@ class Oep extends \Zotlabs\Web\Controller { intval(ITEM_TYPE_CARD) ); + if(! $r) + return; + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) 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 "; @@ -255,7 +258,6 @@ class Oep extends \Zotlabs\Web\Controller { if(! $channel) return $ret; - if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) return $ret; @@ -265,7 +267,7 @@ class Oep extends \Zotlabs\Web\Controller { dbesc($res) ); if($r) { - $sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; + $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " "; } else { return $ret; @@ -278,6 +280,9 @@ class Oep extends \Zotlabs\Web\Controller { intval(ITEM_TYPE_ARTICLE) ); + if(! $r) + return; + $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 "; diff --git a/Zotlabs/Module/Pconfig.php b/Zotlabs/Module/Pconfig.php index f31d5fdf6..06b94b34f 100644 --- a/Zotlabs/Module/Pconfig.php +++ b/Zotlabs/Module/Pconfig.php @@ -24,7 +24,7 @@ class Pconfig extends \Zotlabs\Web\Controller { $aj = intval($_POST['aj']); // Do not store "serialized" data received in the $_POST - if (preg_match('|^a:[0-9]+:{.*}$|s',$v) || preg_match('O:8:"stdClass":[0-9]+:{.*}$|s',$v)) { + if (preg_match('|^a:[0-9]+:{.*}$|s',$v) || preg_match('|O:8:"stdClass":[0-9]+:{.*}$|s',$v)) { return; } diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index e236cc5f4..96a4e1f40 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -159,14 +159,15 @@ class Photo extends \Zotlabs\Web\Controller { // Validate cache $cache = array( 'resid' => $photo, - 'url' => htmlspecialchars_decode($r[0]['display_path']) + 'status' => false ); if($cache_mode['on']) call_hooks('cache_url_hook', $cache); - if($cache['url'] != '') { - if(strpos(z_root(),'https:') !== false && strpos($cache['url'],'https:') === false) - $cache['url'] = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($cache['url']); - header("Location: " . $cache['url']); + if(! $cache['status']) { + $url = htmlspecialchars_decode($r[0]['display_path']); + if(strpos(z_root(),'https:') !== false && strpos($url,'https:') === false) + $url = z_root() . '/sslify/' . $filename . '?f=&url=' . urlencode($url); + header("Location: " . $url); killme(); } } diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index b87c586da..a761dbd14 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -422,7 +422,7 @@ class Photos extends \Zotlabs\Web\Controller { require_once('include/text.php'); $profile_uid = \App::$profile['profile_uid']; - $results = linkify_tags($a, $rawtags, (local_channel()) ? local_channel() : $profile_uid); + $results = linkify_tags($rawtags, (local_channel()) ? local_channel() : $profile_uid); $success = $results['success']; $post_tags = array(); @@ -988,7 +988,7 @@ class Photos extends \Zotlabs\Web\Controller { $photo = array( 'href' => z_root() . '/photo/' . $hires['resource_id'] . '-' . $hires['imgscale'] . '.' . $phototypes[$hires['mimetype']], 'title'=> t('View Full Size'), - 'src' => z_root() . '/photo/' . $lores['resource_id'] . '-' . $lores['imgscale'] . '.' . $phototypes[$lores['mimetype']] . '?f=&_u=' . datetime_convert('','','','ymdhis') + 'src' => z_root() . '/photo/' . $lores['resource_id'] . '-' . $lores['imgscale'] . '.' . $phototypes[$lores['mimetype']] ); if($nextlink) diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index de4075ba9..33e7d8a9d 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -354,20 +354,20 @@ class Profiles extends \Zotlabs\Web\Controller { require_once('include/text.php'); - linkify_tags($a, $likes, local_channel()); - linkify_tags($a, $dislikes, local_channel()); - linkify_tags($a, $about, local_channel()); - linkify_tags($a, $interest, local_channel()); - linkify_tags($a, $interest, local_channel()); - linkify_tags($a, $contact, local_channel()); - linkify_tags($a, $channels, local_channel()); - linkify_tags($a, $music, local_channel()); - linkify_tags($a, $book, local_channel()); - linkify_tags($a, $tv, local_channel()); - linkify_tags($a, $film, local_channel()); - linkify_tags($a, $romance, local_channel()); - linkify_tags($a, $work, local_channel()); - linkify_tags($a, $education, local_channel()); + linkify_tags($likes, local_channel()); + linkify_tags($dislikes, local_channel()); + linkify_tags($about, local_channel()); + linkify_tags($interest, local_channel()); + linkify_tags($interest, local_channel()); + linkify_tags($contact, local_channel()); + linkify_tags($channels, local_channel()); + linkify_tags($music, local_channel()); + linkify_tags($book, local_channel()); + linkify_tags($tv, local_channel()); + linkify_tags($film, local_channel()); + linkify_tags($romance, local_channel()); + linkify_tags($work, local_channel()); + linkify_tags($education, local_channel()); $with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : ''); diff --git a/Zotlabs/Module/Settings/Featured.php b/Zotlabs/Module/Settings/Featured.php index 1d903fcf7..d5d740aff 100644 --- a/Zotlabs/Module/Settings/Featured.php +++ b/Zotlabs/Module/Settings/Featured.php @@ -10,24 +10,6 @@ class Featured { call_hooks('feature_settings_post', $_POST); - if($_POST['affinity_slider-submit']) { - $cmax = intval($_POST['affinity_cmax']); - if($cmax < 0 || $cmax > 99) - $cmax = 99; - $cmin = intval($_POST['affinity_cmin']); - if($cmin < 0 || $cmin > 99) - $cmin = 0; - - $lock = ($_POST['affinity_lock']) ? intval($_POST['affinity_lock']) : 1; - - set_pconfig(local_channel(),'affinity','cmin',$cmin); - set_pconfig(local_channel(),'affinity','cmax',$cmax); - set_pconfig(local_channel(),'affinity','lock',$lock); - - info( t('Affinity Slider settings updated.') . EOL); - - } - build_sync_packet(); return; } @@ -41,34 +23,10 @@ class Featured { if(! $r) $settings_addons = t('No feature settings configured'); - if(feature_enabled(local_channel(),'affinity')) { - - $cmax = intval(get_pconfig(local_channel(),'affinity','cmax')); - $cmax = (($cmax) ? $cmax : 99); - $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( - '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99')) - )); - $cmin = intval(get_pconfig(local_channel(),'affinity','cmin')); - $cmin = (($cmin) ? $cmin : 0); - $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array( - '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0')) - )); - $lock = intval(get_pconfig(local_channel(),'affinity','lock',1)); - $setting_fields .= replace_macros(get_markup_template('field_checkbox.tpl'), array( - '$field' => array('affinity_lock', t('Always reset on new page visit.'), $lock, t('default: yes'), Array('No','Yes')) - )); - - $settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array( - '$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')), - '$content' => $setting_fields - )); - } - call_hooks('feature_settings', $settings_addons); $this->sortpanels($settings_addons); - $tpl = get_markup_template("settings_addons.tpl"); $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("settings_featured"), diff --git a/Zotlabs/Module/Share.php b/Zotlabs/Module/Share.php index c6d0be051..53a06b072 100644 --- a/Zotlabs/Module/Share.php +++ b/Zotlabs/Module/Share.php @@ -1,6 +1,11 @@ <?php namespace Zotlabs\Module; +use App; +use Zotlabs\Daemon\Master; +use Zotlabs\Lib\Activity; + + require_once('include/security.php'); require_once('include/bbcode.php'); @@ -14,23 +19,23 @@ class Share extends \Zotlabs\Web\Controller { if(! $post_id) killme(); - echo '[share=' . $post_id . '][/share]'; - killme(); + if(! local_channel()) { + killme(); + } + $observer = App::get_observer(); - /** - * The remaining code is deprecated and handled in Zotlabs/Lib/Share.php at post - * submission time. - */ + $channel = App::get_channel(); - if(! (local_channel() || remote_channel())) - killme(); - $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", intval($post_id) ); if(! $r) killme(); + + + + if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) killme(); @@ -46,59 +51,86 @@ class Share extends \Zotlabs\Web\Controller { if($r[0]['mimetype'] !== 'text/bbcode') killme(); - - /** @FIXME eventually we want to post remotely via rpost on your home site */ - // When that works remove this next bit: - - if(! local_channel()) - killme(); - + xchan_query($r); - $is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false); - if($is_photo) { - $object = json_decode($r[0]['obj'],true); - $photo_bb = $object['body']; - } - - if (strpos($r[0]['body'], "[/share]") !== false) { - $pos = strpos($r[0]['body'], "[share"); - $o = substr($r[0]['body'], $pos); - } else { - $o = "[share author='" . urlencode($r[0]['author']['xchan_name']) . - "' profile='" . $r[0]['author']['xchan_url'] . - "' avatar='" . $r[0]['author']['xchan_photo_s'] . - "' link='" . $r[0]['plink'] . - "' auth='" . (($r[0]['author']['network'] === 'zot') ? 'true' : 'false') . - "' posted='" . $r[0]['created'] . - "' message_id='" . $r[0]['mid'] . - "']"; - if($r[0]['title']) - $o .= '[b]'.$r[0]['title'].'[/b]'."\r\n"; - $o .= (($is_photo) ? $photo_bb . "\r\n" . $r[0]['body'] : $r[0]['body']); - $o .= "[/share]"; - } - - if(local_channel()) { - echo $o; + $arr = []; + + $item = $r[0]; + + $owner_uid = $r[0]['uid']; + $owner_aid = $r[0]['aid']; + + $can_comment = false; + if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self'])) + $can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments'); + else + $can_comment = can_comment_on_post($observer['xchan_hash'],$item); + + if(! $can_comment) { + notice( t('Permission denied') . EOL); killme(); } + + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['owner_xchan']) + ); + + if($r) + $thread_owner = $r[0]; + else + killme(); - $observer = \App::get_observer(); - $parsed = $observer['xchan_url']; - if($parsed) { - $post_url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '') - . '/rpost'; + $r = q("select * from xchan where xchan_hash = '%s' limit 1", + dbesc($item['author_xchan']) + ); + if($r) + $item_author = $r[0]; + else + killme(); - /** - * @FIXME we were probably called from JS so we don't know the return page. - * In fact we won't be able to load the remote page. - * we might need an iframe - */ + + $arr['aid'] = $owner_aid; + $arr['uid'] = $owner_uid; + + $arr['item_origin'] = 1; + $arr['item_wall'] = $item['item_wall']; + $arr['uuid'] = item_message_id(); + $arr['mid'] = z_root() . '/activity/' . $arr['uuid']; + $arr['parent_mid'] = $item['mid']; + + $mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]'; + $arr['body'] = sprintf( t('🔁 Repeated %1$s\'s %2$s'), $mention, Activity::activity_obj_mapper($item['obj_type'])); + + $arr['author_xchan'] = $channel['channel_hash']; + $arr['owner_xchan'] = $item['author_xchan']; + $arr['obj'] = Activity::encode_item($item); + $arr['obj_type'] = $item['obj_type']; + $arr['verb'] = 'Announce'; + + $post = item_store($arr); + + $post_id = $post['item_id']; + + $arr['id'] = $post_id; - $x = z_post_url($post_url, array('f' => '', 'body' => $o )); - killme(); + call_hooks('post_local_end', $arr); + + info( t('Post repeated') . EOL); + + $r = q("select * from item where id = %d", + intval($post_id) + ); + if($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]); } + + Master::Summon([ 'Notifier','like',$post_id ]); + + killme(); + } } diff --git a/Zotlabs/Module/Viewsrc.php b/Zotlabs/Module/Viewsrc.php index 119990b57..b73d81283 100644 --- a/Zotlabs/Module/Viewsrc.php +++ b/Zotlabs/Module/Viewsrc.php @@ -28,7 +28,7 @@ class Viewsrc extends \Zotlabs\Web\Controller { $item_normal = item_normal(); if(local_channel() && $item_id) { - $r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", + $r = q("select id, mid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", intval(local_channel()), intval($sys['channel_id']), intval($item_id) @@ -53,7 +53,7 @@ class Viewsrc extends \Zotlabs\Web\Controller { if(is_ajax()) { echo '<div class="p-1">'; - echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a></div>'; + echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '</div>'; echo '<hr>'; echo '<pre class="p-1">' . $o . '</pre>'; echo '</div>'; diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php index 4f2a3f1f7..fe0a3fbf9 100644 --- a/Zotlabs/Web/Session.php +++ b/Zotlabs/Web/Session.php @@ -15,7 +15,7 @@ class Session { private $handler = null; private $session_started = false; - + private $custom_handler = false; public function init() { $gc_probability = 50; @@ -23,25 +23,46 @@ class Session { ini_set('session.gc_probability', $gc_probability); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); - + + $this->custom_handler = boolval(get_config('system', 'session_custom', false)); + /* * Set our session storage functions. */ + + if($this->custom_handler) { + /* Custom handler (files, memached, redis..) */ + + $session_save_handler = strval(get_config('system', 'session_save_handler', Null)); + $session_save_path = strval(get_config('system', 'session_save_path', Null)); + $session_gc_probability = intval(get_config('system', 'session_gc_probability', 1)); + $session_gc_divisor = intval(get_config('system', 'session_gc_divisor', 100)); + if(!$session_save_handler || !$session_save_path) { + logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR); + } + else { + ini_set('session.save_handler', $session_save_handler); + ini_set('session.save_path', $session_save_path); + ini_set('session.gc_probability', $session_gc_probability); + ini_set('session.gc_divisor', $session_gc_divisor); + } + } + else { + $handler = new \Zotlabs\Web\SessionHandler(); - $handler = new \Zotlabs\Web\SessionHandler(); - - $this->handler = $handler; + $this->handler = $handler; - $x = session_set_save_handler($handler,false); - if(! $x) - logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); + $x = session_set_save_handler($handler,false); + if(! $x) + logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); + } // Force cookies to be secure (https only) if this site is SSL enabled. // Must be done before session_start(). $arr = session_get_cookie_params(); - + // Note when setting cookies: set the domain to false which creates a single domain // cookie. If you use a hostname it will create a .domain.com wildcard which will // have some nasty side effects if you have any other subdomains running hubzilla. @@ -86,14 +107,15 @@ class Session { $arr = session_get_cookie_params(); - if($this->handler && $this->session_started) { + if(($this->handler || $this->custom_handler) && $this->session_started) { session_regenerate_id(true); // force SessionHandler record creation with the new session_id // which occurs as a side effect of read() - - $this->handler->read(session_id()); + if (! $this->custom_handler) { + $this->handler->read(session_id()); + } } else logger('no session handler'); diff --git a/Zotlabs/Widget/Affinity.php b/Zotlabs/Widget/Affinity.php index 28190e187..572af0503 100644 --- a/Zotlabs/Widget/Affinity.php +++ b/Zotlabs/Widget/Affinity.php @@ -2,60 +2,65 @@ namespace Zotlabs\Widget; +use Zotlabs\Lib\Apps; + class Affinity { function widget($arr) { if(! local_channel()) - return ''; - - $default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); - $default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); + return; + + if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) + return; + + $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); + $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin); $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax); - if(feature_enabled(local_channel(),'affinity')) { - - $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); - if ($affinity_locked) { - set_pconfig(local_channel(),'affinity','cmin',$cmin); - set_pconfig(local_channel(),'affinity','cmax',$cmax); - } - - $labels = array( - t('Me'), - t('Family'), - t('Friends'), - t('Acquaintances'), - t('All') - ); - call_hooks('affinity_labels',$labels); - $label_str = ''; - - if($labels) { - foreach($labels as $l) { - if($label_str) { - $label_str .= ", '|'"; - $label_str .= ", '" . $l . "'"; - } - else - $label_str .= "'" . $l . "'"; + $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); + if ($affinity_locked) { + set_pconfig(local_channel(),'affinity','cmin',$cmin); + set_pconfig(local_channel(),'affinity','cmax',$cmax); + } + + $labels = array( + t('Me'), + t('Family'), + t('Friends'), + t('Acquaintances'), + t('All') + ); + call_hooks('affinity_labels',$labels); + + $label_str = ''; + + if($labels) { + foreach($labels as $l) { + if($label_str) { + $label_str .= ", '|'"; + $label_str .= ", '" . $l . "'"; } + else + $label_str .= "'" . $l . "'"; } - - $tpl = get_markup_template('main_slider.tpl'); - $x = replace_macros($tpl,array( - '$val' => $cmin . ',' . $cmax, - '$refresh' => t('Refresh'), - '$labels' => $label_str, - )); - - $arr = array('html' => $x); - call_hooks('main_slider',$arr); - return $arr['html']; } - return ''; + + $tpl = get_markup_template('main_slider.tpl'); + $x = replace_macros($tpl,array( + '$val' => $cmin . ',' . $cmax, + '$refresh' => t('Refresh'), + '$labels' => $label_str, + )); + + $arr = array('html' => $x); + call_hooks('main_slider',$arr); + + return $arr['html']; + + } } diff --git a/Zotlabs/Widget/Settings_menu.php b/Zotlabs/Widget/Settings_menu.php index c537c3835..25b80a4b4 100644 --- a/Zotlabs/Widget/Settings_menu.php +++ b/Zotlabs/Widget/Settings_menu.php @@ -42,19 +42,12 @@ class Settings_menu { ); - $tabs[] = array( 'label' => t('Display settings'), 'url' => z_root().'/settings/display', 'selected' => ((argv(1) === 'display') ? 'active' : ''), ); - $tabs[] = array( - 'label' => t('Addon settings'), - 'url' => z_root().'/settings/featured', - 'selected' => ((argv(1) === 'featured') ? 'active' : ''), - ); - if($hublocs) { $tabs[] = array( 'label' => t('Manage locations'), diff --git a/Zotlabs/Zot/Finger.php b/Zotlabs/Zot/Finger.php index 77634777a..cb38c7f2b 100644 --- a/Zotlabs/Zot/Finger.php +++ b/Zotlabs/Zot/Finger.php @@ -55,7 +55,7 @@ class Finger { $r = q("select xchan.*, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash - where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 limit 1", + where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 and hubloc_network = 'zot' limit 1", dbesc($xchan_addr) ); diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php index e320e7825..8f8957037 100644 --- a/Zotlabs/Zot6/Zot6Handler.php +++ b/Zotlabs/Zot6/Zot6Handler.php @@ -29,7 +29,7 @@ class Zot6Handler implements IHandler { // Implementation of specific methods follows; - // These generally do a small amout of validation and call Libzot + // These generally do a small amout of validation and call Libzot // to do any heavy lifting static function reply_notify($data,$hub) { @@ -40,7 +40,7 @@ class Zot6Handler implements IHandler { $x = Libzot::fetch($data); $ret['delivery_report'] = $x; - + $ret['success'] = true; return $ret; @@ -58,11 +58,11 @@ class Zot6Handler implements IHandler { * * @param array $sender * @param array $recipients + * @param array $hub * - * @return json_return_and_die() + * @return array */ - - static function reply_refresh($sender, $recipients,$hub) { + static function reply_refresh($sender, $recipients, $hub) { $ret = array('success' => false); if($recipients) { @@ -70,19 +70,18 @@ class Zot6Handler implements IHandler { // This would be a permissions update, typically for one connection foreach ($recipients as $recip) { - $r = q("select channel.*,xchan.* from channel left join xchan on channel_portable_id = xchan_hash where xchan_hash ='%s' limit 1", dbesc($recip) ); - + /// @FIXME $msgtype is undefined $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false)); } } else { // system wide refresh - + /// @FIXME $msgtype is undefined $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false)); } @@ -100,17 +99,16 @@ class Zot6Handler implements IHandler { * for that packet. We will create a message_list array of the entire conversation starting with * the missing parent and invoke delivery to the sender of the packet. * - * Zotlabs/Daemon/Deliver.php (for local delivery) and + * Zotlabs/Daemon/Deliver.php (for local delivery) and * mod/post.php???? @fixme (for web delivery) detect the existence of * this 'message_list' at the destination and split it into individual messages which are * processed/delivered in order. * - * * @param array $data + * @param array $hub * @return array */ - - static function reply_message_request($data,$hub) { + static function reply_message_request($data, $hub) { $ret = [ 'success' => false ]; $message_id = EMPTY_STR; @@ -153,11 +151,10 @@ class Zot6Handler implements IHandler { /* * fetch the requested conversation */ - + /// @FIXME $sender_hash is undefined $messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]); return (($messages) ? : [] ); - } static function rekey_request($sender,$data,$hub) { @@ -183,7 +180,7 @@ class Zot6Handler implements IHandler { dbesc($oldhash) ); } - else + else return $ret; @@ -219,10 +216,10 @@ class Zot6Handler implements IHandler { * * @param array $sender * @param array $recipients + * @param array $hub * - * return json_return_and_die() + * @return array */ - static function reply_purge($sender, $recipients, $hub) { $ret = array('success' => false); @@ -259,9 +256,4 @@ class Zot6Handler implements IHandler { return $ret; } - - - - - } diff --git a/app/affinity.apd b/app/affinity.apd new file mode 100644 index 000000000..3860581aa --- /dev/null +++ b/app/affinity.apd @@ -0,0 +1,7 @@ +version: 1 +url: $baseurl/affinity +requires: local_channel +name: Affinity Tool +photo: icon:arrows-h +categories: Networking +desc: This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream. @@ -50,7 +50,7 @@ require_once('include/attach.php'); require_once('include/bbcode.php'); define ( 'PLATFORM_NAME', 'hubzilla' ); -define ( 'STD_VERSION', '3.9.5' ); +define ( 'STD_VERSION', '3.9.6' ); define ( 'ZOT_REVISION', '6.0a' ); define ( 'DB_UPDATE_VERSION', 1230 ); @@ -84,7 +84,8 @@ define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net'); $DIRECTORY_FALLBACK_SERVERS = array( 'https://hubzilla.zottel.net', - 'https://zotadel.net' + 'https://zotadel.net', + 'https://zotsite.net' ); @@ -467,7 +468,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' ); define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' ); -define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.2' ); +define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.3' ); /** * activity stream defines */ @@ -512,6 +513,7 @@ define ( 'ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood' ); define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' ); define ( 'ACTIVITY_OBJ_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' ); define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' ); +define ( 'ACTIVITY_OBJ_ARTICLE', NAMESPACE_ACTIVITY_SCHEMA . 'article' ); define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' ); define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' ); define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' ); @@ -732,11 +734,11 @@ class App { private static $perms = null; // observer permissions private static $widgets = array(); // widgets for this page public static $config = array(); // config cache - public static $override_intltext_templates = array(); - public static $override_markup_templates = array(); - public static $override_templateroot = null; - public static $override_helproot = null; - public static $override_helpfiles = array(); + public static $override_intltext_templates = array(); + public static $override_markup_templates = array(); + public static $override_templateroot = null; + public static $override_helproot = null; + public static $override_helpfiles = array(); public static $session = null; public static $groups; @@ -887,7 +889,7 @@ class App { // removing trailing / - maybe a nginx problem if (substr(self::$query_string, 0, 1) == "/") self::$query_string = substr(self::$query_string, 1); - // change the first & to ? + // change the first & to ? self::$query_string = preg_replace('/&/','?',self::$query_string,1); } @@ -1578,7 +1580,7 @@ function login($register = false, $form_id = 'main-login', $hiddens = false, $lo // Here's the current description of how the register link works (2018-05-15) - // Register links are enabled on the site home page and login page and navbar. + // Register links are enabled on the site home page and login page and navbar. // They are not shown by default on other pages which may require login. // If the register link is enabled and registration is closed, the request is directed @@ -1590,10 +1592,10 @@ function login($register = false, $form_id = 'main-login', $hiddens = false, $lo // system.register_link may or may not be the same destination as system.sellpage - // system.sellpage is the destination linked from the /pubsites page on other sites. If + // system.sellpage is the destination linked from the /pubsites page on other sites. If // system.sellpage is not set, the 'register' link in /pubsites will go to 'register' on your - // site. - + // site. + // If system.register_link is set to the word 'none', no registration link will be shown on // your site. @@ -1832,8 +1834,6 @@ function proc_run(){ $args = func_get_args(); - $newargs = array(); - if(! count($args)) return; @@ -2281,7 +2281,7 @@ function construct_page() { $cspheader = "Content-Security-Policy:"; foreach ($cspsettings as $cspdirective => $csp) { if (!in_array($cspdirective,$validcspdirectives)) { - logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_DEBUG); + logger("INVALID CSP DIRECTIVE: ".$cspdirective,LOGGER_DEBUG); continue; } $cspsettingsarray=array_unique($cspsettings[$cspdirective]); @@ -2400,7 +2400,7 @@ function z_get_temp_dir() { if(! $temp_dir) $temp_dir = sys_get_temp_dir(); - return $upload_dir; + return $temp_dir; } diff --git a/composer.lock b/composer.lock index bd95e8c78..c4fba0975 100644 --- a/composer.lock +++ b/composer.lock @@ -63,16 +63,16 @@ }, { "name": "bshaffer/oauth2-server-php", - "version": "v1.10.0", + "version": "v1.11.1", "source": { "type": "git", "url": "https://github.com/bshaffer/oauth2-server-php.git", - "reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9" + "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/d158878425392fe5a0cc34f15dbaf46315ae0ed9", - "reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9", + "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/5a0c8000d4763b276919e2106f54eddda6bc50fa", + "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa", "shasum": "" }, "require": { @@ -117,7 +117,7 @@ "oauth", "oauth2" ], - "time": "2017-11-15T01:41:02+00:00" + "time": "2018-12-04T00:29:32+00:00" }, { "name": "commerceguys/intl", @@ -251,16 +251,16 @@ }, { "name": "league/html-to-markdown", - "version": "4.8.0", + "version": "4.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/html-to-markdown.git", - "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d" + "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/f9a879a068c68ff47b722de63f58bec79e448f9d", - "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/250d1bf45f80d15594fb6b316df777d6d4c97ad1", + "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1", "shasum": "" }, "require": { @@ -311,7 +311,7 @@ "html", "markdown" ], - "time": "2018-09-18T12:18:08+00:00" + "time": "2018-12-24T17:21:44+00:00" }, { "name": "lukasreschke/id3parser", @@ -1925,32 +1925,33 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "9f83dded91781a01c63574e387eaa769be769115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", + "reference": "9f83dded91781a01c63574e387eaa769be769115", "shasum": "" }, "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -1980,13 +1981,14 @@ "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2018-12-04T20:46:45+00:00" }, { "name": "myclabs/deep-copy", @@ -2571,6 +2573,7 @@ "testing", "xunit" ], + "abandoned": true, "time": "2018-02-07T06:47:59+00:00" }, { @@ -2827,16 +2830,16 @@ }, { "name": "phpunit/phpunit", - "version": "7.4.4", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd" + "reference": "c23d78776ad415d5506e0679723cb461d71f488f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b1be2c8530c4c29c3519a052c9fb6cee55053bbd", - "reference": "b1be2c8530c4c29c3519a052c9fb6cee55053bbd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c23d78776ad415d5506e0679723cb461d71f488f", + "reference": "c23d78776ad415d5506e0679723cb461d71f488f", "shasum": "" }, "require": { @@ -2857,7 +2860,7 @@ "phpunit/php-timer": "^2.0", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", - "sebastian/environment": "^3.1 || ^4.0", + "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", @@ -2881,7 +2884,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.4-dev" + "dev-master": "7.5-dev" } }, "autoload": { @@ -2907,7 +2910,7 @@ "testing", "xunit" ], - "time": "2018-11-14T16:52:02+00:00" + "time": "2018-12-12T07:20:32+00:00" }, { "name": "psr/container", @@ -3009,6 +3012,46 @@ "time": "2016-08-06T14:39:51+00:00" }, { + "name": "ralouphie/getallheaders", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "~3.7.0", + "satooshi/php-coveralls": ">=1.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2016-02-11T07:05:27+00:00" + }, + { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", "source": { @@ -3175,28 +3218,28 @@ }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/febd209a219cea7b56ad799b30ebbea34b71eb8f", + "reference": "febd209a219cea7b56ad799b30ebbea34b71eb8f", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^7.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -3221,7 +3264,7 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "time": "2018-11-25T09:31:21+00:00" }, { "name": "sebastian/exporter", @@ -3573,16 +3616,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd" + "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c55fe9257003b2d95c0211b3f6941e8dfd26dffd", - "reference": "c55fe9257003b2d95c0211b3f6941e8dfd26dffd", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/db7e59fec9c82d45e745eb500e6ede2d96f4a6e9", + "reference": "db7e59fec9c82d45e745eb500e6ede2d96f4a6e9", "shasum": "" }, "require": { @@ -3599,7 +3642,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3626,20 +3669,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:10:45+00:00" + "time": "2018-11-26T11:49:31+00:00" }, { "name": "symfony/class-loader", - "version": "v3.4.18", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "5605edec7b8f034ead2497ff4aab17bb70d558c1" + "reference": "420458095cf60025eb0841276717e0da7f75e50e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/5605edec7b8f034ead2497ff4aab17bb70d558c1", - "reference": "5605edec7b8f034ead2497ff4aab17bb70d558c1", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/420458095cf60025eb0841276717e0da7f75e50e", + "reference": "420458095cf60025eb0841276717e0da7f75e50e", "shasum": "" }, "require": { @@ -3682,20 +3725,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2018-10-31T09:06:03+00:00" + "time": "2018-11-11T19:48:54+00:00" }, { "name": "symfony/config", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "991fec8bbe77367fc8b48ecbaa8a4bd6e905a238" + "reference": "005d9a083d03f588677d15391a716b1ac9b887c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/991fec8bbe77367fc8b48ecbaa8a4bd6e905a238", - "reference": "991fec8bbe77367fc8b48ecbaa8a4bd6e905a238", + "url": "https://api.github.com/repos/symfony/config/zipball/005d9a083d03f588677d15391a716b1ac9b887c0", + "reference": "005d9a083d03f588677d15391a716b1ac9b887c0", "shasum": "" }, "require": { @@ -3718,7 +3761,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3745,24 +3788,25 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-10-31T09:09:42+00:00" + "time": "2018-11-30T22:21:14+00:00" }, { "name": "symfony/console", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595" + "reference": "4dff24e5d01e713818805c1862d2e3f901ee7dd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/432122af37d8cd52fba1b294b11976e0d20df595", - "reference": "432122af37d8cd52fba1b294b11976e0d20df595", + "url": "https://api.github.com/repos/symfony/console/zipball/4dff24e5d01e713818805c1862d2e3f901ee7dd0", + "reference": "4dff24e5d01e713818805c1862d2e3f901ee7dd0", "shasum": "" }, "require": { "php": "^7.1.3", + "symfony/contracts": "^1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -3786,7 +3830,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3813,20 +3857,88 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-10-31T09:30:44+00:00" + "time": "2018-11-27T07:40:44+00:00" + }, + { + "name": "symfony/contracts", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/contracts.git", + "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "psr/cache": "^1.0", + "psr/container": "^1.0" + }, + "suggest": { + "psr/cache": "When using the Cache contracts", + "psr/container": "When using the Service contracts", + "symfony/cache-contracts-implementation": "", + "symfony/service-contracts-implementation": "", + "symfony/translation-contracts-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\": "" + }, + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A set of abstractions extracted out of the Symfony components", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2018-12-05T08:06:11+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.18", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb" + "reference": "345b9a48595d1ab9630db791dbc3e721bf0233e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/3503415d4aafabc31cd08c3a4ebac7f43fde8feb", - "reference": "3503415d4aafabc31cd08c3a4ebac7f43fde8feb", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/345b9a48595d1ab9630db791dbc3e721bf0233e8", + "reference": "345b9a48595d1ab9630db791dbc3e721bf0233e8", "shasum": "" }, "require": { @@ -3866,37 +3978,39 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-10-02T16:33:53+00:00" + "time": "2018-11-11T19:48:54+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "e72ee2c23d952e4c368ee98610fa22b79b89b483" + "reference": "e4adc57a48d3fa7f394edfffa9e954086d7740e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e72ee2c23d952e4c368ee98610fa22b79b89b483", - "reference": "e72ee2c23d952e4c368ee98610fa22b79b89b483", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e4adc57a48d3fa7f394edfffa9e954086d7740e5", + "reference": "e4adc57a48d3fa7f394edfffa9e954086d7740e5", "shasum": "" }, "require": { "php": "^7.1.3", - "psr/container": "^1.0" + "psr/container": "^1.0", + "symfony/contracts": "^1.0" }, "conflict": { - "symfony/config": "<4.1.1", + "symfony/config": "<4.2", "symfony/finder": "<3.4", "symfony/proxy-manager-bridge": "<3.4", "symfony/yaml": "<3.4" }, "provide": { - "psr/container-implementation": "1.0" + "psr/container-implementation": "1.0", + "symfony/service-contracts-implementation": "1.0" }, "require-dev": { - "symfony/config": "~4.1", + "symfony/config": "~4.2", "symfony/expression-language": "~3.4|~4.0", "symfony/yaml": "~3.4|~4.0" }, @@ -3910,7 +4024,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3937,20 +4051,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-10-31T10:54:16+00:00" + "time": "2018-12-02T15:59:36+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "80e60271bb288de2a2259662cff125cff4f93f95" + "reference": "7438a32108fdd555295f443605d6de2cce473159" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/80e60271bb288de2a2259662cff125cff4f93f95", - "reference": "80e60271bb288de2a2259662cff125cff4f93f95", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/7438a32108fdd555295f443605d6de2cce473159", + "reference": "7438a32108fdd555295f443605d6de2cce473159", "shasum": "" }, "require": { @@ -3967,7 +4081,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3994,24 +4108,25 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:40:59+00:00" + "time": "2018-11-26T10:55:26+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "552541dad078c85d9414b09c041ede488b456cd5" + "reference": "921f49c3158a276d27c0d770a5a347a3b718b328" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/552541dad078c85d9414b09c041ede488b456cd5", - "reference": "552541dad078c85d9414b09c041ede488b456cd5", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/921f49c3158a276d27c0d770a5a347a3b718b328", + "reference": "921f49c3158a276d27c0d770a5a347a3b718b328", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1.3", + "symfony/contracts": "^1.0" }, "conflict": { "symfony/dependency-injection": "<3.4" @@ -4030,7 +4145,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -4057,20 +4172,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-10-10T13:52:42+00:00" + "time": "2018-12-01T08:52:38+00:00" }, { "name": "symfony/filesystem", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981" + "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd7bd6535beb1f0a0a9e3ee960666d0598546981", - "reference": "fd7bd6535beb1f0a0a9e3ee960666d0598546981", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2f4c8b999b3b7cadb2a69390b01af70886753710", + "reference": "2f4c8b999b3b7cadb2a69390b01af70886753710", "shasum": "" }, "require": { @@ -4080,7 +4195,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -4107,7 +4222,7 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-10-30T13:18:25+00:00" + "time": "2018-11-11T19:52:12+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -4170,20 +4285,21 @@ }, { "name": "symfony/translation", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c" + "reference": "c0e2191e9bed845946ab3d99767513b56ca7dcd6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", - "reference": "aa04dc1c75b7d3da7bd7003104cd0cfc5dff635c", + "url": "https://api.github.com/repos/symfony/translation/zipball/c0e2191e9bed845946ab3d99767513b56ca7dcd6", + "reference": "c0e2191e9bed845946ab3d99767513b56ca7dcd6", "shasum": "" }, "require": { "php": "^7.1.3", + "symfony/contracts": "^1.0.2", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -4191,6 +4307,9 @@ "symfony/dependency-injection": "<3.4", "symfony/yaml": "<3.4" }, + "provide": { + "symfony/translation-contracts-implementation": "1.0" + }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.4|~4.0", @@ -4208,7 +4327,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -4235,20 +4354,20 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2018-10-28T18:38:52+00:00" + "time": "2018-12-06T10:45:32+00:00" }, { "name": "symfony/yaml", - "version": "v4.1.7", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9" + "reference": "c41175c801e3edfda90f32e292619d10c27103d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/367e689b2fdc19965be435337b50bc8adf2746c9", - "reference": "367e689b2fdc19965be435337b50bc8adf2746c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c41175c801e3edfda90f32e292619d10c27103d7", + "reference": "c41175c801e3edfda90f32e292619d10c27103d7", "shasum": "" }, "require": { @@ -4267,7 +4386,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -4294,7 +4413,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-10-02T16:36:10+00:00" + "time": "2018-11-11T19:52:12+00:00" }, { "name": "theseer/tokenizer", @@ -4338,20 +4457,21 @@ }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -4384,7 +4504,7 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], diff --git a/doc/hook/collect_public_recipients.bb b/doc/hook/collect_public_recipients.bb new file mode 100644 index 000000000..de3f4049e --- /dev/null +++ b/doc/hook/collect_public_recipients.bb @@ -0,0 +1,42 @@ +[h2]collect_public_recipients[/h2] + +Replace the default list of public recipients (i.e., all contacts). + +Allow plugins to create a list of recipients for public messages instead of the default +of all channel connections. + +Called with the following array: + [ + 'recipients' => [], + 'item' => $item, + 'private_envelope' => $private_envelope, + 'include_groups' => $include_groups + ]; + +[code] + if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') { + + $hookinfo = [ + 'recipients' => [], + 'item' => $item, + 'private_envelope' => $private_envelope, + 'include_groups' => $include_groups + ]; + + call_hooks('collect_public_recipients',$hookinfo); + + if ($hookinfo['recipients']) { + $r = $hookinfo['recipients']; + } else { + $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 ", + intval($item['uid']) + ); + } + + if($r) { + + . . . + +[/code] + +see: include/item.php diff --git a/doc/hook/daemon_master_release.bb b/doc/hook/daemon_master_release.bb new file mode 100644 index 000000000..a17216d48 --- /dev/null +++ b/doc/hook/daemon_master_release.bb @@ -0,0 +1,5 @@ +[h2]daemon_master_release[/h2] + +Permit filtering or alternate methods of processing of background processes when [code] \Zotlabs\Daemon\Master::Release() [/code] is called. + +Default behavior is for a new PHP process to fire immediately upon a call to Master::Summon(). This hook permits pre-emption and the ability to provide queuing or other alternatives to this procedure. diff --git a/doc/hook/jot_header_tpl_filter.bb b/doc/hook/jot_header_tpl_filter.bb new file mode 100644 index 000000000..b17d81d03 --- /dev/null +++ b/doc/hook/jot_header_tpl_filter.bb @@ -0,0 +1,5 @@ +[h2]jot_header_tpl_filter[/h2] + +Allows addon developers to modify the values of replacements fed into jot-header.tpl + +cxref: include/conversation.php diff --git a/doc/hook/jot_tpl_filter.bb b/doc/hook/jot_tpl_filter.bb new file mode 100644 index 000000000..426da3c56 --- /dev/null +++ b/doc/hook/jot_tpl_filter.bb @@ -0,0 +1,5 @@ +[h2]jot_tpl_filter[/h2] + +Allows addon developers to alter the macro replacements prior to being fed into jot.tpl + +cxref: include/conversation.php diff --git a/doc/hook/privacygroup_extras.bb b/doc/hook/privacygroup_extras.bb new file mode 100644 index 000000000..bd67f2470 --- /dev/null +++ b/doc/hook/privacygroup_extras.bb @@ -0,0 +1,12 @@ +[h2]privacygroup_extras[/h2] + +Add items to the Privacy Group edit form + +[code] + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(1) ]; + call_hooks ('privacygroup_extras',$hookinfo); + $pgrp_extras = $hookinfo['pgrp_extras']; +[/code] + +see: Zotlabs/Module/Group.php +see: view/tpl/privacy_groups.tpl diff --git a/doc/hook/privacygroup_extras_drop.bb b/doc/hook/privacygroup_extras_drop.bb new file mode 100644 index 000000000..fd27ab255 --- /dev/null +++ b/doc/hook/privacygroup_extras_drop.bb @@ -0,0 +1,11 @@ +[h2]privacygroup_extras_drop[/h2] + +Called after privacy group is dropped + +[code] + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$argv(2) ]; + call_hooks ('privacygroup_extras_drop',$hookinfo); +[/code] + +see: Zotlabs/Module/Group.php +see: view/tpl/privacy_groups.tpl diff --git a/doc/hook/privacygroup_extras_post.bb b/doc/hook/privacygroup_extras_post.bb new file mode 100644 index 000000000..704db1997 --- /dev/null +++ b/doc/hook/privacygroup_extras_post.bb @@ -0,0 +1,11 @@ +[h2]privacygroup_extras_post[/h2] + +Called as privacy group edit form is edited. + +[code] + $hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ]; + call_hooks ('privacygroup_extras_post',$hookinfo); +[/code] + +see: Zotlabs/Module/Group.php +see: view/tpl/privacy_groups.tpl diff --git a/doc/hooklist.bb b/doc/hooklist.bb index 08fc587e2..5a804c819 100644 --- a/doc/hooklist.bb +++ b/doc/hooklist.bb @@ -136,6 +136,9 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the [zrl=[baseurl]/help/hook/check_siteallowed]check_siteallowed[/zrl] Used to over-ride or bypass the site black/white block lists +[zrl=[baseurl]/help/hook/collect_public_recipients]collect_public_recipients[/zrl] + Used to establish a list of recipients to send a public message to. + [zrl=[baseurl]/help/hook/comment_buttons]comment_buttons[/zrl] Called when rendering the edit buttons for comments @@ -190,6 +193,9 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the [zrl=[baseurl]/help/hook/daemon_addon]daemon_addon[/zrl] Called when invoking the extensible background daemon +[zrl=[baseurl]/help/hook/daemon_master_release]daemon_master_release[/zrl] + Called at the start of processing \Zotlabs\Daemon\Master::Release() + [zrl=[baseurl]/help/hook/directory_item]directory_item[/zrl] Called when generating a directory listing for display @@ -364,6 +370,12 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the [zrl=[baseurl]/help/hook/jot_tool]jot_tool[/zrl] Deprecated and possibly obsolete. Allows one to add action buttons to the post editor. +[zrl=[baseurl]/help/hook/jot_tpl_filter]jot_tpl_filter[/zrl] + Called to filter template vars before replacement in jot.tpl. + +[zrl=[baseurl]/help/hook/jot_header_tpl_filter]jot_header_tpl_filter[/zrl] + Called to filter template vars before replacement in jot_header.tpl. + [zrl=[baseurl]/help/hook/legal_webbie]legal_webbie[/zrl] Called to validate a channel address @@ -553,6 +565,15 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the [zrl=[baseurl]/help/hook/prepare_body_init]prepare_body_init[/zrl] Called before generating the HTML for a displayed conversation item +[zrl=[baseurl]/help/hook/privacygroup_extras]privacygroup_extras[/zrl] + Called before generating the HTML for the Privacy Group edit options + +[zrl=[baseurl]/help/hook/privacygroup_extras_delete]privacygroup_extras_delete[/zrl] + Called after privacy group is dropped. + +[zrl=[baseurl]/help/hook/privacygroup_extras_post]privacygroup_extras_post[/zrl] + Called when privacy group edit form is submitted. + [zrl=[baseurl]/help/hook/proc_run]proc_run[/zrl] Called when invoking PHP sub processes diff --git a/include/attach.php b/include/attach.php index dd718aa14..17a47d9ac 100644 --- a/include/attach.php +++ b/include/attach.php @@ -137,7 +137,7 @@ function z_mime_content_type($filename) { * @param string $hash (optional) * @param string $filename (optional) * @param string $filetype (optional) - * @return associative array with: + * @return array Associative array with: * * \e boolean \b success * * \e int|boolean \b results amount of found results, or false * * \e string \b message with error messages if any @@ -176,15 +176,17 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '', /** * @brief Returns a list of files/attachments. * - * @param $channel_id - * @param $observer - * @param $hash (optional) - * @param $filename (optional) - * @param $filetype (optional) - * @param $orderby - * @param $start - * @param $entries - * @return associative array with: + * @param int $channel_id + * @param string $observer + * @param string $hash (optional) + * @param string $filename (optional) + * @param string $filetype (optional) + * @param string $orderby (optional) + * @param int $start (optional) + * @param int $entries (optional) + * @param string $since (optional) + * @param string $until (optional) + * @return array an associative array with: * * \e boolean \b success * * \e array|boolean \b results array with results, or false * * \e string \b message with error messages if any @@ -1428,8 +1430,17 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { if(! $r) { attach_drop_photo($channel_id,$resource); - $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo'=>$is_photo]; - call_hooks("attach_delete",$arr); + $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; + + /** + * @hooks attach_delete + * Called when deleting an attachment from channel. + * * \e int \b channel_id - the channel_id + * * \e string \b resource + * * \e int \b is_photo + */ + call_hooks('attach_delete', $arr); + return; } @@ -1488,8 +1499,15 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { intval($channel_id) ); - $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo'=>$is_photo]; - call_hooks("attach_delete",$arr); + $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; + /** + * @hooks attach_delete + * Called when deleting an attachment from channel. + * * \e int \b channel_id - the channel_id + * * \e string \b resource + * * \e int \b is_photo + */ + call_hooks('attach_delete', $arr); file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); @@ -1868,7 +1886,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, * @param int $channel_id * @param string $hash * @param string $url - * @return array An associative array for the specified file. + * @return array Associative array for the specified file. */ function get_file_activity_object($channel_id, $hash, $url) { @@ -2110,7 +2128,7 @@ function attach_export_data($channel, $resource_id, $deleted = false) { if($attach_ptr['is_photo']) { - // This query could potentially result in a few megabytes of data use. + // This query could potentially result in a few megabytes of data use. $r = q("select * from photo where resource_id = '%s' and uid = %d order by imgscale asc", dbesc($resource_id), @@ -2352,7 +2370,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { $filename = $r[0]['filename']; - // don't do duplicate check unless our parent folder has changed. + // don't do duplicate check unless our parent folder has changed. if($r[0]['folder'] !== $new_folder_hash) { @@ -2468,7 +2486,7 @@ function attach_move($channel_id, $resource_id, $new_folder_hash) { /** - * Used to generate a select input box of all your folders + * Used to generate a select input box of all your folders */ @@ -2551,10 +2569,10 @@ function attach_syspaths($channel_id,$attach_hash) { /** * in earlier releases we did not fill in os_path and display_path in the attach DB structure. - * (It was not needed or used). Going forward we intend to make use of these fields. + * (It was not needed or used). Going forward we intend to make use of these fields. * A cron task checks for empty values (as older attachments may have arrived at our site - * in a clone operation) and executes attach_syspaths() to generate these field values and correct - * the attach table entry. The operation is limited to 100 DB entries at a time so as not to + * in a clone operation) and executes attach_syspaths() to generate these field values and correct + * the attach table entry. The operation is limited to 100 DB entries at a time so as not to * overload the system in any cron run. Eventually it will catch up with old attach structures * and switch into maintenance mode to correct any that might arrive in clone packets from older * sites. diff --git a/include/bbcode.php b/include/bbcode.php index 817986da0..7531bd774 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -326,22 +326,11 @@ function bb_ShareAttributes($match) { $auth = is_matrix_url($profile); } - // message_id is never used, do we still need it? - $message_id = ""; - preg_match("/message_id='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; - - if(! $message_id) { - preg_match("/guid='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") - $message_id = $matches[1]; - } - + $rnd = mt_rand(); $reldate = '<span class="autotime" title="' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'c') . '" >' . datetime_convert('UTC', date_default_timezone_get(), $posted, 'r') . '</span>'; - $headline = '<div class="shared_container"> <div class="shared_header">'; + $headline = '<div id="shared_container_' . $rnd . '" class="shared_container"> <div id="shared_header_' . $rnd . '" class="shared_header">'; if ($avatar != "") $headline .= '<a href="' . (($auth) ? zid($profile) : $profile) . '" ><img src="' . $avatar . '" alt="' . $author . '" height="32" width="32" /></a>'; @@ -363,7 +352,7 @@ function bb_ShareAttributes($match) { $headline .= '<span>' . $fmt . '</span></div>'; - $text = $headline . '<div class="reshared-content">' . trim($match[2]) . '</div></div>'; + $text = $headline . '<div id="reshared-content-' . $rnd . '" class="reshared-content">' . trim($match[2]) . '</div></div>'; return $text; } diff --git a/include/contact_widgets.php b/include/contact_widgets.php index a105bca19..6ad276b00 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -7,14 +7,14 @@ function findpeople_widget() { if(get_config('system','invitation_only')) { $x = get_pconfig(local_channel(),'system','invites_remaining'); if($x || is_site_admin()) { - App::$page['aside'] .= '<div class="side-link" id="side-invite-remain">' - . sprintf( tt('%d invitation available','%d invitations available',$x), $x) - . '</div>' . $inv; + App::$page['aside'] .= '<div class="side-link" id="side-invite-remain">' + . sprintf( tt('%d invitation available','%d invitations available',$x), $x) + . '</div>'; } } $advanced_search = ((local_channel() && feature_enabled(local_channel(),'advanced_dirsearch')) ? t('Advanced') : false); - + return replace_macros(get_markup_template('peoplefind.tpl'),array( '$findpeople' => t('Find Channels'), '$desc' => t('Enter name or interest'), @@ -22,7 +22,7 @@ function findpeople_widget() { '$hint' => t('Examples: Robert Morgenstein, Fishing'), '$findthem' => t('Find'), '$suggest' => t('Channel Suggestions'), - '$similar' => '', // FIXME and uncomment when mod/match working // t('Similar Interests'), + '$similar' => '', /// @FIXME fixme and uncomment when mod/match working // t('Similar Interests'), '$random' => t('Random Profile'), '$inv' => t('Invite Friends'), '$advanced_search' => $advanced_search, @@ -56,12 +56,11 @@ function fileas_widget($baseurl,$selected = '') { '$all' => t('Everything'), '$terms' => $terms, '$base' => $baseurl, - )); } function categories_widget($baseurl,$selected = '') { - + if(! feature_enabled(App::$profile['profile_uid'],'categories')) return ''; @@ -100,14 +99,13 @@ function categories_widget($baseurl,$selected = '') { '$all' => t('Everything'), '$terms' => $terms, '$base' => $baseurl, - )); } return ''; } function cardcategories_widget($baseurl,$selected = '') { - + if(! feature_enabled(App::$profile['profile_uid'],'categories')) return ''; @@ -128,7 +126,7 @@ function cardcategories_widget($baseurl,$selected = '') { $item_normal $sql_extra order by term.term asc", - intval(App::$profile['profile_uid']), + intval(App::$profile['profile_uid']), intval(TERM_CATEGORY), intval(TERM_OBJ_POST), dbesc(App::$profile['channel_hash']) @@ -144,15 +142,15 @@ function cardcategories_widget($baseurl,$selected = '') { '$all' => t('Everything'), '$terms' => $terms, '$base' => $baseurl, - )); } + return ''; } function articlecategories_widget($baseurl,$selected = '') { - + if(! feature_enabled(App::$profile['profile_uid'],'categories')) return ''; @@ -173,7 +171,7 @@ function articlecategories_widget($baseurl,$selected = '') { $item_normal $sql_extra order by term.term asc", - intval(App::$profile['profile_uid']), + intval(App::$profile['profile_uid']), intval(TERM_CATEGORY), intval(TERM_OBJ_POST), dbesc(App::$profile['channel_hash']) @@ -189,17 +187,14 @@ function articlecategories_widget($baseurl,$selected = '') { '$all' => t('Everything'), '$terms' => $terms, '$base' => $baseurl, - )); } + return ''; } - - - function common_friends_visitor_widget($profile_uid,$cnt = 25) { if(local_channel() == $profile_uid) @@ -218,17 +213,14 @@ function common_friends_visitor_widget($profile_uid,$cnt = 25) { return; $r = common_friends($profile_uid,$observer_hash,0,$cnt,true); - - return replace_macros(get_markup_template('remote_friends_common.tpl'), array( + + return replace_macros(get_markup_template('remote_friends_common.tpl'), [ '$desc' => t('Common Connections'), '$base' => z_root(), '$uid' => $profile_uid, - '$cid' => $observer, '$linkmore' => (($t > $cnt) ? 'true' : ''), '$more' => sprintf( t('View all %d common connections'), $t), - '$items' => $r - )); + '$items' => $r, + ]); }; - - diff --git a/include/conversation.php b/include/conversation.php index 041994b90..e2dd02ffc 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1328,7 +1328,7 @@ function hz_status_editor($a, $x, $popup = false) { $tpl = get_markup_template('jot-header.tpl'); - App::$page['htmlhead'] .= replace_macros($tpl, array( + $tplmacros = [ '$baseurl' => z_root(), '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), '$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''), @@ -1349,7 +1349,10 @@ function hz_status_editor($a, $x, $popup = false) { '$nocomment_disabled' => t('Comments disabled'), '$auto_save_draft' => $feature_auto_save_draft, '$reset' => $reset - )); + ]; + + call_hooks('jot_header_tpl_filter',$tplmacros); + App::$page['htmlhead'] .= replace_macros($tpl, $tplmacros); $tpl = get_markup_template('jot.tpl'); @@ -1389,7 +1392,7 @@ function hz_status_editor($a, $x, $popup = false) { $sharebutton = (x($x,'button') ? $x['button'] : t('Share')); $placeholdtext = (x($x,'content_label') ? $x['content_label'] : $sharebutton); - $o .= replace_macros($tpl, array( + $tplmacros = [ '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), '$action' => z_root() . '/item', '$share' => $sharebutton, @@ -1463,9 +1466,15 @@ function hz_status_editor($a, $x, $popup = false) { '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0), '$reset' => $reset, - '$is_owner' => ((local_channel() && (local_channel() == $x['profile_uid'])) ? true : false) - )); + '$is_owner' => ((local_channel() && (local_channel() == $x['profile_uid'])) ? true : false), + '$custommoretoolsdropdown' => '', + '$custommoretoolsbuttons' => '', + '$customsubmitright' => [] + ]; + + call_hooks('jot_tpl_filter',$tplmacros); + $o .= replace_macros($tpl, $tplmacros); if ($popup === true) { $o = '<div id="jot-popup" style="display:none">' . $o . '</div>'; } diff --git a/include/event.php b/include/event.php index a34250e7a..fdb9e1415 100644 --- a/include/event.php +++ b/include/event.php @@ -6,6 +6,10 @@ use Sabre\VObject; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\Exception\UnsatisfiedDependencyException; + + require_once('include/bbcode.php'); /** @@ -463,8 +467,13 @@ function event_store_event($arr) { $hash = $arr['external_id']; elseif(array_key_exists('event_hash',$arr)) $hash = $arr['event_hash']; - else - $hash = random_string() . '@' . App::get_hostname(); + else { + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } + } $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, event_vdata, allow_cid,allow_gid,deny_cid,deny_gid) @@ -1126,8 +1135,8 @@ function event_store_item($arr, $event) { } if(! $arr['mid']) { - $arr['uuid'] = item_message_id(); - $arr['mid'] = z_root() . '/item/' . $arr['uuid']; + $arr['uuid'] = $event['event_hash']; + $arr['mid'] = z_root() . '/event/' . $event['event_hash']; } $item_arr['aid'] = $z[0]['channel_account_id']; diff --git a/include/features.php b/include/features.php index 05ce3db32..35a4c0dd4 100644 --- a/include/features.php +++ b/include/features.php @@ -361,14 +361,6 @@ function get_features($filtered = true, $level = (-1)) { ], [ - 'affinity', - t('Affinity Tool'), - t('Filter stream activity by depth of relationships'), - false, - get_config('feature_lock','affinity') - ], - - [ 'suggest', t('Suggest Channels'), t('Show friend and connection suggestions'), diff --git a/include/feedutils.php b/include/feedutils.php index afbe4229e..5e52828c3 100644 --- a/include/feedutils.php +++ b/include/feedutils.php @@ -261,13 +261,13 @@ function construct_activity_target($item) { * @param SimplePie $item * @return array $author */ - function get_atom_author($feed, $item) { $author = []; $found_author = $item->get_author(); if($found_author) { + /// @FIXME $rawauthor is undefined here if($rawauthor) { if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); @@ -398,10 +398,10 @@ function get_atom_author($feed, $item) { 'author' => $author ]; /** - * @hooks parse_atom + * @hooks parse_atom_author * * \e SimplePie \b feed - The original SimplePie feed * * \e SimplePie \b item - * * \e array \b result - the result array that will also get returned + * * \e array \b author - the result array that will also get returned */ call_hooks('parse_atom_author', $arr); @@ -416,10 +416,8 @@ function get_atom_author($feed, $item) { * * @param SimplePie $feed * @param SimplePie $item - * @param[out] array $author * @return array Associative array with the parsed item data */ - function get_atom_elements($feed, $item) { require_once('include/html2bbcode.php'); @@ -669,10 +667,10 @@ function get_atom_elements($feed, $item) { $termterm = notags(trim(unxmlify($term))); // Mastodon auto generates an nsfw category tag for any 'content-warning' message. - // Most people use CW and use both summary/content as a spoiler and we honour that - // construct so the post will already be collapsed. The generated tag is almost + // Most people use CW and use both summary/content as a spoiler and we honour that + // construct so the post will already be collapsed. The generated tag is almost // always wrong and even if it isn't we would already be doing the right thing. - + if($mastodon && $termterm === 'nsfw' && $summary && $res['body']) continue; @@ -1336,7 +1334,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { if( ! \Zotlabs\Lib\MessageFilter::evaluate($datarray,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { continue; } - } + } if(! post_is_importable($datarray, $contact)) continue; @@ -1492,7 +1490,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { if( ! \Zotlabs\Lib\MessageFilter::evaluate($datarray,get_config('system','pubstream_incl'),get_config('system','pubstream_excl'))) { continue; } - } + } if(! post_is_importable($datarray, $contact)) continue; @@ -1900,7 +1898,7 @@ function atom_entry($item, $type, $author, $owner, $comment = false, $cid = 0, $ $body = $item['body']; - if($summary) + if($summary) $body = preg_replace('|^(.*?)\[summary\](.*?)\[/summary\](.*?)$|ism','$1$3',$item['body']); if($compat) diff --git a/include/follow.php b/include/follow.php index 038e6e9c0..db77a0160 100644 --- a/include/follow.php +++ b/include/follow.php @@ -190,7 +190,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) return $result; } - $allowed = (($is_zot || $r[0]['xchan_network'] === 'rss') ? 1 : 0); + $allowed = (($is_zot || in_array($r[0]['xchan_network'],['rss','zot6'])) ? 1 : 0); $x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => $allowed, 'singleton' => 0); diff --git a/include/help.php b/include/help.php index f2aa4add3..e82fa96da 100644 --- a/include/help.php +++ b/include/help.php @@ -7,17 +7,18 @@ use \Michelf\MarkdownExtra; * @brief * * @param string $path - * @return string|unknown + * @param string $suffix (optional) default null + * @return string */ -function get_help_fullpath($path,$suffix=null) { +function get_help_fullpath($path, $suffix = null) { $docroot = (\App::$override_helproot) ? \App::$override_helproot : 'doc/'; - $docroot = (substr($docroot,-1)!='/') ? $docroot .= '/' : $docroot; + $docroot = (substr($docroot,-1)!='/') ? $docroot .= '/' : $docroot; // Determine the language and modify the path accordingly $x = determine_help_language(); $lang = $x['language']; - $url_idx = ($x['from_url'] ? 1 : 0); + // The English translation is at the root of /doc/. Other languages are in // subfolders named by the language code such as "de", "es", etc. if($lang !== 'en') { @@ -49,19 +50,18 @@ function get_help_fullpath($path,$suffix=null) { /** * @brief * - * @param string $tocpath - * @return string|unknown + * @param string $tocpath (optional) default false + * @return string */ function get_help_content($tocpath = false) { - global $lang; $doctype = 'markdown'; $text = ''; $path = (($tocpath !== false) ? $tocpath : ''); - $docroot = (\App::$override_helproot) ? \App::$override_helproot : 'doc/'; - $docroot = (substr($docroot,-1)!='/') ? $docroot .= '/' : $docroot; + $docroot = (\App::$override_helproot) ? \App::$override_helproot : 'doc/'; + $docroot = (substr($docroot,-1)!='/') ? $docroot .= '/' : $docroot; if($tocpath === false && argc() > 1) { $path = ''; @@ -74,7 +74,7 @@ function get_help_content($tocpath = false) { if($path) { - $fullpath = get_help_fullpath($path); + $fullpath = get_help_fullpath($path); $title = basename($path); if(! $tocpath) \App::$page['title'] = t('Help:') . ' ' . ucwords(str_replace('-',' ',notags($title))); @@ -88,10 +88,10 @@ function get_help_content($tocpath = false) { load_doc_file($fullpath . '.md') === '' && load_doc_file($fullpath . '.bb') === '' && load_doc_file($fullpath . '.html') === '' - ) { + ) { $path = $title; } - $fullpath = get_help_fullpath($path); + $fullpath = get_help_fullpath($path); $text = load_doc_file($fullpath . '.md'); if(! $text) { @@ -111,15 +111,15 @@ function get_help_content($tocpath = false) { if($tocpath === false) { if(! $text) { - $path = 'Site'; - $fullpath = get_help_fullpath($path,'.md'); + $path = 'Site'; + $fullpath = get_help_fullpath($path,'.md'); $text = load_doc_file($fullpath . '.md'); \App::$page['title'] = t('Help'); } if(! $text) { $doctype = 'bbcode'; - $path = 'main'; - $fullpath = get_help_fullpath($path,'.md'); + $path = 'main'; + $fullpath = get_help_fullpath($path,'.md'); $text = load_doc_file($fullpath . '.bb'); goaway('/help/about/about'); \App::$page['title'] = t('Help'); @@ -172,16 +172,20 @@ function preg_callback_help_include($matches) { } /** - * @brief + * @brief Determines help language. + * + * If the language was specified in the URL, override the language preference + * of the browser. Default to English if both of these are absent. * - * @return boolean|array + * @return array Associative array with: + * * \e string \b language - 2-letter ISO 639-1 code ("en") + * * \e boolean \b from_url - true if language from URL overrides browser default */ function determine_help_language() { $lang_detect = new Text_LanguageDetect(); // Set this mode to recognize language by the short code like "en", "ru", etc. $lang_detect->setNameMode(2); - // If the language was specified in the URL, override the language preference - // of the browser. Default to English if both of these are absent. + if($lang_detect->languageExists(argv(1))) { $lang = argv(1); $from_url = true; @@ -212,14 +216,13 @@ function find_doc_file($s) { } /** - * @brief + * @brief Search in doc files. * - * @param string $s - * @return number|mixed|unknown|boolean + * @param string $s The search string to search for + * @return array */ function search_doc_files($s) { - \App::set_pager_itemspage(60); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); @@ -277,7 +280,6 @@ function doc_rank_sort($s1, $s2) { * * @return string */ - function load_context_help() { $path = App::$cmd; @@ -307,7 +309,7 @@ function load_context_help() { * @brief * * @param string $s - * @return void|boolean[]|number[]|string[]|unknown[] + * @return void|array */ function store_doc_file($s) { @@ -351,7 +353,7 @@ function store_doc_file($s) { $x = item_store_update($item); } else { - $item['uuid'] = $item_message_id(); + $item['uuid'] = item_message_id(); $item['mid'] = $item['parent_mid'] = z_root() . '/item/' . $item['uuid']; $x = item_store($item); } diff --git a/include/import.php b/include/import.php index 53b21c317..f391400bd 100644 --- a/include/import.php +++ b/include/import.php @@ -12,6 +12,7 @@ require_once('include/perm_upgrade.php'); * @param array $channel * @param int $account_id * @param int $seize + * @param string $newname (optional) * @return boolean|array */ function import_channel($channel, $account_id, $seize, $newname = '') { @@ -650,7 +651,7 @@ function import_items($channel, $items, $sync = false, $relocate = null) { // preserve conversations you've been involved in from being expired $stored = $item_result['item']; - if((is_array($stored)) && ($stored['id'] != $stored['parent']) + if((is_array($stored)) && ($stored['id'] != $stored['parent']) && ($stored['author_xchan'] === $channel['channel_hash'])) { retain_item($stored['item']['parent']); } @@ -672,7 +673,7 @@ function import_items($channel, $items, $sync = false, $relocate = null) { /** * @brief Sync items to channel. * - * @see import_items + * @see import_items() * * @param array $channel where to import to * @param array $items @@ -1035,8 +1036,9 @@ function import_mail($channel, $mails, $sync = false) { if(! $m) continue; - $m['aid'] = $channel['channel_account_id']; - $m['uid'] = $channel['channel_id']; + $m['account_id'] = $channel['channel_account_id']; + $m['channel_id'] = $channel['channel_id']; + $mail_id = mail_store($m); if($sync && $mail_id) { Zotlabs\Daemon\Master::Summon(array('Notifier','single_mail',$mail_id)); @@ -1048,7 +1050,7 @@ function import_mail($channel, $mails, $sync = false) { /** * @brief Synchronise mails. * - * @see import_mail + * @see import_mail() * @param array $channel * @param array $mails */ @@ -1336,7 +1338,7 @@ function sync_files($channel, $files) { if($str) $str .= ","; - + $str .= " " . TQUOT . $k . TQUOT . " = '" . (($k === 'content') ? dbescbin($v) : dbesc($v)) . "' "; } $r = dbq("update photo set " . $str . " where id = " . intval($exists[0]['id']) ); diff --git a/include/items.php b/include/items.php index 02d31fcb5..a14e3db3a 100755 --- a/include/items.php +++ b/include/items.php @@ -7,6 +7,7 @@ use Zotlabs\Lib\Enotify; use Zotlabs\Lib\MarkdownSoap; use Zotlabs\Lib\MessageFilter; +use Zotlabs\Lib\ThreadListener; use Zotlabs\Lib\IConfig; use Zotlabs\Access\PermissionLimits; use Zotlabs\Access\AccessList; @@ -25,7 +26,7 @@ require_once('include/permissions.php'); * * @param array $item * @param[out] boolean $private_envelope - * @param boolean $include_groups + * @param boolean $include_groups * @return array containing the recipients */ function collect_recipients($item, &$private_envelope,$include_groups = true) { @@ -95,9 +96,24 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) { //$sys = get_sys_channel(); if(array_key_exists('public_policy',$item) && $item['public_policy'] !== 'self') { - $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 ", + + $hookinfo = [ + 'recipients' => [], + 'item' => $item, + 'private_envelope' => $private_envelope, + 'include_groups' => $include_groups + ]; + + call_hooks('collect_public_recipients',$hookinfo); + + if ($hookinfo['recipients']) { + $r = $hookinfo['recipients']; + } else { + $r = q("select abook_xchan, xchan_network from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and abook_pending = 0 and abook_archived = 0 ", intval($item['uid']) - ); + ); + } + if($r) { // filter out restrictive public_policy settings from remote networks @@ -126,6 +142,22 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) { // $recipients[] = $sys['xchan_hash']; } + + // Forward to thread listeners, *unless* there is even a remote hint that the item + // might have some privacy attached. This could be (for instance) an ActivityPub DM + // in the middle of a public thread. Unless we can guarantee beyond all doubt that + // this is public, don't allow it to go to thread listeners. + + if(! intval($item['item_private'])) { + $r = ThreadListener::fetch_by_target($item['parent_mid']); + if($r) { + foreach($r as $rv) { + $recipients[] = $rv['portable_id']; + } + } + } + + // Add the authors of any posts in this thread, if they are known to us. // This is specifically designed to forward wall-to-wall posts to the original author, // in case they aren't a connection but have permission to write on our wall. @@ -395,7 +427,7 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) { if(! $arr['mid']) { - $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : item_message_id()); + $arr['uuid'] = ((x($arr,'uuid')) ? $arr['uuid'] : item_message_id()); } $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : z_root() . '/item/' . $arr['uuid']); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); @@ -2033,6 +2065,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) { item_update_parent_commented($arr); + + if(strpos($arr['body'],'[embed]') !== false) { + Master::Summon([ 'Cache_embeds', $current_post ]); + } + // If _creating_ a deleted item, don't propagate it further or send out notifications. // We need to store the item details just in case the delete came in before the original post, // so that we have an item in the DB that's marked deleted and won't store a fresh post @@ -2369,6 +2406,13 @@ function item_store_update($arr, $allow_exec = false, $deliver = true) { */ call_hooks('item_stored_update',$arr); + if(strpos($arr['body'],'[embed]') !== false) { + Master::Summon([ 'Cache_embeds', $orig_post_id ]); + } + + + + if($deliver) { send_status_notifications($orig_post_id,$arr); tag_deliver($uid,$orig_post_id); @@ -2385,15 +2429,15 @@ function item_update_parent_commented($item) { $update_parent = true; - // update the commented timestamp on the parent + // update the commented timestamp on the parent // - unless this is a moderated comment or a potential clone of an older item - // which we don't wish to bring to the surface. As the queue only holds deliveries - // for 3 days, it's suspected of being an older cloned item if the creation time + // which we don't wish to bring to the surface. As the queue only holds deliveries + // for 3 days, it's suspected of being an older cloned item if the creation time //is older than that. if(intval($item['item_blocked']) === ITEM_MODERATED) $update_parent = false; - + if($item['created'] < datetime_convert('','','now - 4 days')) $update_parent = false; @@ -2989,7 +3033,9 @@ function tgroup_check($uid, $item) { * @param array $channel * @param array $item * @param int $item_id - * @param boolean $parent + * @param array $parent + * @param boolean $edit (optional) default false + * @return void */ function start_delivery_chain($channel, $item, $item_id, $parent, $edit = false) { @@ -3024,7 +3070,7 @@ function start_delivery_chain($channel, $item, $item_id, $parent, $edit = false) } // This will change the author to the post owner. Useful for RSS feeds which are to be syndicated - // to federated platforms which can't verify the identity of the author. + // to federated platforms which can't verify the identity of the author. // This MAY cause you to run afoul of copyright law. $rewrite_author = intval(get_abconfig($channel['channel_id'],$item['owner_xchan'],'system','rself')); @@ -3537,7 +3583,7 @@ function item_expire($uid,$days,$comment_days = 7) { if(! $comment_days) $comment_days = 7; - + // $expire_network_only = save your own wall posts // and just expire conversations started by others // do not enable this until we can pass bulk delete messages through zot @@ -3838,6 +3884,8 @@ function delete_item_lowlevel($item, $stage = DROPITEM_NORMAL, $force = false) { intval(TERM_OBJ_POST) ); + ThreadListener::delete_by_target($item['mid']); + /** @FIXME remove notifications for this item */ return true; @@ -4532,7 +4580,7 @@ function set_linkified_perms($linkified, &$str_contact_allow, &$str_group_allow, $first_access_tag = true; foreach($linkified as $x) { - $access_tag = $x['access_tag']; + $access_tag = $x['success']['access_tag']; if(($access_tag) && (! $parent_item)) { logger('access_tag: ' . $tag . ' ' . print_r($access_tag,true), LOGGER_DATA); if ($first_access_tag && (! get_pconfig($profile_uid,'system','no_private_mention_acl_override'))) { @@ -4877,7 +4925,7 @@ function copy_of_pubitem($channel,$mid) { dbesc($mid), intval($syschan['channel_id']) ); - + if($r) { $items = fetch_post_tags($r,true); foreach($items as $rv) { @@ -4903,5 +4951,5 @@ function copy_of_pubitem($channel,$mid) { } } - return $result; + return $result; } diff --git a/include/message.php b/include/message.php index 936c01631..037c59c60 100644 --- a/include/message.php +++ b/include/message.php @@ -44,7 +44,7 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep $body = cleanup_bbcode($body); - $results = linkify_tags($a, $body, $uid); + $results = linkify_tags($body, $uid); if(! $raw) { if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) { diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 9aeb2ef17..5c8ed9bdc 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -482,7 +482,6 @@ function guess_image_type($filename, $headers = '') { // logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG); $type = null; if ($headers) { - $hdrs=array(); $h = explode("\n",$headers); foreach ($h as $l) { @@ -490,11 +489,16 @@ function guess_image_type($filename, $headers = '') { $hdrs[strtolower($k)] = $v; } logger('Curl headers: '.var_export($hdrs, true), LOGGER_DEBUG); - if (array_key_exists('content-type', $hdrs)) - $type = $hdrs['content-type']; + if (array_key_exists('content-type', $hdrs)) { + $ph = photo_factory(''); + $types = $ph->supportedTypes(); + + if(array_key_exists($hdrs['content-type'], $types)) + $type = $hdrs['content-type']; + } } - if (is_null($type)){ + if (is_null($type)){ $ignore_imagick = get_config('system', 'ignore_imagick'); // Guessing from extension? Isn't that... dangerous? if(class_exists('Imagick') && file_exists($filename) && is_readable($filename) && !$ignore_imagick) { @@ -638,7 +642,6 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) { $img_str = $result['body']; $type = guess_image_type($photo, $result['header']); $modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $o) ? strtotime($o[1] . 'Z') : time())); - if(is_null($type)) $photo_failure = true; } diff --git a/include/photos.php b/include/photos.php index ae51703e0..44406e0b0 100644 --- a/include/photos.php +++ b/include/photos.php @@ -356,7 +356,7 @@ function photo_upload($channel, $observer, $args) { $large_photos = feature_enabled($channel['channel_id'], 'large_photos'); - linkify_tags($a, $args['body'], $channel_id); + linkify_tags($args['body'], $channel_id); if($large_photos) { $scale = 1; diff --git a/include/plugin.php b/include/plugin.php index 7eeb39ce8..c789ad522 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -7,13 +7,15 @@ /** - * @brief Handle errors in plugin calls + * @brief Handle errors in plugin calls. * * @param string $plugin name of the addon - * @param string $error_text text of error - * @param bool $uninstall uninstall plugin + * @param string $notice UI visible text of error + * @param string $log technical error message for logging + * @param bool $uninstall (optional) default false + * uninstall plugin on error */ -function handleerrors_plugin($plugin,$notice,$log,$uninstall=false){ +function handleerrors_plugin($plugin, $notice, $log, $uninstall = false){ logger("Addons: [" . $plugin . "] Error: ".$log, LOGGER_ERROR); if ($notice != '') { notice("[" . $plugin . "] Error: ".$notice, LOGGER_ERROR); @@ -23,7 +25,7 @@ function handleerrors_plugin($plugin,$notice,$log,$uninstall=false){ $idx = array_search($plugin, \App::$plugins); unset(\App::$plugins[$idx]); uninstall_plugin($plugin); - set_config("system","addon", implode(", ",\App::$plugins)); + set_config("system", "addon", implode(", ", \App::$plugins)); } } @@ -213,8 +215,8 @@ function reload_plugins() { try { $func(); } catch (Exception $e) { - handleerrors_plugin($plugin,"","UNLOAD FAILED (uninstalling) : ".$e->getMessage(),true); - continue; + handleerrors_plugin($pl, '', 'UNLOAD FAILED (uninstalling) : ' . $e->getMessage(),true); + continue; } } if(function_exists($pl . '_load')) { @@ -222,8 +224,8 @@ function reload_plugins() { try { $func(); } catch (Exception $e) { - handleerrors_plugin($plugin,"","LOAD FAILED (uninstalling): ".$e->getMessage(),true); - continue; + handleerrors_plugin($pl, '', 'LOAD FAILED (uninstalling): ' . $e->getMessage(),true); + continue; } } q("UPDATE addon SET tstamp = %d WHERE id = %d", @@ -305,7 +307,7 @@ function plugins_sync() { * @return array */ function visible_plugin_list() { - + $r = q("select * from addon where hidden = 0 order by aname asc"); $x = (($r) ? ids_to_array($r,'aname') : array()); $y = []; @@ -315,7 +317,7 @@ function visible_plugin_list() { $y[] = $xv; } } - } + } return $y; } @@ -381,8 +383,6 @@ function unregister_hook($hook, $file, $function) { * array in their theme_init() and use this to customise the app behaviour. * use insert_hook($hookname,$function_name) to do this. */ - - function load_hooks() { App::$hooks = []; @@ -456,21 +456,21 @@ function insert_hook($hook, $fn, $version = 0, $priority = 0) { function call_hooks($name, &$data = null) { $a = 0; - if (isset(App::$hooks[$name])) { + if (isset(App::$hooks[$name])) { foreach(App::$hooks[$name] as $hook) { if ($name != 'permit_hook') { // avoid looping $checkhook = [ - 'name'=>$name, - 'hook'=>$hook, - 'data'=>$data, + 'name'=>$name, + 'hook'=>$hook, + 'data'=>$data, // Note: Since PHP uses COPY-ON-WRITE - // for variables, there is no cost to + // for variables, there is no cost to // passing the $data structure (unless // the permit_hook processors change the // information it contains. - 'permit'=>true - ]; + 'permit'=>true + ]; call_hooks('permit_hook',$checkhook); if (!$checkhook['permit']) { continue; @@ -618,7 +618,7 @@ function get_widget_info($widget){ } } - if(! ($widget_found && $f)) + if(! ($widget_found && $f)) return $info; $f = escape_tags($f); @@ -1041,7 +1041,7 @@ function get_intltext_template($s, $root = '') { if (isset(\App::$override_intltext_templates[$testroot][$s]["content"])) { return \App::$override_intltext_templates[$testroot][$s]["content"]; } else { - if (isset(\App::$override_intltext_templates[$testroot][$s]["root"]) && + if (isset(\App::$override_intltext_templates[$testroot][$s]["root"]) && isset(\App::$override_intltext_templates[$testroot][$s]["file"])) { $s = \App::$override_intltext_templates[$testroot][$s]["file"]; $root = \App::$override_intltext_templates[$testroot][$s]["root"]; @@ -1058,37 +1058,38 @@ function get_intltext_template($s, $root = '') { } function get_markup_template($s, $root = '') { - $testroot = ($root=='') ? $testroot = "ROOT" : $root; + $testroot = ($root=='') ? $testroot = "ROOT" : $root; - $t = App::template_engine(); + $t = App::template_engine(); - if (isset(\App::$override_markup_templates[$testroot][$s]["content"])) { - return \App::$override_markup_templates[$testroot][$s]["content"]; - } else { - if (isset(\App::$override_markup_templates[$testroot][$s]["root"]) && - isset(\App::$override_markup_templates[$testroot][$s]["file"])) { - $root = \App::$override_markup_templates[$testroot][$s]["root"]; - $s = \App::$override_markup_templates[$testroot][$s]["file"]; - $template = $t->get_markup_template($s, $root); - } elseif (\App::$override_templateroot) { - $newroot = \App::$override_templateroot; - if ($newroot != '' && substr($newroot,-1) != '/' ) { - $newroot .= '/'; - } - $newroot .= $root; - $template = $t->get_markup_template($s, $newroot); - } else { - $template = $t->get_markup_template($s, $root); + if (isset(\App::$override_markup_templates[$testroot][$s]["content"])) { + return \App::$override_markup_templates[$testroot][$s]["content"]; + } else { + if (isset(\App::$override_markup_templates[$testroot][$s]["root"]) && + isset(\App::$override_markup_templates[$testroot][$s]["file"])) { + $root = \App::$override_markup_templates[$testroot][$s]["root"]; + $s = \App::$override_markup_templates[$testroot][$s]["file"]; + $template = $t->get_markup_template($s, $root); + } elseif (\App::$override_templateroot) { + $newroot = \App::$override_templateroot; + if ($newroot != '' && substr($newroot,-1) != '/' ) { + $newroot .= '/'; + } + $newroot .= $root; + $template = $t->get_markup_template($s, $newroot); + } else { + $template = $t->get_markup_template($s, $root); } - return $template; - } + return $template; + } } /** - * @brief + * @brief Test if a folder exists. * * @param string $folder * @return boolean|string + * False if folder does not exist, or canonicalized absolute pathname */ function folder_exists($folder) { // Get canonicalized absolute pathname diff --git a/include/queue_fn.php b/include/queue_fn.php index f7e2922c6..85f98aaf9 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -13,7 +13,7 @@ function update_queue_item($id, $add_priority = 0) { return; - $y = q("select min(outq_created) as earliest from outq where outq_posturl = '%s'", + $y = q("select outq_created as earliest from outq where outq_posturl = '%s' order by earliest limit 1", dbesc($x[0]['outq_posturl']) ); @@ -311,4 +311,4 @@ function queue_deliver($outq, $immediate = false) { return; } -}
\ No newline at end of file +} diff --git a/include/text.php b/include/text.php index b6a1004c2..b017b038a 100644 --- a/include/text.php +++ b/include/text.php @@ -41,12 +41,12 @@ function replace_macros($s, $r) { $t = App::template_engine(); - try { - $output = $t->replace_macros($arr['template'], $arr['params']); - } catch (Exception $e) { - logger("Unable to render template: ".$e->getMessage()); - $output = "<h3>ERROR: there was an error creating the output.</h3>"; - } + try { + $output = $t->replace_macros($arr['template'], $arr['params']); + } catch (Exception $e) { + logger('Unable to render template: ' . $e->getMessage()); + $output = '<h3>ERROR: there was an error creating the output.</h3>'; + } return $output; } @@ -539,7 +539,14 @@ function paginate(&$a) { return $o; } - +/** + * @brief + * + * @param int $i + * @param string $more + * @param string $less + * @return string Parsed HTML from template 'alt_pager.tpl' + */ function alt_pager($i, $more = '', $less = '') { if(! $more) @@ -810,7 +817,7 @@ function activity_match($haystack,$needle) { * and strip the period from any tags which end with one. * * @param string $s - * @return Returns array of tags found, or empty array. + * @return array Returns an array of tags found, or empty array. */ function get_tags($s) { $ret = array(); @@ -825,7 +832,7 @@ function get_tags($s) { // ignore anything in [color= ], because it may contain color codes which are mistaken for tags $s = preg_replace('/\[color=(.*?)\]/sm','',$s); - + // skip anchors in URL $s = preg_replace('/\[url=(.*?)\]/sm','',$s); @@ -900,6 +907,7 @@ function tag_sort_length($a,$b) { function total_sort($a,$b) { if($a['total'] == $b['total']) return 0; + return(($b['total'] > $a['total']) ? 1 : (-1)); } @@ -986,7 +994,7 @@ function contact_block() { // There is no setting to discover if you are bi-directionally connected // Use the ability to post comments as an indication that this relationship is more - // than wishful thinking; even though soapbox channels and feeds will disable it. + // than wishful thinking; even though soapbox channels and feeds will disable it. if(! intval(get_abconfig(App::$profile['uid'],$rr['xchan_hash'],'their_perms','post_comments'))) { $rr['oneway'] = true; @@ -1004,9 +1012,15 @@ function contact_block() { '$micropro' => $micropro, )); - $arr = array('contacts' => $r, 'output' => $o); - + $arr = ['contacts' => $r, 'output' => $o]; + /** + * @hooks contact_block_end + * Called at the end of contact_block(), but can not manipulate the output. + * * \e array \b contacts - Result array from database + * * \e string \b output - the generated output + */ call_hooks('contact_block_end', $arr); + return $o; } @@ -1108,23 +1122,27 @@ function linkify($s, $me = false) { * to a local redirector which uses https and which redirects to the selected content * * @param string $s - * @param int $uid * @returns string */ function sslify($s) { - + // Local photo cache - $str = array( + $str = [ 'body' => $s, 'uid' => local_channel() - ); + ]; + /** + * @hooks cache_body_hook + * * \e string \b body The content to parse and also the return value + * * \e int|bool \b uid + */ call_hooks('cache_body_hook', $str); - + $s = $str['body']; if (strpos(z_root(),'https:') === false) return $s; - + // By default we'll only sslify img tags because media files will probably choke. // You can set sslify_everything if you want - but it will likely white-screen if it hits your php memory limit. // The downside is that http: media files will likely be blocked by your browser @@ -1222,7 +1240,11 @@ function get_mood_verbs() { /** * @brief Function to list all smilies, both internal and from addons. * - * @return Returns array with keys 'texts' and 'icons' + * @param boolean $default_only (optional) default false + * true will prevent that plugins can add smilies + * @return array Returns an associative array with: + * * \e array \b texts + * * \e array \b icons */ function list_smilies($default_only = false) { @@ -1300,6 +1322,11 @@ function list_smilies($default_only = false) { if($default_only) return $params; + /** + * @hooks smile + * * \e array \b texts - default values and also return value + * * \e array \b icons - default values and also return value + */ call_hooks('smilie', $params); return $params; @@ -1455,7 +1482,7 @@ function theme_attachments(&$item) { foreach($arr as $r) { $icon = getIconFromType($r['type']); - + if($r['title']) $label = urldecode(htmlspecialchars($r['title'], ENT_COMPAT, 'UTF-8')); @@ -1625,6 +1652,10 @@ function generate_named_map($location) { function prepare_body(&$item,$attach = false,$opts = false) { + /** + * @hooks prepare_body_init + * * \e array \b item + */ call_hooks('prepare_body_init', $item); $s = ''; @@ -1656,13 +1687,19 @@ function prepare_body(&$item,$attach = false,$opts = false) { $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); - $prep_arr = array( + $prep_arr = [ 'item' => $item, 'html' => $event ? $event['content'] : $s, 'event' => $event['header'], 'photo' => $photo - ); - + ]; + /** + * @hooks prepare_body + * * \e array \b item + * * \e string \b html - the parsed HTML to return + * * \e string \b event - the event header to return + * * \e string \b photo - the photo to return + */ call_hooks('prepare_body', $prep_arr); $s = $prep_arr['html']; @@ -1694,7 +1731,14 @@ function prepare_body(&$item,$attach = false,$opts = false) { if(local_channel() == $item['uid']) $filer = format_filer($item); - $s = sslify($s); + if($s) + $s = sslify($s); + + if($photo) + $photo = sslify($photo); + + if($event) + $event = sslify($event); $prep_arr = array( 'item' => $item, @@ -1725,17 +1769,24 @@ function prepare_binary($item) { /** - * @brief Given a text string, convert from bbcode to html and add smilie icons. + * @brief Given a text string, convert from content_type to HTML. * - * @param string $text - * @param string $content_type (optional) default text/bbcode - * @param boolean $cache (optional) default false + * Take a text in plain text, html, markdown, bbcode, PDL or PHP and prepare + * it to return HTML. + * + * In bbcode this function will add smilie icons. * + * @param string $text + * @param string $content_type (optional) + * default 'text/bbcode', other values are 'text/plain', 'text/html', + * 'text/markdown', 'application/x-pdl', 'application/x-php' + * @param boolean|array $opts (optional) + * default false, otherwise configuration array for bbcode() * @return string + * The parsed $text as prepared HTML. */ function prepare_text($text, $content_type = 'text/bbcode', $opts = false) { - switch($content_type) { case 'text/plain': $s = escape_tags($text); @@ -1775,8 +1826,8 @@ function prepare_text($text, $content_type = 'text/bbcode', $opts = false) { default: require_once('include/bbcode.php'); - if(stristr($text,'[nosmile]')) - $s = bbcode($text, [ 'cache' => $cache ]); + if(stristr($text, '[nosmile]')) + $s = bbcode($text, ((is_array($opts)) ? $opts : [] )); else $s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] ))); @@ -2136,7 +2187,7 @@ function legal_webbie($s) { return ''; // WARNING: This regex may not work in a federated environment. - // You will probably want something like + // You will probably want something like // preg_replace('/([^a-z0-9\_])/','',strtolower($s)); $r = preg_replace('/([^a-z0-9\-\_])/','',strtolower($s)); @@ -2235,19 +2286,24 @@ function ids_to_querystr($arr,$idx = 'id',$quote = false) { } /** - * @brief array_elm_to_str($arr,$elm,$delim = ',') extract unique individual elements from an array of arrays and return them as a string separated by a delimiter - * similar to ids_to_querystr, but allows a different delimiter instead of a db-quote option - * empty elements (evaluated after trim()) are ignored. - * @param $arr array - * @param $elm array key to extract from sub-array - * @param $delim string default ',' - * @param $each filter function to apply to each element before evaluation, default is 'trim'. + * @brief Extract unique individual elements from an array of arrays and return + * them as a string separated by a delimiter. + * + * Similar to ids_to_querystr, but allows a different delimiter instead of a + * db-quote option empty elements (evaluated after trim()) are ignored. + * + * @see ids_to_querystr() + * + * @param array $arr + * @param string $elm key to extract from sub-array + * @param string $delim (optional) default ',' + * @param string $each (optional) default is 'trim' + * Filter function to apply to each element before evaluation. * @returns string */ - -function array_elm_to_str($arr,$elm,$delim = ',',$each = 'trim') { - +function array_elm_to_str($arr, $elm, $delim = ',', $each = 'trim') { $tmp = []; + if($arr && is_array($arr)) { foreach($arr as $x) { if(is_array($x) && array_key_exists($elm,$x)) { @@ -2258,7 +2314,8 @@ function array_elm_to_str($arr,$elm,$delim = ',',$each = 'trim') { } } } - return implode($delim,$tmp); + + return implode($delim, $tmp); } function trim_and_unpunify($s) { @@ -2482,9 +2539,9 @@ function design_tools() { } /** - * @brief Creates website portation tools menu + * @brief Creates website portation tools menu. * - * @return string + * @return string Parsed HTML code from template 'website_portation_tools.tpl' */ function website_portation_tools() { @@ -2497,7 +2554,7 @@ function website_portation_tools() { $sys = true; } - return replace_macros(get_markup_template('website_portation_tools.tpl'), array( + return replace_macros(get_markup_template('website_portation_tools.tpl'), [ '$title' => t('Import'), '$import_label' => t('Import website...'), '$import_placeholder' => t('Select folder to import'), @@ -2514,7 +2571,7 @@ function website_portation_tools() { '$cloud_export_desc' => t('/path/to/export/folder'), '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), '$cloud_export_select' => t('Specify folder'), - )); + ]); } /** @@ -2574,8 +2631,9 @@ function extra_query_args() { * @param boolean $in_network default true * @return boolean true if replaced, false if not replaced */ -function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $in_network = true) { +function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) { + $channel = App::get_channel(); $replaced = false; $r = null; $match = array(); @@ -2631,21 +2689,20 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i $str_tags .= $newtag; } - return [ - 'replaced' => $replaced, - 'termtype' => $termtype, - 'term' => $basetag, - 'url' => $url, - 'contact' => [] - ]; - + return [ [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $basetag, + 'url' => $url, + 'contact' => [], + 'access_tag' => '', + ]]; } // END hashtags // BEGIN mentions - if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) { // The @! tag and !! tag will alter permissions @@ -2656,13 +2713,13 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i $exclusive = (((strpos(substr($tag,1), '!') === 0) && $in_network) ? true : false); //is it already replaced? - if(strpos($tag,'[zrl=') || strpos($tag,'[url=')) + if(strpos($tag,"[zrl=") || strpos($tag,"[url=")) return $replaced; // get the channel name // First extract the name or name fragment we are going to replace - $name = substr($tag,(($exclusive) ? 2 : 1)); + $name = substr($tag,(($exclusive) ? 2 : 1)); $newname = $name; // make a copy that we can mess with $tagcid = 0; @@ -2674,7 +2731,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i $newname = substr($name,1); $newname = substr($newname,0,-1); - $r = q("select * from xchan where xchan_addr = '%s' or xchan_url = '%s' limit 1", + $r = q("select * from xchan where xchan_addr = '%s' or xchan_url = '%s'", dbesc($newname), dbesc($newname) ); @@ -2697,7 +2754,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i // select someone from this user's contacts by name $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash - WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", + WHERE xchan_name = '%s' AND abook_channel = %d ", dbesc($newname), intval($profile_uid) ); @@ -2705,8 +2762,8 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i // select anybody by full hubloc_addr if((! $r) && strpos($newname,'@')) { - $r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash - WHERE hubloc_addr = '%s' LIMIT 1", + $r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash + WHERE hubloc_addr = '%s' ", dbesc($newname) ); } @@ -2715,7 +2772,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i if(! $r) { $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash - WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1", + WHERE xchan_addr like ('%s') AND abook_channel = %d ", dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), intval($profile_uid) ); @@ -2723,17 +2780,62 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i } + + + + + $fn_results = []; + $access_tag = EMPTY_STR; + + // $r is set if we found something - $channel = App::get_channel(); - if($r) { - $profile = $r[0]['xchan_url']; - $newname = $r[0]['xchan_name']; - // add the channel's xchan_hash to $access_tag if exclusive - if($exclusive) { - $access_tag .= 'cid:' . $r[0]['xchan_hash']; + foreach($r as $xc) { + $profile = $xc['xchan_url']; + $newname = $xc['xchan_name']; + // add the channel's xchan_hash to $access_tag if exclusive + if($exclusive) { + $access_tag = 'cid:' . $xc['xchan_hash']; + } + + // if there is a url for this channel + + if(isset($profile)) { + $replaced = true; + //create profile link + $profile = str_replace(',','%2c',$profile); + $url = $profile; + if($termtype === TERM_FORUM) { + $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; + $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); + } + else { + // ( $termtype === TERM_MENTION ) + $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; + $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); + } + + // append tag to str_tags + if(! stristr($str_tags,$newtag)) { + if(strlen($str_tags)) + $str_tags .= ','; + $str_tags .= $newtag; + } + } + + + $fn_results[] = [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $newname, + 'url' => $url, + 'access_tag' => $access_tag, + 'contact' => (($r) ? $xc : []), + ]; + } + } else { @@ -2745,7 +2847,6 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i // weird - as all the other tags are linked to something. if(local_channel() && local_channel() == $profile_uid) { - require_once('include/group.php'); $grp = group_byname($profile_uid,$name); if($grp) { @@ -2762,58 +2863,62 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i } } } - } - // if there is a url for this channel - if(isset($profile)) { - $replaced = true; - //create profile link - $profile = str_replace(',','%2c',$profile); - $url = $profile; - if($termtype === TERM_FORUM) { - $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; - $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); - } - else { - // ( $termtype === TERM_MENTION ) - $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; - $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); - } + // if there is a url for this channel + + if(isset($profile)) { + $replaced = true; + //create profile link + $profile = str_replace(',','%2c',$profile); + $url = $profile; + if($termtype === TERM_FORUM) { + $newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; + $body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body); + } + else { + // ( $termtype === TERM_MENTION ) + $newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]'; + $body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body); + } - // append tag to str_tags - if(! stristr($str_tags,$newtag)) { - if(strlen($str_tags)) - $str_tags .= ','; - $str_tags .= $newtag; + // append tag to str_tags + if(! stristr($str_tags,$newtag)) { + if(strlen($str_tags)) + $str_tags .= ','; + $str_tags .= $newtag; + } } + + $fn_results[] = [ + 'replaced' => $replaced, + 'termtype' => $termtype, + 'term' => $newname, + 'url' => $url, + 'access_tag' => $access_tag, + 'contact' => [], + ]; } } + + return $fn_results; - return [ - 'replaced' => $replaced, - 'termtype' => $termtype, - 'term' => $newname, - 'url' => $url, - 'contact' => (($r) ? $r[0] : []) - ]; } -function linkify_tags($a, &$body, $uid, $in_network = true) { +function linkify_tags(&$body, $uid, $in_network = true) { $str_tags = EMPTY_STR; - $tagged = []; $results = []; $tags = get_tags($body); if(count($tags)) { foreach($tags as $tag) { - $access_tag = ''; - $success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); + $success = handle_tag($body, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); - $results[] = array('success' => $success, 'access_tag' => $access_tag); - if($success['replaced']) $tagged[] = $tag; + foreach($success as $handled_tag) { + $results[] = [ 'success' => $handled_tag ]; + } } } @@ -2871,7 +2976,7 @@ function getIconFromType($type) { 'video/x-matroska' => 'fa-file-video-o' ); - $catMap = [ + $catMap = [ 'application' => 'fa-file-code-o', 'multipart' => 'fa-folder', 'audio' => 'fa-file-audio-o', @@ -2879,7 +2984,7 @@ function getIconFromType($type) { 'text' => 'fa-file-text-o', 'image' => 'fa=file-picture-o', 'message' => 'fa-file-text-o' - ]; + ]; $iconFromType = ''; @@ -2889,7 +2994,7 @@ function getIconFromType($type) { } else { $parts = explode('/',$type); - if($parts[0] && $catMap[$parts[0]]) { + if($parts[0] && $catMap[$parts[0]]) { $iconFromType = $catMap[$parts[0]]; } } @@ -2977,9 +3082,9 @@ function item_url_replace($channel,&$item,$old,$new,$oldnick = '') { json_url_replace('/' . $oldnick . '/' ,'/' . $channel['channel_address'] . '/' ,$item['target']); } - $x = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']); - if($x) { - $item['body'] = $x; + $x = preg_replace("/".preg_quote($old,'/')."\/(search|\w+\/".$channel['channel_address'].")/", $new.'/${1}', $item['body']); + if($x) { + $item['body'] = $x; $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); $item['item_verified'] = 1; } @@ -3089,7 +3194,13 @@ function pdl_selector($uid, $current='') { intval($uid) ); - $arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); + $arr = ['channel_id' => $uid, 'current' => $current, 'entries' => $r]; + /** + * @hooks pdl_selector + * * \e int \b channel_id + * * \e string \b current + * * \e array \b entries - Result from database query + */ call_hooks('pdl_selector', $arr); $entries = $arr['entries']; @@ -3145,7 +3256,7 @@ function flatten_array_recursive($arr) { * @param string $lang Which language should be highlighted * @return string * Important: The returned text has the text pattern 'http' translated to '%eY9-!' which should be converted back - * after further processing. This was done to prevent oembed links from occurring inside code blocks. + * after further processing. This was done to prevent oembed links from occurring inside code blocks. * See include/bbcode.php */ function text_highlight($s, $lang) { @@ -3164,7 +3275,6 @@ function text_highlight($s, $lang) { 'language' => $lang, 'success' => false ]; - /** * @hooks text_highlight * * \e string \b text @@ -3365,13 +3475,17 @@ function punify($s) { } -// Be aware that unpunify will only convert domain names and not pathnames - +/** + * Be aware that unpunify() will only convert domain names and not pathnames. + * + * @param string $s + * @return string + */ function unpunify($s) { require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php'); $x = new idna_convert(['encoding' => 'utf8']); - return $x->decode($s); + return $x->decode($s); } @@ -3379,7 +3493,7 @@ function unique_multidim_array($array, $key) { $temp_array = array(); $i = 0; $key_array = array(); - + foreach($array as $val) { if (!in_array($val[$key], $key_array)) { $key_array[$i] = $val[$key]; @@ -3407,7 +3521,7 @@ function get_forum_channels($uid) { intval($uid) ); - if($x2) { + if($x2) { $xf = ids_to_querystr($x2,'xchan',true); // private forums @@ -3420,7 +3534,7 @@ function get_forum_channels($uid) { } } - $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); + $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); $r = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_addr, 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 and abook_archived = 0 $sql_extra order by xchan_name", intval($uid) @@ -3456,14 +3570,14 @@ function print_array($arr, $level = 0) { $o .= $tabs . '[' . $k . '] => ' . print_array($v, $level + 1) . "\n"; } else { - $o .= $tabs . '[' . $k . '] => ' . print_val($v) . ",\n"; + $o .= $tabs . '[' . $k . '] => ' . print_val($v) . ",\n"; } } } $o .= substr($tabs,0,-1) . ']' . (($level) ? ',' : ';' ). "\n"; return $o; } - + } function print_val($v) { @@ -3490,7 +3604,7 @@ function array_path_exists($str,$arr) { } else { return false; - } + } } return true; } @@ -3507,12 +3621,11 @@ function array_path_exists($str,$arr) { */ function new_uuid() { - try { - $hash = Uuid::uuid4()->toString(); - } catch (UnsatisfiedDependencyException $e) { - $hash = random_string(48); - } + try { + $hash = Uuid::uuid4()->toString(); + } catch (UnsatisfiedDependencyException $e) { + $hash = random_string(48); + } - return $hash; + return $hash; } - diff --git a/include/zid.php b/include/zid.php index fe06948ba..a37ebe1f6 100644 --- a/include/zid.php +++ b/include/zid.php @@ -70,9 +70,9 @@ function zid($s, $address = '') { $zurl .= '#' . $fragment; $arr = [ - 'url' => $s, - 'zid' => urlencode($myaddr), - 'result' => $zurl + 'url' => $s, + 'zid' => urlencode($myaddr), + 'result' => $zurl ]; /** * @hooks zid diff --git a/include/zot.php b/include/zot.php index 9934dae07..3b089831b 100644 --- a/include/zot.php +++ b/include/zot.php @@ -174,7 +174,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * @param array $recipients * envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts - * @param string msg + * @param string $msg * optional message * @param string $remote_key * optional public site key of target hub used to encrypt entire packet @@ -1100,6 +1100,8 @@ function zot_process_response($hub, $arr, $outq) { return; } + $dreport = true; + $x = json_decode($arr['body'], true); if(! $x) { @@ -1116,31 +1118,44 @@ function zot_process_response($hub, $arr, $outq) { } if(! (is_array($x['delivery_report']) && count($x['delivery_report']))) { logger('encrypted delivery report could not be decrypted'); - return; + $dreport = false; } } - foreach($x['delivery_report'] as $xx) { - call_hooks('dreport_process',$xx); - if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) { - q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s','%s' ) ", - dbesc($xx['message_id']), - dbesc($xx['location']), - dbesc($xx['recipient']), - dbesc($xx['name']), - dbesc($xx['status']), - dbesc(datetime_convert('UTC','UTC',$xx['date'])), - dbesc($xx['sender']) - ); + if($dreport) { + foreach($x['delivery_report'] as $xx) { + call_hooks('dreport_process',$xx); + if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) { + + // legacy zot recipients add a space and their name to the xchan. split those if true. + $legacy_recipient = strpos($xx['recipient'], ' '); + if($legacy_recipient !== false) { + $legacy_recipient_parts = explode(' ', $xx['recipient'], 2); + $xx['recipient'] = $legacy_recipient_parts[0]; + $xx['name'] = $legacy_recipient_parts[1]; + } + + q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s','%s','%s','%s','%s','%s' ) ", + dbesc($xx['message_id']), + dbesc($xx['location']), + dbesc($xx['recipient']), + dbesc($xx['name']), + dbesc($xx['status']), + dbesc(datetime_convert('UTC','UTC',$xx['date'])), + dbesc($xx['sender']) + ); + } } } } - // we have a more descriptive delivery report, so discard the per hub 'queued' report. - q("delete from dreport where dreport_queue = '%s' ", - dbesc($outq['outq_hash']) - ); + if($dreport) { + // we have a more descriptive delivery report, so discard the per hub 'queued' report. + q("delete from dreport where dreport_queue = '%s' ", + dbesc($outq['outq_hash']) + ); + } // update the timestamp for this site @@ -1814,7 +1829,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ else { $arr['item_wall'] = 0; } - + if ((! $tag_delivery) && (! $local_public)) { $allowed = (perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)); @@ -1828,7 +1843,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $allowed = can_comment_on_post($d['hash'],$parent[0]); } } - + if (! $allowed) { logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); $DR->update('permission denied'); @@ -2315,7 +2330,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { if(! perm_is_allowed($channel['channel_id'],$sender['hash'],'post_mail')) { - /* + /* * Always allow somebody to reply if you initiated the conversation. It's anti-social * and a bit rude to send a private message to somebody and block their ability to respond. * If you are being harrassed and want to put an end to it, delete the conversation. @@ -2343,7 +2358,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { ); if($r) { if(intval($arr['mail_recalled'])) { - msg_drop($r[0]['id'], $channel['channel_id'], $r[0]['conv_guid']); + msg_drop($r[0]['id'], $channel['channel_id'], $r[0]['conv_guid']); $DR->update('mail recalled'); $result[] = $DR->get(); logger('mail_recalled'); @@ -3232,7 +3247,7 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $channel = $r[0]; - // don't provide these in the export + // don't provide these in the export unset($channel['channel_active']); unset($channel['channel_password']); @@ -3601,7 +3616,7 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { if(array_key_exists('channel_pageflags',$arr['channel']) && intval($arr['channel']['channel_pageflags'])) { // Several pageflags are site-specific and cannot be sync'd. - // Only allow those bits which are shareable from the remote and then + // Only allow those bits which are shareable from the remote and then // logically OR with the local flags $arr['channel']['channel_pageflags'] = $arr['channel']['channel_pageflags'] & (PAGE_HIDDEN|PAGE_AUTOCONNECT|PAGE_APPLICATION|PAGE_PREMIUM|PAGE_ADULT); @@ -4924,12 +4939,13 @@ function zot_reply_pickup($data) { /* * Everything is good if we made it here, so find all messages that are going to this location - * and send them all. + * and send them all - or a reasonable number if there are a lot so we don't overflow memory. */ - $r = q("select * from outq where outq_posturl = '%s'", + $r = q("select * from outq where outq_posturl = '%s' limit 100", dbesc($data['callback']) ); + if($r) { logger('mod_zot: successful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG); @@ -4955,6 +4971,19 @@ function zot_reply_pickup($data) { } } + // It's possible that we have more than 100 messages waiting to be sent. + + // See if there are any more messages in the queue. + $x = q("select * from outq where outq_posturl = '%s' order by outq_created limit 1", + dbesc($data['callback']) + ); + + // If so, kick off a new delivery notification for the next batch + if ($x) { + logger("Send additional pickup request.", LOGGER_DEBUG); + queue_deliver($x[0],true); + } + // this is a bit of a hack because we don't have the hubloc_url here, only the callback url. // worst case is we'll end up using aes256cbc if they've got a different post endpoint @@ -4966,6 +4995,8 @@ function zot_reply_pickup($data) { $encrypted = crypto_encapsulate(json_encode($ret),$sitekey,$algorithm); json_return_and_die($encrypted); + // @FIXME: There is a possibility that the transmission will get interrupted + // and fail - in which case this packet of messages will be lost. /* pickup: end */ } @@ -5026,7 +5057,7 @@ function zot_reply_auth_check($data,$encrypted_packet) { } // There should be exactly one recipient, the original auth requestor - + /// @FIXME $recipients is undefined here. $ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL; if ($data['recipients']) { diff --git a/install/sample-lighttpd.conf b/install/sample-lighttpd.conf index db26c3b64..b65d86645 100644 --- a/install/sample-lighttpd.conf +++ b/install/sample-lighttpd.conf @@ -79,7 +79,7 @@ $HTTP["url"] =~ "\.(out|log|htaccess)$" { url.access-deny = ("") } -$HTTP["url"] =~ "(^|/)\.git|(^|/)store" { +$HTTP["url"] =~ "(^|/)\.git|(^|/)store|(^|/)util" { url.access-deny = ("") } diff --git a/install/sample-nginx.conf b/install/sample-nginx.conf index 839f208ae..6a986d426 100644 --- a/install/sample-nginx.conf +++ b/install/sample-nginx.conf @@ -141,5 +141,10 @@ server { deny all; } +#deny access to util + location ~ /util { + deny all; + } + } diff --git a/tests/phpunit-pgsql.xml b/tests/phpunit-pgsql.xml index 078056d56..8b11aae31 100644 --- a/tests/phpunit-pgsql.xml +++ b/tests/phpunit-pgsql.xml @@ -1,24 +1,26 @@ <?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.0/phpunit.xsd" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd" bootstrap="../boot.php" forceCoversAnnotation="false" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> - <testsuite name="Hubzilla default Test Suite"> - <directory suffix="Test.php">./unit/</directory> - </testsuite> - <testsuite name="API Test Suite"> - <directory suffix="Test.php" prefix="API">./unit/</directory> - </testsuite> + <testsuites> + <testsuite name="Hubzilla default Test Suite"> + <directory suffix="Test.php">./unit/</directory> + </testsuite> + <testsuite name="API Test Suite"> + <directory suffix="Test.php" prefix="API">./unit/</directory> + </testsuite> + </testsuites> <groups> <exclude> <group>mysql</group> </exclude> </groups> - <!--cover reporting--> + <!--coverage reporting--> <filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">../Zotlabs/</directory> @@ -26,21 +28,22 @@ </whitelist> </filter> <logging> - <log type="junit" target="./results/junit.xml" logIncompleteSkipped="false"/> - <log type="coverage-clover" target="./results/coverage-clover.xml"/> + <log type="junit" target="./results/junit.xml"/> + <!--<log type="coverage-clover" target="./results/coverage-clover.xml"/>--> <log type="coverage-html" target="./results/coverage-report/" lowUpperBound="35" highLowerBound="70"/> - <log type="testdox-text" target="./results/testdox.txt"/> + <!--<log type="testdox-text" target="./results/testdox.txt"/>--> + <log type="testdox-html" target="./results/testdox.html"/> </logging> <php> <!-- Default test database config, only used if no environment variables with same names are set. !!! Never run against a real database, it will truncate all tables --> - <env name="hz_db_server" value="127.0.0.1"/> + <env name="hz_db_server" value="postgres"/> <env name="hz_db_scheme" value="pgsql"/> <env name="hz_db_port" value="5432"/> - <env name="hz_db_user" value="travis_hz"/> - <env name="hz_db_pass" value="hubzilla"/> - <env name="hz_db_database" value="travis_hubzilla"/> + <env name="hz_db_user" value="ci-user"/> + <env name="hz_db_pass" value="ci-pass"/> + <env name="hz_db_database" value="ci-db"/> </php> </phpunit> diff --git a/tests/unit/get_tags_test.php b/tests/unit/get_tags_test.php index 40f016747..bdffd8311 100644 --- a/tests/unit/get_tags_test.php +++ b/tests/unit/get_tags_test.php @@ -106,10 +106,9 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { $tags=get_tags($text);
- $inform=''; $str_tags=''; foreach($tags as $tag) { - handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); + handle_tag($text, $str_tags, 11, $tag); } //correct tags found? @@ -117,7 +116,6 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { $this->assertTrue(in_array("@Mike", $tags)); //correct output from handle_tag? - $this->assertEquals("cid:15", $inform); $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url]", $text);
} @@ -135,9 +133,8 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { $this->assertEquals(1, count($tags));
$this->assertTrue(in_array("@Mike.because", $tags)); - $inform='';
$str_tags='';
- handle_tag($this->a, $text, $inform, $str_tags, 11, $tags[0]);
+ handle_tag($text, $str_tags, 11, $tags[0]);
// (mike) - This is a tricky case. // we support mentions as in @mike@example.com - which contains a period. @@ -149,7 +146,6 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { // $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); // $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text);
- $this->assertEquals("", $inform); $this->assertEquals("", $str_tags); } @@ -195,13 +191,11 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { $this->assertTrue(in_array("@Mike This", $tags));
$this->assertTrue(in_array("#test_case", $tags));
- $inform=''; $str_tags=''; foreach($tags as $tag) { - handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); + handle_tag($text, $str_tags, 11, $tag); } - $this->assertEquals("cid:15", $inform); $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url],#[url=baseurl/search?tag=test%20case]test case[/url]", $str_tags); $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url] This is a #[url=baseurl/search?tag=test%20case]test case[/url]", $text); @@ -255,16 +249,13 @@ class GetTagsTest extends PHPUnit_Framework_TestCase { //happens right now, but it shouldn't be necessary $this->assertTrue(in_array("@mike+15 id", $tags)); - $inform='';
$str_tags=''; foreach($tags as $tag) { - handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); + handle_tag($text, $str_tags, 11, $tag); } $this->assertEquals("Test with @[url=http://justatest.de]Mike Lastname[/url] id tag", $text);
$this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags);
- // this test may produce two cid:15 entries - which is OK because duplicates are pruned before delivery - $this->assertContains("cid:15",$inform); } /** diff --git a/util/Doxyfile b/util/Doxyfile index 7be774a81..14464df81 100644 --- a/util/Doxyfile +++ b/util/Doxyfile @@ -1,7 +1,8 @@ INPUT = README.md index.php boot.php include/ install/ util/ view/ Zotlabs/ RECURSIVE = YES PROJECT_NAME = "The Hubzilla" -PROJECT_LOGO = images/rm-64.png +PROJECT_LOGO = images/hz-64.png +IMAGE_PATH = images/ EXCLUDE = .htconfig.php library/ doc/ store/ vendor/ .git/ util/zotsh/easywebdav/ util/generate-hooks-index/ EXCLUDE_PATTERNS = *smarty3* *strings.php *.out *test* OUTPUT_DIRECTORY = doc @@ -33,5 +34,6 @@ INTERACTIVE_SVG = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = NO # fix @var (https://bugzilla.gnome.org/show_bug.cgi?id=626105) +# Should be obsolete with doxygen >= 1.8.15 #INPUT_FILTER = "sed -e 's/@var\s/@see /'" INPUT_FILTER = "php util/Doxygen_phpvarfilter.php" diff --git a/util/admins b/util/admins new file mode 100755 index 000000000..3c7f0e83e --- /dev/null +++ b/util/admins @@ -0,0 +1,60 @@ +#!/usr/bin/env php +<?php + +if(!file_exists('include/cli_startup.php')) { + echo 'Run admins from the top level Hubzilla web directory, as util/admins <args>' . PHP_EOL; + exit(1); +} + + + +require_once('include/cli_startup.php'); + +cli_startup(); + +$helpArgs = getopt('h', array('help')); +if (count($helpArgs) === 1) { + echo <<<'EndOfOutput' +adds, removes, or lists admins + +Usage: util/admins + util/admins list + util/admins add <account_id> + util/admins remove <account_id> + +EndOfOutput; + return; +} + +if($argc == 1) { + $r = q("select account_id, account_roles, account_email from account"); + if($r) { + foreach($r as $rr) { + echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL; + } + } +} + + + +if($argc > 1 && $argv[1] === 'list') { + $r = q("select account_id, account_roles, account_email from account where (account_roles & 4096) > 0"); + if($r) { + foreach($r as $rr) { + echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL; + } + } +} + + +if($argc > 2 && $argv[1] === 'add' && intval($argv[2])) { + $r = q("update account set account_roles = (account_roles | 4096) where account_id = %d", + intval($argv[2]) + ); +} + +if($argc > 2 && $argv[1] === 'remove' && intval($argv[2])) { + $r = q("update account set account_roles = (account_roles - 4096) where account_id = %d and (account_roles & 4096) > 0", + intval($argv[2]) + ); +} diff --git a/vendor/bshaffer/oauth2-server-php/CHANGELOG.md b/vendor/bshaffer/oauth2-server-php/CHANGELOG.md index 7671b2396..1b87f3da3 100644 --- a/vendor/bshaffer/oauth2-server-php/CHANGELOG.md +++ b/vendor/bshaffer/oauth2-server-php/CHANGELOG.md @@ -8,7 +8,7 @@ To see the files changed for a given bug, go to https://github.com/bshaffer/oaut To get the diff between two versions, go to https://github.com/bshaffer/oauth2-server-php/compare/v1.0...v1.1 To get the diff for a specific change, go to https://github.com/bshaffer/oauth2-server-php/commit/XXX where XXX is the change hash -* 1.10.0 (2017-12-14) +* 1.10.0 (2017-11-15) PR: https://github.com/bshaffer/oauth2-server-php/pull/889 @@ -26,7 +26,7 @@ To get the diff for a specific change, go to https://github.com/bshaffer/oauth2- * #794 - [docs] Fix typo in composer.json * #885 - [testing] Use PHPUnit\Framework\TestCase instead of PHPUnit_Framework_TestCase -* 1.9.0 (2016-01-06) +* 1.9.0 (2017-01-06) PR: https://github.com/bshaffer/oauth2-server-php/pull/788 diff --git a/vendor/bshaffer/oauth2-server-php/README.md b/vendor/bshaffer/oauth2-server-php/README.md index f1788e9ce..117743d4f 100644 --- a/vendor/bshaffer/oauth2-server-php/README.md +++ b/vendor/bshaffer/oauth2-server-php/README.md @@ -1,7 +1,7 @@ oauth2-server-php ================= -[![Build Status](https://travis-ci.org/bshaffer/oauth2-server-php.svg?branch=develop)](https://travis-ci.org/bshaffer/oauth2-server-php) +[![Build Status](https://travis-ci.org/bshaffer/oauth2-server-php.svg?branch=master)](https://travis-ci.org/bshaffer/oauth2-server-php) [![Total Downloads](https://poser.pugx.org/bshaffer/oauth2-server-php/downloads.png)](https://packagist.org/packages/bshaffer/oauth2-server-php) diff --git a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Request.php b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Request.php index c96cb972f..f547bf6e8 100644 --- a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Request.php +++ b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Request.php @@ -64,7 +64,12 @@ class Request implements RequestInterface $this->files = $files; $this->server = $server; $this->content = $content; - $this->headers = is_null($headers) ? $this->getHeadersFromServer($this->server) : $headers; + + if ($headers === null) { + $headers = array(); + } + + $this->headers = $headers + $this->getHeadersFromServer($this->server); } /** diff --git a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Response.php b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Response.php index ccd797ae7..88c1ad5f7 100644 --- a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Response.php +++ b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Response.php @@ -366,7 +366,7 @@ class Response implements ResponseInterface if (count($this->parameters) > 0) { // add parameters to URL redirection $parts = parse_url($url); - $sep = isset($parts['query']) && count($parts['query']) > 0 ? '&' : '?'; + $sep = isset($parts['query']) && !empty($parts['query']) ? '&' : '?'; $url .= $sep . http_build_query($this->parameters); } diff --git a/vendor/bshaffer/oauth2-server-php/src/OAuth2/ResponseType/JwtAccessToken.php b/vendor/bshaffer/oauth2-server-php/src/OAuth2/ResponseType/JwtAccessToken.php index 0af9705ff..0ee3708aa 100644 --- a/vendor/bshaffer/oauth2-server-php/src/OAuth2/ResponseType/JwtAccessToken.php +++ b/vendor/bshaffer/oauth2-server-php/src/OAuth2/ResponseType/JwtAccessToken.php @@ -128,7 +128,7 @@ class JwtAccessToken extends AccessToken $expires = time() + $this->config['access_lifetime']; $id = $this->generateAccessToken(); - return array( + $payload = array( 'id' => $id, // for BC (see #591) 'jti' => $id, 'iss' => $this->config['issuer'], @@ -139,5 +139,21 @@ class JwtAccessToken extends AccessToken 'token_type' => $this->config['token_type'], 'scope' => $scope ); + + if (isset($this->config['jwt_extra_payload_callable'])) { + if (!is_callable($this->config['jwt_extra_payload_callable'])) { + throw new \InvalidArgumentException('jwt_extra_payload_callable is not callable'); + } + + $extra = call_user_func($this->config['jwt_extra_payload_callable'], $client_id, $user_id, $scope); + + if (!is_array($extra)) { + throw new \InvalidArgumentException('jwt_extra_payload_callable must return array'); + } + + $payload = array_merge($extra, $payload); + } + + return $payload; } } diff --git a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Server.php b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Server.php index 62ae8970d..cf040c2bc 100644 --- a/vendor/bshaffer/oauth2-server-php/src/OAuth2/Server.php +++ b/vendor/bshaffer/oauth2-server-php/src/OAuth2/Server.php @@ -88,12 +88,12 @@ class Server implements ResourceControllerInterface, /** * @var array */ - protected $grantTypes = []; + protected $grantTypes = array(); /** * @var array */ - protected $responseTypes = []; + protected $responseTypes = array(); /** * @var TokenTypeInterface @@ -161,6 +161,7 @@ class Server implements ResourceControllerInterface, // merge all config values. These get passed to our controller objects $this->config = array_merge(array( 'use_jwt_access_tokens' => false, + 'jwt_extra_payload_callable' => null, 'store_encrypted_token_string' => true, 'use_openid_connect' => false, 'id_lifetime' => 3600, @@ -840,7 +841,7 @@ class Server implements ResourceControllerInterface, $refreshStorage = $this->storages['refresh_token']; } - $config = array_intersect_key($this->config, array_flip(explode(' ', 'store_encrypted_token_string issuer access_lifetime refresh_token_lifetime'))); + $config = array_intersect_key($this->config, array_flip(explode(' ', 'store_encrypted_token_string issuer access_lifetime refresh_token_lifetime jwt_extra_payload_callable'))); return new JwtAccessToken($this->storages['public_key'], $tokenStorage, $refreshStorage, $config); } @@ -1015,4 +1016,4 @@ class Server implements ResourceControllerInterface, { return isset($this->config[$name]) ? $this->config[$name] : $default; } -}
\ No newline at end of file +} diff --git a/vendor/bshaffer/oauth2-server-php/test/OAuth2/RequestTest.php b/vendor/bshaffer/oauth2-server-php/test/OAuth2/RequestTest.php index cbf8f096b..770cd8994 100644 --- a/vendor/bshaffer/oauth2-server-php/test/OAuth2/RequestTest.php +++ b/vendor/bshaffer/oauth2-server-php/test/OAuth2/RequestTest.php @@ -86,6 +86,24 @@ class RequestTest extends TestCase $this->assertEquals('correct', $request->query('client_id', $request->request('client_id'))); } + public function testRequestHasHeadersAndServerHeaders() + { + $request = new Request( + array(), + array(), + array(), + array(), + array(), + array('CONTENT_TYPE' => 'text/xml', 'PHP_AUTH_USER' => 'client_id', 'PHP_AUTH_PW' => 'client_pass'), + null, + array('CONTENT_TYPE' => 'application/json') + ); + + $this->assertSame('client_id', $request->headers('PHP_AUTH_USER')); + $this->assertSame('client_pass', $request->headers('PHP_AUTH_PW')); + $this->assertSame('application/json', $request->headers('CONTENT_TYPE')); + } + private function getTestServer($config = array()) { $storage = Bootstrap::getInstance()->getMemoryStorage(); diff --git a/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseTest.php b/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseTest.php index 2d2c57ee6..172bc88fd 100644 --- a/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseTest.php +++ b/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseTest.php @@ -1,6 +1,8 @@ <?php -namespace OAuth2;use PHPUnit\Framework\TestCase; +namespace OAuth2; + +use PHPUnit\Framework\TestCase; class ResponseTest extends TestCase { @@ -14,4 +16,23 @@ class ResponseTest extends TestCase $string = $response->getResponseBody('xml'); $this->assertContains('<response><foo>bar</foo><halland>oates</halland></response>', $string); } + + public function testSetRedirect() + { + $response = new Response(); + $url = 'https://foo/bar'; + $state = 'stateparam'; + $response->setRedirect(301, $url, $state); + $this->assertEquals( + sprintf('%s?state=%s', $url, $state), + $response->getHttpHeader('Location') + ); + + $query = 'query=foo'; + $response->setRedirect(301, $url . '?' . $query, $state); + $this->assertEquals( + sprintf('%s?%s&state=%s', $url, $query, $state), + $response->getHttpHeader('Location') + ); + } } diff --git a/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseType/JwtAccessTokenTest.php b/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseType/JwtAccessTokenTest.php index 7e37509ef..6195d557a 100644 --- a/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseType/JwtAccessTokenTest.php +++ b/vendor/bshaffer/oauth2-server-php/test/OAuth2/ResponseType/JwtAccessTokenTest.php @@ -40,6 +40,23 @@ class JwtAccessTokenTest extends TestCase $this->assertEquals(3600, $delta); $this->assertEquals($decodedAccessToken['id'], $decodedAccessToken['jti']); } + + public function testExtraPayloadCallback() + { + $jwtconfig = array('jwt_extra_payload_callable' => function() { + return array('custom_param' => 'custom_value'); + }); + + $server = $this->getTestServer($jwtconfig); + $jwtResponseType = $server->getResponseType('token'); + + $accessToken = $jwtResponseType->createAccessToken('Test Client ID', 123, 'test', false); + $jwt = new Jwt; + $decodedAccessToken = $jwt->decode($accessToken['access_token'], null, false); + + $this->assertArrayHasKey('custom_param', $decodedAccessToken); + $this->assertEquals('custom_value', $decodedAccessToken['custom_param']); + } public function testGrantJwtAccessToken() { @@ -140,7 +157,7 @@ class JwtAccessTokenTest extends TestCase $this->assertNotNull($response->getParameter('access_token')); } - private function getTestServer() + private function getTestServer($jwtconfig = array()) { $memoryStorage = Bootstrap::getInstance()->getMemoryStorage(); @@ -153,7 +170,7 @@ class JwtAccessTokenTest extends TestCase $server->addGrantType(new ClientCredentials($memoryStorage)); // make the "token" response type a JwtAccessToken - $config = array('issuer' => 'https://api.example.com'); + $config = array_merge(array('issuer' => 'https://api.example.com'), $jwtconfig); $server->addResponseType(new JwtAccessToken($memoryStorage, $memoryStorage, null, $config)); return $server; diff --git a/vendor/bshaffer/oauth2-server-php/test/OAuth2/Storage/PdoTest.php b/vendor/bshaffer/oauth2-server-php/test/OAuth2/Storage/PdoTest.php index 57eb39072..4599f69bf 100644 --- a/vendor/bshaffer/oauth2-server-php/test/OAuth2/Storage/PdoTest.php +++ b/vendor/bshaffer/oauth2-server-php/test/OAuth2/Storage/PdoTest.php @@ -6,7 +6,8 @@ class PdoTest extends BaseTest { public function testCreatePdoStorageUsingPdoClass() { - $pdo = new \PDO(sprintf('sqlite://%s', Bootstrap::getInstance()->getSqliteDir())); + $dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir()); + $pdo = new \PDO($dsn); $storage = new Pdo($pdo); $this->assertNotNull($storage->getClientDetails('oauth_test_client')); @@ -14,7 +15,7 @@ class PdoTest extends BaseTest public function testCreatePdoStorageUsingDSN() { - $dsn = sprintf('sqlite://%s', Bootstrap::getInstance()->getSqliteDir()); + $dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir()); $storage = new Pdo($dsn); $this->assertNotNull($storage->getClientDetails('oauth_test_client')); @@ -22,7 +23,8 @@ class PdoTest extends BaseTest public function testCreatePdoStorageUsingConfig() { - $config = array('dsn' => sprintf('sqlite://%s', Bootstrap::getInstance()->getSqliteDir())); + $dsn = sprintf('sqlite:%s', Bootstrap::getInstance()->getSqliteDir()); + $config = array('dsn' => $dsn); $storage = new Pdo($config); $this->assertNotNull($storage->getClientDetails('oauth_test_client')); diff --git a/vendor/bshaffer/oauth2-server-php/test/lib/OAuth2/Storage/Bootstrap.php b/vendor/bshaffer/oauth2-server-php/test/lib/OAuth2/Storage/Bootstrap.php index 3d7bdd4e9..8e428f9b5 100644 --- a/vendor/bshaffer/oauth2-server-php/test/lib/OAuth2/Storage/Bootstrap.php +++ b/vendor/bshaffer/oauth2-server-php/test/lib/OAuth2/Storage/Bootstrap.php @@ -36,7 +36,7 @@ class Bootstrap { if (!$this->sqlite) { $this->removeSqliteDb(); - $pdo = new \PDO(sprintf('sqlite://%s', $this->getSqliteDir())); + $pdo = new \PDO(sprintf('sqlite:%s', $this->getSqliteDir())); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->createSqliteDb($pdo); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 86acbb152..ff64c904f 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -380,9 +380,6 @@ return array( 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', 'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php', 'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php', 'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php', @@ -1343,6 +1340,8 @@ return array( 'Zotlabs\\Update\\_1226' => $baseDir . '/Zotlabs/Update/_1226.php', 'Zotlabs\\Update\\_1227' => $baseDir . '/Zotlabs/Update/_1227.php', 'Zotlabs\\Update\\_1228' => $baseDir . '/Zotlabs/Update/_1228.php', + 'Zotlabs\\Update\\_1229' => $baseDir . '/Zotlabs/Update/_1229.php', + 'Zotlabs\\Update\\_1230' => $baseDir . '/Zotlabs/Update/_1230.php', 'Zotlabs\\Web\\Controller' => $baseDir . '/Zotlabs/Web/Controller.php', 'Zotlabs\\Web\\HTTPHeaders' => $baseDir . '/Zotlabs/Web/HTTPHeaders.php', 'Zotlabs\\Web\\HTTPSig' => $baseDir . '/Zotlabs/Web/HTTPSig.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index ea23de753..69c31dd65 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -548,9 +548,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', 'Ramsey\\Uuid\\BinaryUtils' => __DIR__ . '/..' . '/ramsey/uuid/src/BinaryUtils.php', 'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php', 'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php', @@ -1511,6 +1508,8 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d 'Zotlabs\\Update\\_1226' => __DIR__ . '/../..' . '/Zotlabs/Update/_1226.php', 'Zotlabs\\Update\\_1227' => __DIR__ . '/../..' . '/Zotlabs/Update/_1227.php', 'Zotlabs\\Update\\_1228' => __DIR__ . '/../..' . '/Zotlabs/Update/_1228.php', + 'Zotlabs\\Update\\_1229' => __DIR__ . '/../..' . '/Zotlabs/Update/_1229.php', + 'Zotlabs\\Update\\_1230' => __DIR__ . '/../..' . '/Zotlabs/Update/_1230.php', 'Zotlabs\\Web\\Controller' => __DIR__ . '/../..' . '/Zotlabs/Web/Controller.php', 'Zotlabs\\Web\\HTTPHeaders' => __DIR__ . '/../..' . '/Zotlabs/Web/HTTPHeaders.php', 'Zotlabs\\Web\\HTTPSig' => __DIR__ . '/../..' . '/Zotlabs/Web/HTTPSig.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index d2f86ea99..7f829f6a4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -58,17 +58,17 @@ }, { "name": "bshaffer/oauth2-server-php", - "version": "v1.10.0", - "version_normalized": "1.10.0.0", + "version": "v1.11.1", + "version_normalized": "1.11.1.0", "source": { "type": "git", "url": "https://github.com/bshaffer/oauth2-server-php.git", - "reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9" + "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/d158878425392fe5a0cc34f15dbaf46315ae0ed9", - "reference": "d158878425392fe5a0cc34f15dbaf46315ae0ed9", + "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/5a0c8000d4763b276919e2106f54eddda6bc50fa", + "reference": "5a0c8000d4763b276919e2106f54eddda6bc50fa", "shasum": "" }, "require": { @@ -89,7 +89,7 @@ "predis/predis": "Required to use Redis storage", "thobbs/phpcassa": "Required to use Cassandra storage" }, - "time": "2017-11-15T01:41:02+00:00", + "time": "2018-12-04T00:29:32+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -254,17 +254,17 @@ }, { "name": "league/html-to-markdown", - "version": "4.8.0", - "version_normalized": "4.8.0.0", + "version": "4.8.1", + "version_normalized": "4.8.1.0", "source": { "type": "git", "url": "https://github.com/thephpleague/html-to-markdown.git", - "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d" + "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/f9a879a068c68ff47b722de63f58bec79e448f9d", - "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/250d1bf45f80d15594fb6b316df777d6d4c97ad1", + "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1", "shasum": "" }, "require": { @@ -277,7 +277,7 @@ "phpunit/phpunit": "4.*", "scrutinizer/ocular": "~1.1" }, - "time": "2018-09-18T12:18:08+00:00", + "time": "2018-12-24T17:21:44+00:00", "bin": [ "bin/html-to-markdown" ], diff --git a/vendor/league/html-to-markdown/CHANGELOG.md b/vendor/league/html-to-markdown/CHANGELOG.md index ab07c94f5..e1893be9a 100644 --- a/vendor/league/html-to-markdown/CHANGELOG.md +++ b/vendor/league/html-to-markdown/CHANGELOG.md @@ -4,6 +4,14 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip ## [Unreleased][unreleased] +## [4.8.1] - 2018-12-24 +### Added + - Added support for PHP 7.3 + +### Fixed + - Fixed paragraphs following tables (#165, #166) + - Fixed incorrect list item escaping (#168, #169) + ## [4.8.0] - 2018-09-18 ### Added - Added support for email auto-linking @@ -235,7 +243,8 @@ not ideally set, so this releases fixes that. Moving forwards this should reduce ### Added - Initial release -[unreleased]: https://github.com/thephpleague/html-to-markdown/compare/4.8.0...master +[unreleased]: https://github.com/thephpleague/html-to-markdown/compare/4.8.1...master +[4.8.1]: https://github.com/thephpleague/html-to-markdown/compare/4.8.0...4.8.1 [4.8.0]: https://github.com/thephpleague/html-to-markdown/compare/4.7.0...4.8.0 [4.7.0]: https://github.com/thephpleague/html-to-markdown/compare/4.6.2...4.7.0 [4.6.2]: https://github.com/thephpleague/html-to-markdown/compare/4.6.1...4.6.2 diff --git a/vendor/league/html-to-markdown/src/Converter/DefaultConverter.php b/vendor/league/html-to-markdown/src/Converter/DefaultConverter.php index 964a71093..8de0af210 100644 --- a/vendor/league/html-to-markdown/src/Converter/DefaultConverter.php +++ b/vendor/league/html-to-markdown/src/Converter/DefaultConverter.php @@ -37,7 +37,13 @@ class DefaultConverter implements ConverterInterface, ConfigurationAwareInterfac return $element->getValue(); } - return html_entity_decode($element->getChildrenAsString()); + $markdown = html_entity_decode($element->getChildrenAsString()); + + if ($element->getTagName() === 'table') { + $markdown .= "\n\n"; + } + + return $markdown; } /** diff --git a/vendor/league/html-to-markdown/src/Converter/HardBreakConverter.php b/vendor/league/html-to-markdown/src/Converter/HardBreakConverter.php index d079d9127..1be10bd63 100644 --- a/vendor/league/html-to-markdown/src/Converter/HardBreakConverter.php +++ b/vendor/league/html-to-markdown/src/Converter/HardBreakConverter.php @@ -35,7 +35,10 @@ class HardBreakConverter implements ConverterInterface, ConfigurationAwareInterf $next_value = $next->getValue(); if ($next_value) { if (in_array(substr($next_value, 0, 2), array('- ', '* ', '+ '))) { - $return .= '\\'; + $parent = $element->getParent(); + if ($parent && $parent->getTagName() == 'li') { + $return .= '\\'; + } } } } diff --git a/view/js/autocomplete.js b/view/js/autocomplete.js index 6b77f0631..278a0a176 100644 --- a/view/js/autocomplete.js +++ b/view/js/autocomplete.js @@ -216,8 +216,6 @@ function string2bb(element) { }); textcomplete.register([contacts,forums,smilies,tags]); }); - - }; })( jQuery ); diff --git a/view/js/main.js b/view/js/main.js index a69bcfa64..4718fb98f 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -44,6 +44,17 @@ $(document).ready(function() { $(document).on('click', '.conversation-settings-link', getConversationSettings); $(document).on('click', '#settings_module_ajax_submit', postConversationSettings); + $(document).on('click focus', '.comment-edit-form textarea', function(e) { + if(! this.autocomplete_handled) { + /* autocomplete @nicknames */ + $(this).editor_autocomplete(baseurl+"/acl?f=&n=1"); + /* autocomplete bbcode */ + $(this).bbco_autocomplete('bbcode'); + + this.autocomplete_handled = true; + } + }); + var tf = new Function('n', 's', 'var k = s.split("/")['+aStr['plural_func']+']; return (k ? k : s);'); jQuery.timeago.settings.strings = { @@ -239,7 +250,11 @@ function handle_comment_form(e) { },10000); }); - function commentSaveChanges(convId,isFinal = false) { + function commentSaveChanges(convId, isFinal) { + + if(typeof isFinal === 'undefined') + isFinal = false; + if(auto_save_draft) { tmp = $('#' + emptyCommentElm).val(); if(tmp) { @@ -453,6 +468,9 @@ function notificationsUpdate(cached_data) { $.get(pingCmd,function(data) { // Put the object into storage + if(! data) + return; + sessionStorage.setItem('notifications_cache', JSON.stringify(data)); var fnotifs = []; @@ -716,6 +734,7 @@ function updateConvItems(mode,data) { title.replace(/\s+$/, ''); if (title) { savedTitle = title + " " + savedTitle; + document.title = title; } } } @@ -751,12 +770,7 @@ function updateConvItems(mode,data) { mediaPlaying = false; }); - /* autocomplete @nicknames */ - $(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl?f=&n=1"); - /* autocomplete bbcode */ - $(".comment-edit-form textarea").bbco_autocomplete('bbcode'); - - var bimgs = ((preloadImages) ? false : $(".wall-item-body img").not(function() { return this.complete; })); + var bimgs = ((preloadImages) ? false : $(".wall-item-body img, .wall-photo-item img").not(function() { return this.complete; })); var bimgcount = bimgs.length; if (bimgcount) { @@ -832,10 +846,10 @@ function collapseHeight() { }); var collapsedContentHeight = Math.ceil($("#region_2").height()); - contentHeightDiff = origContentHeight - collapsedContentHeight; + contentHeightDiff = liking ? 0 : origContentHeight - collapsedContentHeight; console.log('collapseHeight() - contentHeightDiff: ' + contentHeightDiff + 'px'); - if(i){ + if(i && !liking){ var sval = position - cDiff + ($(".divgrow-showmore").outerHeight() * i); console.log('collapsed above viewport count: ' + i); $(window).scrollTop(sval); @@ -888,7 +902,12 @@ function liveUpdate(notify_id) { if((src === null) || (stopped) || (! profile_uid)) { $('.like-rotator').hide(); return; } - if(($('.comment-edit-text.expanded').length) || (in_progress) || (mediaPlaying)) { + // if auto updates are enabled and a comment box is open, + // prevent live updates until the comment is submitted + + var lockUpdates = (($('.comment-edit-text.expanded').length && (! bParam_static)) ? true : false); + + if(lockUpdates || in_progress || mediaPlaying) { if(livetime) { clearTimeout(livetime); } @@ -981,7 +1000,7 @@ function liveUpdate(notify_id) { $("#profile-jot-text-loading").hide(); // adjust scroll position if new content was added above viewport - if(update_mode === 'update') { + if(update_mode === 'update' && !justifiedGalleryActive) { $(window).scrollTop($(window).scrollTop() + $("#region_2").height() - orgHeight + contentHeightDiff); } diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index b406e710c..18c3db665 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -1806,6 +1806,5 @@ dl.bb-dl > dd > li { /* default highlighted text if not specified by schema: */ span.default-highlight { - background-color: yellow; - padding: 2px 4px; + background-color: yellow; } diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 7dddf9c01..428529de2 100755 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -9,12 +9,12 @@ <div class="wall-item-outside-wrapper{{if $item.is_comment}} comment{{/if}}{{if $item.previewing}} preview{{/if}}" id="wall-item-outside-wrapper-{{$item.id}}" > <div class="clearfix wall-item-content-wrapper{{if $item.is_comment}} comment{{/if}}" id="wall-item-content-wrapper-{{$item.id}}"> {{if $item.photo}} - <div class="wall-photo-item" id="wall-photo-item-{{$item.id}}"> + <div class="wall-photo-item{{if $item.is_new && !$item.title}} wall-item-head-new rounded-top{{/if}}" id="wall-photo-item-{{$item.id}}"> {{$item.photo}} </div> {{/if}} {{if $item.event}} - <div class="wall-event-item" id="wall-event-item-{{$item.id}}"> + <div class="wall-event-item{{if $item.is_new && !$item.title}} wall-item-head-new rounded-top{{/if}}" id="wall-event-item-{{$item.id}}"> {{$item.event}} </div> {{/if}} @@ -26,7 +26,7 @@ <hr class="m-0"> {{/if}} {{/if}} - <div class="p-2 clearfix wall-item-head{{if $item.is_new && !$item.title && !$item.event && !$item.is_comment}} wall-item-head-new rounded-top{{/if}}"> + <div class="p-2 clearfix wall-item-head{{if $item.is_new && !$item.title && !$item.event && !$item.is_comment && !$item.photo}} wall-item-head-new rounded-top{{/if}}"> <div class="wall-item-info " id="wall-item-info-{{$item.id}}" > <div class="wall-item-photo-wrapper{{if $item.owner_url}} wwfrom{{/if}} h-card p-author" id="wall-item-photo-wrapper-{{$item.id}}"> <img src="{{$item.thumb}}" class="fakelink wall-item-photo{{$item.sparkle}} u-photo p-name" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" data-toggle="dropdown" /> @@ -146,6 +146,9 @@ {{if $item.share}} <a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a> {{/if}} + {{if $item.embed}} + <a class="dropdown-item" href="#" onclick="jotEmbed({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-share" title="{{$item.embed.0}}"></i>{{$item.embed.0}}</a> + {{/if}} {{if $item.plink}} <a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a> {{/if}} diff --git a/view/tpl/conv_list.tpl b/view/tpl/conv_list.tpl index c6da3d8a4..63e74b159 100755 --- a/view/tpl/conv_list.tpl +++ b/view/tpl/conv_list.tpl @@ -9,12 +9,12 @@ <div class="wall-item-outside-wrapper{{if $item.is_comment}} comment{{/if}}{{if $item.previewing}} preview{{/if}}" id="wall-item-outside-wrapper-{{$item.id}}" > <div class="clearfix wall-item-content-wrapper{{if $item.is_comment}} comment{{/if}}" id="wall-item-content-wrapper-{{$item.id}}"> {{if $item.photo}} - <div class="wall-photo-item" id="wall-photo-item-{{$item.id}}"> + <div class="wall-photo-item{{if $item.is_new && !$item.title}} wall-item-head-new rounded-top{{/if}}" id="wall-photo-item-{{$item.id}}"> {{$item.photo}} </div> {{/if}} {{if $item.event}} - <div class="wall-event-item" id="wall-event-item-{{$item.id}}"> + <div class="wall-event-item{{if $item.is_new && !$item.title}} wall-item-head-new rounded-top{{/if}}" id="wall-event-item-{{$item.id}}"> {{$item.event}} </div> {{/if}} @@ -26,7 +26,7 @@ <hr class="m-0"> {{/if}} {{/if}} - <div class="p-2 clearfix wall-item-head{{if $item.is_new && !$item.title && !$item.event && !$item.is_comment}} wall-item-head-new rounded-top{{/if}}"> + <div class="p-2 clearfix wall-item-head{{if $item.is_new && !$item.title && !$item.event && !$item.is_comment && !$item.photo}} wall-item-head-new rounded-top{{/if}}"> <div class="wall-item-info" id="wall-item-info-{{$item.id}}" > <div class="wall-item-photo-wrapper{{if $item.owner_url}} wwfrom{{/if}} h-card p-author" id="wall-item-photo-wrapper-{{$item.id}}"> <img src="{{$item.thumb}}" class="fakelink wall-item-photo{{$item.sparkle}} u-photo p-name" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" data-toggle="dropdown" /></a> @@ -139,6 +139,9 @@ {{if $item.share}} <a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a> {{/if}} + {{if $item.embed}} + <a class="dropdown-item" href="#" onclick="jotEmbed({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-share" title="{{$item.embed.0}}"></i>{{$item.embed.0}}</a> + {{/if}} {{if $item.plink}} <a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a> {{/if}} diff --git a/view/tpl/group_edit.tpl b/view/tpl/group_edit.tpl index 88f037abe..60038701e 100755 --- a/view/tpl/group_edit.tpl +++ b/view/tpl/group_edit.tpl @@ -13,6 +13,7 @@ <input type='hidden' name='form_security_token' value='{{$form_security_token_edit}}'> {{include file="field_input.tpl" field=$gname}} {{include file="field_checkbox.tpl" field=$public}} + {{$pgrp_extras}} <a href="group/drop/{{$gid}}?t={{$form_security_token_drop}}" onclick="return confirmDelete();" class="btn btn-sm btn-danger"> {{$delete}} </a> diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl index b286b6071..9a44f1a54 100755 --- a/view/tpl/jot-header.tpl +++ b/view/tpl/jot-header.tpl @@ -198,16 +198,24 @@ var activeCommentText = ''; }) } - function jotShare(id,post_type) { + $('#like-rotator-' + id).show(); + $.get('{{$baseurl}}/share/' + id, function(data) { + $('#like-rotator-' + id).hide(); + updateInit(); + }); + } + + function jotEmbed(id,post_type) { if(post_type == 6) { window.location.href = 'rpost?f=&post_id='+id; } else { + if ($('#jot-popup').length != 0) $('#jot-popup').show(); $('#like-rotator-' + id).show(); - $.get('{{$baseurl}}/share/' + id, function(data) { + $.get('{{$baseurl}}/embed/' + id, function(data) { if (!editor) $("#profile-jot-text").val(""); initEditor(function(){ addeditortext(data); diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl index 4eae33d13..12509fc59 100755 --- a/view/tpl/jot.tpl +++ b/view/tpl/jot.tpl @@ -137,8 +137,11 @@ <i id="profile-nocomment" class="fa fa-comments jot-icons"></i> </button> {{/if}} + {{if $custommoretoolsbuttons}} + {{$custommoretoolsbuttons}} + {{/if}} </div> - {{if $writefiles || $weblink || $setloc || $clearloc || $feature_expire || $feature_encrypt || $feature_voting}} + {{if $writefiles || $weblink || $setloc || $clearloc || $feature_expire || $feature_encrypt || $feature_voting || $custommoretoolsdropdown}} <div class="btn-group d-lg-none"> <button type="button" id="more-tools" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <i id="more-tools-icon" class="fa fa-cog jot-icons"></i> @@ -176,6 +179,8 @@ {{if $feature_nocomment}} <a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i> {{$nocommenttitlesub}}</a> {{/if}} + <hr /> + {{$custommoretoolsdropdown}} </div> </div> {{/if}} @@ -186,6 +191,11 @@ </div> </div> <div id="profile-jot-submit-right" class="btn-group float-right"> + {{foreach $customsubmitright as $csr}} + <button class="btn btn-outline-secondary btn-sm" {{$csr.buttonparams}} title="{{$csr.preview}}"> + {{$csr.buttoncontent}} + </button> + {{/foreach}} {{if $preview}} <button class="btn btn-outline-secondary btn-sm" onclick="preview_post();return false;" title="{{$preview}}"> <i class="fa fa-eye jot-icons" ></i> diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl index 98047f1d9..bc7f80906 100644 --- a/view/tpl/notifications_widget.tpl +++ b/view/tpl/notifications_widget.tpl @@ -24,7 +24,7 @@ }); window.onpopstate = function(e) { - if(e.state !== null) + if(e.state !== null && e.state.b64mid !== bParam_mid) getData(e.state.b64mid, ''); }; }); diff --git a/view/tpl/privacy_groups.tpl b/view/tpl/privacy_groups.tpl index b4e27ef2c..327a15aee 100644 --- a/view/tpl/privacy_groups.tpl +++ b/view/tpl/privacy_groups.tpl @@ -8,6 +8,7 @@ <input type='hidden' name='form_security_token' value='{{$form_security_token}}'> {{include file="field_input.tpl" field=$gname}} {{include file="field_checkbox.tpl" field=$public}} + {{$pgrp_extras}} <button type="submit" name="submit" class="btn btn-sm btn-primary float-right">{{$submit}}</button> </form> </div> diff --git a/view/tpl/remote_friends_common.tpl b/view/tpl/remote_friends_common.tpl index efc0ab49a..6ef3a7dde 100755 --- a/view/tpl/remote_friends_common.tpl +++ b/view/tpl/remote_friends_common.tpl @@ -7,10 +7,9 @@ <div class="contact-block-content"> {{foreach $items as $item}} <div class="contact-block-div"> - <a class="contact-block-link mpfriend" href="{{$base}}/chanview?f=&url={{$item.xchan_url}}"><img class="contact-block-img mpfriend" src="{{$item.xchan_photo_s}}"alt="{{$item.xchan_name}}" title="{{$item.xchan_name}} [{{$item.xchan_addr}}]" /></a> + <a class="contact-block-link mpfriend" href="{{$base}}/chanview?f=&url={{$item.xchan_url}}"><img class="contact-block-img mpfriend" src="{{$item.xchan_photo_s}}" alt="{{$item.xchan_name}}" title="{{$item.xchan_name}} [{{$item.xchan_addr}}]" /></a> </div> {{/foreach}} </div> {{/if}} </div> - |