Merge branch 'dev'

This commit is contained in:
Mario Vavti 2018-11-14 11:12:20 +01:00
commit dfc5159aa3
33 changed files with 826 additions and 406 deletions

View File

@ -1,3 +1,21 @@
Hubzilla 3.8.4 (2018-11-14)
- Fix xss issue (thanks to Eduardo)
- Implement hook in enotify to be used by superblock
- Various css fixes
- Improve photo cache handling
- Provide a function hz_syslog() to log to syslog
- Fix request_target in z_post_url()
- Fix plural handling for various languages
- Some preparatory work for zot6
- Fix warning in gallery addon
- Fix date issue on xchan photo update in diaspora and pubcrawl addons
- Fix typos in startpage addon
- Improve activitypub addressing
- Fix taxonomy in activitypub direct messages
- Fix syntax error in diaspora addon
- New e-learning addon flashcards
Hubzilla 3.8.3 (2018-11-05) Hubzilla 3.8.3 (2018-11-05)
- Do not count likes in forum notifications if likes notifications are disabled - Do not count likes in forum notifications if likes notifications are disabled
- Fix typo in spanish translation which broke javascript - Fix typo in spanish translation which broke javascript

View File

@ -825,7 +825,7 @@ class Enotify {
// convert this logic into a json array just like the system notifications // convert this logic into a json array just like the system notifications
return array( $x = array(
'notify_link' => $item['llink'], 'notify_link' => $item['llink'],
'name' => $item['author']['xchan_name'], 'name' => $item['author']['xchan_name'],
'url' => $item['author']['xchan_url'], 'url' => $item['author']['xchan_url'],
@ -835,9 +835,19 @@ class Enotify {
'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])), 'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
'notify_id' => 'undefined', 'notify_id' => 'undefined',
'thread_top' => (($item['item_thread_top']) ? true : false), 'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => strip_tags(bbcode($itemem_text)) 'message' => strip_tags(bbcode($itemem_text)),
// these are for the superblock addon
'hash' => $item['author']['xchan_hash'],
'uid' => local_channel(),
'display' => true
); );
call_hooks('enotify_format',$x);
if(! $x['display']) {
return [];
}
return $x;
} }
} }

View File

