diff options
102 files changed, 2456 insertions, 1189 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..9edd00667 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,10 +309,12 @@ class Apps { if($ret) { if($translate) self::translate_system_apps($ret); + return $ret; } + return false; - } + } static public function translate_system_apps(&$arr) { @@ -309,17 +330,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 +385,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 +434,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 +447,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 +528,7 @@ class Apps { if($x) { $hosturl = $x['scheme'] . '://' . $x['host'] . '/'; } - } + } } $install_action = (($installed) ? t('Update') : t('Install')); @@ -590,8 +611,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 +626,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 +638,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 +646,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 +654,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 +676,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 +701,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 +733,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 +779,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 +804,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 +848,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 +865,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 +892,7 @@ class Apps { } } - return($r); + return $r; } static public function app_order($uid,$apps,$menu) { @@ -837,13 +924,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 +940,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 +959,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 +972,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -885,6 +980,7 @@ class Apps { } if(! $position) return; + $dest_position = $position - 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -896,11 +992,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 +1013,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 +1026,6 @@ class Apps { if(! $syslist) return; - $newlist = []; - foreach($syslist as $k => $li) { if($li['guid'] === $guid) { $position = $k; @@ -932,6 +1034,7 @@ class Apps { } if($position >= count($syslist) - 1) return; + $dest_position = $position + 1; $saved = $syslist[$dest_position]; $syslist[$dest_position] = $syslist[$position]; @@ -943,7 +1046,6 @@ class Apps { } set_pconfig($uid,'system',$conf,implode(',',$narr)); - } static public function app_decode($s) { @@ -951,8 +1053,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 +1068,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 +1262,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 +1308,7 @@ class Apps { if($app['app_price']) $ret['price'] = $app['app_price']; - + if($app['app_page']) $ret['page'] = $app['app_page']; @@ -1224,12 +1332,12 @@ class Apps { foreach($app['term'] as $t) { if($s) $s .= ','; + $s .= $t['term']; } $ret['categories'] = $s; } - if(! $embed) return $ret; @@ -1237,18 +1345,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..bdbfc4385 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -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,16 @@ class ThreadItem { $privacy_warning = true; } + if ($lock) { + if (count(get_terms_oftype($item['term'],TERM_FORUM))) { + $privacy_warning = true; + } + } + + if ($lock && $privacy_warning) { + $lock = t('Privacy conflict. Discretion advised.'); + } + $mode = $conv->get_mode(); switch($item['item_type']) { @@ -293,8 +304,14 @@ 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 = array( t('Repeat This'), t('repeat')); + } + $embed = array( t('Share This'), t('share')); + } $dreport = ''; @@ -408,12 +425,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, 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/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/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/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/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/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/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; } - - - - - } @@ -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/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> - |