aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs')
-rw-r--r--Zotlabs/Daemon/Cache_embeds.php27
-rw-r--r--Zotlabs/Daemon/Cron.php23
-rw-r--r--Zotlabs/Daemon/Master.php129
-rw-r--r--Zotlabs/Daemon/Notifier.php20
-rw-r--r--Zotlabs/Daemon/Onepoll.php2
-rw-r--r--Zotlabs/Daemon/Poller.php2
-rw-r--r--Zotlabs/Daemon/Queue.php15
-rw-r--r--Zotlabs/Identity/OAuth2Storage.php2
-rw-r--r--Zotlabs/Lib/Activity.php640
-rw-r--r--Zotlabs/Lib/ActivityStreams.php20
-rw-r--r--Zotlabs/Lib/Apps.php410
-rw-r--r--Zotlabs/Lib/DReport.php18
-rw-r--r--Zotlabs/Lib/Enotify.php1
-rw-r--r--Zotlabs/Lib/Libzot.php302
-rw-r--r--Zotlabs/Lib/PConfig.php38
-rw-r--r--Zotlabs/Lib/Share.php1
-rw-r--r--Zotlabs/Lib/ThreadItem.php34
-rw-r--r--Zotlabs/Lib/ThreadListener.php53
-rw-r--r--Zotlabs/Lib/ZotURL.php91
-rw-r--r--Zotlabs/Module/Admin.php2
-rw-r--r--Zotlabs/Module/Admin/Addons.php5
-rw-r--r--Zotlabs/Module/Admin/Site.php2
-rw-r--r--Zotlabs/Module/Affinity.php94
-rw-r--r--Zotlabs/Module/Appman.php6
-rw-r--r--Zotlabs/Module/Apporder.php33
-rw-r--r--Zotlabs/Module/Articles.php12
-rw-r--r--Zotlabs/Module/Cards.php16
-rw-r--r--Zotlabs/Module/Channel.php8
-rw-r--r--Zotlabs/Module/Chanview.php2
-rw-r--r--Zotlabs/Module/Connedit.php29
-rw-r--r--Zotlabs/Module/Display.php8
-rw-r--r--Zotlabs/Module/Dreport.php3
-rw-r--r--Zotlabs/Module/Embed.php22
-rw-r--r--Zotlabs/Module/Embedphotos.php122
-rw-r--r--Zotlabs/Module/Events.php4
-rw-r--r--Zotlabs/Module/Filestorage.php40
-rw-r--r--Zotlabs/Module/Group.php21
-rw-r--r--Zotlabs/Module/Id.php119
-rw-r--r--Zotlabs/Module/Import.php9
-rw-r--r--Zotlabs/Module/Item.php157
-rw-r--r--Zotlabs/Module/Like.php8
-rw-r--r--Zotlabs/Module/Linkinfo.php6
-rw-r--r--Zotlabs/Module/Mail.php4
-rw-r--r--Zotlabs/Module/Network.php13
-rw-r--r--Zotlabs/Module/New_channel.php4
-rw-r--r--Zotlabs/Module/Notes.php20
-rw-r--r--Zotlabs/Module/Oep.php11
-rw-r--r--Zotlabs/Module/Pconfig.php5
-rw-r--r--Zotlabs/Module/Photo.php89
-rw-r--r--Zotlabs/Module/Photos.php17
-rw-r--r--Zotlabs/Module/Ping.php14
-rw-r--r--Zotlabs/Module/Profiles.php28
-rw-r--r--Zotlabs/Module/Rmagic.php18
-rw-r--r--Zotlabs/Module/Settings/Featured.php34
-rw-r--r--Zotlabs/Module/Setup.php5
-rw-r--r--Zotlabs/Module/Share.php140
-rw-r--r--Zotlabs/Module/Sslify.php14
-rw-r--r--Zotlabs/Module/Viewsrc.php6
-rw-r--r--Zotlabs/Module/Wfinger.php3
-rw-r--r--Zotlabs/Module/Wiki.php4
-rw-r--r--Zotlabs/Photo/PhotoDriver.php498
-rw-r--r--Zotlabs/Photo/PhotoGd.php176
-rw-r--r--Zotlabs/Photo/PhotoImagick.php200
-rw-r--r--Zotlabs/Storage/Browser.php14
-rw-r--r--Zotlabs/Update/_1228.php4
-rw-r--r--Zotlabs/Update/_1229.php32
-rw-r--r--Zotlabs/Update/_1230.php12
-rw-r--r--Zotlabs/Web/Session.php46
-rw-r--r--Zotlabs/Widget/Affinity.php84
-rw-r--r--Zotlabs/Widget/Notifications.php8
-rw-r--r--Zotlabs/Widget/Settings_menu.php7
-rw-r--r--Zotlabs/Zot/Finger.php2
-rw-r--r--Zotlabs/Zot6/Zot6Handler.php36
73 files changed, 3196 insertions, 908 deletions
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/Cron.php b/Zotlabs/Daemon/Cron.php
index 25e49b817..8b6b42c8a 100644
--- a/Zotlabs/Daemon/Cron.php
+++ b/Zotlabs/Daemon/Cron.php
@@ -94,6 +94,29 @@ class Cron {
@time_sleep_until(microtime(true) + (float) $interval);
}
}
+
+ // Clean expired photos from cache
+
+ $age = get_config('system','active_expire_days', '30');
+ $r = q("SELECT DISTINCT xchan, content FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
+ intval(PHOTO_CACHE),
+ db_utcnow(),
+ db_quoteinterval($age . ' DAY')
+ );
+ if($r) {
+ foreach($r as $rr) {
+ $file = dbunescbin($rr['content']);
+ if(is_file($file)) {
+ @unlink($file);
+ logger('info: deleted cached photo file ' . $file, LOGGER_DEBUG);
+ }
+ }
+ }
+ q("DELETE FROM photo WHERE photo_usage = %d AND expires < %s - INTERVAL %s",
+ intval(PHOTO_CACHE),
+ db_utcnow(),
+ db_quoteinterval($age . ' DAY')
+ );
// publish any applicable items that were set to be published in the future
// (time travel posts). Restrict to items that have come of age in the last
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 8b81c49da..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');
@@ -625,7 +627,12 @@ class Notifier {
continue;
}
- $hash = new_uuid();
+ // Do not change this to a uuid as long as we have traditional zot servers
+ // in the loop. The signature verification step can't handle dashes in the
+ // hashes.
+
+ $hash = random_string(48);
+
$packet = null;
$pmsg = '';
@@ -672,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/Identity/OAuth2Storage.php b/Zotlabs/Identity/OAuth2Storage.php
index bbf61cf2b..a4ba9c526 100644
--- a/Zotlabs/Identity/OAuth2Storage.php
+++ b/Zotlabs/Identity/OAuth2Storage.php
@@ -64,7 +64,7 @@ class OAuth2Storage extends \OAuth2\Storage\Pdo {
return( [
'webfinger' => channel_reddress($x),
'portable_id' => $x['channel_hash'],
- 'email' => $a['account_email'],
+ 'email' => $a[0]['account_email'],
'username' => $x['channel_address'],
'user_id' => $x['channel_id'],
'name' => $x['channel_name'],
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php
index 6ddbbb9db..9aaf6d866 100644
--- a/Zotlabs/Lib/Activity.php
+++ b/Zotlabs/Lib/Activity.php
@@ -2,18 +2,21 @@
namespace Zotlabs\Lib;
-use Zotlabs\Lib\Libzot;
-use Zotlabs\Lib\Libsync;
-use Zotlabs\Lib\ActivityStreams;
-use Zotlabs\Lib\Group;
+use Zotlabs\Zot6\HTTPSig;
class Activity {
static function encode_object($x) {
+
if(($x) && (! is_array($x)) && (substr(trim($x),0,1)) === '{' ) {
$x = json_decode($x,true);
}
+
+ if(is_array($x) && array_key_exists('asld',$x)) {
+ $x = $x['asld'];
+ }
+
if($x['type'] === ACTIVITY_OBJ_PERSON) {
return self::fetch_person($x);
}
@@ -26,11 +29,57 @@ 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;
}
+ static function fetch($url,$channel = null) {
+ $redirects = 0;
+ if(! check_siteallowed($url)) {
+ logger('blacklisted: ' . $url);
+ return null;
+ }
+ if(! $channel) {
+ $channel = get_sys_channel();
+ }
+
+ logger('fetch: ' . $url, LOGGER_DEBUG);
+
+ if(strpos($url,'x-zot:') === 0) {
+ $x = ZotURL::fetch($url,$channel);
+ }
+ else {
+ $m = parse_url($url);
+ $headers = [
+ 'Accept' => 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+ 'Host' => $m['host'],
+ '(request-target)' => 'get ' . get_request_string($url),
+ 'Date' => datetime_convert('UTC','UTC','now','D, d M Y H:i:s') . ' UTC'
+ ];
+ $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
+ $x = z_fetch_url($url, true, $redirects, [ 'headers' => $h ] );
+ }
+
+ if($x['success']) {
+ $y = json_decode($x['body'],true);
+ logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
+ return json_decode($x['body'], true);
+ }
+ else {
+ logger('fetch failed: ' . $url);
+ }
+ return null;
+ }
+
+
+
static function fetch_person($x) {
return self::fetch_profile($x);
@@ -93,6 +142,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 = [
@@ -167,7 +273,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'])
@@ -342,10 +448,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'])
@@ -417,6 +530,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;
@@ -540,6 +657,12 @@ class Activity {
}
+
+
+
+
+
+
static function activity_mapper($verb) {
if(strpos($verb,'/') === false) {
@@ -556,6 +679,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'
];
@@ -582,6 +708,70 @@ class Activity {
}
+
+ static function activity_decode_mapper($verb) {
+
+ $acts = [
+ 'http://activitystrea.ms/schema/1.0/post' => 'Create',
+ 'http://activitystrea.ms/schema/1.0/share' => 'Announce',
+ 'http://activitystrea.ms/schema/1.0/update' => 'Update',
+ 'http://activitystrea.ms/schema/1.0/like' => 'Like',
+ 'http://activitystrea.ms/schema/1.0/favorite' => 'Like',
+ 'http://purl.org/zot/activity/dislike' => 'Dislike',
+ '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'
+ ];
+
+
+ foreach($acts as $k => $v) {
+ if($verb === $v) {
+ return $k;
+ }
+ }
+
+ logger('Unmapped activity: ' . $verb);
+ return 'Create';
+
+ }
+
+ static function activity_obj_decode_mapper($obj) {
+
+ $objs = [
+ 'http://activitystrea.ms/schema/1.0/note' => 'Note',
+ 'http://activitystrea.ms/schema/1.0/note' => 'Article',
+ 'http://activitystrea.ms/schema/1.0/comment' => 'Note',
+ 'http://activitystrea.ms/schema/1.0/person' => 'Person',
+ 'http://purl.org/zot/activity/profile' => 'Profile',
+ 'http://activitystrea.ms/schema/1.0/photo' => 'Image',
+ 'http://activitystrea.ms/schema/1.0/profile-photo' => 'Icon',
+ 'http://activitystrea.ms/schema/1.0/event' => 'Event',
+ 'http://activitystrea.ms/schema/1.0/wiki' => 'Document',
+ 'http://purl.org/zot/activity/location' => 'Place',
+ 'http://purl.org/zot/activity/chessgame' => 'Game',
+ 'http://purl.org/zot/activity/tagterm' => 'zot:Tag',
+ 'http://purl.org/zot/activity/thing' => 'Object',
+ 'http://purl.org/zot/activity/file' => 'zot:File',
+ 'http://purl.org/zot/activity/mood' => 'zot:Mood',
+
+ ];
+
+ foreach($objs as $k => $v) {
+ if($obj === $v) {
+ return $k;
+ }
+ }
+
+ logger('Unmapped activity object: ' . $obj);
+ return 'Note';
+ }
+
+
+
+
static function activity_obj_mapper($obj) {
if(strpos($obj,'/') === false) {
@@ -1149,7 +1339,7 @@ class Activity {
}
if($act->obj['type'] === 'Note' && $s['attach']) {
- $s['body'] .= self::bb_attach($s['attach']);
+ $s['body'] .= self::bb_attach($s['attach'],$s['body']);
}
// we will need a hook here to extract magnet links e.g. peertube
@@ -1230,21 +1420,39 @@ class Activity {
}
+ static function get_actor_bbmention($id) {
- static function decode_note($act) {
+ $x = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash where hubloc_hash = '%s' or hubloc_id_url = '%s' limit 1",
+ dbesc($id),
+ dbesc($id)
+ );
- $s = [];
+ if($x) {
+ return sprintf('@[zrl=%s]%s[/zrl]',$x[0]['xchan_url'],$x[0]['xchan_name']);
+ }
+ return '@{' . $id . '}';
+ }
- $content = self::get_content($act->obj);
+ static function decode_note($act) {
+
+ $response_activity = false;
+
+ $s = [];
+ if(is_array($act->obj)) {
+ $content = self::get_content($act->obj);
+ }
+
$s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id'];
- $s['mid'] = $act->id;
- $s['parent_mid'] = $act->parent_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;
if($act->data['published']) {
$s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
@@ -1259,105 +1467,325 @@ class Activity {
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
}
- if(! $s['created'])
- $s['created'] = datetime_convert();
- if(! $s['edited'])
- $s['edited'] = $s['created'];
+ if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept' ])) {
- if(in_array($act->type,['Announce'])) {
- $root_content = self::get_content($act->raw);
+ $response_activity = true;
- $s['title'] = self::bb_content($root_content,'name');
- $s['summary'] = self::bb_content($root_content,'summary');
- $s['body'] = (self::bb_content($root_content,'bbcode') ? : self::bb_content($root_content,'content'));
+ $s['mid'] = $act->id;
+ $s['parent_mid'] = $act->obj['id'];
- if(strpos($s['body'],'[share') === false) {
+ // over-ride the object timestamp with the activity
- // @fixme - error check and set defaults
+ if($act->data['published']) {
+ $s['created'] = datetime_convert('UTC','UTC',$act->data['published']);
+ }
- $name = urlencode($act->obj['actor']['name']);
- $profile = $act->obj['actor']['id'];
- $photo = $act->obj['icon']['url'];
+ if($act->data['updated']) {
+ $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']);
+ }
+
+ $obj_actor = ((isset($act->obj['actor'])) ? $act->obj['actor'] : $act->get_actor('attributedTo', $act->obj));
+ // ensure we store the original actor
+ self::actor_store($obj_actor['id'],$obj_actor);
- $s['body'] .= "\r\n[share author='" . $name .
- "' profile='" . $profile .
- "' avatar='" . $photo .
- "' link='" . $act->obj['id'] .
- "' auth='" . ((is_matrix_url($act->obj['id'])) ? 'true' : 'false' ) .
- "' posted='" . $act->obj['published'] .
- "' message_id='" . $act->obj['id'] .
- "']";
+ $mention = self::get_actor_bbmention($obj_actor['id']);
+
+ if($act->type === 'Like') {
+ $content['content'] = sprintf( t('Likes %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+ }
+ if($act->type === 'Dislike') {
+ $content['content'] = sprintf( t('Doesn\'t like %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+ }
+ if($act->type === 'Accept' && $act->obj['type'] === 'Event' ) {
+ $content['content'] = sprintf( t('Will attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+ }
+ if($act->type === 'Reject' && $act->obj['type'] === 'Event' ) {
+ $content['content'] = sprintf( t('Will not attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+ }
+ if($act->type === 'TentativeAccept' && $act->obj['type'] === 'Event' ) {
+ $content['content'] = sprintf( t('May attend %1$s\'s %2$s'),$mention,$act->obj['type']) . "\n\n" . $content['content'];
+ }
+ if($act->type === 'Announce') {
+ $content['content'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
}
}
- else {
- $s['title'] = self::bb_content($content,'name');
- $s['summary'] = self::bb_content($content,'summary');
- $s['body'] = (self::bb_content($content,'bbcode') ? : self::bb_content($content,'content'));
- }
- $s['verb'] = self::activity_mapper($act->type);
+ if(! $s['created'])
+ $s['created'] = datetime_convert();
+
+ if(! $s['edited'])
+ $s['edited'] = $s['created'];
+
+ $s['title'] = self::bb_content($content,'name');
+ $s['summary'] = self::bb_content($content,'summary');
+ $s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content'));
- if($act->type === 'Tombstone') {
+ $s['verb'] = self::activity_decode_mapper($act->type);
+
+
+ if($act->type === 'Tombstone' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) {
$s['item_deleted'] = 1;
}
- $s['obj_type'] = self::activity_obj_mapper($act->obj['type']);
- $s['obj'] = $act->obj;
+ $s['obj_type'] = self::activity_obj_decode_mapper($act->obj['type']);
+ if($s['obj_type'] === ACTIVITY_OBJ_NOTE && $s['mid'] !== $s['parent_mid']) {
+ $s['obj_type'] = ACTIVITY_OBJ_COMMENT;
+ }
+
+ 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)
+ if((! $instrument) && (! $response_activity)) {
$instrument = $act->get_property_obj('instrument',$act->obj);
+ }
if($instrument && array_key_exists('type',$instrument)
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) {
$s['app'] = escape_tags($instrument['name']);
}
- $a = self::decode_taxonomy($act->obj);
- if($a) {
- $s['term'] = $a;
+
+ if(! $response_activity) {
+ $a = self::decode_taxonomy($act->obj);
+ if($a) {
+ $s['term'] = $a;
+ foreach($a as $b) {
+ if($b['ttype'] === TERM_EMOJI) {
+ $s['title'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['title']);
+ $s['summary'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['summary']);
+ $s['body'] = str_replace($b['term'],'[img=16x16]' . $b['url'] . '[/img]',$s['body']);
+ }
+ }
+ }
+
+ $a = self::decode_attachment($act->obj);
+ if($a) {
+ $s['attach'] = $a;
+ }
}
- $a = self::decode_attachment($act->obj);
- if($a) {
- $s['attach'] = $a;
+ if($act->obj['type'] === 'Note' && $s['attach']) {
+ $s['body'] .= self::bb_attach($s['attach'],$s['body']);
}
+
// we will need a hook here to extract magnet links e.g. peertube
// right now just link to the largest mp4 we find that will fit in our
// standard content region
- if($act->obj['type'] === 'Video') {
+ if(! $response_activity) {
+ if($act->obj['type'] === 'Video') {
- $vtypes = [
- 'video/mp4',
- 'video/ogg',
- 'video/webm'
- ];
+ $vtypes = [
+ 'video/mp4',
+ 'video/ogg',
+ 'video/webm'
+ ];
- $mps = [];
- if(array_key_exists('url',$act->obj) && is_array($act->obj['url'])) {
- foreach($act->obj['url'] as $vurl) {
- if(in_array($vurl['mimeType'], $vtypes)) {
- if(! array_key_exists('width',$vurl)) {
- $vurl['width'] = 0;
+ $mps = [];
+ $ptr = null;
+
+ if(array_key_exists('url',$act->obj)) {
+ if(is_array($act->obj['url'])) {
+ if(array_key_exists(0,$act->obj['url'])) {
+ $ptr = $act->obj['url'];
}
- $mps[] = $vurl;
+ else {
+ $ptr = [ $act->obj['url'] ];
+ }
+ foreach($ptr as $vurl) {
+ // peertube uses the non-standard element name 'mimeType' here
+ if(array_key_exists('mimeType',$vurl)) {
+ if(in_array($vurl['mimeType'], $vtypes)) {
+ if(! array_key_exists('width',$vurl)) {
+ $vurl['width'] = 0;
+ }
+ $mps[] = $vurl;
+ }
+ }
+ elseif(array_key_exists('mediaType',$vurl)) {
+ if(in_array($vurl['mediaType'], $vtypes)) {
+ if(! array_key_exists('width',$vurl)) {
+ $vurl['width'] = 0;
+ }
+ $mps[] = $vurl;
+ }
+ }
+ }
+ }
+ if($mps) {
+ usort($mps,[ __CLASS__, 'vid_sort' ]);
+ foreach($mps as $m) {
+ if(intval($m['width']) < 500 && self::media_not_in_body($m['href'],$s['body'])) {
+ $s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
+ break;
+ }
+ }
+ }
+ elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) {
+ $s['body'] .= "\n\n" . '[video]' . $act->obj['url'] . '[/video]';
}
}
}
- if($mps) {
- usort($mps,'as_vid_sort');
- foreach($mps as $m) {
- if(intval($m['width']) < 500) {
- $s['body'] .= "\n\n" . '[video]' . $m['href'] . '[/video]';
- break;
+
+ if($act->obj['type'] === 'Audio') {
+
+ $atypes = [
+ 'audio/mpeg',
+ 'audio/ogg',
+ 'audio/wav'
+ ];
+
+ $ptr = null;
+
+ if(array_key_exists('url',$act->obj)) {
+ if(is_array($act->obj['url'])) {
+ if(array_key_exists(0,$act->obj['url'])) {
+ $ptr = $act->obj['url'];
+ }
+ else {
+ $ptr = [ $act->obj['url'] ];
+ }
+ foreach($ptr as $vurl) {
+ if(in_array($vurl['mediaType'], $atypes) && self::media_not_in_body($vurl['href'],$s['body'])) {
+ $s['body'] .= "\n\n" . '[audio]' . $vurl['href'] . '[/audio]';
+ break;
+ }
+ }
+ }
+ elseif(is_string($act->obj['url']) && self::media_not_in_body($act->obj['url'],$s['body'])) {
+ $s['body'] .= "\n\n" . '[audio]' . $act->obj['url'] . '[/audio]';
+ }
+ }
+
+ }
+
+ // avoid double images from hubzilla to zap/osada
+
+ if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) {
+
+ $ptr = null;
+
+ if(array_key_exists('url',$act->obj)) {
+ if(is_array($act->obj['url'])) {
+ if(array_key_exists(0,$act->obj['url'])) {
+ $ptr = $act->obj['url'];
+ }
+ else {
+ $ptr = [ $act->obj['url'] ];
+ }
+ foreach($ptr as $vurl) {
+ if(strpos($s['body'],$vurl['href']) === false) {
+ $s['body'] .= "\n\n" . '[zmg]' . $vurl['href'] . '[/zmg]';
+ break;
+ }
+ }
+ }
+ elseif(is_string($act->obj['url'])) {
+ if(strpos($s['body'],$act->obj['url']) === false) {
+ $s['body'] .= "\n\n" . '[zmg]' . $act->obj['url'] . '[/zmg]';
+ }
+ }
+ }
+ }
+
+
+ if($act->obj['type'] === 'Page' && ! $s['body']) {
+
+ $ptr = null;
+ $purl = EMPTY_STR;
+
+ if(array_key_exists('url',$act->obj)) {
+ if(is_array($act->obj['url'])) {
+ if(array_key_exists(0,$act->obj['url'])) {
+ $ptr = $act->obj['url'];
+ }
+ else {
+ $ptr = [ $act->obj['url'] ];
+ }
+ foreach($ptr as $vurl) {
+ if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') {
+ $purl = $vurl['href'];
+ break;
+ }
+ elseif(array_key_exists('mimeType',$vurl) && $vurl['mimeType'] === 'text/html') {
+ $purl = $vurl['href'];
+ break;
+ }
+ }
+ }
+ elseif(is_string($act->obj['url'])) {
+ $purl = $act->obj['url'];
+ }
+ if($purl) {
+ $li = z_fetch_url(z_root() . '/linkinfo?binurl=' . bin2hex($purl));
+ if($li['success'] && $li['body']) {
+ $s['body'] .= "\n" . $li['body'];
+ }
+ else {
+ $s['body'] .= "\n\n" . $purl;
+ }
}
}
}
}
+
+
+ if(in_array($act->obj['type'],[ 'Note','Article','Page' ])) {
+ $ptr = null;
+
+ if(array_key_exists('url',$act->obj)) {
+ if(is_array($act->obj['url'])) {
+ if(array_key_exists(0,$act->obj['url'])) {
+ $ptr = $act->obj['url'];
+ }
+ else {
+ $ptr = [ $act->obj['url'] ];
+ }
+ foreach($ptr as $vurl) {
+ if(array_key_exists('mediaType',$vurl) && $vurl['mediaType'] === 'text/html') {
+ $s['plink'] = $vurl['href'];
+ break;
+ }
+ }
+ }
+ elseif(is_string($act->obj['url'])) {
+ $s['plink'] = $act->obj['url'];
+ }
+ }
+ }
+
+ if(! $s['plink']) {
+ $s['plink'] = $s['mid'];
+ }
+
if($act->recips && (! in_array(ACTIVITY_PUBLIC_INBOX,$act->recips)))
$s['item_private'] = 1;
@@ -1371,8 +1799,6 @@ class Activity {
}
-
-
static function announce_note($channel,$observer_hash,$act) {
$s = [];
@@ -1464,7 +1890,7 @@ class Activity {
$body .= self::bb_content($content,'content');
if($act->obj['type'] === 'Note' && $s['attach']) {
- $body .= self::bb_attach($s['attach']);
+ $body .= self::bb_attach($s['attach'],$body);
}
$body .= "[/share]";
@@ -1642,19 +2068,26 @@ class Activity {
}
- static function bb_attach($attach) {
+
+ static function bb_attach($attach,$body) {
$ret = false;
foreach($attach as $a) {
if(strpos($a['type'],'image') !== false) {
- $ret .= "\n\n" . '[img]' . $a['href'] . '[/img]';
+ if(self::media_not_in_body($a['href'],$body)) {
+ $ret .= "\n\n" . '[img]' . $a['href'] . '[/img]';
+ }
}
if(array_key_exists('type',$a) && strpos($a['type'], 'video') === 0) {
- $ret .= "\n\n" . '[video]' . $a['href'] . '[/video]';
+ if(self::media_not_in_body($a['href'],$body)) {
+ $ret .= "\n\n" . '[video]' . $a['href'] . '[/video]';
+ }
}
if(array_key_exists('type',$a) && strpos($a['type'], 'audio') === 0) {
- $ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]';
+ if(self::media_not_in_body($a['href'],$body)) {
+ $ret .= "\n\n" . '[audio]' . $a['href'] . '[/audio]';
+ }
}
}
@@ -1662,16 +2095,31 @@ class Activity {
}
+ // check for the existence of existing media link in body
+
+ static function media_not_in_body($s,$body) {
+
+ if((strpos($body,']' . $s . '[/img]') === false) &&
+ (strpos($body,']' . $s . '[/zmg]') === false) &&
+ (strpos($body,']' . $s . '[/video]') === false) &&
+ (strpos($body,']' . $s . '[/audio]') === false)) {
+ return true;
+ }
+ return false;
+ }
+
static function bb_content($content,$field) {
require_once('include/html2bbcode.php');
-
+ require_once('include/event.php');
$ret = false;
if(is_array($content[$field])) {
foreach($content[$field] as $k => $v) {
- $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]';
+ $ret .= html2bbcode($v);
+ // save this for auto-translate or dynamic filtering
+ // $ret .= '[language=' . $k . ']' . html2bbcode($v) . '[/language]';
}
}
else {
@@ -1682,6 +2130,9 @@ class Activity {
$ret = html2bbcode($content[$field]);
}
}
+ if($field === 'content' && $content['event'] && (! strpos($ret,'[event'))) {
+ $ret .= format_event_bbcode($content['event']);
+ }
return $ret;
}
@@ -1690,21 +2141,52 @@ class Activity {
static function get_content($act) {
$content = [];
- if (! $act) {
+ $event = null;
+
+ if ((! $act) || (! is_array($act))) {
return $content;
}
+ if($act['type'] === 'Event') {
+ $adjust = false;
+ $event = [];
+ $event['event_hash'] = $act['id'];
+ if(array_key_exists('startTime',$act) && strpos($act['startTime'],-1,1) === 'Z') {
+ $adjust = true;
+ $event['adjust'] = 1;
+ $event['dtstart'] = datetime_convert('UTC','UTC',$event['startTime'] . (($adjust) ? '' : 'Z'));
+ }
+ if(array_key_exists('endTime',$act)) {
+ $event['dtend'] = datetime_convert('UTC','UTC',$event['endTime'] . (($adjust) ? '' : 'Z'));
+ }
+ else {
+ $event['nofinish'] = true;
+ }
+ }
+
foreach ([ 'name', 'summary', 'content' ] as $a) {
if (($x = self::get_textfield($act,$a)) !== false) {
$content[$a] = $x;
}
}
+
+ if($event) {
+ $event['summary'] = html2bbcode($content['summary']);
+ $event['description'] = html2bbcode($content['content']);
+ if($event['summary'] && $event['dtstart']) {
+ $content['event'] = $event;
+ }
+ }
+
if (array_key_exists('source',$act) && array_key_exists('mediaType',$act['source'])) {
if ($act['source']['mediaType'] === 'text/bbcode') {
$content['bbcode'] = purify_html($act['source']['content']);
}
}
+
+
+
return $content;
}
@@ -1722,4 +2204,6 @@ class Activity {
}
return $content;
}
+
+
} \ No newline at end of file
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php
index 49978031e..a357b6d69 100644
--- a/Zotlabs/Lib/ActivityStreams.php
+++ b/Zotlabs/Lib/ActivityStreams.php
@@ -263,24 +263,8 @@ class ActivityStreams {
return self::fetch($url);
}
- static function fetch($url) {
- $redirects = 0;
- if(! check_siteallowed($url)) {
- logger('blacklisted: ' . $url);
- return null;
- }
- logger('fetch: ' . $url, LOGGER_DEBUG);
- $x = z_fetch_url($url, true, $redirects,
- [ 'headers' => [ 'Accept: application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ]]);
- if($x['success']) {
- $y = json_decode($x['body'],true);
- logger('returned: ' . json_encode($y,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
- return json_decode($x['body'], true);
- }
- else {
- logger('fetch failed: ' . $url);
- }
- return null;
+ static function fetch($url,$channel = null) {
+ return Activity::fetch($url,$channel);
}
static function is_an_actor($s) {
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index b13658be2..56283ff76 100644
--- a/Zotlabs/Lib/Apps.php
+++ b/Zotlabs/Lib/Apps.php
@@ -1,32 +1,34 @@
-<?php /** @file */
+<?php
namespace Zotlabs\Lib;
-/**
- * Apps
- *
- */
-
require_once('include/plugin.php');
require_once('include/channel.php');
-
+/**
+ * @brief Apps class.
+ *
+ */
class Apps {
static public $available_apps = null;
static public $installed_apps = null;
-
static public $base_apps = null;
-
-
+ /**
+ * @brief
+ *
+ * @param boolean $translate (optional) default true
+ * @return array
+ */
static public function get_system_apps($translate = true) {
+ $ret = [];
- $ret = array();
if(is_dir('apps'))
$files = glob('apps/*.apd');
else
$files = glob('app/*.apd');
+
if($files) {
foreach($files as $f) {
$x = self::parse_app_description($f,$translate);
@@ -50,14 +52,17 @@ class Apps {
}
}
- call_hooks('get_system_apps',$ret);
+ /**
+ * @hooks get_system_apps
+ * Hook to manipulate the system apps array.
+ */
+ call_hooks('get_system_apps', $ret);
return $ret;
-
}
static public function get_base_apps() {
- return get_config('system','base_apps',[
+ $x = get_config('system','base_apps',[
'Connections',
'Network',
'Settings',
@@ -72,6 +77,14 @@ class Apps {
'Mail',
'Profile Photo'
]);
+
+ /**
+ * @hooks get_base_apps
+ * Hook to manipulate the base apps array.
+ */
+ call_hooks('get_base_apps', $x);
+
+ return $x;
}
static public function import_system_apps() {
@@ -79,7 +92,7 @@ class Apps {
return;
self::$base_apps = self::get_base_apps();
-
+
$apps = self::get_system_apps(false);
self::$available_apps = q("select * from app where app_channel = 0");
@@ -104,6 +117,7 @@ class Apps {
// $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id === false)
continue;
+
if($id !== true) {
// if we already installed this app, but it changed, preserve any categories we created
$s = EMPTY_STR;
@@ -124,16 +138,17 @@ class Apps {
$app['guid'] = hash('whirlpool',$app['name']);
$app['system'] = 1;
self::app_install(local_channel(),$app);
-
}
- }
+ }
}
/**
- * Install the system app if no system apps have been installed, or if a new system app
+ * Install the system app if no system apps have been installed, or if a new system app
* is discovered, or if the version of a system app changes.
+ *
+ * @param array $app
+ * @return boolean|int
*/
-
static public function check_install_system_app($app) {
if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) {
return true;
@@ -157,17 +172,16 @@ class Apps {
return $notfound;
}
-
/**
- * Install the system app if no system apps have been installed, or if a new system app
- * is discovered, or if the version of a system app changes.
+ * Install the personal app if no personal apps have been installed, or if a new personal app
+ * is discovered, or if the version of a personal app changes.
+ *
+ * @param array $app
+ * @return boolean|int
*/
-
-
-
static public function check_install_personal_app($app) {
$installed = false;
- foreach(self::$installed_apps as $iapp) {
+ foreach(self::$installed_apps as $iapp) {
if($iapp['app_id'] == hash('whirlpool',$app['name'])) {
$installed = true;
if(($iapp['app_version'] != $app['version'])
@@ -187,19 +201,24 @@ class Apps {
return strcasecmp($a['name'],$b['name']);
}
-
- static public function parse_app_description($f,$translate = true) {
-
- $ret = array();
+ /**
+ * @brief Parse app description.
+ *
+ * @param string $f filename
+ * @param boolean $translate (optional) default true
+ * @return boolean|array
+ */
+ static public function parse_app_description($f, $translate = true) {
+ $ret = [];
+ $matches = [];
$baseurl = z_root();
- $channel = \App::get_channel();
- $address = (($channel) ? $channel['channel_address'] : '');
-
+ //$channel = \App::get_channel();
+ //$address = (($channel) ? $channel['channel_address'] : '');
+
//future expansion
$observer = \App::get_observer();
-
$lines = @file($f);
if($lines) {
@@ -208,7 +227,7 @@ class Apps {
$ret[$matches[1]] = trim($matches[2]);
}
}
- }
+ }
if(! $ret['photo'])
$ret['photo'] = $baseurl . '/' . get_default_profile_photo(80);
@@ -290,15 +309,18 @@ class Apps {
if($ret) {
if($translate)
self::translate_system_apps($ret);
+
return $ret;
}
+
return false;
- }
+ }
static public function translate_system_apps(&$arr) {
$apps = array(
'Apps' => t('Apps'),
+ 'Affinity Tool' => t('Affinity Tool'),
'Articles' => t('Articles'),
'Cards' => t('Cards'),
'Admin' => t('Site Admin'),
@@ -309,17 +331,17 @@ class Apps {
'Remote Diagnostics' => t('Remote Diagnostics'),
'Suggest Channels' => t('Suggest Channels'),
'Login' => t('Login'),
- 'Channel Manager' => t('Channel Manager'),
+ 'Channel Manager' => t('Channel Manager'),
'Network' => t('Stream'),
'Settings' => t('Settings'),
'Files' => t('Files'),
'Webpages' => t('Webpages'),
'Wiki' => t('Wiki'),
- 'Channel Home' => t('Channel Home'),
+ 'Channel Home' => t('Channel Home'),
'View Profile' => t('View Profile'),
- 'Photos' => t('Photos'),
- 'Events' => t('Events'),
- 'Directory' => t('Directory'),
+ 'Photos' => t('Photos'),
+ 'Events' => t('Events'),
+ 'Directory' => t('Directory'),
'Help' => t('Help'),
'Mail' => t('Mail'),
'Mood' => t('Mood'),
@@ -364,30 +386,31 @@ class Apps {
if(array_key_exists($arr[$x]['name'],$apps)) {
$arr[$x]['name'] = $apps[$arr[$x]['name']];
} else {
- // Try to guess by app name if not in list
- $arr[$x]['name'] = t(trim($arr[$x]['name']));
+ // Try to guess by app name if not in list
+ $arr[$x]['name'] = t(trim($arr[$x]['name']));
}
}
}
-
}
-
- // papp is a portable app
-
- static public function app_render($papp,$mode = 'view') {
-
- /**
- * modes:
- * view: normal mode for viewing an app via bbcode from a conversation or page
- * provides install/update button if you're logged in locally
- * install: like view but does not display app-bin options if they are present
- * list: normal mode for viewing an app on the app page
- * no buttons are shown
- * edit: viewing the app page in editing mode provides a delete button
- * nav: render apps for app-bin
- */
-
+ /**
+ * @brief
+ *
+ * @param array $papp
+ * papp is a portable app
+ * @param string $mode (optional) default 'view'
+ * Render modes:
+ * * \b view: normal mode for viewing an app via bbcode from a conversation or page
+ * provides install/update button if you're logged in locally
+ * * \b install: like view but does not display app-bin options if they are present
+ * * \b list: normal mode for viewing an app on the app page
+ * no buttons are shown
+ * * \b edit: viewing the app page in editing mode provides a delete button
+ * * \b nav: render apps for app-bin
+ *
+ * @return void|string Parsed HTML
+ */
+ static public function app_render($papp, $mode = 'view') {
$installed = false;
if(! $papp)
@@ -412,7 +435,7 @@ class Apps {
$sys = get_sys_channel();
$view_channel = $sys['channel_id'];
}
- self::app_macros($view_channel,$papp);
+ self::app_macros($view_channel,$papp);
}
if(strpos($papp['url'], ',')) {
@@ -425,7 +448,6 @@ class Apps {
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
-
foreach($papp as $k => $v) {
if(strpos($v,'http') === 0 && $k != 'papp') {
if(! (local_channel() && strpos($v,z_root()) === 0)) {
@@ -507,7 +529,7 @@ class Apps {
if($x) {
$hosturl = $x['scheme'] . '://' . $x['host'] . '/';
}
- }
+ }
}
$install_action = (($installed) ? t('Update') : t('Install'));
@@ -590,8 +612,14 @@ class Apps {
return false;
}
-
- static public function can_delete($uid,$app) {
+ /**
+ * @brief
+ *
+ * @param mixed $uid If not set return false, otherwise no influence
+ * @param array $app
+ * @return boolean
+ */
+ static public function can_delete($uid, $app) {
if(! $uid) {
return false;
}
@@ -599,7 +627,7 @@ class Apps {
$base_apps = self::get_base_apps();
if($base_apps) {
foreach($base_apps as $b) {
- if($app['guid'] === hash('whirlpool',$b)) {
+ if($app['guid'] === hash('whirlpool', $b)) {
return false;
}
}
@@ -611,7 +639,6 @@ class Apps {
static public function app_destroy($uid,$app) {
if($uid && $app['guid']) {
-
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']),
intval($uid)
@@ -620,7 +647,7 @@ class Apps {
if(! intval($x[0]['app_deleted'])) {
$x[0]['app_deleted'] = 1;
if(self::can_delete($uid,$app)) {
- $r = q("delete from app where app_id = '%s' and app_channel = %d",
+ q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
@@ -628,10 +655,15 @@ class Apps {
intval(TERM_OBJ_APP),
intval($x[0]['id'])
);
+ /**
+ * @hooks app_destroy
+ * Called after app entry got removed from database
+ * and provide app array from database.
+ */
call_hooks('app_destroy', $x[0]);
}
else {
- $r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
+ q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
@@ -645,22 +677,23 @@ class Apps {
}
}
}
-
}
- static public function app_undestroy($uid,$app) {
-
- // undelete a system app
-
+ /**
+ * @brief Undelete a system app.
+ *
+ * @param int $uid
+ * @param array $app
+ */
+ static public function app_undestroy($uid, $app) {
if($uid && $app['guid']) {
-
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']),
intval($uid)
);
if($x) {
if($x[0]['app_system']) {
- $r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
+ q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']),
intval($uid)
);
@@ -669,7 +702,15 @@ class Apps {
}
}
- static public function app_feature($uid,$app,$term) {
+ /**
+ * @brief
+ *
+ * @param int $uid
+ * @param array $app
+ * @param string $term
+ * @return void
+ */
+ static public function app_feature($uid, $app, $term) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']),
intval($uid)
@@ -693,23 +734,37 @@ class Apps {
}
}
- static public function app_installed($uid,$app,$bypass_filter=false) {
+ /**
+ * @brief
+ *
+ * @param int $uid
+ * @param array $app
+ * @param boolean $bypass_filter (optional) default false
+ * @return boolean
+ */
+ static public function app_installed($uid, $app, $bypass_filter = false) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
- dbesc((array_key_exists('guid',$app)) ? $app['guid'] : ''),
+ dbesc((array_key_exists('guid', $app)) ? $app['guid'] : ''),
intval($uid)
);
- if (!$bypass_filter) {
+ if(!$bypass_filter) {
$filter_arr = [
- 'uid'=>$uid,
- 'app'=>$app,
- 'installed'=>$r
+ 'uid' => $uid,
+ 'app' => $app,
+ 'installed' => $r
];
- call_hooks('app_installed_filter',$filter_arr);
+ /**
+ * @hooks app_installed_filter
+ * * \e int \b uid
+ * * \e array \b app
+ * * \e mixed \b installed - return value
+ */
+ call_hooks('app_installed_filter', $filter_arr);
$r = $filter_arr['installed'];
}
- return(($r) ? true : false);
+ return(($r) ? true : false);
}
@@ -725,11 +780,17 @@ class Apps {
'app'=>$app,
'installed'=>$r
];
- call_hooks('addon_app_installed_filter',$filter_arr);
+ /**
+ * @hooks addon_app_installed_filter
+ * * \e int \b uid
+ * * \e array \b app
+ * * \e mixed \b installed - return value
+ */
+ call_hooks('addon_app_installed_filter', $filter_arr);
$r = $filter_arr['installed'];
}
- return(($r) ? true : false);
+ return(($r) ? true : false);
}
static public function system_app_installed($uid,$app,$bypass_filter=false) {
@@ -744,28 +805,39 @@ class Apps {
'app'=>$app,
'installed'=>$r
];
- call_hooks('system_app_installed_filter',$filter_arr);
+ /**
+ * @hooks system_app_installed_filter
+ * * \e int \b uid
+ * * \e array \b app
+ * * \e mixed \b installed - return value
+ */
+ call_hooks('system_app_installed_filter', $filter_arr);
$r = $filter_arr['installed'];
}
- return(($r) ? true : false);
+ return(($r) ? true : false);
}
-
-
+ /**
+ * @brief
+ *
+ * @param int $uid
+ * @param boolean $deleted
+ * @param array $cats
+ * @return boolean|array
+ */
static public function app_list($uid, $deleted = false, $cats = []) {
- if($deleted)
- $sql_extra = "";
+ if($deleted)
+ $sql_extra = '';
else
- $sql_extra = " and app_deleted = 0 ";
+ $sql_extra = ' and app_deleted = 0 ';
if($cats) {
-
- $cat_sql_extra = " and ( ";
+ $cat_sql_extra = ' and ( ';
foreach($cats as $cat) {
if(strpos($cat_sql_extra, 'term'))
- $cat_sql_extra .= "or ";
+ $cat_sql_extra .= 'or ';
$cat_sql_extra .= "term = '" . dbesc($cat) . "' ";
}
@@ -777,11 +849,13 @@ class Apps {
);
if(! $r)
return $r;
- $sql_extra .= " and app.id in ( ";
+
+ $sql_extra .= ' and app.id in ( ';
$s = '';
foreach($r as $rr) {
if($s)
$s .= ',';
+
$s .= intval($rr['oid']);
}
$sql_extra .= $s . ') ';
@@ -792,12 +866,26 @@ class Apps {
);
if($r) {
- $hookinfo = Array('uid'=>$uid,'deleted'=>$deleted,'cats'=>$cats,'apps'=>$r);
- call_hooks('app_list',$hookinfo);
+ $hookinfo = [
+ 'uid' => $uid,
+ 'deleted' => $deleted,
+ 'cats' => $cats,
+ 'apps' => $r,
+ ];
+ /**
+ * @hooks app_list
+ * * \e int \b uid
+ * * \e boolean \b deleted
+ * * \e array \b cats
+ * * \e array \b apps - return value
+ */
+ call_hooks('app_list', $hookinfo);
$r = $hookinfo['apps'];
- for($x = 0; $x < count($r); $x ++) {
+
+ for($x = 0; $x < count($r); $x++) {
if(! $r[$x]['app_system'])
$r[$x]['type'] = 'personal';
+
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($r[$x]['id'])
@@ -805,15 +893,17 @@ class Apps {
}
}
- return($r);
+ return $r;
}
- static public function app_order($uid,$apps) {
+ static public function app_order($uid,$apps,$menu) {
if(! $apps)
return $apps;
- $x = (($uid) ? get_pconfig($uid,'system','app_order') : get_config('system','app_order'));
+ $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
+
+ $x = (($uid) ? get_pconfig($uid,'system',$conf) : get_config('system',$conf));
if(($x) && (! is_array($x))) {
$y = explode(',',$x);
$y = array_map('trim',$y);
@@ -835,13 +925,14 @@ class Apps {
$ret[] = $ap;
}
}
- return $ret;
+ return $ret;
}
static function find_app_in_array($name,$arr) {
if(! $arr)
return false;
+
foreach($arr as $x) {
if($x['name'] === $name) {
return $x;
@@ -850,25 +941,38 @@ class Apps {
return false;
}
- static function moveup($uid,$guid) {
- $syslist = array();
- $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']);
+ /**
+ * @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');
+
+ $list = self::app_list($uid, false, [ $menu ]);
if($list) {
foreach($list as $li) {
- $syslist[] = self::app_encode($li);
+ $papp = self::app_encode($li);
+ if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
+ continue;
+
+ $syslist[] = $papp;
}
}
self::translate_system_apps($syslist);
usort($syslist,'self::app_name_compare');
- $syslist = self::app_order($uid,$syslist);
+ $syslist = self::app_order($uid,$syslist,$menu);
if(! $syslist)
return;
- $newlist = [];
-
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
@@ -877,6 +981,7 @@ class Apps {
}
if(! $position)
return;
+
$dest_position = $position - 1;
$saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position];
@@ -887,29 +992,41 @@ class Apps {
$narr[] = $x['name'];
}
- set_pconfig($uid,'system','app_order',implode(',',$narr));
-
+ set_pconfig($uid,'system',$conf,implode(',',$narr));
}
- static function movedown($uid,$guid) {
- $syslist = array();
- $list = self::app_list($uid, false, ['nav_featured_app', 'nav_pinned_app']);
+ /**
+ * @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');
+
+ $list = self::app_list($uid, false, [ $menu ]);
if($list) {
foreach($list as $li) {
- $syslist[] = self::app_encode($li);
+ $papp = self::app_encode($li);
+ if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
+ continue;
+
+ $syslist[] = $papp;
}
}
self::translate_system_apps($syslist);
usort($syslist,'self::app_name_compare');
- $syslist = self::app_order($uid,$syslist);
+ $syslist = self::app_order($uid,$syslist,$menu);
if(! $syslist)
return;
- $newlist = [];
-
foreach($syslist as $k => $li) {
if($li['guid'] === $guid) {
$position = $k;
@@ -918,6 +1035,7 @@ class Apps {
}
if($position >= count($syslist) - 1)
return;
+
$dest_position = $position + 1;
$saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position];
@@ -928,8 +1046,7 @@ class Apps {
$narr[] = $x['name'];
}
- set_pconfig($uid,'system','app_order',implode(',',$narr));
-
+ set_pconfig($uid,'system',$conf,implode(',',$narr));
}
static public function app_decode($s) {
@@ -937,8 +1054,14 @@ class Apps {
return json_decode($x,true);
}
-
- static public function app_macros($uid,&$arr) {
+ /**
+ * @brief
+ *
+ * @param int $uid
+ * @param[in,out] array $arr
+ * @return void
+ */
+ static public function app_macros($uid, &$arr) {
if(! intval($uid))
return;
@@ -946,21 +1069,17 @@ class Apps {
$baseurl = z_root();
$channel = channelx_by_n($uid);
$address = (($channel) ? $channel['channel_address'] : '');
-
+
//future expansion
- $observer = \App::get_observer();
-
+ //$observer = \App::get_observer();
+
$arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']);
$arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']);
-
}
-
-
-
static public function app_store($arr) {
//logger('app_store: ' . print_r($arr,true));
@@ -1144,16 +1263,20 @@ class Apps {
}
return $ret;
-
}
-
- static public function app_encode($app,$embed = false) {
-
- $ret = array();
+ /**
+ * @brief
+ *
+ * @param array $app
+ * @param boolean $embed (optional) default false
+ * @return array|string
+ */
+ static public function app_encode($app, $embed = false) {
+ $ret = [];
$ret['type'] = 'personal';
-
+
if($app['app_id'])
$ret['guid'] = $app['app_id'];
@@ -1186,7 +1309,7 @@ class Apps {
if($app['app_price'])
$ret['price'] = $app['app_price'];
-
+
if($app['app_page'])
$ret['page'] = $app['app_page'];
@@ -1210,12 +1333,12 @@ class Apps {
foreach($app['term'] as $t) {
if($s)
$s .= ',';
+
$s .= $t['term'];
}
$ret['categories'] = $s;
}
-
if(! $embed)
return $ret;
@@ -1223,18 +1346,15 @@ class Apps {
if(array_key_exists('categories',$ret))
unset($ret['categories']);
-
+
$j = json_encode($ret);
- return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
+ return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
}
static public function papp_encode($papp) {
return chunk_split(base64_encode(json_encode($papp)),72,"\n");
-
}
}
-
-
diff --git a/Zotlabs/Lib/DReport.php b/Zotlabs/Lib/DReport.php
index 21b320cac..18087e29f 100644
--- a/Zotlabs/Lib/DReport.php
+++ b/Zotlabs/Lib/DReport.php
@@ -87,17 +87,29 @@ class DReport {
// Is the sender one of our channels?
- $c = q("select channel_id from channel where channel_hash = '%s' limit 1",
+ $c = q("select channel_id from channel where channel_hash = '%s' or channel_portable_id = '%s' limit 1",
+ dbesc($dr['sender']),
dbesc($dr['sender'])
);
+
if(! $c)
return false;
+ // legacy zot recipients add a space and their name to the xchan. remove it if true.
+
+ $legacy_recipient = strpos($dr['recipient'], ' ');
+ if($legacy_recipient !== false) {
+ $legacy_recipient_parts = explode(' ', $dr['recipient'], 2);
+ $rxchan = $legacy_recipient_parts[0];
+ }
+ else {
+ $rxchan = $dr['recipient'];
+ }
- // is the recipient one of our connections, or do we want to store every report?
- $rxchan = $dr['recipient'];
+ // is the recipient one of our connections, or do we want to store every report?
+
$pcf = get_pconfig($c[0]['channel_id'],'system','dreport_store_all');
if($pcf)
return true;
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 25c96d9cc..5e5798cac 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -828,6 +828,7 @@ class Enotify {
$x = array(
'notify_link' => $item['llink'],
'name' => $item['author']['xchan_name'],
+ 'addr' => (($item['author']['xchan_addr']) ? $item['author']['xchan_addr'] : $item['author']['xchan_url']),
'url' => $item['author']['xchan_url'],
'photo' => $item['author']['xchan_photo_s'],
'when' => relative_date(($edit)? $item['edited'] : $item['created']),
diff --git a/Zotlabs/Lib/Libzot.php b/Zotlabs/Lib/Libzot.php
index 70f013eb7..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
@@ -397,9 +388,7 @@ logger('4');
}
}
- $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness');
- if($closeness === false)
- $closeness = 80;
+ $closeness = get_pconfig($channel['channel_id'],'system','new_abook_closeness',80);
$y = abook_store_lowlevel(
[
@@ -418,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",
@@ -526,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),
@@ -550,7 +543,6 @@ logger('4');
}
return $r[0];
-
}
/**
@@ -561,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 ];
@@ -809,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),
@@ -956,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);
@@ -988,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()),
@@ -996,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']),
@@ -1075,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
@@ -1087,7 +1077,6 @@ logger('4');
* * [1] => \e string $delivery_status
* * [2] => \e string $address
*/
-
static function import($arr) {
$env = $arr;
@@ -1109,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') {
@@ -1167,7 +1156,6 @@ logger('4');
$deliveries = self::public_recips($env,$AS);
-
}
$deliveries = array_unique($deliveries);
@@ -1186,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;
}
@@ -1239,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;
}
@@ -1287,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');
@@ -1418,7 +1413,7 @@ logger('4');
$DR = new DReport(z_root(),$sender,$d,$arr['mid']);
- $channel = channelx_by_hash($d);
+ $channel = channelx_by_portid($d);
if (! $channel) {
$DR->update('recipient not found');
@@ -1434,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']) {
@@ -1489,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');
@@ -1505,14 +1523,18 @@ logger('4');
}
}
- if($arr['mid'] != $arr['parent_mid']) {
+ // logger('item: ' . print_r($arr,true), LOGGER_DATA);
+
+ if($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('4');
// 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('4');
}
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'];
@@ -1602,13 +1621,13 @@ logger('4');
// 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('4');
* * \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('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) {
$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('4');
$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('4');
$arr['owner_xchan'] = $a['signature']['signer'];
}
- // @fixme - spoofable
+
+ /// @FIXME - spoofable
if($AS->data['hubloc']) {
$arr['item_verified'] = true;
}
@@ -1824,7 +1844,7 @@ logger('4');
$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('4');
* * \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('4');
}
$i = $r[0];
-
+
if($i['target'])
$i['target'] = json_decode($i['target'],true);
if($i['object'])
@@ -1913,8 +1933,8 @@ logger('4');
* @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('4');
}
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('4');
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('4');
* @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('4');
* * \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('4');
$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('4');
*
* @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('4');
}
-
/**
* @brief Returns an array with all known distinct hubs for this channel.
*
@@ -2252,7 +2273,6 @@ logger('4');
* * \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('4');
if(! $z['site_id']) {
$z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']);
}
-
+
$ret[] = $z;
}
}
@@ -2306,10 +2326,8 @@ logger('4');
* @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('4');
$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('4');
$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('4');
$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('4');
$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('4');
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('4');
}
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('4');
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('4');
if ($key && count($x) === 2) {
$alg = $x[0];
$signature = base64url_decode($x[1]);
-
+
$verify = @openssl_verify($data,$signature,$key,$alg);
if ($verify === (-1)) {
while ($msg = openssl_error_string()) {
logger('openssl_verify: ' . $msg,LOGGER_NORMAL,LOG_ERR);
}
- btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR);
+ btlogger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR);
}
}
return(($verify > 0) ? true : false);
}
-
-
+ /**
+ * @brief
+ *
+ * @return boolean
+ */
static function is_zot_request() {
-
$x = getBestSupportedMimeType([ 'application/x-zot+json' ]);
+
return(($x) ? true : false);
}
diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php
index 5e5954c95..c08c11e75 100644
--- a/Zotlabs/Lib/PConfig.php
+++ b/Zotlabs/Lib/PConfig.php
@@ -112,9 +112,11 @@ class PConfig {
* The configuration key to set
* @param string $value
* The value to store
+ * @param string $updated (optional)
+ * The datetime to store
* @return mixed Stored $value or false
*/
- static public function Set($uid, $family, $key, $value, $updated=NULL) {
+ static public function Set($uid, $family, $key, $value, $updated = NULL) {
// this catches subtle errors where this function has been called
// with local_channel() when not logged in (which returns false)
@@ -131,14 +133,19 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
+ $now = datetime_convert();
if (! $updated) {
- $updated = datetime_convert();
+ //Sometimes things happen fast... very fast.
+ //To make sure legitimate updates aren't rejected
+ //because not enough time has passed. We say our updates
+ //happened just a short time in the past rather than right now.
+ $updated = datetime_convert('UTC','UTC','-2 seconds');
}
$hash = hash('sha256',$family.':'.$key);
if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
- if (self::Get($uid, 'hz_delpconfig', $hash) > $updated) {
+ if (self::Get($uid, 'hz_delpconfig', $hash) > $now) {
logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
return self::Get($uid,$family,$key);
} else {
@@ -173,7 +180,7 @@ class PConfig {
}
else {
- $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
+ $new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if ($new) {
@@ -234,16 +241,18 @@ class PConfig {
* The category of the configuration value
* @param string $key
* The configuration key to delete
- * @return mixed
+ * @param string $updated (optional)
+ * The datetime to store
+ * @return boolean
*/
static public function Delete($uid, $family, $key, $updated = NULL) {
if(is_null($uid) || $uid === false)
return false;
- $updated = ($updated) ? $updated : datetime_convert();
-
- $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
+ $updated = ($updated) ? $updated : datetime_convert('UTC','UTC','-2 seconds');
+ $now = datetime_convert();
+ $newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $now);
if (! $newer) {
logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
@@ -266,22 +275,13 @@ class PConfig {
dbesc($key)
);
+ // Synchronize delete with clones.
+
if ($family != 'hz_delpconfig') {
$hash = hash('sha256',$family.':'.$key);
set_pconfig($uid,'hz_delpconfig',$hash,$updated);
}
- // Synchronize delete with clones.
-
- if(! array_key_exists('transient', \App::$config[$uid]))
- \App::$config[$uid]['transient'] = array();
- if(! array_key_exists($family, \App::$config[$uid]['transient']))
- \App::$config[$uid]['transient'][$family] = array();
-
- if ($new) {
- \App::$config[$uid]['transient'][$family]['pcfgdel:'.$key] = $updated;
- }
-
return $ret;
}
diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php
index d3ecbf7fa..3a2ab1783 100644
--- a/Zotlabs/Lib/Share.php
+++ b/Zotlabs/Lib/Share.php
@@ -54,6 +54,7 @@ class Share {
if(! $this->item)
return $obj;
+ $obj['asld'] = $this->item['mid'];
$obj['type'] = $this->item['obj_type'];
$obj['id'] = $this->item['mid'];
$obj['content'] = $this->item['body'];
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 83d243177..40c0fca4b 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -76,7 +76,7 @@ class ThreadItem {
* _ false on failure
*/
- public function get_template_data($conv_responses, $thread_level=1) {
+ public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
$result = array();
@@ -101,6 +101,7 @@ class ThreadItem {
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? t('Private Message')
: false);
+
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
// allow an exemption for sharing stuff from your private feeds
@@ -115,6 +116,19 @@ class ThreadItem {
$privacy_warning = true;
}
+ if ($lock) {
+ if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) {
+ $privacy_warning = true;
+ $conv_flags['parent_privacy_warning'] = true;
+ }
+ }
+
+ $privacy_warning = (isset($conv_flags['parent_privacy_warning'])) ? $conv_flags['parent_privacy_warning'] : $privacy_warning;
+
+ if ($lock && $privacy_warning) {
+ $lock = t('Privacy conflict. Discretion advised.');
+ }
+
$mode = $conv->get_mode();
switch($item['item_type']) {
@@ -293,8 +307,15 @@ class ThreadItem {
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
}
- if ($shareable)
- $share = array( t('Share This'), t('share'));
+ if ($shareable) {
+ // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
+ // Will allow it only for uri resolvable sources.
+ if(strpos($item['mid'],'http') === 0) {
+ $share = []; //Not yet ready for primetime
+ //$share = array( t('Repeat This'), t('repeat'));
+ }
+ $embed = array( t('Share This'), t('share'));
+ }
$dreport = '';
@@ -408,12 +429,13 @@ class ThreadItem {
'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'share' => $share,
+ 'embed' => $embed,
'rawmid' => $item['mid'],
'plink' => get_plink($item),
'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
- 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''),
+ 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
- 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''),
+ 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''),
'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
'drop' => $drop,
@@ -470,7 +492,7 @@ class ThreadItem {
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) {
- $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1);
+ $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
}
// Collapse
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
diff --git a/Zotlabs/Lib/ThreadListener.php b/Zotlabs/Lib/ThreadListener.php
new file mode 100644
index 000000000..308e02255
--- /dev/null
+++ b/Zotlabs/Lib/ThreadListener.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Zotlabs\Lib;
+
+class ThreadListener {
+
+ static public function store($target_id,$portable_id,$ltype = 0) {
+ $x = self::fetch($target_id,$portable_id,$ltype = 0);
+ if(! $x) {
+ $r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ",
+ dbesc($target_id),
+ dbesc($portable_id),
+ intval($ltype)
+ );
+ }
+ }
+
+ static public function fetch($target_id,$portable_id,$ltype = 0) {
+ $x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1",
+ dbesc($target_id),
+ dbesc($portable_id),
+ intval($ltype)
+ );
+ if($x) {
+ return $x[0];
+ }
+ return false;
+ }
+
+ static public function fetch_by_target($target_id,$ltype = 0) {
+ $x = q("select * from listeners where target_id = '%s' and ltype = %d",
+ dbesc($target_id),
+ intval($ltype)
+ );
+
+ return $x;
+ }
+
+ static public function delete_by_target($target_id, $ltype = 0) {
+ return q("delete from listeners where target_id = '%s' and ltype = %d",
+ dbesc($target_id),
+ intval($ltype)
+ );
+ }
+
+ static public function delete_by_pid($portable_id, $ltype = 0) {
+ return q("delete from listeners where portable_id = '%s' and ltype = %d",
+ dbesc($portable_id),
+ intval($ltype)
+ );
+ }
+
+}
diff --git a/Zotlabs/Lib/ZotURL.php b/Zotlabs/Lib/ZotURL.php
new file mode 100644
index 000000000..d1c705fcb
--- /dev/null
+++ b/Zotlabs/Lib/ZotURL.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Zotlabs\Lib;
+
+use Zotlabs\Zot6\HTTPSig;
+
+
+class ZotURL {
+
+ static public function fetch($url,$channel) {
+
+ $ret = [ 'success' => false ];
+
+ if(strpos($url,'x-zot:') !== 0) {
+ return $ret;
+ }
+
+
+ if(! $url) {
+ return $ret;
+ }
+
+ $portable_url = substr($url,6);
+ $u = explode('/',$portable_url);
+ $portable_id = $u[0];
+
+ $hosts = self::lookup($portable_id);
+
+ if(! $hosts) {
+ return $ret;
+ }
+
+ foreach($hosts as $h) {
+ $newurl = $h . '/id/' . $portable_url;
+
+ $m = parse_url($newurl);
+
+ $data = json_encode([ 'zot_token' => random_string() ]);
+
+ if($channel && $m) {
+
+ $headers = [
+ 'Accept' => 'application/x-zot+json',
+ 'Content-Type' => 'application/x-zot+json',
+ 'X-Zot-Token' => random_string(),
+ 'Digest' => HTTPSig::generate_digest_header($data),
+ 'Host' => $m['host'],
+ '(request-target)' => 'post ' . get_request_string($newurl)
+ ];
+ $h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel),false);
+ }
+ else {
+ $h = [ 'Accept: application/x-zot+json' ];
+ }
+
+ $result = [];
+
+ $redirects = 0;
+ $x = z_post_url($newurl,$data,$redirects, [ 'headers' => $h ] );
+ if($x['success']) {
+ return $x;
+ }
+ }
+
+ return $ret;
+
+ }
+
+ static public function is_zoturl($s) {
+
+ if(strpos($url,'x-zot:') === 0) {
+ return true;
+ }
+ return false;
+ }
+
+
+ static public function lookup($portable_id) {
+
+ $r = q("select * from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and site_dead = 0 order by hubloc_primary desc",
+ dbesc($portable_id)
+ );
+
+ if(! $r) {
+ // extend to network lookup
+ return false;
+ }
+ return ids_to_array($r,'hubloc_url');
+ }
+
+} \ No newline at end of file
diff --git a/Zotlabs/Module/Admin.php b/Zotlabs/Module/Admin.php
index 8ccdaf4f5..88b84b9d2 100644
--- a/Zotlabs/Module/Admin.php
+++ b/Zotlabs/Module/Admin.php
@@ -86,7 +86,7 @@ class Admin extends \Zotlabs\Web\Controller {
// list total user accounts, expirations etc.
$accounts = array();
- $r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
+ $r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
db_utcnow(),
db_utcnow(),
dbesc(NULL_DATE),
diff --git a/Zotlabs/Module/Admin/Addons.php b/Zotlabs/Module/Admin/Addons.php
index b35922aef..b8e3e3a2e 100644
--- a/Zotlabs/Module/Admin/Addons.php
+++ b/Zotlabs/Module/Admin/Addons.php
@@ -375,6 +375,9 @@ class Addons {
if($files) {
foreach($files as $file) {
if (is_dir($file)){
+ if($file == 'addon/addon_common/')
+ continue;
+
list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins);
@@ -476,4 +479,4 @@ class Addons {
return(strcmp(strtolower($a[2]['name']),strtolower($b[2]['name'])));
}
-} \ No newline at end of file
+}
diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php
index 09b038729..55c8ca928 100644
--- a/Zotlabs/Module/Admin/Site.php
+++ b/Zotlabs/Module/Admin/Site.php
@@ -119,7 +119,7 @@ class Site {
del_config('system', 'admininfo');
} else {
require_once('include/text.php');
- linkify_tags($a, $admininfo, local_channel());
+ linkify_tags($admininfo, local_channel());
set_config('system', 'admininfo', $admininfo);
}
set_config('system','siteinfo',$siteinfo);
diff --git a/Zotlabs/Module/Affinity.php b/Zotlabs/Module/Affinity.php
new file mode 100644
index 000000000..f0d99f1e7
--- /dev/null
+++ b/Zotlabs/Module/Affinity.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Zotlabs\Module;
+
+use App;
+use Zotlabs\Lib\Apps;
+use Zotlabs\Lib\Libsync;
+
+class Affinity extends \Zotlabs\Web\Controller {
+
+ function post() {
+
+ if(! local_channel())
+ return;
+
+ if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
+ return;
+
+ check_form_security_token_redirectOnErr('affinity', 'affinity');
+
+ $cmax = intval($_POST['affinity_cmax']);
+ if($cmax < 0 || $cmax > 99)
+ $cmax = 99;
+
+ $cmin = intval($_POST['affinity_cmin']);
+ if($cmin < 0 || $cmin > 99)
+ $cmin = 0;
+
+ $lock = intval($_POST['affinity_lock']);
+
+ set_pconfig(local_channel(),'affinity','cmin',$cmin);
+ set_pconfig(local_channel(),'affinity','cmax',$cmax);
+ set_pconfig(local_channel(),'affinity','lock',$lock);
+
+ info( t('Affinity Tool settings updated.') . EOL);
+
+ Libsync::build_sync_packet();
+
+ }
+
+
+ function get() {
+
+ if(! local_channel())
+ return;
+
+ $desc = t('This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.');
+ if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) {
+ //Do not display any associated widgets at this point
+ App::$pdl = '';
+
+ $o = '<b>' . t('Affinity Tool App') . ' (' . t('Not Installed') . '):</b><br>';
+ $o .= $desc;
+ return $o;
+ }
+
+ $text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.');
+
+ $content = '<div class="section-content-info-wrapper">' . $text . '</div>';
+
+ $cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
+ $cmax = (($cmax) ? $cmax : 99);
+ $content .= replace_macros(get_markup_template('field_input.tpl'), array(
+ '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
+ ));
+
+ $cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
+ $cmin = (($cmin) ? $cmin : 0);
+ $content .= replace_macros(get_markup_template('field_input.tpl'), array(
+ '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
+ ));
+
+ $lock = intval(get_pconfig(local_channel(),'affinity','lock',1));
+
+ $content .= replace_macros(get_markup_template('field_checkbox.tpl'), array(
+ '$field' => array('affinity_lock', t('Persistent affinity levels'), $lock, t('If disabled the max and min levels will be reset to default after page reload'), ['No','Yes'])
+ ));
+
+ $tpl = get_markup_template("settings_addon.tpl");
+
+ $o = replace_macros($tpl, array(
+ '$action_url' => 'affinity',
+ '$form_security_token' => get_form_security_token("affinity"),
+ '$title' => t('Affinity Tool Settings'),
+ '$content' => $content,
+ '$baseurl' => z_root(),
+ '$submit' => t('Submit'),
+ ));
+
+ return $o;
+ }
+
+
+}
diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php
index f50dcc2ab..39689665e 100644
--- a/Zotlabs/Module/Appman.php
+++ b/Zotlabs/Module/Appman.php
@@ -90,12 +90,12 @@ class Appman extends \Zotlabs\Web\Controller {
$channel = \App::get_channel();
- if(argc() > 2) {
+ if(argc() > 3) {
if(argv(2) === 'moveup') {
- Zlib\Apps::moveup(local_channel(),argv(1));
+ Zlib\Apps::moveup(local_channel(),argv(1),argv(3));
}
if(argv(2) === 'movedown') {
- Zlib\Apps::movedown(local_channel(),argv(1));
+ Zlib\Apps::movedown(local_channel(),argv(1),argv(3));
}
goaway(z_root() . '/apporder');
}
diff --git a/Zotlabs/Module/Apporder.php b/Zotlabs/Module/Apporder.php
index a9f66ba69..eac1abc2d 100644
--- a/Zotlabs/Module/Apporder.php
+++ b/Zotlabs/Module/Apporder.php
@@ -17,25 +17,28 @@ class Apporder extends \Zotlabs\Web\Controller {
nav_set_selected('Order Apps');
- $syslist = array();
- $list = Zlib\Apps::app_list(local_channel(), false, ['nav_featured_app', 'nav_pinned_app']);
- if($list) {
- foreach($list as $li) {
- $syslist[] = Zlib\Apps::app_encode($li);
+ foreach( [ 'nav_featured_app', 'nav_pinned_app' ] as $l ) {
+ $syslist = [];
+ $list = Zlib\Apps::app_list(local_channel(), false, [ $l ]);
+ if($list) {
+ foreach($list as $li) {
+ $syslist[] = Zlib\Apps::app_encode($li);
+ }
}
- }
- Zlib\Apps::translate_system_apps($syslist);
+
+ Zlib\Apps::translate_system_apps($syslist);
- usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare');
+ usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare');
- $syslist = Zlib\Apps::app_order(local_channel(),$syslist);
+ $syslist = Zlib\Apps::app_order(local_channel(),$syslist, $l);
- foreach($syslist as $app) {
- if(strpos($app['categories'],'nav_pinned_app') !== false) {
- $navbar_apps[] = Zlib\Apps::app_render($app,'nav-order');
- }
- else {
- $nav_apps[] = Zlib\Apps::app_render($app,'nav-order');
+ foreach($syslist as $app) {
+ if($l === 'nav_pinned_app') {
+ $navbar_apps[] = Zlib\Apps::app_render($app,'nav-order');
+ }
+ elseif(strpos($app['categories'],'nav_pinned_app') === false) {
+ $nav_apps[] = Zlib\Apps::app_render($app,'nav-order');
+ }
}
}
diff --git a/Zotlabs/Module/Articles.php b/Zotlabs/Module/Articles.php
index 58c16be45..ca132c01e 100644
--- a/Zotlabs/Module/Articles.php
+++ b/Zotlabs/Module/Articles.php
@@ -17,8 +17,16 @@ class Articles extends Controller {
if(argc() > 1)
$which = argv(1);
- else
- return;
+
+ if(! $which) {
+ if(local_channel()) {
+ $channel = App::get_channel();
+ if($channel && $channel['channel_address'])
+ $which = $channel['channel_address'];
+ } else {
+ return;
+ }
+ }
profile_load($which);
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/Channel.php b/Zotlabs/Module/Channel.php
index 12d87885f..5fdefd805 100644
--- a/Zotlabs/Module/Channel.php
+++ b/Zotlabs/Module/Channel.php
@@ -124,6 +124,11 @@ class Channel extends Controller {
$mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : '');
+ if(strpos($mid,'b64.') === 0)
+ $decoded = @base64url_decode(substr($mid,4));
+ if($decoded)
+ $mid = $decoded;
+
$datequery = ((x($_GET,'dend') && is_a_date_arg($_GET['dend'])) ? notags($_GET['dend']) : '');
$datequery2 = ((x($_GET,'dbegin') && is_a_date_arg($_GET['dbegin'])) ? notags($_GET['dbegin']) : '');
@@ -375,6 +380,9 @@ class Channel extends Controller {
if((! $update) && (! $load)) {
+ if($decoded)
+ $mid = 'b64.' . base64url_encode($mid);
+
// This is ugly, but we can't pass the profile_uid through the session to the ajax updater,
// because browser prefetching might change it on us. We have to deliver it with the page.
diff --git a/Zotlabs/Module/Chanview.php b/Zotlabs/Module/Chanview.php
index 779c7e646..2e653d030 100644
--- a/Zotlabs/Module/Chanview.php
+++ b/Zotlabs/Module/Chanview.php
@@ -106,7 +106,7 @@ class Chanview extends \Zotlabs\Web\Controller {
if (\App::$poi) {
$url = \App::$poi['xchan_url'];
- if(\App::$poi['xchan_network'] === 'zot') {
+ if(in_array(\App::$poi['xchan_network'], ['zot', 'zot6'])) {
$is_zot = true;
}
if(local_channel()) {
diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php
index a9f643306..a587324df 100644
--- a/Zotlabs/Module/Connedit.php
+++ b/Zotlabs/Module/Connedit.php
@@ -101,7 +101,8 @@ class Connedit extends \Zotlabs\Web\Controller {
}
- $profile_id = $_POST['profile_assign'];
+ $profile_id = ((array_key_exists('profile_assign',$_POST)) ? $_POST['profile_assign'] : $orig_record[0]['abook_profile']);
+
if($profile_id) {
$r = q("SELECT profile_guid FROM profile WHERE profile_guid = '%s' AND uid = %d LIMIT 1",
dbesc($profile_id),
@@ -113,18 +114,23 @@ class Connedit extends \Zotlabs\Web\Controller {
}
}
- $abook_incl = escape_tags($_POST['abook_incl']);
- $abook_excl = escape_tags($_POST['abook_excl']);
-
+ $abook_incl = ((array_key_exists('abook_incl',$_POST)) ? escape_tags($_POST['abook_incl']) : $orig_record[0]['abook_incl']);
+ $abook_excl = ((array_key_exists('abook_excl',$_POST)) ? escape_tags($_POST['abook_excl']) : $orig_record[0]['abook_excl']);
+
+
$hidden = intval($_POST['hidden']);
$priority = intval($_POST['poll']);
if($priority > 5 || $priority < 0)
$priority = 0;
+ if(! array_key_exists('closeness',$_POST)) {
+ $_POST['closeness'] = 80;
+ }
$closeness = intval($_POST['closeness']);
- if($closeness < 0)
- $closeness = 99;
+ if($closeness < 0 || $closeness > 99) {
+ $closeness = 80;
+ }
$rating = intval($_POST['rating']);
if($rating < (-10))
@@ -231,6 +237,8 @@ class Connedit extends \Zotlabs\Web\Controller {
}
$abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']);
+
+
$r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d,
abook_incl = '%s', abook_excl = '%s'
@@ -702,7 +710,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$tpl = get_markup_template("abook_edit.tpl");
- if(feature_enabled(local_channel(),'affinity')) {
+ if(Apps::system_app_installed(local_channel(),'Affinity Tool')) {
$sections['affinity'] = [
'label' => t('Affinity'),
@@ -733,9 +741,12 @@ class Connedit extends \Zotlabs\Web\Controller {
}
$slider_tpl = get_markup_template('contact_slider.tpl');
+
+ $slideval = intval($contact['abook_closeness']);
+
$slide = replace_macros($slider_tpl,array(
'$min' => 1,
- '$val' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 99),
+ '$val' => $slideval,
'$labels' => $label_str,
));
}
@@ -892,7 +903,7 @@ class Connedit extends \Zotlabs\Web\Controller {
'$inherited' => t('inherited'),
'$submit' => t('Submit'),
'$lbl_vis2' => sprintf( t('Please choose the profile you would like to display to %s when viewing your profile securely.'), $contact['xchan_name']),
- '$close' => $contact['abook_closeness'],
+ '$close' => (($contact['abook_closeness']) ? $contact['abook_closeness'] : 80),
'$them' => t('Their Settings'),
'$me' => t('My Settings'),
'$perms' => $perms,
diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php
index d1755c183..04e5f9fce 100644
--- a/Zotlabs/Module/Display.php
+++ b/Zotlabs/Module/Display.php
@@ -178,8 +178,7 @@ class Display extends \Zotlabs\Web\Controller {
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
-
-
+
$simple_update = (($update) ? " AND item_unseen = 1 " : '');
if($update && $_SESSION['loadtime'])
@@ -314,7 +313,7 @@ class Display extends \Zotlabs\Web\Controller {
}
}
- if(! $r) {
+ if($r === null) {
// in case somebody turned off public access to sys channel content using permissions
// make that content unsearchable by ensuring the owner_xchan can't match
if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))
@@ -375,8 +374,7 @@ class Display extends \Zotlabs\Web\Controller {
}
$o .= '</noscript>';
- if ($items[0]['title'])
- \App::$page['title'] = $items[0]['title'] . " - " . \App::$page['title'];
+ \App::$page['title'] = (($items[0]['title']) ? $items[0]['title'] . " - " . \App::$page['title'] : \App::$page['title']);
$o .= conversation($items, 'display', $update, 'client');
}
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/Filestorage.php b/Zotlabs/Module/Filestorage.php
index 23bd63f95..2c247cd65 100644
--- a/Zotlabs/Module/Filestorage.php
+++ b/Zotlabs/Module/Filestorage.php
@@ -5,6 +5,8 @@ namespace Zotlabs\Module;
*
*/
+
+
class Filestorage extends \Zotlabs\Web\Controller {
function post() {
@@ -71,14 +73,6 @@ class Filestorage extends \Zotlabs\Web\Controller {
return;
}
- // Since we have ACL'd files in the wild, but don't have ACL here yet, we
- // need to return for anyone other than the owner, despite the perms check for now.
-
- $is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
- if(! ($is_owner || is_site_admin())){
- info( t('Permission Denied.') . EOL );
- return;
- }
if(argc() > 3 && argv(3) === 'delete') {
@@ -101,18 +95,31 @@ class Filestorage extends \Zotlabs\Web\Controller {
}
$file = intval(argv(2));
- $r = q("SELECT hash FROM attach WHERE id = %d AND uid = %d LIMIT 1",
+ $r = q("SELECT hash, creator FROM attach WHERE id = %d AND uid = %d LIMIT 1",
dbesc($file),
intval($owner)
);
if(! $r) {
+ notice( t('File not found.') . EOL);
+
if($json_return)
json_return_and_die([ 'success' => false ]);
- notice( t('File not found.') . EOL);
goaway(z_root() . '/cloud/' . $which);
}
+ if(local_channel() !== $owner) {
+ if($r[0]['creator'] && $r[0]['creator'] !== $ob_hash) {
+ notice( t('Permission denied.') . EOL);
+
+ if($json_return)
+ json_return_and_die([ 'success' => false ]);
+
+ goaway(z_root() . '/cloud/' . $which);
+ }
+ }
+
+
$f = $r[0];
$channel = channelx_by_n($owner);
@@ -134,6 +141,19 @@ class Filestorage extends \Zotlabs\Web\Controller {
goaway(dirname($url));
}
+
+
+
+ // Since we have ACL'd files in the wild, but don't have ACL here yet, we
+ // need to return for anyone other than the owner, despite the perms check for now.
+
+ $is_owner = (((local_channel()) && ($owner == local_channel())) ? true : false);
+ if(! ($is_owner || is_site_admin())){
+ info( t('Permission Denied.') . EOL );
+ return;
+ }
+
+
if(argc() > 3 && argv(3) === 'edit') {
require_once('include/acl_selectors.php');
if(! $perms['write_storage']) {
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/Id.php b/Zotlabs/Module/Id.php
new file mode 100644
index 000000000..15abfa2a3
--- /dev/null
+++ b/Zotlabs/Module/Id.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Zotlabs\Module;
+
+/**
+ *
+ * Controller for responding to x-zot: protocol requests
+ * x-zot:_jkfRG85nJ-714zn-LW_VbTFW8jSjGAhAydOcJzHxqHkvEHWG2E0RbA_pbch-h4R63RG1YJZifaNzgccoLa3MQ/453c1678-1a79-4af7-ab65-6b012f6cab77
+ *
+ */
+
+use Zotlabs\Lib\Activity;
+use Zotlabs\Lib\ActivityStreams;
+use Zotlabs\Lib\LDSignatures;
+use Zotlabs\Zot6\HTTPSig;
+use Zotlabs\Web\Controller;
+use Zotlabs\Lib\Libzot;
+use Zotlabs\Lib\ThreadListener;
+use Zotlabs\Lib\IConfig;
+use Zotlabs\Lib\Enotify;
+use App;
+
+require_once('include/attach.php');
+require_once('include/bbcode.php');
+require_once('include/security.php');
+
+
+class Id extends Controller {
+
+ function init() {
+
+ if(Libzot::is_zot_request()) {
+
+ $conversation = false;
+
+ $request_portable_id = argv(1);
+ if(argc() > 2) {
+ $item_id = argv(2);
+ }
+
+ $portable_id = EMPTY_STR;
+
+ $sigdata = HTTPSig::verify(EMPTY_STR);
+ if($sigdata['portable_id'] && $sigdata['header_valid']) {
+ $portable_id = $sigdata['portable_id'];
+ }
+
+
+ $chan = channelx_by_hash($request_portable_id);
+
+ if($chan) {
+ $channel_id = $chan['channel_id'];
+ if(! $item_id) {
+ $handler = new Channel();
+ App::$argc = 2;
+ App::$argv[0] = 'channel';
+ App::$argv[1] = $chan['channel_address'];
+ $handler->init();
+ }
+ }
+ else {
+ http_status_exit(404, 'Not found');
+ }
+
+
+ $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 uuid = '%s' $item_normal $sql_extra and uid = %d limit 1",
+ dbesc($item_id),
+ intval($channel_id)
+ );
+ if(! $r) {
+
+ $r = q("select * from item where uuid = '%s' $item_normal and uid = %d limit 1",
+ dbesc($item_id),
+ intval($channel_id)
+ );
+ if($r) {
+ http_status_exit(403, 'Forbidden');
+ }
+ http_status_exit(404, 'Not found');
+ }
+
+ if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
+ http_status_exit(403, 'Forbidden');
+
+ xchan_query($r,true);
+ $items = fetch_post_tags($r,true);
+
+ $i = Activity::encode_item($items[0]);
+
+ 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' ;
+ $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();
+
+ }
+
+ }
+
+}
+
+
diff --git a/Zotlabs/Module/Import.php b/Zotlabs/Module/Import.php
index 6016328a5..0daf28aa9 100644
--- a/Zotlabs/Module/Import.php
+++ b/Zotlabs/Module/Import.php
@@ -127,6 +127,15 @@ class Import extends \Zotlabs\Web\Controller {
//
// }
+
+ // prevent incompatible osada or zap data from horking your database
+
+ if(array_path_exists('compatibility/codebase',$data)) {
+ notice('Data export format is not compatible with this software');
+ return;
+ }
+
+
if($moving)
$seize = 1;
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 1d64ef60c..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);
@@ -760,7 +905,7 @@ class Item extends Controller {
// fix permalinks for cards
if($webpage == ITEM_TYPE_CARD) {
- $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16));
+ $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid);
}
if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_CARD)) {
$r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.iid = %d limit 1",
@@ -772,7 +917,7 @@ class Item extends Controller {
}
if($webpage == ITEM_TYPE_ARTICLE) {
- $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16));
+ $plink = z_root() . '/articles/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : $uuid);
}
if(($parent_item) && ($parent_item['item_type'] == ITEM_TYPE_ARTICLE)) {
$r = q("select v from iconfig where iconfig.cat = 'system' and iconfig.k = 'ARTICLE' and iconfig.iid = %d limit 1",
@@ -784,7 +929,7 @@ class Item extends Controller {
}
if ((! $plink) && ($item_thread_top)) {
- $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid;
+ $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . gen_link_id($mid);
$plink = substr($plink,0,190);
}
diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php
index c39726b88..3d1f503b6 100644
--- a/Zotlabs/Module/Like.php
+++ b/Zotlabs/Module/Like.php
@@ -1,6 +1,8 @@
<?php
namespace Zotlabs\Module;
+use Zotlabs\Lib\Activity;
+
require_once('include/security.php');
require_once('include/bbcode.php');
require_once('include/items.php');
@@ -50,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;
@@ -249,6 +251,9 @@ class Like extends \Zotlabs\Web\Controller {
}
}
else {
+
+ if(! $observer)
+ killme();
// this is used to like an item or comment
@@ -400,6 +405,7 @@ class Like extends \Zotlabs\Web\Controller {
$object = json_encode(array(
'type' => $objtype,
'id' => $item['mid'],
+ 'asld' => Activity::fetch_item( [ 'id' => $item['mid'] ] ),
'parent' => (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']),
'link' => $links,
'title' => $item['title'],
diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php
index 7c7dc0e88..32b4c0281 100644
--- a/Zotlabs/Module/Linkinfo.php
+++ b/Zotlabs/Module/Linkinfo.php
@@ -138,8 +138,8 @@ class Linkinfo extends \Zotlabs\Web\Controller {
}
$image = "";
-
- if(sizeof($siteinfo["images"]) > 0){
+
+ if(is_array($siteinfo["images"]) && count($siteinfo["images"])){
/* Execute below code only if image is present in siteinfo */
$total_images = 0;
@@ -161,7 +161,7 @@ class Linkinfo extends \Zotlabs\Web\Controller {
$total_images ++;
if($max_images && $max_images >= $total_images)
break;
- }
+ }
}
if(strlen($text)) {
diff --git a/Zotlabs/Module/Mail.php b/Zotlabs/Module/Mail.php
index d38c1d88c..3202d38a5 100644
--- a/Zotlabs/Module/Mail.php
+++ b/Zotlabs/Module/Mail.php
@@ -34,7 +34,7 @@ class Mail extends \Zotlabs\Web\Controller {
}
else {
$body = cleanup_bbcode($body);
- $results = linkify_tags($a, $body, local_channel());
+ $results = linkify_tags($body, local_channel());
if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) {
$attachments = array();
@@ -111,7 +111,7 @@ class Mail extends \Zotlabs\Web\Controller {
}
require_once('include/text.php');
- linkify_tags($a, $body, local_channel());
+ linkify_tags($body, local_channel());
if(! $recipient) {
diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php
index b93faa612..2019082ed 100644
--- a/Zotlabs/Module/Network.php
+++ b/Zotlabs/Module/Network.php
@@ -1,6 +1,8 @@
<?php
namespace Zotlabs\Module;
+use Zotlabs\Lib\Group;
+use Zotlabs\Lib\Apps;
use App;
require_once('include/items.php');
@@ -114,8 +116,8 @@ class Network extends \Zotlabs\Web\Controller {
$def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
}
- $default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1));
- $default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1));
+ $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1));
+ $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1));
$cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0);
$star = ((x($_GET,'star')) ? intval($_GET['star']) : 0);
@@ -132,6 +134,13 @@ class Network extends \Zotlabs\Web\Controller {
$deftag = '';
+ if (Apps::system_app_installed(local_channel(),'Affinity Tool')) {
+ $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
+ if ($affinity_locked) {
+ set_pconfig(local_channel(),'affinity','cmin',$cmin);
+ set_pconfig(local_channel(),'affinity','cmax',$cmax);
+ }
+ }
if(x($_GET,'search') || $file || (!$pf && $cid) || $hashtags || $verb || $category || $conv || $unseen)
$nouveau = true;
diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php
index a9022a03a..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']))) {
@@ -145,7 +145,7 @@ class New_channel extends \Zotlabs\Web\Controller {
$canadd = true;
if($r && ($limit !== false)) {
$channel_usage_message = sprintf( t("You have created %1$.0f of %2$.0f allowed channels."), $r[0]['total'], $limit);
- if ($r[0]['total'] >= $limit) {
+ if ($r[0]['total'] > $limit) {
$canadd = false;
}
}
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 44fe5d9a9..06b94b34f 100644
--- a/Zotlabs/Module/Pconfig.php
+++ b/Zotlabs/Module/Pconfig.php
@@ -22,6 +22,11 @@ class Pconfig extends \Zotlabs\Web\Controller {
$k = trim(escape_tags($_POST['k']));
$v = trim($_POST['v']);
$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)) {
+ return;
+ }
if(in_array(argv(2),$this->disallowed_pconfig())) {
notice( t('This setting requires special processing and editing has been blocked.') . EOL);
diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php
index 30e8340e2..96a4e1f40 100644
--- a/Zotlabs/Module/Photo.php
+++ b/Zotlabs/Module/Photo.php
@@ -1,19 +1,20 @@
<?php
+
+
namespace Zotlabs\Module;
require_once('include/security.php');
require_once('include/attach.php');
require_once('include/photo/photo_driver.php');
-
class Photo extends \Zotlabs\Web\Controller {
function init() {
- $prvcachecontrol = false;
$streaming = null;
$channel = null;
$person = 0;
+ $renew = false;
switch(argc()) {
case 4:
@@ -29,7 +30,15 @@ class Photo extends \Zotlabs\Web\Controller {
killme();
// NOTREACHED
}
-
+
+ $cache_mode = array(
+ 'on' => false,
+ 'age' => 86400,
+ 'exp' => true,
+ 'leak' => false
+ );
+ call_hooks('cache_mode_hook', $cache_mode);
+
$observer_xchan = get_observer_hash();
$ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
@@ -106,13 +115,14 @@ class Photo extends \Zotlabs\Web\Controller {
License link: http://creativecommons.org/licenses/by/3.0/
*/
+ // @FIXME It seems this part doesn't work because we are not setting such cookie
$cookie_value = false;
if (isset($_COOKIE['devicePixelRatio'])) {
$cookie_value = intval($_COOKIE['devicePixelRatio']);
}
else {
// Force revalidation of cache on next request
- $cache_directive = 'no-cache';
+ // $prvcachecontrol = 'no-cache';
$status = 'no cookie';
}
@@ -129,27 +139,42 @@ class Photo extends \Zotlabs\Web\Controller {
$resolution = 1;
}
- $r = q("SELECT uid, photo_usage FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
+ $r = q("SELECT uid, photo_usage, display_path FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo),
intval($resolution)
);
if($r) {
-
$allowed = (-1);
- if(intval($r[0]['photo_usage'])) {
+ $u = intval($r[0]['photo_usage']);
+ if($u) {
$allowed = 1;
- if(intval($r[0]['photo_usage']) === PHOTO_COVER)
+ if($u === PHOTO_COVER)
if($resolution < PHOTO_RES_COVER_1200)
$allowed = (-1);
- if(intval($r[0]['photo_usage']) === PHOTO_PROFILE)
+ if($u === PHOTO_PROFILE)
if(! in_array($resolution,[4,5,6]))
$allowed = (-1);
+ if($u === PHOTO_CACHE) {
+ // Validate cache
+ $cache = array(
+ 'resid' => $photo,
+ 'status' => false
+ );
+ if($cache_mode['on'])
+ call_hooks('cache_url_hook', $cache);
+ 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();
+ }
+ }
}
- if($allowed === (-1)) {
+ if($allowed === (-1))
$allowed = attach_can_view($r[0]['uid'],$observer_xchan,$photo);
- }
$channel = channelx_by_n($r[0]['uid']);
@@ -158,18 +183,21 @@ class Photo extends \Zotlabs\Web\Controller {
dbesc($photo),
intval($resolution)
);
-
+
$exists = (($e) ? true : false);
-
+
if($exists && $allowed) {
+ $expires = strtotime($e[0]['expires'] . 'Z');
$data = dbunescbin($e[0]['content']);
$filesize = $e[0]['filesize'];
$mimetype = $e[0]['mimetype'];
$modified = strtotime($e[0]['edited'] . 'Z');
- if(intval($e[0]['os_storage']))
+
+ if(intval($e[0]['os_storage'])) {
$streaming = $data;
+ }
if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
- $prvcachecontrol = true;
+ $prvcachecontrol = 'no-store, no-cache, must-revalidate';
}
else {
if(! $allowed) {
@@ -180,9 +208,9 @@ class Photo extends \Zotlabs\Web\Controller {
}
}
- } else {
+ }
+ else
http_status_exit(404,'not found');
- }
}
header_remove('Pragma');
@@ -225,24 +253,14 @@ class Photo extends \Zotlabs\Web\Controller {
$mimetype = $ph->getType();
}
}
-
- // @FIXME Seems never invoked
- // Writing in cachefile
- if (isset($cachefile) && $cachefile != '') {
- file_put_contents($cachefile, $data);
- $modified = filemtime($cachefile);
- }
-
- header("Content-type: " . $mimetype);
-
- if($prvcachecontrol) {
+ if(isset($prvcachecontrol)) {
// it is a private photo that they have no permission to view.
// tell the browser not to cache it, in case they authenticate
// and subsequently have permission to see it
- header("Cache-Control: no-store, no-cache, must-revalidate");
+ header("Cache-Control: " . $prvcachecontrol);
}
else {
@@ -255,18 +273,23 @@ class Photo extends \Zotlabs\Web\Controller {
// This has performance considerations but we highly recommend you
// leave it alone.
- $cache = get_config('system','photo_cache_time', 86400); // 1 day by default
+ $maxage = $cache_mode['age'];
- header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT");
- header("Cache-Control: max-age=" . $cache);
+ if($cache_mode['exp'] || (! isset($expires)) || (isset($expires) && $expires - 60 < time()))
+ $expires = time() + $maxage;
+ else
+ $maxage = $expires - time();
+
+ header("Expires: " . gmdate("D, d M Y H:i:s", $expires) . " GMT");
+ header("Cache-Control: max-age=" . $maxage);
}
+ header("Content-type: " . $mimetype);
header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modified) . " GMT");
header("Content-Length: " . (isset($filesize) ? $filesize : strlen($data)));
// If it's a file resource, stream it.
-
if($streaming && $channel) {
if(strpos($streaming,'store') !== false)
$istream = fopen($streaming,'rb');
diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php
index 21f6293ef..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)
@@ -1122,6 +1122,7 @@ class Photos extends \Zotlabs\Web\Controller {
$comments = '';
if(! $r) {
if($observer && ($can_post || $can_comment)) {
+ $feature_auto_save_draft = ((feature_enabled($owner_uid, 'auto_save_draft')) ? "true" : "false");
$commentbox = replace_macros($cmnt_tpl,array(
'$return_path' => '',
'$mode' => 'photos',
@@ -1137,7 +1138,8 @@ class Photos extends \Zotlabs\Web\Controller {
'$submit' => t('Submit'),
'$preview' => t('Preview'),
'$ww' => '',
- '$feature_encrypt' => false
+ '$feature_encrypt' => false,
+ '$auto_save_draft' => $feature_auto_save_draft
));
}
}
@@ -1270,8 +1272,14 @@ class Photos extends \Zotlabs\Web\Controller {
if(feature_enabled($owner_uid,'dislike'))
$response_verbs[] = 'dislike';
-
$responses = get_responses($conv_responses,$response_verbs,'',$link_item);
+
+ $hookdata = [
+ 'onclick' => '$.colorbox({href: \'' . $photo['href'] . '\'}); return false;',
+ 'raw_photo' => $ph[0],
+ 'nickname' => \App::$data['channel']['channel_address']
+ ];
+ call_hooks('photo_view_filter', $hookdata);
$photo_tpl = get_markup_template('photo_view.tpl');
$o .= replace_macros($photo_tpl, array(
@@ -1309,6 +1317,7 @@ class Photos extends \Zotlabs\Web\Controller {
'$comments' => $comments,
'$commentbox' => $commentbox,
'$paginate' => $paginate,
+ '$onclick' => $hookdata['onclick']
));
\App::$data['photo_html'] = $o;
diff --git a/Zotlabs/Module/Ping.php b/Zotlabs/Module/Ping.php
index f660c3b55..a367b1062 100644
--- a/Zotlabs/Module/Ping.php
+++ b/Zotlabs/Module/Ping.php
@@ -330,6 +330,7 @@ class Ping extends \Zotlabs\Web\Controller {
$notifs[] = array(
'notify_link' => z_root() . '/mail/' . $zz['id'],
'name' => $zz['xchan_name'],
+ 'addr' => $zz['xchan_addr'],
'url' => $zz['xchan_url'],
'photo' => $zz['xchan_photo_s'],
'when' => relative_date($zz['created']),
@@ -346,6 +347,10 @@ class Ping extends \Zotlabs\Web\Controller {
if(argc() > 1 && (argv(1) === 'network' || argv(1) === 'home')) {
$result = array();
+ if(argv(1) === 'home') {
+ $sql_extra .= ' and item_wall = 1 ';
+ }
+
$r = q("SELECT * FROM item
WHERE uid = %d
AND item_unseen = 1
@@ -361,8 +366,6 @@ class Ping extends \Zotlabs\Web\Controller {
if($r) {
xchan_query($r);
foreach($r as $item) {
- if((argv(1) === 'home') && (! intval($item['item_wall'])))
- continue;
$result[] = \Zotlabs\Lib\Enotify::format($item);
}
}
@@ -383,6 +386,7 @@ class Ping extends \Zotlabs\Web\Controller {
$result[] = array(
'notify_link' => z_root() . '/connections/ifpending',
'name' => $rr['xchan_name'],
+ 'addr' => $rr['xchan_addr'],
'url' => $rr['xchan_url'],
'photo' => $rr['xchan_photo_s'],
'when' => relative_date($rr['abook_created']),
@@ -407,6 +411,7 @@ class Ping extends \Zotlabs\Web\Controller {
$result[] = array(
'notify_link' => z_root() . '/admin/accounts',
'name' => $rr['account_email'],
+ 'addr' => $rr['account_email'],
'url' => '',
'photo' => z_root() . '/' . get_default_profile_photo(48),
'when' => relative_date($rr['account_created']),
@@ -444,6 +449,7 @@ class Ping extends \Zotlabs\Web\Controller {
$result[] = array(
'notify_link' => z_root() . '/events', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'],
'name' => $rr['xchan_name'],
+ 'addr' => $rr['xchan_addr'],
'url' => $rr['xchan_url'],
'photo' => $rr['xchan_photo_s'],
'when' => $when,
@@ -460,7 +466,7 @@ class Ping extends \Zotlabs\Web\Controller {
if(argc() > 1 && (argv(1) === 'files')) {
$result = array();
- $r = q("SELECT item.created, xchan.xchan_name, xchan.xchan_url, xchan.xchan_photo_s FROM item
+ $r = q("SELECT item.created, xchan.xchan_name, xchan.xchan_addr, xchan.xchan_url, xchan.xchan_photo_s FROM item
LEFT JOIN xchan on author_xchan = xchan_hash
WHERE item.verb = '%s'
AND item.obj_type = '%s'
@@ -477,6 +483,7 @@ class Ping extends \Zotlabs\Web\Controller {
$result[] = array(
'notify_link' => z_root() . '/sharedwithme',
'name' => $rr['xchan_name'],
+ 'addr' => $rr['xchan_addr'],
'url' => $rr['xchan_url'],
'photo' => $rr['xchan_photo_s'],
'when' => relative_date($rr['created']),
@@ -658,6 +665,7 @@ class Ping extends \Zotlabs\Web\Controller {
if($r[0]['unseen']) {
$forums[$x]['notify_link'] = (($forums[$x]['private_forum']) ? $forums[$x]['xchan_url'] : z_root() . '/network/?f=&pf=1&unseen=1&cid=' . $forums[$x]['abook_id']);
$forums[$x]['name'] = $forums[$x]['xchan_name'];
+ $forums[$x]['addr'] = $forums[$x]['xchan_addr'];
$forums[$x]['url'] = $forums[$x]['xchan_url'];
$forums[$x]['photo'] = $forums[$x]['xchan_photo_s'];
$forums[$x]['unseen'] = $r[0]['unseen'];
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/Rmagic.php b/Zotlabs/Module/Rmagic.php
index 33a6689ca..8c1e5cdab 100644
--- a/Zotlabs/Module/Rmagic.php
+++ b/Zotlabs/Module/Rmagic.php
@@ -14,6 +14,15 @@ class Rmagic extends \Zotlabs\Web\Controller {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($me)
);
+ if(! $r) {
+ $w = discover_by_webbie($me);
+ if($w) {
+ $r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
+ dbesc($me)
+ );
+ }
+ }
+
if($r) {
if($r[0]['hubloc_url'] === z_root())
goaway(z_root() . '/login');
@@ -49,7 +58,16 @@ class Rmagic extends \Zotlabs\Web\Controller {
$r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
dbesc($address)
);
+ if(! $r) {
+ $w = discover_by_webbie($address);
+ if($w) {
+ $r = q("select hubloc_url from hubloc where hubloc_addr = '%s' limit 1",
+ dbesc($address)
+ );
+ }
+ }
}
+
if($r) {
$url = $r[0]['hubloc_url'];
}
diff --git a/Zotlabs/Module/Settings/Featured.php b/Zotlabs/Module/Settings/Featured.php
index 542a05363..d5d740aff 100644
--- a/Zotlabs/Module/Settings/Featured.php
+++ b/Zotlabs/Module/Settings/Featured.php
@@ -10,20 +10,6 @@ class Featured {
call_hooks('feature_settings_post', $_POST);
- if($_POST['affinity_slider-submit']) {
- $cmax = intval($_POST['affinity_cmax']);
- if($cmax < 0 || $cmax > 99)
- $cmax = 99;
- $cmin = intval($_POST['affinity_cmin']);
- if($cmin < 0 || $cmin > 99)
- $cmin = 0;
- set_pconfig(local_channel(),'affinity','cmin',$cmin);
- set_pconfig(local_channel(),'affinity','cmax',$cmax);
-
- info( t('Affinity Slider settings updated.') . EOL);
-
- }
-
build_sync_packet();
return;
}
@@ -37,30 +23,10 @@ class Featured {
if(! $r)
$settings_addons = t('No feature settings configured');
- if(feature_enabled(local_channel(),'affinity')) {
-
- $cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
- $cmax = (($cmax) ? $cmax : 99);
- $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
- '$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
- ));
- $cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
- $cmin = (($cmin) ? $cmin : 0);
- $setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
- '$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
- ));
-
- $settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array(
- '$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')),
- '$content' => $setting_fields
- ));
- }
-
call_hooks('feature_settings', $settings_addons);
$this->sortpanels($settings_addons);
-
$tpl = get_markup_template("settings_addons.tpl");
$o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("settings_featured"),
diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php
index c0716ca7c..370b7b9f8 100644
--- a/Zotlabs/Module/Setup.php
+++ b/Zotlabs/Module/Setup.php
@@ -441,13 +441,18 @@ class Setup extends \Zotlabs\Web\Controller {
require_once 'include/environment.php';
$help = '';
+ $mem_warning = '';
$result = getPhpiniUploadLimits();
+ if($result['post_max_size'] < 4194304 || $result['max_upload_filesize'] < 4194304) {
+ $mem_warning = '<strong>' .t('This is not sufficient to upload larger images or files. You should be able to upload at least 4 MB at once.') . '</strong>';
+ }
$help = sprintf(t('Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once.'),
userReadableSize($result['post_max_size']),
userReadableSize($result['max_upload_filesize']),
$result['max_file_uploads']
);
+ $help .= $mem_warning;
$help .= '<br><br>' . t('You can adjust these settings in the server php.ini file.');
$this->check_add($checks, t('PHP upload limits'), true, false, $help);
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('&#x1f501; 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/Sslify.php b/Zotlabs/Module/Sslify.php
index 2891f3691..37be4423b 100644
--- a/Zotlabs/Module/Sslify.php
+++ b/Zotlabs/Module/Sslify.php
@@ -12,10 +12,16 @@ class Sslify extends \Zotlabs\Web\Controller {
list($k,$v) = array_map("trim", explode(":", trim($l), 2));
$hdrs[strtolower($k)] = $v;
}
- if (array_key_exists('content-type', $hdrs)) {
- $type = $hdrs['content-type'];
- header('Content-Type: ' . $type);
- }
+
+ if (array_key_exists('content-type', $hdrs))
+ header('Content-Type: ' . $hdrs['content-type']);
+ if (array_key_exists('last-modified', $hdrs))
+ header('Last-Modified: ' . $hdrs['last-modified']);
+ if (array_key_exists('cache-control', $hdrs))
+ header('Cache-Control: ' . $hdrs['cache-control']);
+ if (array_key_exists('expires', $hdrs))
+ header('Expires: ' . $hdrs['expires']);
+
echo $x['body'];
killme();
diff --git a/Zotlabs/Module/Viewsrc.php b/Zotlabs/Module/Viewsrc.php
index 119990b57..3e49b9db4 100644
--- a/Zotlabs/Module/Viewsrc.php
+++ b/Zotlabs/Module/Viewsrc.php
@@ -25,10 +25,10 @@ class Viewsrc extends \Zotlabs\Web\Controller {
notice( t('Item not found.') . EOL);
}
- $item_normal = item_normal();
+ $item_normal = item_normal_search();
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/Module/Wfinger.php b/Zotlabs/Module/Wfinger.php
index e4591df12..03275abbc 100644
--- a/Zotlabs/Module/Wfinger.php
+++ b/Zotlabs/Module/Wfinger.php
@@ -43,6 +43,9 @@ class Wfinger extends \Zotlabs\Web\Controller {
if(strpos($resource,'acct:') === 0) {
$channel = str_replace('acct:','',$resource);
+ if(substr($channel,0,1) === '@' && strpos(substr($channel,1),'@')) {
+ $channel = substr($channel,1);
+ }
if(strpos($channel,'@') !== false) {
$host = substr($channel,strpos($channel,'@')+1);
diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php
index 892810241..502f96a62 100644
--- a/Zotlabs/Module/Wiki.php
+++ b/Zotlabs/Module/Wiki.php
@@ -442,8 +442,8 @@ class Wiki extends Controller {
$mimeType = $_POST['mimetype'];
if($mimeType === 'text/bbcode') {
- $linkconverted = NativeWikiPage::convert_links($content,$wikiURL);
- $html = zidify_links(smilies(bbcode($linkconverted)));
+ $html = zidify_links(smilies(bbcode($content)));
+ $html = NativeWikiPage::convert_links($html,$wikiURL);
}
elseif($mimeType === 'text/markdown') {
$linkconverted = NativeWikiPage::convert_links($content,$wikiURL);
diff --git a/Zotlabs/Photo/PhotoDriver.php b/Zotlabs/Photo/PhotoDriver.php
new file mode 100644
index 000000000..c47a7c3b2
--- /dev/null
+++ b/Zotlabs/Photo/PhotoDriver.php
@@ -0,0 +1,498 @@
+<?php
+
+namespace Zotlabs\Photo;
+
+/**
+ * @brief Abstract photo driver class.
+ *
+ * Inheritance seems not to be the best design pattern for such photo drivers.
+ */
+abstract class PhotoDriver {
+
+ /**
+ * @brief This variable keeps the image.
+ *
+ * For GD it is a PHP image resource.
+ * For ImageMagick it is an \Imagick object.
+ *
+ * @var resource|\Imagick
+ */
+ protected $image;
+
+ /**
+ * @var integer
+ */
+ protected $width;
+
+ /**
+ * @var integer
+ */
+ protected $height;
+
+ /**
+ * @var boolean
+ */
+ protected $valid;
+
+ /**
+ * @brief The mimetype of the image.
+ *
+ * @var string
+ */
+ protected $type;
+
+ /**
+ * @brief Supported mimetypes by the used photo driver.
+ *
+ * @var array
+ */
+ protected $types;
+
+ /**
+ * @brief Return an array with supported mimetypes.
+ *
+ * @return array
+ * Associative array with mimetype as key and file extension as value.
+ */
+ abstract public function supportedTypes();
+
+ abstract protected function load($data, $type);
+
+ abstract protected function destroy();
+
+ abstract protected function setDimensions();
+
+ /**
+ * @brief Return the current image.
+ *
+ * @fixme Shouldn't his method be protected, because outside of the current
+ * driver it makes no sense at all because of the different return values.
+ *
+ * @return boolean|resource|\Imagick
+ * false on failure, a PHP image resource for GD driver, an \Imagick object
+ * for ImageMagick driver.
+ */
+ abstract public function getImage();
+
+ abstract public function doScaleImage($new_width, $new_height);
+
+ abstract public function rotate($degrees);
+
+ abstract public function flip($horiz = true, $vert = false);
+
+ /**
+ * @brief Crops the image.
+ *
+ * @param int $maxx width of the new image
+ * @param int $maxy height of the new image
+ * @param int $x x-offset for region
+ * @param int $y y-offset for region
+ * @param int $w width of region
+ * @param int $h height of region
+ *
+ * @return boolean|void false on failure
+ */
+ abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h);
+
+ /**
+ * @brief Return a binary string from the image resource.
+ *
+ * @return string A Binary String.
+ */
+ abstract public function imageString();
+
+ abstract public function clearexif();
+
+
+ /**
+ * @brief PhotoDriver constructor.
+ *
+ * @param string $data Image
+ * @param string $type mimetype
+ */
+ public function __construct($data, $type = '') {
+ $this->types = $this->supportedTypes();
+ if(! array_key_exists($type, $this->types)) {
+ $type = 'image/jpeg';
+ }
+ $this->type = $type;
+ $this->valid = false;
+ $this->load($data, $type);
+ }
+
+ public function __destruct() {
+ if($this->is_valid())
+ $this->destroy();
+ }
+
+ /**
+ * @brief Is it a valid image object.
+ *
+ * @return boolean
+ */
+ public function is_valid() {
+ return $this->valid;
+ }
+
+ /**
+ * @brief Get the width of the image.
+ *
+ * @return boolean|number Width of image in pixels, or false on failure
+ */
+ public function getWidth() {
+ if(! $this->is_valid())
+ return false;
+
+ return $this->width;
+ }
+
+ /**
+ * @brief Get the height of the image.
+ *
+ * @return boolean|number Height of image in pixels, or false on failure
+ */
+ public function getHeight() {
+ if(! $this->is_valid())
+ return false;
+
+ return $this->height;
+ }
+
+ /**
+ * @brief Saves the image resource to a file in filesystem.
+ *
+ * @param string $path Path and filename where to save the image
+ * @return boolean False on failure, otherwise true
+ */
+ public function saveImage($path) {
+ if(! $this->is_valid())
+ return false;
+
+ return (file_put_contents($path, $this->imageString()) ? true : false);
+ }
+
+ /**
+ * @brief Return mimetype of the image resource.
+ *
+ * @return boolean|string False on failure, otherwise mimetype.
+ */
+ public function getType() {
+ if(! $this->is_valid())
+ return false;
+
+ return $this->type;
+ }
+
+ /**
+ * @brief Return file extension of the image resource.
+ *
+ * @return boolean|string False on failure, otherwise file extension.
+ */
+ public function getExt() {
+ if(! $this->is_valid())
+ return false;
+
+ return $this->types[$this->getType()];
+ }
+
+ /**
+ * @brief Scale image to max pixel size in either dimension.
+ *
+ * @param int $max maximum pixel size in either dimension
+ * @param boolean $float_height (optional)
+ * If true allow height to float to any length on tall images, constraining
+ * only the width
+ * @return boolean|void false on failure, otherwise void
+ */
+ public function scaleImage($max, $float_height = true) {
+ if(! $this->is_valid())
+ return false;
+
+ $width = $this->width;
+ $height = $this->height;
+
+ $dest_width = $dest_height = 0;
+
+ if((! $width) || (! $height))
+ return false;
+
+ if($width > $max && $height > $max) {
+
+ // very tall image (greater than 16:9)
+ // constrain the width - let the height float.
+
+ if(((($height * 9) / 16) > $width) && ($float_height)) {
+ $dest_width = $max;
+ $dest_height = intval(($height * $max) / $width);
+ } // else constrain both dimensions
+ elseif($width > $height) {
+ $dest_width = $max;
+ $dest_height = intval(($height * $max) / $width);
+ } else {
+ $dest_width = intval(($width * $max) / $height);
+ $dest_height = $max;
+ }
+ } else {
+ if($width > $max) {
+ $dest_width = $max;
+ $dest_height = intval(($height * $max) / $width);
+ } else {
+ if($height > $max) {
+
+ // very tall image (greater than 16:9)
+ // but width is OK - don't do anything
+
+ if(((($height * 9) / 16) > $width) && ($float_height)) {
+ $dest_width = $width;
+ $dest_height = $height;
+ } else {
+ $dest_width = intval(($width * $max) / $height);
+ $dest_height = $max;
+ }
+ } else {
+ $dest_width = $width;
+ $dest_height = $height;
+ }
+ }
+ }
+ $this->doScaleImage($dest_width, $dest_height);
+ }
+
+ public function scaleImageUp($min) {
+ if(! $this->is_valid()) {
+ return false;
+ }
+
+ $width = $this->width;
+ $height = $this->height;
+
+ $dest_width = $dest_height = 0;
+
+ if((! $width) || (! $height))
+ return false;
+
+ if($width < $min && $height < $min) {
+ if($width > $height) {
+ $dest_width = $min;
+ $dest_height = intval(($height * $min) / $width);
+ } else {
+ $dest_width = intval(($width * $min) / $height);
+ $dest_height = $min;
+ }
+ } else {
+ if($width < $min) {
+ $dest_width = $min;
+ $dest_height = intval(($height * $min) / $width);
+ } else {
+ if($height < $min) {
+ $dest_width = intval(($width * $min) / $height);
+ $dest_height = $min;
+ } else {
+ $dest_width = $width;
+ $dest_height = $height;
+ }
+ }
+ }
+ $this->doScaleImage($dest_width, $dest_height);
+ }
+
+ /**
+ * @brief Scales image to a square.
+ *
+ * @param int $dim Pixel of square image
+ * @return boolean|void false on failure, otherwise void
+ */
+ public function scaleImageSquare($dim) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->doScaleImage($dim, $dim);
+ }
+
+ /**
+ * @brief Crops a square image.
+ *
+ * @see cropImageRect()
+ *
+ * @param int $max size of the new image
+ * @param int $x x-offset for region
+ * @param int $y y-offset for region
+ * @param int $w width of region
+ * @param int $h height of region
+ *
+ * @return boolean|void false on failure
+ */
+ public function cropImage($max, $x, $y, $w, $h) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->cropImageRect($max, $max, $x, $y, $w, $h);
+ }
+
+ /**
+ * @brief Reads exif data from a given filename.
+ *
+ * @param string $filename
+ * @return boolean|array
+ */
+ public function exif($filename) {
+ if((! function_exists('exif_read_data'))
+ || (! in_array($this->getType(), ['image/jpeg', 'image/tiff'])))
+ {
+ return false;
+ }
+
+ /*
+ * PHP 7.2 allows you to use a stream resource, which should reduce/avoid
+ * memory exhaustion on large images.
+ */
+
+ if(version_compare(PHP_VERSION, '7.2.0') >= 0) {
+ $f = @fopen($filename, 'rb');
+ } else {
+ $f = $filename;
+ }
+
+ if($f) {
+ return @exif_read_data($f, null, true);
+ }
+
+ return false;
+ }
+
+ /**
+ * @brief Orients current image based on exif orientation information.
+ *
+ * @param array $exif
+ * @return boolean true if oriented, otherwise false
+ */
+ public function orient($exif) {
+ if(! ($this->is_valid() && $exif)) {
+ return false;
+ }
+
+ $ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
+
+ if(! $ort) {
+ return false;
+ }
+
+ switch($ort) {
+ case 1 : // nothing
+ break;
+ case 2 : // horizontal flip
+ $this->flip();
+ break;
+ case 3 : // 180 rotate left
+ $this->rotate(180);
+ break;
+ case 4 : // vertical flip
+ $this->flip(false, true);
+ break;
+ case 5 : // vertical flip + 90 rotate right
+ $this->flip(false, true);
+ $this->rotate(-90);
+ break;
+ case 6 : // 90 rotate right
+ $this->rotate(-90);
+ break;
+ case 7 : // horizontal flip + 90 rotate right
+ $this->flip();
+ $this->rotate(-90);
+ break;
+ case 8 : // 90 rotate left
+ $this->rotate(90);
+ break;
+ default :
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * @brief Save photo to database.
+ *
+ * @param array $arr
+ * @param boolean $skipcheck (optional) default false
+ * @return boolean|array
+ */
+ public function save($arr, $skipcheck = false) {
+ if(! ($skipcheck || $this->is_valid())) {
+ logger('Attempt to store invalid photo.');
+ return false;
+ }
+
+ $p = [];
+
+ $p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
+ $p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
+ $p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
+ $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
+ $p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
+ $p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
+ $p['album'] = (($arr['album']) ? $arr['album'] : '');
+ $p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
+ $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
+ $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
+ $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
+ $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
+ $p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
+ $p['title'] = (($arr['title']) ? $arr['title'] : '');
+ $p['description'] = (($arr['description']) ? $arr['description'] : '');
+ $p['photo_usage'] = intval($arr['photo_usage']);
+ $p['os_storage'] = intval($arr['os_storage']);
+ $p['os_path'] = $arr['os_path'];
+ $p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : '');
+ $p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
+ $p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
+ $p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
+ $p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400)));
+
+ if(! intval($p['imgscale']))
+ logger('save: ' . print_r($arr, true), LOGGER_DATA);
+
+ $x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale']));
+
+ if($x) {
+ $p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
+ $r = q("UPDATE photo set
+ aid = %d,
+ uid = %d,
+ xchan = '%s',
+ resource_id = '%s',
+ created = '%s',
+ edited = '%s',
+ filename = '%s',
+ mimetype = '%s',
+ album = '%s',
+ height = %d,
+ width = %d,
+ content = '%s',
+ os_storage = %d,
+ filesize = %d,
+ imgscale = %d,
+ photo_usage = %d,
+ title = '%s',
+ description = '%s',
+ os_path = '%s',
+ display_path = '%s',
+ allow_cid = '%s',
+ allow_gid = '%s',
+ deny_cid = '%s',
+ deny_gid = '%s',
+ expires = '%s'
+ where id = %d",
+ intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($x[0]['id']));
+ } else {
+ $p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
+ $r = q("INSERT INTO photo
+ ( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
+ VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']));
+ }
+ logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
+
+ return $r;
+ }
+
+}
diff --git a/Zotlabs/Photo/PhotoGd.php b/Zotlabs/Photo/PhotoGd.php
new file mode 100644
index 000000000..4054e1866
--- /dev/null
+++ b/Zotlabs/Photo/PhotoGd.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace Zotlabs\Photo;
+
+/**
+ * @brief GD photo driver.
+ *
+ */
+class PhotoGd extends PhotoDriver {
+
+ /**
+ * {@inheritDoc}
+ * @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
+ */
+ public function supportedTypes() {
+ $t = [];
+ $t['image/jpeg'] = 'jpg';
+ if(imagetypes() & IMG_PNG)
+ $t['image/png'] = 'png';
+ if(imagetypes() & IMG_GIF)
+ $t['image/gif'] = 'gif';
+
+ return $t;
+ }
+
+ protected function load($data, $type) {
+ $this->valid = false;
+ if(! $data)
+ return;
+
+ $this->image = @imagecreatefromstring($data);
+ if($this->image !== false) {
+ $this->valid = true;
+ $this->setDimensions();
+ imagealphablending($this->image, false);
+ imagesavealpha($this->image, true);
+ }
+ }
+
+ protected function setDimensions() {
+ $this->width = imagesx($this->image);
+ $this->height = imagesy($this->image);
+ }
+
+ /**
+ * @brief GD driver does not preserve EXIF, so not need to clear it.
+ *
+ * @return void
+ */
+ public function clearexif() {
+ return;
+ }
+
+ protected function destroy() {
+ if($this->is_valid()) {
+ imagedestroy($this->image);
+ }
+ }
+
+ /**
+ * @brief Return a PHP image resource of the current image.
+ *
+ * @see \Zotlabs\Photo\PhotoDriver::getImage()
+ *
+ * @return boolean|resource
+ */
+ public function getImage() {
+ if(! $this->is_valid())
+ return false;
+
+ return $this->image;
+ }
+
+ public function doScaleImage($dest_width, $dest_height) {
+
+ $dest = imagecreatetruecolor($dest_width, $dest_height);
+ $width = imagesx($this->image);
+ $height = imagesy($this->image);
+
+ imagealphablending($dest, false);
+ imagesavealpha($dest, true);
+ if($this->type == 'image/png')
+ imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
+
+ imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
+ if($this->image)
+ imagedestroy($this->image);
+
+ $this->image = $dest;
+ $this->setDimensions();
+ }
+
+ public function rotate($degrees) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->image = imagerotate($this->image, $degrees, 0);
+ $this->setDimensions();
+ }
+
+ public function flip($horiz = true, $vert = false) {
+ if(! $this->is_valid())
+ return false;
+
+ $w = imagesx($this->image);
+ $h = imagesy($this->image);
+ $flipped = imagecreate($w, $h);
+ if($horiz) {
+ for($x = 0; $x < $w; $x++) {
+ imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
+ }
+ }
+ if($vert) {
+ for($y = 0; $y < $h; $y++) {
+ imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
+ }
+ }
+ $this->image = $flipped;
+ $this->setDimensions(); // Shouldn't really be necessary
+ }
+
+ public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
+ if(! $this->is_valid())
+ return false;
+
+ $dest = imagecreatetruecolor($maxx, $maxy);
+ imagealphablending($dest, false);
+ imagesavealpha($dest, true);
+ if($this->type == 'image/png')
+ imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
+
+ imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
+ if($this->image)
+ imagedestroy($this->image);
+
+ $this->image = $dest;
+ $this->setDimensions();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see \Zotlabs\Photo\PhotoDriver::imageString()
+ */
+ public function imageString() {
+ if(! $this->is_valid())
+ return false;
+
+ $quality = false;
+
+ ob_start();
+
+ switch($this->getType()){
+ case 'image/png':
+ $quality = get_config('system', 'png_quality');
+ if((! $quality) || ($quality > 9))
+ $quality = PNG_QUALITY;
+
+ \imagepng($this->image, NULL, $quality);
+ break;
+ case 'image/jpeg':
+ // gd can lack imagejpeg(), but we verify during installation it is available
+ default:
+ $quality = get_config('system', 'jpeg_quality');
+ if((! $quality) || ($quality > 100))
+ $quality = JPEG_QUALITY;
+
+ \imagejpeg($this->image, NULL, $quality);
+ break;
+ }
+ $string = ob_get_contents();
+ ob_end_clean();
+
+ return $string;
+ }
+
+}
diff --git a/Zotlabs/Photo/PhotoImagick.php b/Zotlabs/Photo/PhotoImagick.php
new file mode 100644
index 000000000..a7026e8ca
--- /dev/null
+++ b/Zotlabs/Photo/PhotoImagick.php
@@ -0,0 +1,200 @@
+<?php
+
+namespace Zotlabs\Photo;
+
+/**
+ * @brief ImageMagick photo driver.
+ */
+class PhotoImagick extends PhotoDriver {
+
+ public function supportedTypes() {
+ return [
+ 'image/jpeg' => 'jpg',
+ 'image/png' => 'png',
+ 'image/gif' => 'gif',
+ ];
+ }
+
+ private function get_FormatsMap() {
+ return [
+ 'image/jpeg' => 'JPG',
+ 'image/png' => 'PNG',
+ 'image/gif' => 'GIF',
+ ];
+ }
+
+
+ protected function load($data, $type) {
+ $this->valid = false;
+ $this->image = new \Imagick();
+
+ if(! $data)
+ return;
+
+ try {
+ $this->image->readImageBlob($data);
+ } catch(\Exception $e) {
+ logger('Imagick readImageBlob() exception:' . print_r($e, true));
+ return;
+ }
+
+ /*
+ * Setup the image to the format it will be saved to
+ */
+
+ $map = $this->get_FormatsMap();
+ $format = $map[$type];
+
+ if($this->image) {
+ $this->image->setFormat($format);
+
+ // Always coalesce, if it is not a multi-frame image it won't hurt anyway
+ $this->image = $this->image->coalesceImages();
+
+ $this->valid = true;
+ $this->setDimensions();
+
+ /*
+ * setup the compression here, so we'll do it only once
+ */
+ switch($this->getType()) {
+ case 'image/png':
+ $quality = get_config('system', 'png_quality');
+ if((! $quality) || ($quality > 9))
+ $quality = PNG_QUALITY;
+ /*
+ * From http://www.imagemagick.org/script/command-line-options.php#quality:
+ *
+ * 'For the MNG and PNG image formats, the quality value sets
+ * the zlib compression level (quality / 10) and filter-type (quality % 10).
+ * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering,
+ * unless the image has a color map, in which case it means compression level 7 with no PNG filtering'
+ */
+ $quality = $quality * 10;
+ $this->image->setCompressionQuality($quality);
+ break;
+ case 'image/jpeg':
+ $quality = get_config('system', 'jpeg_quality');
+ if((! $quality) || ($quality > 100))
+ $quality = JPEG_QUALITY;
+ $this->image->setCompressionQuality($quality);
+ default:
+ break;
+ }
+ }
+ }
+
+ protected function destroy() {
+ if($this->is_valid()) {
+ $this->image->clear();
+ $this->image->destroy();
+ }
+ }
+
+ protected function setDimensions() {
+ $this->width = $this->image->getImageWidth();
+ $this->height = $this->image->getImageHeight();
+ }
+
+ /**
+ * @brief Strips the image of all profiles and comments.
+ *
+ * Keep ICC profile for better colors.
+ *
+ * @see \Zotlabs\Photo\PhotoDriver::clearexif()
+ */
+ public function clearexif() {
+ $profiles = $this->image->getImageProfiles('icc', true);
+
+ $this->image->stripImage();
+
+ if(! empty($profiles)) {
+ $this->image->profileImage('icc', $profiles['icc']);
+ }
+ }
+
+
+ /**
+ * @brief Return a \Imagick object of the current image.
+ *
+ * @see \Zotlabs\Photo\PhotoDriver::getImage()
+ *
+ * @return boolean|\Imagick
+ */
+ public function getImage() {
+ if(! $this->is_valid())
+ return false;
+
+ $this->image = $this->image->deconstructImages();
+ return $this->image;
+ }
+
+ public function doScaleImage($dest_width, $dest_height) {
+ /*
+ * If it is not animated, there will be only one iteration here,
+ * so don't bother checking
+ */
+ // Don't forget to go back to the first frame
+ $this->image->setFirstIterator();
+ do {
+ $this->image->scaleImage($dest_width, $dest_height);
+ } while($this->image->nextImage());
+
+ $this->setDimensions();
+ }
+
+ public function rotate($degrees) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->image->setFirstIterator();
+ do {
+ // ImageMagick rotates in the opposite direction of imagerotate()
+ $this->image->rotateImage(new \ImagickPixel(), -$degrees);
+ } while($this->image->nextImage());
+
+ $this->setDimensions();
+ }
+
+ public function flip($horiz = true, $vert = false) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->image->setFirstIterator();
+ do {
+ if($horiz) $this->image->flipImage();
+ if($vert) $this->image->flopImage();
+ } while($this->image->nextImage());
+
+ $this->setDimensions(); // Shouldn't really be necessary
+ }
+
+ public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
+ if(! $this->is_valid())
+ return false;
+
+ $this->image->setFirstIterator();
+ do {
+ $this->image->cropImage($w, $h, $x, $y);
+ /*
+ * We need to remove the canvas,
+ * or the image is not resized to the crop:
+ * http://php.net/manual/en/imagick.cropimage.php#97232
+ */
+ $this->image->setImagePage(0, 0, 0, 0);
+ } while($this->image->nextImage());
+
+ $this->doScaleImage($maxx, $maxy);
+ }
+
+ public function imageString() {
+ if(! $this->is_valid())
+ return false;
+
+ /* Clean it */
+ $this->image = $this->image->deconstructImages();
+
+ return $this->image->getImagesBlob();
+ }
+
+}
diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php
index c2d70b854..fde66efcd 100644
--- a/Zotlabs/Storage/Browser.php
+++ b/Zotlabs/Storage/Browser.php
@@ -205,17 +205,21 @@ class Browser extends DAV\Browser\Plugin {
// upload access. system.thumbnail_security should be set to 1 if you want to include these
// types
+ $is_creator = false;
$photo_icon = '';
$preview_style = intval(get_config('system','thumbnail_security',0));
- $r = q("select content from attach where hash = '%s' and uid = %d limit 1",
+ $r = q("select content, creator from attach where hash = '%s' and uid = %d limit 1",
dbesc($attachHash),
intval($owner)
);
- if($r && file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
- $photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
-// logger('found thumb: ' . $photo_icon);
+ if($r) {
+ $is_creator = (($r[0]['creator'] === get_observer_hash()) ? true : false);
+ if(file_exists(dbunescbin($r[0]['content']) . '.thumb')) {
+ $photo_icon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents(dbunescbin($r[0]['content']) . '.thumb'));
+// logger('found thumb: ' . $photo_icon);
+ }
}
if(strpos($type,'image/') === 0 && $attachHash) {
@@ -247,6 +251,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['attachIcon'] = (($size) ? $attachIcon : '');
// @todo Should this be an item value, not a global one?
$ft['is_owner'] = $is_owner;
+ $ft['is_creator'] = $is_creator;
$ft['fullPath'] = $fullPath;
$ft['displayName'] = $displayName;
$ft['type'] = $type;
@@ -256,6 +261,7 @@ class Browser extends DAV\Browser\Plugin {
$ft['iconFromType'] = getIconFromType($type);
$f[] = $ft;
+
}
diff --git a/Zotlabs/Update/_1228.php b/Zotlabs/Update/_1228.php
index f8a506bb4..9e6bf8047 100644
--- a/Zotlabs/Update/_1228.php
+++ b/Zotlabs/Update/_1228.php
@@ -11,8 +11,8 @@ class _1228 {
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE item ADD uuid text NOT NULL DEFAULT '' ");
- $r2 = q("create index \"uuid_idx\" on channel (\"uuid\")");
- $r3 = q("ALTER TABLE item add summary TEXT NOT NULL");
+ $r2 = q("create index \"uuid_idx\" on item (\"uuid\")");
+ $r3 = q("ALTER TABLE item add summary TEXT NOT NULL DEFAULT ''");
$r = ($r1 && $r2 && $r3);
}
diff --git a/Zotlabs/Update/_1229.php b/Zotlabs/Update/_1229.php
new file mode 100644
index 000000000..8e8711644
--- /dev/null
+++ b/Zotlabs/Update/_1229.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Zotlabs\Update;
+
+class _1229 {
+
+ function run() {
+
+ q("START TRANSACTION");
+
+ if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
+ $r1 = q("ALTER TABLE photo ADD expires timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' ");
+ $r2 = q("create index \"photo_expires_idx\" on photo (\"expires\")");
+
+ $r = ($r1 && $r2);
+ }
+ else {
+ $r = q("ALTER TABLE `photo` ADD `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' ,
+ ADD INDEX `expires` (`expires`)");
+ }
+
+ if($r) {
+ q("COMMIT");
+ return UPDATE_SUCCESS;
+ }
+
+ q("ROLLBACK");
+ return UPDATE_FAILED;
+
+ }
+
+}
diff --git a/Zotlabs/Update/_1230.php b/Zotlabs/Update/_1230.php
new file mode 100644
index 000000000..fe59f2e08
--- /dev/null
+++ b/Zotlabs/Update/_1230.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Zotlabs\Update;
+
+class _1230 {
+
+ function run() {
+ q("update abook set abook_closeness = 80 where abook_closeness = 0 and abook_self = 0");
+ return UPDATE_SUCCESS;
+
+ }
+} \ No newline at end of file
diff --git a/Zotlabs/Web/Session.php b/Zotlabs/Web/Session.php
index 4f2a3f1f7..fe0a3fbf9 100644
--- a/Zotlabs/Web/Session.php
+++ b/Zotlabs/Web/Session.php
@@ -15,7 +15,7 @@ class Session {
private $handler = null;
private $session_started = false;
-
+ private $custom_handler = false;
public function init() {
$gc_probability = 50;
@@ -23,25 +23,46 @@ class Session {
ini_set('session.gc_probability', $gc_probability);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1);
-
+
+ $this->custom_handler = boolval(get_config('system', 'session_custom', false));
+
/*
* Set our session storage functions.
*/
+
+ if($this->custom_handler) {
+ /* Custom handler (files, memached, redis..) */
+
+ $session_save_handler = strval(get_config('system', 'session_save_handler', Null));
+ $session_save_path = strval(get_config('system', 'session_save_path', Null));
+ $session_gc_probability = intval(get_config('system', 'session_gc_probability', 1));
+ $session_gc_divisor = intval(get_config('system', 'session_gc_divisor', 100));
+ if(!$session_save_handler || !$session_save_path) {
+ logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR);
+ }
+ else {
+ ini_set('session.save_handler', $session_save_handler);
+ ini_set('session.save_path', $session_save_path);
+ ini_set('session.gc_probability', $session_gc_probability);
+ ini_set('session.gc_divisor', $session_gc_divisor);
+ }
+ }
+ else {
+ $handler = new \Zotlabs\Web\SessionHandler();
- $handler = new \Zotlabs\Web\SessionHandler();
-
- $this->handler = $handler;
+ $this->handler = $handler;
- $x = session_set_save_handler($handler,false);
- if(! $x)
- logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
+ $x = session_set_save_handler($handler,false);
+ if(! $x)
+ logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
+ }
// Force cookies to be secure (https only) if this site is SSL enabled.
// Must be done before session_start().
$arr = session_get_cookie_params();
-
+
// Note when setting cookies: set the domain to false which creates a single domain
// cookie. If you use a hostname it will create a .domain.com wildcard which will
// have some nasty side effects if you have any other subdomains running hubzilla.
@@ -86,14 +107,15 @@ class Session {
$arr = session_get_cookie_params();
- if($this->handler && $this->session_started) {
+ if(($this->handler || $this->custom_handler) && $this->session_started) {
session_regenerate_id(true);
// force SessionHandler record creation with the new session_id
// which occurs as a side effect of read()
-
- $this->handler->read(session_id());
+ if (! $this->custom_handler) {
+ $this->handler->read(session_id());
+ }
}
else
logger('no session handler');
diff --git a/Zotlabs/Widget/Affinity.php b/Zotlabs/Widget/Affinity.php
index 4fb2874ae..572af0503 100644
--- a/Zotlabs/Widget/Affinity.php
+++ b/Zotlabs/Widget/Affinity.php
@@ -2,55 +2,65 @@
namespace Zotlabs\Widget;
+use Zotlabs\Lib\Apps;
+
class Affinity {
function widget($arr) {
if(! local_channel())
- return '';
-
- $default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
- $default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
+ return;
+
+ if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
+ return;
+
+ $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
+ $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
$cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin);
$cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax);
+ $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
+ if ($affinity_locked) {
+ set_pconfig(local_channel(),'affinity','cmin',$cmin);
+ set_pconfig(local_channel(),'affinity','cmax',$cmax);
+ }
+
+ $labels = array(
+ t('Me'),
+ t('Family'),
+ t('Friends'),
+ t('Acquaintances'),
+ t('All')
+ );
+ call_hooks('affinity_labels',$labels);
+
+ $label_str = '';
- if(feature_enabled(local_channel(),'affinity')) {
-
- $labels = array(
- t('Me'),
- t('Family'),
- t('Friends'),
- t('Acquaintances'),
- t('All')
- );
- call_hooks('affinity_labels',$labels);
- $label_str = '';
-
- if($labels) {
- foreach($labels as $l) {
- if($label_str) {
- $label_str .= ", '|'";
- $label_str .= ", '" . $l . "'";
- }
- else
- $label_str .= "'" . $l . "'";
+ if($labels) {
+ foreach($labels as $l) {
+ if($label_str) {
+ $label_str .= ", '|'";
+ $label_str .= ", '" . $l . "'";
}
+ else
+ $label_str .= "'" . $l . "'";
}
-
- $tpl = get_markup_template('main_slider.tpl');
- $x = replace_macros($tpl,array(
- '$val' => $cmin . ',' . $cmax,
- '$refresh' => t('Refresh'),
- '$labels' => $label_str,
- ));
-
- $arr = array('html' => $x);
- call_hooks('main_slider',$arr);
- return $arr['html'];
}
- return '';
+
+ $tpl = get_markup_template('main_slider.tpl');
+ $x = replace_macros($tpl,array(
+ '$val' => $cmin . ',' . $cmax,
+ '$refresh' => t('Refresh'),
+ '$labels' => $label_str,
+ ));
+
+ $arr = array('html' => $x);
+ call_hooks('main_slider',$arr);
+
+ return $arr['html'];
+
+
}
}
- \ No newline at end of file
+
diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php
index 0f9f609e4..37d9139ec 100644
--- a/Zotlabs/Widget/Notifications.php
+++ b/Zotlabs/Widget/Notifications.php
@@ -24,7 +24,7 @@ class Notifications {
],
'filter' => [
'posts_label' => t('Show new posts only'),
- 'name_label' => t('Filter by name')
+ 'name_label' => t('Filter by name or address')
]
];
@@ -43,7 +43,7 @@ class Notifications {
],
'filter' => [
'posts_label' => t('Show new posts only'),
- 'name_label' => t('Filter by name')
+ 'name_label' => t('Filter by name or address')
]
];
@@ -119,7 +119,7 @@ class Notifications {
'label' => t('Forums'),
'title' => t('Forums'),
'filter' => [
- 'name_label' => t('Filter by name')
+ 'name_label' => t('Filter by name or address')
]
];
}
@@ -150,7 +150,7 @@ class Notifications {
],
'filter' => [
'posts_label' => t('Show new posts only'),
- 'name_label' => t('Filter by name')
+ 'name_label' => t('Filter by name or address')
]
];
}
diff --git a/Zotlabs/Widget/Settings_menu.php b/Zotlabs/Widget/Settings_menu.php
index c537c3835..25b80a4b4 100644
--- a/Zotlabs/Widget/Settings_menu.php
+++ b/Zotlabs/Widget/Settings_menu.php
@@ -42,19 +42,12 @@ class Settings_menu {
);
-
$tabs[] = array(
'label' => t('Display settings'),
'url' => z_root().'/settings/display',
'selected' => ((argv(1) === 'display') ? 'active' : ''),
);
- $tabs[] = array(
- 'label' => t('Addon settings'),
- 'url' => z_root().'/settings/featured',
- 'selected' => ((argv(1) === 'featured') ? 'active' : ''),
- );
-
if($hublocs) {
$tabs[] = array(
'label' => t('Manage locations'),
diff --git a/Zotlabs/Zot/Finger.php b/Zotlabs/Zot/Finger.php
index 77634777a..cb38c7f2b 100644
--- a/Zotlabs/Zot/Finger.php
+++ b/Zotlabs/Zot/Finger.php
@@ -55,7 +55,7 @@ class Finger {
$r = q("select xchan.*, hubloc.* from xchan
left join hubloc on xchan_hash = hubloc_hash
- where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 limit 1",
+ where xchan_addr = '%s' and hubloc_primary = 1 and hubloc_deleted = 0 and hubloc_network = 'zot' limit 1",
dbesc($xchan_addr)
);
diff --git a/Zotlabs/Zot6/Zot6Handler.php b/Zotlabs/Zot6/Zot6Handler.php
index e320e7825..8f8957037 100644
--- a/Zotlabs/Zot6/Zot6Handler.php
+++ b/Zotlabs/Zot6/Zot6Handler.php
@@ -29,7 +29,7 @@ class Zot6Handler implements IHandler {
// Implementation of specific methods follows;
- // These generally do a small amout of validation and call Libzot
+ // These generally do a small amout of validation and call Libzot
// to do any heavy lifting
static function reply_notify($data,$hub) {
@@ -40,7 +40,7 @@ class Zot6Handler implements IHandler {
$x = Libzot::fetch($data);
$ret['delivery_report'] = $x;
-
+
$ret['success'] = true;
return $ret;
@@ -58,11 +58,11 @@ class Zot6Handler implements IHandler {
*
* @param array $sender
* @param array $recipients
+ * @param array $hub
*
- * @return json_return_and_die()
+ * @return array
*/
-
- static function reply_refresh($sender, $recipients,$hub) {
+ static function reply_refresh($sender, $recipients, $hub) {
$ret = array('success' => false);
if($recipients) {
@@ -70,19 +70,18 @@ class Zot6Handler implements IHandler {
// This would be a permissions update, typically for one connection
foreach ($recipients as $recip) {
-
$r = q("select channel.*,xchan.* from channel
left join xchan on channel_portable_id = xchan_hash
where xchan_hash ='%s' limit 1",
dbesc($recip)
);
-
+ /// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false));
}
}
else {
// system wide refresh
-
+ /// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false));
}
@@ -100,17 +99,16 @@ class Zot6Handler implements IHandler {
* for that packet. We will create a message_list array of the entire conversation starting with
* the missing parent and invoke delivery to the sender of the packet.
*
- * Zotlabs/Daemon/Deliver.php (for local delivery) and
+ * Zotlabs/Daemon/Deliver.php (for local delivery) and
* mod/post.php???? @fixme (for web delivery) detect the existence of
* this 'message_list' at the destination and split it into individual messages which are
* processed/delivered in order.
*
- *
* @param array $data
+ * @param array $hub
* @return array
*/
-
- static function reply_message_request($data,$hub) {
+ static function reply_message_request($data, $hub) {
$ret = [ 'success' => false ];
$message_id = EMPTY_STR;
@@ -153,11 +151,10 @@ class Zot6Handler implements IHandler {
/*
* fetch the requested conversation
*/
-
+ /// @FIXME $sender_hash is undefined
$messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]);
return (($messages) ? : [] );
-
}
static function rekey_request($sender,$data,$hub) {
@@ -183,7 +180,7 @@ class Zot6Handler implements IHandler {
dbesc($oldhash)
);
}
- else
+ else
return $ret;
@@ -219,10 +216,10 @@ class Zot6Handler implements IHandler {
*
* @param array $sender
* @param array $recipients
+ * @param array $hub
*
- * return json_return_and_die()
+ * @return array
*/
-
static function reply_purge($sender, $recipients, $hub) {
$ret = array('success' => false);
@@ -259,9 +256,4 @@ class Zot6Handler implements IHandler {
return $ret;
}
-
-
-
-
-
}