@ -7,16 +7,10 @@ namespace Zotlabs\Lib;
* *
*/ */
use Zotlabs\Lib\DReport; use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Lib\Enotify; use Zotlabs\Access\Permissions;
use Zotlabs\Lib\Group; use Zotlabs\Access\PermissionLimits;
use Zotlabs\Lib\Libsync; use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Libzotdir;
use Zotlabs\Lib\System;
use Zotlabs\Lib\MessageFilter;
use Zotlabs\Lib\Queue;
use Zotlabs\Lib\Zotfinger;
use Zotlabs\Web\HTTPSig;
require_once('include/crypto.php'); require_once('include/crypto.php');
@ -213,7 +207,8 @@ class Libzot {
$headers = [ $headers = [
'X-Zot-Token' => random_string(), 'X-Zot-Token' => random_string(),
'Digest' => HTTPSig::generate_digest_header($data), 'Digest' => HTTPSig::generate_digest_header($data),
'Content-type' => 'application/x-zot+json' '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',
@ -378,13 +373,13 @@ class Libzot {
else { else {
// if we were just granted read stream permission and didn't have it before, try to pull in some posts // if we were just granted read stream permission and didn't have it before, try to pull in some posts
if((! $old_read_stream_perm) && (intval($permissions['view_stream']))) if((! $old_read_stream_perm) && (intval($permissions['view_stream'])))
\Zotlabs\Daemon\Master::Summon(array('Onepoll',$r[0]['abook_id'])); Master::Summon([ 'Onepoll', $r[0]['abook_id'] ]);
} }
} }
else { else {
$p = \Zotlabs\Access\Permissions::connect_perms($channel['channel_id']); $p = Permissions::connect_perms($channel['channel_id']);
$my_perms = \Zotlabs\Access\Permissions::serialise($p['perms']); $my_perms = Permissions::serialise($p['perms']);
$automatic = $p['automatic']; $automatic = $p['automatic'];
@ -424,8 +419,8 @@ class Libzot {
); );
if($new_connection) { if($new_connection) {
if(! \Zotlabs\Access\Permissions::PermsCompare($new_perms,$previous_perms)) if(! Permissions::PermsCompare($new_perms,$previous_perms))
\Zotlabs\Daemon\Master::Summon(array('Notifier','permissions_create',$new_connection[0]['abook_id'])); Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]);
Enotify::submit( Enotify::submit(
[ [
'type' => NOTIFY_INTRO, 'type' => NOTIFY_INTRO,
@ -438,7 +433,7 @@ class Libzot {
if(intval($permissions['view_stream'])) { if(intval($permissions['view_stream'])) {
if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING) if(intval(get_pconfig($channel['channel_id'],'perm_limits','send_stream') & PERMS_PENDING)
|| (! intval($new_connection[0]['abook_pending']))) || (! intval($new_connection[0]['abook_pending'])))
\Zotlabs\Daemon\Master::Summon(array('Onepoll',$new_connection[0]['abook_id'])); Master::Summon([ 'Onepoll', $new_connection[0]['abook_id'] ]);
} }
@ -975,6 +970,11 @@ class Libzot {
$x = json_decode($x,true); $x = json_decode($x,true);
} }
if(! is_array($x)) {
btlogger('failed communication - no response');
}
if($x) {
if(! $x['success']) { if(! $x['success']) {
// handle remote validation issues // handle remote validation issues
@ -986,8 +986,9 @@ class Libzot {
); );
} }
if(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) { 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)) { if(is_array($xx) && array_key_exists('message_id',$xx) && DReport::is_storable($xx)) {
q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_name, dreport_result, dreport_time, dreport_xchan ) values ( '%s', '%s', '%s','%s','%s','%s','%s' ) ", 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['message_id']),
@ -1007,7 +1008,7 @@ class Libzot {
dbesc($outq['outq_hash']) dbesc($outq['outq_hash'])
); );
} }
}
// update the timestamp for this site // update the timestamp for this site
q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'", q("update site set site_dead = 0, site_update = '%s' where site_url = '%s'",
@ -1092,13 +1093,27 @@ class Libzot {
return; return;
} }
$message_request = ((array_key_exists('message_id',$env)) ? true : false); $message_request = false;
if($message_request)
logger('processing message request');
$has_data = array_key_exists('data',$env) && $env['data']; $has_data = array_key_exists('data',$env) && $env['data'];
$data = (($has_data) ? $env['data'] : false); $data = (($has_data) ? $env['data'] : false);
$AS = null;
if($env['encoding'] === 'activitystreams') {
$AS = new ActivityStreams($data);
if(! $AS->is_valid()) {
logger('Activity rejected: ' . print_r($data,true));
return;
}
$arr = Activity::decode_note($AS);
logger($AS->debug());
}
$deliveries = null; $deliveries = null;
if(array_key_exists('recipients',$env) && count($env['recipients'])) { if(array_key_exists('recipients',$env) && count($env['recipients'])) {
@ -1140,7 +1155,7 @@ class Libzot {
// and who are allowed to see them based on the sender's permissions // and who are allowed to see them based on the sender's permissions
// @fixme; // @fixme;
$deliveries = self::public_recips($env); $deliveries = self::public_recips($env,$AS);
} }
@ -1157,26 +1172,9 @@ class Libzot {
if(in_array($env['type'],['activity','response'])) { if(in_array($env['type'],['activity','response'])) {
if($env['encoding'] === 'zot') { $arr = Activity::decode_note($AS);
$arr = get_item_elements($data);
$v = validate_item_elements($data,$arr); //logger($AS->debug());
if(! $v['success']) {
logger('Activity rejected: ' . $v['message'] . ' ' . print_r($data,true));
return;
}
}
elseif($env['encoding'] === 'activitystreams') {
$AS = new \Zotlabs\Lib\ActivityStreams($data);
if(! $AS->is_valid()) {
logger('Activity rejected: ' . print_r($data,true));
return;
}
$arr = \Zotlabs\Lib\Activity::decode_note($AS);
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' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
@ -1185,8 +1183,20 @@ class Libzot {
if($r) { if($r) {
$arr['author_xchan'] = $r[0]['hubloc_hash']; $arr['author_xchan'] = $r[0]['hubloc_hash'];
} }
// @fixme (in individual delivery, change owner if needed)
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' 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) { if($private) {
$arr['item_private'] = true; $arr['item_private'] = true;
} }
@ -1198,7 +1208,6 @@ class Libzot {
IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false); IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
} }
}
logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG);
@ -1225,16 +1234,32 @@ class Libzot {
} }
static function is_top_level($env) { static function is_top_level($env,$act) {
if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) {
return true; return true;
} }
if($env['encoding'] === 'activitystreams') { if($act) {
if(array_key_exists('inReplyTo',$env['data']) && $env['data']['inReplyTo']) { if(in_array($act->type, ['Like','Dislike'])) {
return false; return false;
} }
$x = self::find_parent($env,$act);
if($x === $act->id || $x === $act->obj['id']) {
return true; return true;
} }
}
return false;
}
static function find_parent($env,$act) {
if($act) {
if(in_array($act->type, ['Like','Dislike'])) {
return $act->obj['id'];
}
if($act->parent_id) {
return $act->parent_id;
}
}
return false; return false;
} }
@ -1255,7 +1280,7 @@ class Libzot {
* @return NULL|array * @return NULL|array
*/ */
static function public_recips($msg) { static function public_recips($msg, $act) {
require_once('include/channel.php'); require_once('include/channel.php');
@ -1269,7 +1294,7 @@ class Libzot {
$perm = 'send_stream'; $perm = 'send_stream';
if(self::is_top_level($msg)) { if(self::is_top_level($msg,$act)) {
$check_mentions = true; $check_mentions = true;
} }
} }
@ -1301,9 +1326,9 @@ class Libzot {
if($check_mentions) { if($check_mentions) {
// It's a top level post. Look at the tags. See if any of them are mentions and are on this hub. // It's a top level post. Look at the tags. See if any of them are mentions and are on this hub.
if(array_path_exists('data/object/tag',$msg)) { if($act && $act->obj) {
if(is_array($msg['data']['object']['tag']) && $msg['data']['object']['tag']) { if(is_array($act->obj['tag']) && $act->obj['tag']) {
foreach($msg['data']['object']['tag'] as $tag) { foreach($act->obj['tag'] as $tag) {
if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) { if($tag['type'] === 'Mention' && (strpos($tag['href'],z_root()) !== false)) {
$address = basename($tag['href']); $address = basename($tag['href']);
if($address) { if($address) {
@ -1325,9 +1350,12 @@ class Libzot {
// everybody that stored a copy of the parent. This way we know we're covered. We'll check the // everybody that stored a copy of the parent. This way we know we're covered. We'll check the
// comment permissions when we deliver them. // comment permissions when we deliver them.
if(array_path_exists('data/inReplyTo',$msg)) { $thread_parent = self::find_parent($msg,$act);
$z = q("select owner_xchan as hash from item where parent_mid = '%s' ",
dbesc($msg['data']['inReplyTo']) if($thread_parent) {
$z = q("select channel_hash as hash from channel left join item on channel.channel_id = item.uid where ( item.thr_parent = '%s' OR item.parent_mid = '%s' ) ",
dbesc($thread_parent),
dbesc($thread_parent)
); );
if($z) { if($z) {
foreach($z as $zv) { foreach($z as $zv) {
@ -1341,7 +1369,7 @@ class Libzot {
// It's a bit of work since it's a multi-dimensional array // It's a bit of work since it's a multi-dimensional array
if($r) { if($r) {
$r = array_unique($r); $r = array_values(array_unique($r));
} }
logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG); logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG);
@ -1378,7 +1406,7 @@ class Libzot {
$local_public = $public; $local_public = $public;
$DR = new \Zotlabs\Lib\DReport(z_root(),$sender,$d,$arr['mid']); $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$channel = channelx_by_hash($d); $channel = channelx_by_hash($d);
@ -1413,7 +1441,7 @@ class Libzot {
$local_public = true; $local_public = true;
$r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1", $r = q("select xchan_selfcensored from xchan where xchan_hash = '%s' limit 1",
dbesc($sender['hash']) dbesc($sender)
); );
// don't import sys channel posts from selfcensored authors // don't import sys channel posts from selfcensored authors
if($r && (intval($r[0]['xchan_selfcensored']))) { if($r && (intval($r[0]['xchan_selfcensored']))) {
@ -1441,12 +1469,31 @@ class Libzot {
$arr['item_wall'] = 0; $arr['item_wall'] = 0;
} }
if((! perm_is_allowed($channel['channel_id'],$sender,$perm)) && (! $tag_delivery) && (! $local_public)) { $friendofriend = false;
if ((! $tag_delivery) && (! $local_public)) {
$allowed = (perm_is_allowed($channel['channel_id'],$sender,$perm));
if((! $allowed) && $perm === 'post_comments') {
$parent = q("select * from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']),
intval($channel['channel_id'])
);
if ($parent) {
$allowed = can_comment_on_post($d,$parent[0]);
}
}
if($request) {
$allowed = true;
$friendofriend = true;
}
if (! $allowed) {
logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}"); logger("permission denied for delivery to channel {$channel['channel_id']} {$channel['channel_address']}");
$DR->update('permission denied'); $DR->update('permission denied');
$result[] = $DR->get(); $result[] = $DR->get();
continue; continue;
} }
}
if($arr['mid'] != $arr['parent_mid']) { if($arr['mid'] != $arr['parent_mid']) {
@ -1456,7 +1503,7 @@ class Libzot {
// As a side effect we will also do a preliminary check that we have the top-level-post, otherwise // As a side effect we will also do a preliminary check that we have the top-level-post, otherwise
// processing it is pointless. // processing it is pointless.
$r = q("select route, id from item where mid = '%s' and uid = %d limit 1", $r = q("select route, id, owner_xchan, item_private from item where mid = '%s' and uid = %d limit 1",
dbesc($arr['parent_mid']), dbesc($arr['parent_mid']),
intval($channel['channel_id']) intval($channel['channel_id'])
); );
@ -1480,15 +1527,21 @@ class Libzot {
if((! $relay) && (! $request) && (! $local_public) if((! $relay) && (! $request) && (! $local_public)
&& perm_is_allowed($channel['channel_id'],$sender,'send_stream')) { && perm_is_allowed($channel['channel_id'],$sender,'send_stream')) {
\Zotlabs\Daemon\Master::Summon(array('Notifier', 'request', $channel['channel_id'], $sender, $arr['parent_mid'])); self::fetch_conversation($channel,$arr['parent_mid']);
} }
continue; continue;
} }
if($relay) {
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 // 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 // use our parent's route so when we go back downstream we'll match
// with whatever route our parent has. // 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,
// so just set the owner and route accordingly.
$arr['route'] = $r[0]['route']; $arr['route'] = $r[0]['route'];
$arr['owner_xchan'] = $r[0]['owner_xchan'];
} }
else { else {
@ -1546,13 +1599,13 @@ class Libzot {
$arr['aid'] = $channel['channel_account_id']; $arr['aid'] = $channel['channel_account_id'];
$arr['uid'] = $channel['channel_id']; $arr['uid'] = $channel['channel_id'];
$item_id = delete_imported_item($sender,$arr,$channel['channel_id'],$relay); $item_id = self::delete_imported_item($sender,$arr,$channel['channel_id'],$relay);
$DR->update(($item_id) ? 'deleted' : 'delete_failed'); $DR->update(($item_id) ? 'deleted' : 'delete_failed');
$result[] = $DR->get(); $result[] = $DR->get();
if($relay && $item_id) { if($relay && $item_id) {
logger('process_delivery: invoking relay'); logger('process_delivery: invoking relay');
\Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id))); Master::Summon([ 'Notifier', 'relay', intval($item_id) ]);
$DR->update('relayed'); $DR->update('relayed');
$result[] = $DR->get(); $result[] = $DR->get();
} }
@ -1662,7 +1715,7 @@ class Libzot {
if($relay && $item_id) { if($relay && $item_id) {
logger('Invoking relay'); logger('Invoking relay');
\Zotlabs\Daemon\Master::Summon(array('Notifier','relay',intval($item_id))); Master::Summon([ 'Notifier', 'relay', intval($item_id) ]);
$DR->addto_update('relayed'); $DR->addto_update('relayed');
$result[] = $DR->get(); $result[] = $DR->get();
} }
@ -1676,6 +1729,98 @@ class Libzot {
return $result; return $result;
} }
static public function fetch_conversation($channel,$mid) {
// Use Zotfinger to create a signed request
$a = Zotfinger::exec($mid,$channel);
logger('received conversation: ' . print_r($a,true), LOGGER_DATA);
if($a['data']['type'] !== 'OrderedCollection') {
return;
}
if(! intval($a['data']['totalItems'])) {
return;
}
$ret = [];
foreach($a['data']['orderedItems'] as $activity) {
$AS = new ActivityStreams($activity);
if(! $AS->is_valid()) {
logger('FOF Activity rejected: ' . print_r($activity,true));
continue;
}
$arr = Activity::decode_note($AS);
logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' 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",
dbesc($AS->actor['id'])
);
}
if(! $r) {
logger('FOF Activity: no actor');
continue;
}
}
if($AS->obj['actor'] && $AS->obj['actor']['id'] && $AS->obj['actor']['id'] !== $AS->actor['id']) {
$y = import_author_xchan([ 'url' => $AS->obj['actor']['id'] ]);
if(! $y) {
logger('FOF Activity: no object actor');
continue;
}
}
if($r) {
$arr['author_xchan'] = $r[0]['hubloc_hash'];
}
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
dbesc($a['signature']['signer'])
);
if($s) {
$arr['owner_xchan'] = $s[0]['hubloc_hash'];
}
else {
$arr['owner_xchan'] = $a['signature']['signer'];
}
// @fixme - spoofable
if($AS->data['hubloc']) {
$arr['item_verified'] = true;
}
if($AS->data['signed_data']) {
IConfig::Set($arr,'activitystreams','signed_data',$AS->data['signed_data'],false);
}
logger('FOF Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG);
logger('FOF Activity recipient: ' . $channel['channel_hash'], LOGGER_DATA, LOG_DEBUG);
$result = self::process_delivery($arr['owner_xchan'],$arr, [ $channel['channel_hash'] ],false,false,true);
if ($result) {
$ret = array_merge($ret, $result);
}
}
return $ret;
}
/** /**
* @brief Remove community tag. * @brief Remove community tag.
* *
@ -1900,7 +2045,7 @@ class Libzot {
foreach($deliveries as $d) { foreach($deliveries as $d) {
$DR = new \Zotlabs\Lib\DReport(z_root(),$sender,$d,$arr['mid']); $DR = new DReport(z_root(),$sender,$d,$arr['mid']);
$r = q("select * from channel where channel_hash = '%s' limit 1", $r = q("select * from channel where channel_hash = '%s' limit 1",
dbesc($d['hash']) dbesc($d['hash'])
@ -2112,7 +2257,8 @@ class Libzot {
if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root())
$hub['hubloc_deleted'] = 1; $hub['hubloc_deleted'] = 1;
$ret[] = [
$z = [
'host' => $hub['hubloc_host'], 'host' => $hub['hubloc_host'],
'address' => $hub['hubloc_addr'], 'address' => $hub['hubloc_addr'],
'id_url' => $hub['hubloc_id_url'], 'id_url' => $hub['hubloc_id_url'],
@ -2120,10 +2266,25 @@ class Libzot {
'url' => $hub['hubloc_url'], 'url' => $hub['hubloc_url'],
'url_sig' => $hub['hubloc_url_sig'], 'url_sig' => $hub['hubloc_url_sig'],
'site_id' => $hub['hubloc_site_id'], 'site_id' => $hub['hubloc_site_id'],
'callback' => $hub['hubloc_callback'], 'callback' => $hub['hubloc_url'] . '/zot',
'sitekey' => $hub['hubloc_sitekey'], 'sitekey' => $hub['hubloc_sitekey'],
'deleted' => (intval($hub['hubloc_deleted']) ? true : false) 'deleted' => (intval($hub['hubloc_deleted']) ? true : false)
]; ];
// version compatibility tweaks
if(! strpos($z['url_sig'],'.')) {
$z['url_sig'] = 'sha256.' . $z['url_sig'];
}
if(! $z['id_url']) {
$z['id_url'] = $z['url'] . '/channel/' . substr($z['address'],0,strpos($z['address'],'@'));
}
if(! $z['site_id']) {
$z['site_id'] = Libzot::make_xchan_hash($z['url'],$z['sitekey']);
}
$ret[] = $z;
} }
} }
@ -2331,6 +2492,10 @@ class Libzot {
// we may only end up with one; which results in posts with no author name or photo and are a bit // we may only end up with one; which results in posts with no author name or photo and are a bit
// of a hassle to repair. If either or both are missing, do a full discovery probe. // of a hassle to repair. If either or both are missing, do a full discovery probe.
if(! array_key_exists('id',$x)) {
return import_author_activitypub($x);
}
$hash = self::make_xchan_hash($x['id'],$x['key']); $hash = self::make_xchan_hash($x['id'],$x['key']);
$desturl = $x['url']; $desturl = $x['url'];
@ -2502,7 +2667,7 @@ class Libzot {
} }
else { else {
// check if it has characteristics of a public forum based on custom permissions. // check if it has characteristics of a public forum based on custom permissions.
$m = \Zotlabs\Access\Permissions::FilledAutoperms($e['channel_id']); $m = Permissions::FilledAutoperms($e['channel_id']);
if($m) { if($m) {
foreach($m as $k => $v) { foreach($m as $k => $v) {
if($k == 'tag_deliver' && intval($v) == 1) if($k == 'tag_deliver' && intval($v) == 1)
@ -2584,13 +2749,13 @@ class Libzot {
]; ];
$ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom'); $ret['channel_role'] = get_pconfig($e['channel_id'],'system','permissions_role','custom');
$ret['protocols'] = [ 'zot6' ];
$ret['searchable'] = $searchable; $ret['searchable'] = $searchable;
$ret['adult_content'] = $adult_channel; $ret['adult_content'] = $adult_channel;
$ret['public_forum'] = $public_forum; $ret['public_forum'] = $public_forum;
$ret['comments'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($e['channel_id'],'post_comments')); $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments'));
$ret['mail'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($e['channel_id'],'post_mail')); $ret['mail'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_mail'));
if($deleted) if($deleted)
$ret['deleted'] = $deleted; $ret['deleted'] = $deleted;

View File

@ -57,6 +57,7 @@ class PConfig {
\App::$config[$uid][$c]['config_loaded'] = true; \App::$config[$uid][$c]['config_loaded'] = true;
} }
\App::$config[$uid][$c][$k] = $rr['v']; \App::$config[$uid][$c][$k] = $rr['v'];
\App::$config[$uid][$c]['pcfgud:'.$k] = $rr['updated'];
} }
} }
} }
@ -113,7 +114,7 @@ class PConfig {
* The value to store * The value to store
* @return mixed Stored $value or false * @return mixed Stored $value or false
*/ */
static public function Set($uid, $family, $key, $value) { static public function Set($uid, $family, $key, $value, $updated=NULL) {
// this catches subtle errors where this function has been called // this catches subtle errors where this function has been called
// with local_channel() when not logged in (which returns false) // with local_channel() when not logged in (which returns false)
@ -130,28 +131,73 @@ class PConfig {
$dbvalue = ((is_array($value)) ? serialize($value) : $value); $dbvalue = ((is_array($value)) ? serialize($value) : $value);
$dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue);
if (! $updated) {
$updated = datetime_convert();
}
$hash = hash('sha256',$family.':'.$key);
if (self::Get($uid, 'hz_delpconfig', $hash) !== false) {
if (Get($uid, 'hz_delpconfig', $hash) > $updated) {
logger('Refusing to update pconfig with outdated info (Item deleted more recently).', LOGGER_NORMAL, LOG_ERR);
return self::Get($uid,$family,$key);
} else {
self::Delete($uid,'hz_delpconfig',$hash);
}
}
if(self::Get($uid, $family, $key) === false) { if(self::Get($uid, $family, $key) === false) {
if(! array_key_exists($uid, \App::$config)) if(! array_key_exists($uid, \App::$config))
\App::$config[$uid] = array(); \App::$config[$uid] = array();
if(! array_key_exists($family, \App::$config[$uid])) if(! array_key_exists($family, \App::$config[$uid]))
\App::$config[$uid][$family] = array(); \App::$config[$uid][$family] = array();
$ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ",
$ret = q("INSERT INTO pconfig ( uid, cat, k, v, updated ) VALUES ( %d, '%s', '%s', '%s', '%s' ) ",
intval($uid), intval($uid),
dbesc($family), dbesc($family),
dbesc($key), dbesc($key),
dbesc($dbvalue) dbesc($dbvalue),
dbesc($updated)
); );
// There is a possible race condition if another process happens
// to insert something after this thread has Loaded and now. We should
// at least make a note of it if it happens.
if (!$ret) {
logger("Error: Insert to pconfig failed.",LOGGER_NORMAL, LOG_ERR);
}
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
} }
else { else {
$new = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
$ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s'", if ($new) {
// @NOTE There is still a possible race condition under limited circumstances
// where a value will be updated by another thread with more current data than
// we have. At this point there is no easy way to test for it, so we update
// and hope for the best.
$ret = q("UPDATE pconfig SET v = '%s', updated = '%s' WHERE uid = %d and cat = '%s' AND k = '%s' ",
dbesc($dbvalue), dbesc($dbvalue),
dbesc($updated),
intval($uid), intval($uid),
dbesc($family), dbesc($family),
dbesc($key) dbesc($key)
); );
\App::$config[$uid][$family]['pcfgud:'.$key] = $updated;
} else {
logger('Refusing to update pconfig with outdated info.', LOGGER_NORMAL, LOG_ERR);
return self::Get($uid, $family, $key);
} }
}
// keep a separate copy for all variables which were // keep a separate copy for all variables which were
// set in the life of this page. We need this to // set in the life of this page. We need this to
@ -163,7 +209,11 @@ class PConfig {
\App::$config[$uid]['transient'][$family] = array(); \App::$config[$uid]['transient'][$family] = array();
\App::$config[$uid][$family][$key] = $value; \App::$config[$uid][$family][$key] = $value;
if ($new) {
\App::$config[$uid]['transient'][$family][$key] = $value; \App::$config[$uid]['transient'][$family][$key] = $value;
\App::$config[$uid]['transient'][$family]['pcfgud:'.$key] = $updated;
}
if($ret) if($ret)
return $value; return $value;
@ -186,18 +236,29 @@ class PConfig {
* The configuration key to delete * The configuration key to delete
* @return mixed * @return mixed
*/ */
static public function Delete($uid, $family, $key) { static public function Delete($uid, $family, $key, $updated = NULL) {
if(is_null($uid) || $uid === false) if(is_null($uid) || $uid === false)
return false; return false;
$updated = ($updated) ? $updated : datetime_convert();
$newer = (\App::$config[$uid][$family]['pcfgud:'.$key] < $updated);
if (! $newer) {
logger('Refusing to delete pconfig with outdated delete request.', LOGGER_NORMAL, LOG_ERR);
return false;
}
$ret = false; $ret = false;
if(array_key_exists($uid,\App::$config) if (isset(\App::$config[$uid][$family][$key])) {
&& is_array(\App::$config['uid'])
&& array_key_exists($family,\App::$config['uid'])
&& array_key_exists($key, \App::$config[$uid][$family]))
unset(\App::$config[$uid][$family][$key]); unset(\App::$config[$uid][$family][$key]);
}
if (isset(\App::$config[$uid][$family]['pcfgud:'.$key])) {
unset(\App::$config[$uid][$family]['pcfgud:'.$key]);
}
$ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'", $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s'",
intval($uid), intval($uid),
@ -205,6 +266,22 @@ class PConfig {
dbesc($key) dbesc($key)
); );
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; return $ret;
} }

View File

@ -31,15 +31,17 @@ class Owa extends \Zotlabs\Web\Controller {
if($keyId) { if($keyId) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_addr = '%s' ", where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ",
dbesc(str_replace('acct:','',$keyId)) dbesc(str_replace('acct:','',$keyId)),
dbesc($keyId)
); );
if(! $r) { if(! $r) {
$found = discover_by_webbie(str_replace('acct:','',$keyId)); $found = discover_by_webbie(str_replace('acct:','',$keyId));
if($found) { if($found) {
$r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash
where hubloc_addr = '%s' ", where ( hubloc_addr = '%s' or hubloc_id_url = '%s' ) ",
dbesc(str_replace('acct:','',$keyId)) dbesc(str_replace('acct:','',$keyId)),
dbesc($keyId)
); );
} }
} }

View File

@ -4,7 +4,6 @@ namespace Zotlabs\Module;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/attach.php'); require_once('include/attach.php');
require_once('include/photo/photo_driver.php'); require_once('include/photo/photo_driver.php');
require_once('include/photos.php');
class Photo extends \Zotlabs\Web\Controller { class Photo extends \Zotlabs\Web\Controller {
@ -32,8 +31,7 @@ class Photo extends \Zotlabs\Web\Controller {
} }
$observer_xchan = get_observer_hash(); $observer_xchan = get_observer_hash();
$ismodified = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
$default = z_root() . '/' . get_default_profile_photo();
if(isset($type)) { if(isset($type)) {
@ -42,16 +40,17 @@ class Photo extends \Zotlabs\Web\Controller {
* *
*/ */
$default = get_default_profile_photo();
if($type === 'profile') { if($type === 'profile') {
switch($res) { switch($res) {
case 'm': case 'm':
$resolution = 5; $resolution = 5;
$default = z_root() . '/' . get_default_profile_photo(80); $default = get_default_profile_photo(80);
break; break;
case 's': case 's':
$resolution = 6; $resolution = 6;
$default = z_root() . '/' . get_default_profile_photo(48); $default = get_default_profile_photo(48);
break; break;
case 'l': case 'l':
default: default:
@ -60,6 +59,8 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
$modified = filemtime($default);
$default = z_root() . '/' . $default;
$uid = $person; $uid = $person;
$d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ]; $d = [ 'imgscale' => $resolution, 'channel_id' => $uid, 'default' => $default, 'data' => '', 'mimetype' => '' ];
@ -78,16 +79,17 @@ class Photo extends \Zotlabs\Web\Controller {
intval(PHOTO_PROFILE) intval(PHOTO_PROFILE)
); );
if($r) { if($r) {
$modified = strtotime($r[0]['edited'] . "Z");
$data = dbunescbin($r[0]['content']); $data = dbunescbin($r[0]['content']);
$mimetype = $r[0]['mimetype']; $mimetype = $r[0]['mimetype'];
} }
if(intval($r[0]['os_storage'])) if(intval($r[0]['os_storage']))
$data = file_get_contents($data); $data = file_get_contents($data);
} }
if(! $data) { if(! $data) {
$data = fetch_image_from_url($default,$mimetype); $x = z_fetch_url($default,true,0,[ 'novalidate' => true ]);
} $data = ($x['success'] ? $x['body'] : EMPTY_STR);
if(! $mimetype) {
$mimetype = 'image/png'; $mimetype = 'image/png';
} }
} }
@ -124,10 +126,8 @@ class Photo extends \Zotlabs\Web\Controller {
$photo = substr($photo,0,-2); $photo = substr($photo,0,-2);
// If viewing on a high-res screen, attempt to serve a higher resolution image: // If viewing on a high-res screen, attempt to serve a higher resolution image:
if ($resolution == 2 && ($cookie_value > 1)) if ($resolution == 2 && ($cookie_value > 1))
{
$resolution = 1; $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 FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
dbesc($photo), dbesc($photo),
@ -163,10 +163,13 @@ class Photo extends \Zotlabs\Web\Controller {
if($exists && $allowed) { if($exists && $allowed) {
$data = dbunescbin($e[0]['content']); $data = dbunescbin($e[0]['content']);
$filesize = $e[0]['filesize'];
$mimetype = $e[0]['mimetype']; $mimetype = $e[0]['mimetype'];
if(intval($e[0]['os_storage'])) { $modified = strtotime($e[0]['edited'] . 'Z');
if(intval($e[0]['os_storage']))
$streaming = $data; $streaming = $data;
} if($e[0]['allow_cid'] != '' || $e[0]['allow_gid'] != '' || $e[0]['deny_gid'] != '' || $e[0]['deny_gid'] != '')
$prvcachecontrol = true;
} }
else { else {
if(! $allowed) { if(! $allowed) {
@ -177,27 +180,40 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
} else {
http_status_exit(404,'not found');
} }
} }
header_remove('Pragma');
if($ismodified === gmdate("D, d M Y H:i:s", $modified) . " GMT") {
header_remove('Expires');
header_remove('Cache-Control');
header_remove('Set-Cookie');
http_status_exit(304,'not modified');
}
if(! isset($data)) { if(! isset($data)) {
if(isset($resolution)) { if(isset($resolution)) {
switch($resolution) { switch($resolution) {
case 4: case 4:
$data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(),$mimetype); $default = get_default_profile_photo();
break; break;
case 5: case 5:
$data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(80),$mimetype); $default = get_default_profile_photo(80);
break; break;
case 6: case 6:
$data = fetch_image_from_url(z_root() . '/' . get_default_profile_photo(48),$mimetype); $default = get_default_profile_photo(48);
break; break;
default: default:
killme(); killme();
// NOTREACHED // NOTREACHED
break; break;
} }
$x = z_fetch_url(z_root() . '/' . $default,true,0,[ 'novalidate' => true ]);
$data = ($x['success'] ? $x['body'] : EMPTY_STR);
$mimetype = 'image/png';
} }
} }
@ -210,15 +226,14 @@ class Photo extends \Zotlabs\Web\Controller {
} }
} }
// @FIXME Seems never invoked
// Writing in cachefile // Writing in cachefile
if (isset($cachefile) && $cachefile != '') if (isset($cachefile) && $cachefile != '') {
file_put_contents($cachefile, $data); file_put_contents($cachefile, $data);
$modified = filemtime($cachefile);
if(function_exists('header_remove')) {
header_remove('Pragma');
header_remove('pragma');
} }
header("Content-type: " . $mimetype); header("Content-type: " . $mimetype);
if($prvcachecontrol) { if($prvcachecontrol) {
@ -240,15 +255,16 @@ class Photo extends \Zotlabs\Web\Controller {
// This has performance considerations but we highly recommend you // This has performance considerations but we highly recommend you
// leave it alone. // leave it alone.
$cache = get_config('system','photo_cache_time'); $cache = get_config('system','photo_cache_time', 86400); // 1 day by default
if(! $cache)
$cache = (3600 * 24); // 1 day
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT"); header("Expires: " . gmdate("D, d M Y H:i:s", time() + $cache) . " GMT");
header("Cache-Control: max-age=" . $cache); header("Cache-Control: max-age=" . $cache);
} }
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 it's a file resource, stream it.
if($streaming && $channel) { if($streaming && $channel) {

View File

@ -263,7 +263,8 @@ class Photos extends \Zotlabs\Web\Controller {
$fsize = strlen($data); $fsize = strlen($data);
} }
$x = q("update photo set content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 0", $x = q("update photo set edited = '%s', content = '%s', filesize = %d, height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 0",
dbesc(datetime_convert()),
dbescbin($data), dbescbin($data),
intval($fsize), intval($fsize),
intval($height), intval($height),
@ -278,7 +279,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth(); $width = $ph->getWidth();
$height = $ph->getHeight(); $height = $ph->getHeight();
$x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 1", $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 1",
dbesc(datetime_convert()),
dbescbin($ph->imageString()), dbescbin($ph->imageString()),
intval($height), intval($height),
intval($width), intval($width),
@ -293,7 +295,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth(); $width = $ph->getWidth();
$height = $ph->getHeight(); $height = $ph->getHeight();
$x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 2", $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 2",
dbesc(datetime_convert()),
dbescbin($ph->imageString()), dbescbin($ph->imageString()),
intval($height), intval($height),
intval($width), intval($width),
@ -308,7 +311,8 @@ class Photos extends \Zotlabs\Web\Controller {
$width = $ph->getWidth(); $width = $ph->getWidth();
$height = $ph->getHeight(); $height = $ph->getHeight();
$x = q("update photo set content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 3", $x = q("update photo set edited = '%s', content = '%s', height = %d, width = %d where resource_id = '%s' and uid = %d and imgscale = 3",
dbesc(datetime_convert()),
dbescbin($ph->imageString()), dbescbin($ph->imageString()),
intval($height), intval($height),
intval($width), intval($width),

View File

@ -6,7 +6,7 @@ class Search extends \Zotlabs\Web\Controller {
function init() { function init() {
if(x($_REQUEST,'search')) if(x($_REQUEST,'search'))
\App::$data['search'] = $_REQUEST['search']; \App::$data['search'] = escape_tags($_REQUEST['search']);
} }
@ -46,12 +46,12 @@ class Search extends \Zotlabs\Web\Controller {
if(x(\App::$data,'search')) if(x(\App::$data,'search'))
$search = trim(\App::$data['search']); $search = trim(\App::$data['search']);
else else
$search = ((x($_GET,'search')) ? trim(rawurldecode($_GET['search'])) : ''); $search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : '');
$tag = false; $tag = false;
if(x($_GET,'tag')) { if(x($_GET,'tag')) {
$tag = true; $tag = true;
$search = ((x($_GET,'tag')) ? trim(rawurldecode($_GET['tag'])) : ''); $search = ((x($_GET,'tag')) ? trim(escape_tags(rawurldecode($_GET['tag']))) : '');
} }
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
@ -227,9 +227,9 @@ class Search extends \Zotlabs\Web\Controller {
} }
if($tag) if($tag)
$o .= '<h2>' . sprintf( t('Items tagged with: %s'),htmlspecialchars($search, ENT_COMPAT,'UTF-8')) . '</h2>'; $o .= '<h2>' . sprintf( t('Items tagged with: %s'),$search) . '</h2>';
else else
$o .= '<h2>' . sprintf( t('Search results for: %s'),htmlspecialchars($search, ENT_COMPAT,'UTF-8')) . '</h2>'; $o .= '<h2>' . sprintf( t('Search results for: %s'),$search) . '</h2>';
$o .= conversation($items,'search',$update,'client'); $o .= conversation($items,'search',$update,'client');

26
Zotlabs/Update/_1225.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace Zotlabs\Update;
class _1225 {
function run() {
if(ACTIVE_DBTYPE == DBTYPE_POSTGRES) {
$r1 = q("ALTER TABLE pconfig ADD updated timestamp NOT NULL DEFAULT '0001-01-01 00:00:00' ");
$r2 = q("create index \"pconfig_updated_idx\" on pconfig (\"updated\")");
$r = ($r1 && $r2);
}
else {
$r = q("ALTER TABLE `pconfig` ADD `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' ,
ADD INDEX `pconfig_updated` (`updated`)");
}
if($r)
return UPDATE_SUCCESS;
return UPDATE_FAILED;
}
}

View File

@ -50,11 +50,10 @@ require_once('include/attach.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '3.8.3' ); define ( 'STD_VERSION', '3.8.4' );
define ( 'ZOT_REVISION', '6.0a' ); define ( 'ZOT_REVISION', '6.0a' );
define ( 'DB_UPDATE_VERSION', 1225 );
define ( 'DB_UPDATE_VERSION', 1224 );
define ( 'PROJECT_BASE', __DIR__ ); define ( 'PROJECT_BASE', __DIR__ );

View File

@ -1198,24 +1198,24 @@ function bbcode($Text, $options = []) {
// Images // Images
// [img]pathtoimage[/img] // [img]pathtoimage[/img]
if (strpos($Text,'[/img]') !== false) { if (strpos($Text,'[/img]') !== false) {
$Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img style="max-width=100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text);
} }
if (strpos($Text,'[/zmg]') !== false) { if (strpos($Text,'[/zmg]') !== false) {
$Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width=100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" style="max-width: 100%;" src="$1" alt="' . t('Image/photo') . '" />', $Text);
} }
// [img float={left, right}]pathtoimage[/img] // [img float={left, right}]pathtoimage[/img]
if (strpos($Text,'[/img]') !== false) { if (strpos($Text,'[/img]') !== false) {
$Text = preg_replace("/\[img float=left\](.*?)\[\/img\]/ism", '<img style="max-width=100%;" src="$1" style="float: left;" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[img float=left\](.*?)\[\/img\]/ism", '<img src="$1" style="max-width: 100%; float: left;" alt="' . t('Image/photo') . '" />', $Text);
} }
if (strpos($Text,'[/img]') !== false) { if (strpos($Text,'[/img]') !== false) {
$Text = preg_replace("/\[img float=right\](.*?)\[\/img\]/ism", '<img style="max-width=100%;" src="$1" style="float: right;" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[img float=right\](.*?)\[\/img\]/ism", '<img src="$1" style="max-width: 100%; float: right;" alt="' . t('Image/photo') . '" />', $Text);
} }
if (strpos($Text,'[/zmg]') !== false) { if (strpos($Text,'[/zmg]') !== false) {
$Text = preg_replace("/\[zmg float=left\](.*?)\[\/zmg\]/ism", '<img style="max-width=100%;" class="zrl" src="$1" style="float: left;" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[zmg float=left\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$1" style="max-width: 100%; float: left;" alt="' . t('Image/photo') . '" />', $Text);
} }
if (strpos($Text,'[/zmg]') !== false) { if (strpos($Text,'[/zmg]') !== false) {
$Text = preg_replace("/\[zmg float=right\](.*?)\[\/zmg\]/ism", '<img style="max-width=100%;" class="zrl" src="$1" style="float: right;" alt="' . t('Image/photo') . '" />', $Text); $Text = preg_replace("/\[zmg float=right\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$1" style="max-width: 100%; float: right;" alt="' . t('Image/photo') . '" />', $Text);
} }
// [img=widthxheight]pathtoimage[/img] // [img=widthxheight]pathtoimage[/img]

View File

@ -59,8 +59,8 @@ function set_pconfig($uid, $family, $key, $value) {
return Zlib\PConfig::Set($uid,$family,$key,$value); return Zlib\PConfig::Set($uid,$family,$key,$value);
} }
function del_pconfig($uid, $family, $key) { function del_pconfig($uid, $family, $key, $updated = NULL) {
return Zlib\PConfig::Delete($uid,$family,$key); return Zlib\PConfig::Delete($uid,$family,$key,$updated);
} }
function load_xconfig($xchan) { function load_xconfig($xchan) {

View File

@ -173,14 +173,14 @@ abstract class dba_driver {
return false; return false;
} }
if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1') && (! strpbrk($server,':;'))) { // if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1') && (! strpbrk($server,':;'))) {
if(! z_dns_check($server)) { // if(! z_dns_check($server)) {
$this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server); // $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server);
$this->connected = false; // $this->connected = false;
$this->db = null; // $this->db = null;
return false; // return false;
} // }
} // }
return true; return true;
} }

View File

@ -233,7 +233,7 @@ function z_post_url($url, $params, $redirects = 0, $opts = array()) {
return $ret; return $ret;
if(! array_key_exists('request_target',$opts)) { if(! array_key_exists('request_target',$opts)) {
$opts['request_target'] = 'get ' . get_request_string($url); $opts['request_target'] = 'post ' . get_request_string($url);
} }
@curl_setopt($ch, CURLOPT_HEADER, true); @curl_setopt($ch, CURLOPT_HEADER, true);

View File

@ -350,8 +350,7 @@ abstract class photo_driver {
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : ''); $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : ''); $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : ''); $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
$p['created'] = (($arr['created']) ? $arr['created'] : datetime_convert()); $p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
$p['edited'] = (($arr['edited']) ? $arr['edited'] : $p['created']);
$p['title'] = (($arr['title']) ? $arr['title'] : ''); $p['title'] = (($arr['title']) ? $arr['title'] : '');
$p['description'] = (($arr['description']) ? $arr['description'] : ''); $p['description'] = (($arr['description']) ? $arr['description'] : '');
$p['photo_usage'] = intval($arr['photo_usage']); $p['photo_usage'] = intval($arr['photo_usage']);
@ -365,13 +364,15 @@ abstract class photo_driver {
if(! intval($p['imgscale'])) if(! intval($p['imgscale']))
logger('save: ' . print_r($arr,true), LOGGER_DATA); logger('save: ' . print_r($arr,true), LOGGER_DATA);
$x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", $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']), dbesc($p['resource_id']),
intval($p['uid']), intval($p['uid']),
dbesc($p['xchan']), dbesc($p['xchan']),
intval($p['imgscale']) intval($p['imgscale'])
); );
if($x) { if($x) {
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
$r = q("UPDATE photo set $r = q("UPDATE photo set
aid = %d, aid = %d,
uid = %d, uid = %d,
@ -427,6 +428,7 @@ abstract class photo_driver {
); );
} }
else { else {
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
$r = q("INSERT INTO photo $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 ) ( 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 )
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' )", 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' )",
@ -464,11 +466,6 @@ abstract class photo_driver {
/** /**
* Guess image mimetype from filename or from Content-Type header * Guess image mimetype from filename or from Content-Type header
* *
@ -485,11 +482,11 @@ function guess_image_type($filename, $headers = '') {
$h = explode("\n",$headers); $h = explode("\n",$headers);
foreach ($h as $l) { foreach ($h as $l) {
list($k,$v) = array_map("trim", explode(":", trim($l), 2)); list($k,$v) = array_map("trim", explode(":", trim($l), 2));
$hdrs[$k] = $v; $hdrs[strtolower($k)] = $v;
} }
logger('Curl headers: '.var_export($hdrs, true), LOGGER_DEBUG); logger('Curl headers: '.var_export($hdrs, true), LOGGER_DEBUG);
if (array_key_exists('Content-Type', $hdrs)) if (array_key_exists('content-type', $hdrs))
$type = $hdrs['Content-Type']; $type = $hdrs['content-type'];
} }
if (is_null($type)){ if (is_null($type)){
@ -570,22 +567,48 @@ function delete_thing_photo($url,$ob_hash) {
function import_xchan_photo($photo,$xchan,$thing = false) { /**
* @brief fetches an photo from external site and prepares its miniatures.
*
* @param string $photo
* external URL to fetch base image
* @param string $xchan
* channel unique hash
* @param boolean $thing
* TRUE if this is a thing URL
* @param boolean $force
* TRUE if ignore image modification date check (force fetch)
*
* @return array of results
* * \e string \b 0 => local URL to full image
* * \e string \b 1 => local URL to standard thumbnail
* * \e string \b 2 => local URL to micro thumbnail
* * \e string \b 3 => image type
* * \e boolean \b 4 => TRUE if fetch failure
* * \e string \b 5 => modification date
*/
function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
$modified = '';
$flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN); $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN);
$album = (($thing) ? 'Things' : 'Contact Photos'); $album = (($thing) ? 'Things' : 'Contact Photos');
logger('import_xchan_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); logger('import_xchan_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG);
if($thing) if($thing) {
$hash = photo_new_resource(); $hash = photo_new_resource();
}
else { else {
$r = q("select resource_id from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1", $r = q("select resource_id, edited, mimetype from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1",
dbesc($xchan), dbesc($xchan),
intval(PHOTO_XCHAN) intval(PHOTO_XCHAN)
); );
if($r) { if($r) {
$hash = $r[0]['resource_id']; $hash = $r[0]['resource_id'];
$modified = $r[0]['edited'];
$type = $r[0]['mimetype'];
} }
else { else {
$hash = photo_new_resource(); $hash = photo_new_resource();
@ -598,29 +621,36 @@ function import_xchan_photo($photo,$xchan,$thing = false) {
if($photo) { if($photo) {
$filename = basename($photo); $filename = basename($photo);
if($force || $modified == '') {
$result = z_fetch_url($photo,true); $result = z_fetch_url($photo,true);
}
else {
$h = array('headers' => array("If-Modified-Since: " . gmdate("D, d M Y H:i:s", strtotime($modified . "Z")) . " GMT"));
$result = z_fetch_url($photo,true,0,$h);
}
if($result['success']) { if($result['success']) {
$img_str = $result['body']; $img_str = $result['body'];
$type = guess_image_type($photo, $result['header']); $type = guess_image_type($photo, $result['header']);
$modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $o) ? strtotime($o[1] . 'Z') : time()));
$h = explode("\n",$result['header']); if(is_null($type))
if($h) {
foreach($h as $hl) {
if(stristr($hl,'content-type:')) {
if(! stristr($hl,'image/')) {
$photo_failure = true; $photo_failure = true;
} }
} elseif($result['return_code'] == 304) {
} $photo = z_root() . '/photo/' . $hash . '-4';
} $thumb = z_root() . '/photo/' . $hash . '-5';
} $micro = z_root() . '/photo/' . $hash . '-6';
} }
else { else {
$photo_failure = true; $photo_failure = true;
} }
if(! $photo_failure) { }
else
$photo_failure = true;
if(! $photo_failure && $result['return_code'] != 304) {
$img = photo_factory($img_str, $type); $img = photo_factory($img_str, $type);
if($img->is_valid()) { if($img->is_valid()) {
$width = $img->getWidth(); $width = $img->getWidth();
@ -646,7 +676,15 @@ function import_xchan_photo($photo,$xchan,$thing = false) {
else else
$photo_failure = true; $photo_failure = true;
$p = array('xchan' => $xchan,'resource_id' => $hash, 'filename' => basename($photo), 'album' => $album, 'photo_usage' => $flags, 'imgscale' => 4); $p = array(
'xchan' => $xchan,
'resource_id' => $hash,
'filename' => basename($photo),
'album' => $album,
'photo_usage' => $flags,
'imgscale' => 4,
'edited' => $modified
);
$r = $img->save($p); $r = $img->save($p);
@ -679,13 +717,16 @@ function import_xchan_photo($photo,$xchan,$thing = false) {
} }
} }
if($photo_failure) { if($photo_failure) {
$photo = z_root() . '/' . get_default_profile_photo(); $default = get_default_profile_photo();
$photo = z_root() . '/' . $default;
$thumb = z_root() . '/' . get_default_profile_photo(80); $thumb = z_root() . '/' . get_default_profile_photo(80);
$micro = z_root() . '/' . get_default_profile_photo(48); $micro = z_root() . '/' . get_default_profile_photo(48);
$type = 'image/png'; $type = 'image/png';
$modified = gmdate('Y-m-d H:i:s', filemtime($default));
} }
return(array($photo,$thumb,$micro,$type,$photo_failure)); logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified . '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG);
return(array($photo,$thumb,$micro,$type,$photo_failure,$modified));
} }
@ -700,18 +741,10 @@ function import_channel_photo_from_url($photo,$aid,$uid) {
$img_str = $result['body']; $img_str = $result['body'];
$type = guess_image_type($photo, $result['header']); $type = guess_image_type($photo, $result['header']);
$h = explode("\n",$result['header']); if(is_null($type))
if($h) {
foreach($h as $hl) {
if(stristr($hl,'content-type:')) {
if(! stristr($hl,'image/')) {
$photo_failure = true; $photo_failure = true;
} }
} }
}
}
}
}
else { else {
$photo_failure = true; $photo_failure = true;
} }

View File

@ -1011,23 +1011,3 @@ function profile_photo_set_profile_perms($uid, $profileid = 0) {
} }
} }
} }
function fetch_image_from_url($url,&$mimetype) {
$redirects = 0;
$x = z_fetch_url($url,true,$redirects,[ 'novalidate' => true ]);
if($x['success']) {
$hdrs = [];
$h = explode("\n",$x['header']);
foreach ($h as $l) {
list($k,$v) = array_map("trim", explode(":", trim($l), 2));
$hdrs[strtolower($k)] = $v;
}
if (array_key_exists('content-type', $hdrs))
$mimetype = $hdrs['content-type'];
return $x['body'];
}
return EMPTY_STR;
}

View File

@ -633,6 +633,19 @@ function attribute_contains($attr, $s) {
return false; return false;
} }
/**
* @brief Log to syslog
*
* @param string $msg Message to log
* @param int $priority - compatible with syslog
*/
function hz_syslog($msg, $priority = LOG_INFO) {
openlog("hz-log", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog($priority, $msg);
closelog();
}
/** /**
* @brief Logging function for Hubzilla. * @brief Logging function for Hubzilla.
* *
@ -3219,8 +3232,16 @@ function create_table_from_array($table, $arr, $binary_fields = []) {
if(! ($arr && $table)) if(! ($arr && $table))
return false; return false;
$columns = db_columns($table);
$clean = []; $clean = [];
foreach($arr as $k => $v) { foreach($arr as $k => $v) {
if(! in_array($k,$columns)) {
continue;
}
$matches = false; $matches = false;
if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) { if(preg_match('/([^a-zA-Z0-9\-\_\.])/',$k,$matches)) {
return false; return false;

View File

@ -1,5 +1,7 @@
<?php <?php
use Zotlabs\Zot6\HTTPSig;
function xchan_store_lowlevel($arr) { function xchan_store_lowlevel($arr) {
@ -39,6 +41,13 @@ function xchan_store_lowlevel($arr) {
function xchan_store($arr) { function xchan_store($arr) {
$update_photo = false;
$update_name = false;
if(! ($arr['guid'] || $arr['hash'])) {
$arr = json_decode(file_get_contents('php://input'),true);
}
logger('xchan_store: ' . print_r($arr,true)); logger('xchan_store: ' . print_r($arr,true));
if(! $arr['hash']) if(! $arr['hash'])
@ -49,8 +58,9 @@ function xchan_store($arr) {
$r = q("select * from xchan where xchan_hash = '%s' limit 1", $r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($arr['hash']) dbesc($arr['hash'])
); );
if($r) if(! $r) {
return true;
$update_photo = true;
if(! $arr['network']) if(! $arr['network'])
$arr['network'] = 'unknown'; $arr['network'] = 'unknown';
@ -61,6 +71,12 @@ function xchan_store($arr) {
if(! $arr['photo']) if(! $arr['photo'])
$arr['photo'] = z_root() . '/' . get_default_profile_photo(); $arr['photo'] = z_root() . '/' . get_default_profile_photo();
if($arr['network'] === 'zot6') {
if((! $arr['key']) || (! Libzot::verify($arr['id'],$arr['id_sig'],$arr['key']))) {
logger('Unable to verify signature for ' . $arr['hash']);
return false;
}
}
if($arr['network'] === 'zot') { if($arr['network'] === 'zot') {
if((! $arr['key']) || (! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key']))) { if((! $arr['key']) || (! rsa_verify($arr['guid'],base64url_decode($arr['guid_sig']),$arr['key']))) {
@ -69,26 +85,44 @@ function xchan_store($arr) {
} }
} }
$columns = db_columns('xchan');
$x = []; $x = [];
foreach($arr as $k => $v) { foreach($arr as $k => $v) {
if($k === 'key') { if($k === 'key') {
$x['xchan_pubkey'] = $v; $x['xchan_pubkey'] = HTTPSig::convertKey(escape_tags($v));;
continue; continue;
} }
if($k === 'photo') { if($k === 'photo') {
continue; continue;
} }
$x['xchan_' . $k] = $v; if(in_array($columns,'xchan_' . $k))
$x['xchan_' . $k] = escape_tags($v);
} }
$x['xchan_name_date'] = datetime_convert(); $x['xchan_name_date'] = datetime_convert();
$x['xchan_photo_date'] = datetime_convert();
$x['xchan_system'] = false;
$r = xchan_store_lowlevel($x); $result = xchan_store_lowlevel($x);
if(! $r) if(! $result)
return $r; return $result;
}
else {
if($r[0]['network'] === 'zot6') {
return true;
}
if($r[0]['xchan_photo_date'] < datetime_convert('UTC','UTC',$arr['photo_date'])) {
$update_photo = true;
}
if($r[0]['xchan_name_date'] < datetime_convert('UTC','UTC',$arr['name_date'])) {
$update_name = true;
}
}
if($update_photo && $arr['photo']) {
$photos = import_xchan_photo($arr['photo'],$arr['hash']); $photos = import_xchan_photo($arr['photo'],$arr['hash']);
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'", $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_hash = '%s'",
dbesc(datetime_convert()), dbesc(datetime_convert()),
@ -98,8 +132,16 @@ function xchan_store($arr) {
dbesc($photos[3]), dbesc($photos[3]),
dbesc($arr['hash']) dbesc($arr['hash'])
); );
return $r; }
if($update_name && $arr['name']) {
$x = q("update xchan set xchan_name = '%s', xchan_name_date = '%s' where xchan_hash = '%s'",
dbesc(escape_tags($arr['name'])),
dbesc(datetime_convert()),
dbesc($arr['hash'])
);
}
return true;
} }

View File

@ -3507,8 +3507,41 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) {
if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) {
foreach($arr['config'] as $cat => $k) { foreach($arr['config'] as $cat => $k) {
foreach($arr['config'][$cat] as $k => $v)
set_pconfig($channel['channel_id'],$cat,$k,$v); $pconfig_updated = [];
$pconfig_del = [];
foreach($arr['config'][$cat] as $k => $v) {
if (strpos($k,'pcfgud:')===0) {
$realk = substr($k,7);
$pconfig_updated[$realk] = $v;
unset($arr['config'][$cat][$k]);
}
if (strpos($k,'pcfgdel:')===0) {
$realk = substr($k,8);
$pconfig_del[$realk] = datetime_convert();
unset($arr['config'][$cat][$k]);
}
}
foreach($arr['config'][$cat] as $k => $v) {
if (!isset($pconfig_updated[$k])) {
$pconfig_updated[$k] = NULL;
}
set_pconfig($channel['channel_id'],$cat,$k,$v,$pconfig_updated[$k]);
}
foreach($pconfig_del as $k => $updated) {
del_pconfig($channel['channel_id'],$cat,$k,$updated);
}
} }
} }

View File

@ -1505,6 +1505,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Update\\_1222' => __DIR__ . '/../..' . '/Zotlabs/Update/_1222.php', 'Zotlabs\\Update\\_1222' => __DIR__ . '/../..' . '/Zotlabs/Update/_1222.php',
'Zotlabs\\Update\\_1223' => __DIR__ . '/../..' . '/Zotlabs/Update/_1223.php', 'Zotlabs\\Update\\_1223' => __DIR__ . '/../..' . '/Zotlabs/Update/_1223.php',
'Zotlabs\\Update\\_1224' => __DIR__ . '/../..' . '/Zotlabs/Update/_1224.php', 'Zotlabs\\Update\\_1224' => __DIR__ . '/../..' . '/Zotlabs/Update/_1224.php',
'Zotlabs\\Update\\_1225' => __DIR__ . '/../..' . '/Zotlabs/Update/_1225.php',
'Zotlabs\\Web\\CheckJS' => __DIR__ . '/../..' . '/Zotlabs/Web/CheckJS.php', 'Zotlabs\\Web\\CheckJS' => __DIR__ . '/../..' . '/Zotlabs/Web/CheckJS.php',
'Zotlabs\\Web\\Controller' => __DIR__ . '/../..' . '/Zotlabs/Web/Controller.php', 'Zotlabs\\Web\\Controller' => __DIR__ . '/../..' . '/Zotlabs/Web/Controller.php',
'Zotlabs\\Web\\HTTPHeaders' => __DIR__ . '/../..' . '/Zotlabs/Web/HTTPHeaders.php', 'Zotlabs\\Web\\HTTPHeaders' => __DIR__ . '/../..' . '/Zotlabs/Web/HTTPHeaders.php',

View File

@ -35,7 +35,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: de\n" "Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Access/Permissions.php:56 #: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts" msgid "Can view my channel stream and posts"

View File

@ -22,7 +22,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: es_ES\n" "Language: es_ES\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Access/Permissions.php:56 #: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts" msgid "Can view my channel stream and posts"

View File

@ -2,10 +2,10 @@
if(! function_exists("string_plural_select_es_es")) { if(! function_exists("string_plural_select_es_es")) {
function string_plural_select_es_es($n){ function string_plural_select_es_es($n){
return ($n != 1);; return ($n != 1 ? 1 : 0);
}} }}
App::$rtl = 0; App::$rtl = 0;
App::$strings["plural_function_code"] = "(n != 1)"; App::$strings["plural_function_code"] = "(n != 1 ? 1 : 0)";
App::$strings["Can view my channel stream and posts"] = "Pueden verse la actividad y publicaciones de mi canal"; App::$strings["Can view my channel stream and posts"] = "Pueden verse la actividad y publicaciones de mi canal";
App::$strings["Can send me their channel stream and posts"] = "Se me pueden enviar entradas y contenido de un canal"; App::$strings["Can send me their channel stream and posts"] = "Se me pueden enviar entradas y contenido de un canal";
App::$strings["Can view my default channel profile"] = "Puede verse mi perfil de canal predeterminado."; App::$strings["Can view my default channel profile"] = "Puede verse mi perfil de canal predeterminado.";

View File

@ -22,7 +22,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: fr\n" "Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1 ? 1 : 0);\n"
#: ../../Zotlabs/Access/Permissions.php:56 #: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts" msgid "Can view my channel stream and posts"

View File

@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: he\n" "Language: he\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Storage/Browser.php:107 ../../Zotlabs/Storage/Browser.php:239 #: ../../Zotlabs/Storage/Browser.php:107 ../../Zotlabs/Storage/Browser.php:239
msgid "parent" msgid "parent"

View File

@ -18,7 +18,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: it\n" "Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Access/Permissions.php:56 #: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts" msgid "Can view my channel stream and posts"

View File

@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: nb_NO\n" "Language: nb_NO\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Storage/Browser.php:107 ../../Zotlabs/Storage/Browser.php:239 #: ../../Zotlabs/Storage/Browser.php:107 ../../Zotlabs/Storage/Browser.php:239
msgid "parent" msgid "parent"

View File

@ -18,7 +18,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: nl\n" "Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../Zotlabs/Access/Permissions.php:56 #: ../../Zotlabs/Access/Permissions.php:56
msgid "Can view my channel stream and posts" msgid "Can view my channel stream and posts"

View File

@ -25,7 +25,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n" "Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1 ? 1 : 0);\n"
#: ../../include/dba/dba_driver.php:50 #: ../../include/dba/dba_driver.php:50
#, php-format #, php-format

View File

@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: sv\n" "Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"
#: ../../include/dba/dba_driver.php:142 #: ../../include/dba/dba_driver.php:142
#, php-format #, php-format

View File

@ -872,10 +872,6 @@ div.jGrowl div.jGrowl-notification {
margin-left: 20px; margin-left: 20px;
} }
.reshared-content img {
width: 100%;
}
.shared_header img { .shared_header img {
border-radius: $radius; border-radius: $radius;
margin-right: 10px; margin-right: 10px;
@ -884,21 +880,19 @@ div.jGrowl div.jGrowl-notification {
.tag1 { .tag1 {
font-size : 0.9em !important; font-size : 0.9em !important;
} }
.tag2 { .tag2 {
font-size : 1.0em !important; font-size : 1.0em !important;
} }
.tag3 { .tag3 {
font-size : 1.1em !important; font-size : 1.1em !important;
} }
.tag4 { .tag4 {
font-size : 1.2em !important; font-size : 1.2em !important;
} }
.tag5 { .tag5 {
font-size : 1.3em !important; font-size : 1.3em !important;
} }
@ -918,12 +912,10 @@ div.jGrowl div.jGrowl-notification {
font-size : 1.6em !important; font-size : 1.6em !important;
} }
.tag9 { .tag9 {
font-size : 1.7em !important; font-size : 1.7em !important;
} }
.tag10 { .tag10 {
font-size : 1.8em !important; font-size : 1.8em !important;
} }

View File

@ -3,10 +3,11 @@
if (! $nav_bg) if (! $nav_bg)
$nav_bg = "#f8f9fa"; $nav_bg = "#f8f9fa";
if (! $nav_icon_colour) if (! $nav_icon_colour)
$nav_icon_colour = "rgba(0, 0, 0, 0.5);"; $nav_icon_colour = "rgba(0, 0, 0, 0.5)";
if (! $nav_active_icon_colour) if (! $nav_active_icon_colour)
$nav_active_icon_colour = "rgba(0, 0, 0, 0.7)"; $nav_active_icon_colour = "rgba(0, 0, 0, 0.7)";
if (! $radius) if (! $radius)
$radius = "4px"; $radius = "4px";
if (! $banner_colour) if (! $banner_colour)
$banner_colour = "rgba(0, 0, 0, 0.7)"; $banner_colour = "rgba(0, 0, 0, 0.7)";

View File

@ -17,7 +17,7 @@
</form> </form>
</div> </div>
<script> <script>
$("#cid-filter").name_autocomplete(baseurl + '/acl', 'a', true, function(data) { $("#cid-filter").contact_autocomplete(baseurl + '/acl', 'a', true, function(data) {
$("#cid").val(data.id); $("#cid").val(data.id);
}); });
</script> </script>