diff options
-rw-r--r-- | CHANGELOG | 18 | ||||
-rw-r--r-- | Zotlabs/Daemon/Cron.php | 23 | ||||
-rw-r--r-- | Zotlabs/Lib/Activity.php | 391 | ||||
-rw-r--r-- | Zotlabs/Lib/Enotify.php | 1 | ||||
-rw-r--r-- | Zotlabs/Lib/Libzot.php | 8 | ||||
-rw-r--r-- | Zotlabs/Module/Import.php | 9 | ||||
-rw-r--r-- | Zotlabs/Module/Item.php | 6 | ||||
-rw-r--r-- | Zotlabs/Module/Like.php | 3 | ||||
-rw-r--r-- | Zotlabs/Module/Photo.php | 87 | ||||
-rw-r--r-- | Zotlabs/Module/Ping.php | 8 | ||||
-rw-r--r-- | Zotlabs/Module/Wfinger.php | 3 | ||||
-rw-r--r-- | Zotlabs/Update/_1229.php | 32 | ||||
-rw-r--r-- | Zotlabs/Widget/Notifications.php | 8 | ||||
-rwxr-xr-x | boot.php | 3 | ||||
-rw-r--r-- | include/channel.php | 15 | ||||
-rwxr-xr-x | include/items.php | 8 | ||||
-rwxr-xr-x | include/oembed.php | 6 | ||||
-rw-r--r-- | include/photo/photo_driver.php | 31 | ||||
-rw-r--r-- | include/photo/photo_imagick.php | 10 | ||||
-rw-r--r-- | include/text.php | 45 | ||||
-rw-r--r-- | install/schema_mysql.sql | 2 | ||||
-rw-r--r-- | install/schema_postgres.sql | 2 | ||||
-rw-r--r-- | view/es-es/hstrings.php | 4 | ||||
-rw-r--r-- | view/js/main.js | 5 | ||||
-rw-r--r-- | view/theme/redbasic/js/redbasic.js | 6 | ||||
-rw-r--r-- | view/tpl/notifications_widget.tpl | 13 |
26 files changed, 592 insertions, 155 deletions
@@ -1,3 +1,21 @@ +Hubzilla 3.8.6 (2018-12-03) + - Prevent incompatible export files (osada/zap) from being imported + - Catch exception if readImageBlob() receives bogus data + - Streamline PDF previews + - Allow notification filtering by name or address + - Fix too restrictive attached photo permissions + - Update ES translation + - Use flex for the default template + - Do not store serialized pconfig value received via to Module/Pconfig.php + - Update jquery-file-upload lib and move to composer + - Update imagesloaded lib and move to composer + - Fix activitypub tag notifications + - Fix call to undefined function in PConfig + - Fix typo which prevented propagation of comments to zot6 (dev) + - Activitypub: add support for pterotype (wordpress plugin) + - Openstreetmap: check validity of lat+lon before rendering a map + + Hubzilla 3.8.5 (2018-11-19) - Fix pconfig for new installs - Fix delayed publication of posts in combination with channel clones 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/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 6ddbbb9db..8cef41c42 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -11,9 +11,15 @@ 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); } @@ -1149,7 +1155,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 @@ -1233,19 +1239,20 @@ class Activity { static function decode_note($act) { - $s = []; - - + $response_activity = false; - $content = self::get_content($act->obj); + $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['mid'] = $act->obj['id']; $s['parent_mid'] = $act->parent_id; - if($act->data['published']) { $s['created'] = datetime_convert('UTC','UTC',$act->data['published']); } @@ -1259,43 +1266,60 @@ 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']); + } + + if($act->data['updated']) { + $s['edited'] = datetime_convert('UTC','UTC',$act->data['updated']); + } - $name = urlencode($act->obj['actor']['name']); - $profile = $act->obj['actor']['id']; - $photo = $act->obj['icon']['url']; + $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('🔁 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')); } + 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')); + $s['verb'] = self::activity_mapper($act->type); if($act->type === 'Tombstone') { @@ -1306,58 +1330,229 @@ class Activity { $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']; + } + 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; + } + } } - $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]'; + } + } + + } + + if($act->obj['type'] === 'Image') { + + $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 +1566,6 @@ class Activity { } - - static function announce_note($channel,$observer_hash,$act) { $s = []; @@ -1464,7 +1657,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 +1835,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 +1862,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 +1897,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 +1908,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 +1971,6 @@ class Activity { } return $content; } + + }
\ No newline at end of file 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..0fbde9765 100644 --- a/Zotlabs/Lib/Libzot.php +++ b/Zotlabs/Lib/Libzot.php @@ -1418,7 +1418,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'); @@ -1505,8 +1505,10 @@ 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 @@ -1591,7 +1593,7 @@ logger('4'); $arr['route'] = $last_prior_route; } } - +logger('hey'); $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", intval($channel['channel_id']), dbesc($arr['owner_xchan']) 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..24949c626 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -760,7 +760,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 +772,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 +784,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..d19154eb4 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'); @@ -400,6 +402,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/Photo.php b/Zotlabs/Module/Photo.php index 30e8340e2..ddff3a68e 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' => get_config('system','photo_cache_enable', 0), + '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,40 @@ 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, + 'uid' => $r[0]['uid'], + 'status' => false + ); + if($cache_mode['on']) + call_hooks('cache_url_hook', $cache); + if(! $cache['status']) { + header("Location: " . htmlspecialchars_decode($r[0]['display_path'])); + 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 +181,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 +206,9 @@ class Photo extends \Zotlabs\Web\Controller { } } - } else { + } + else http_status_exit(404,'not found'); - } } header_remove('Pragma'); @@ -225,24 +251,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 +271,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/Ping.php b/Zotlabs/Module/Ping.php index f660c3b55..12244d88c 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']), @@ -383,6 +384,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 +409,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 +447,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 +464,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 +481,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 +663,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/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/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/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') ] ]; } @@ -53,7 +53,7 @@ define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'STD_VERSION', '3.9.5' ); define ( 'ZOT_REVISION', '6.0a' ); -define ( 'DB_UPDATE_VERSION', 1228 ); +define ( 'DB_UPDATE_VERSION', 1229 ); define ( 'PROJECT_BASE', __DIR__ ); @@ -217,6 +217,7 @@ define ( 'PHOTO_PROFILE', 0x0001 ); define ( 'PHOTO_XCHAN', 0x0002 ); define ( 'PHOTO_THING', 0x0004 ); define ( 'PHOTO_COVER', 0x0010 ); +define ( 'PHOTO_CACHE', 0x0020 ); define ( 'PHOTO_ADULT', 0x0008 ); define ( 'PHOTO_FLAG_OS', 0x4000 ); diff --git a/include/channel.php b/include/channel.php index 1e5570f6b..95a3f96cf 100644 --- a/include/channel.php +++ b/include/channel.php @@ -2333,6 +2333,21 @@ function channelx_by_hash($hash) { return(($r) ? $r[0] : false); } + +/** + * @brief Get a channel array by a channel_hash. + * + * @param string $hash + * @return array|boolean false if channel ID not found, otherwise the channel array + */ +function channelx_by_portid($hash) { + $r = q("SELECT * FROM channel left join xchan on channel_portable_id = xchan_hash WHERE channel_portable_id = '%s' and channel_removed = 0 LIMIT 1", + dbesc($hash) + ); + + return(($r) ? $r[0] : false); +} + /** * @brief Get a channel array by a channel ID. * diff --git a/include/items.php b/include/items.php index 33ef6edad..0cbca23fc 100755 --- a/include/items.php +++ b/include/items.php @@ -4629,10 +4629,10 @@ function fix_attached_photo_permissions($uid,$xchan_hash,$body, if(! stristr($image,z_root() . '/photo/')) continue; $image_uri = substr($image,strrpos($image,'/') + 1); - if(strpos($image_uri,'-') !== false) - $image_uri = substr($image_uri,0, strpos($image_uri,'-')); - if(strpos($image_uri,'.') !== false) - $image_uri = substr($image_uri,0, strpos($image_uri,'.')); + if(strrpos($image_uri,'-') !== false) + $image_uri = substr($image_uri,0, strrpos($image_uri,'-')); + if(strrpos($image_uri,'.') !== false) + $image_uri = substr($image_uri,0, strrpos($image_uri,'.')); if(! strlen($image_uri)) continue; $srch = '<' . $xchan_hash . '>'; diff --git a/include/oembed.php b/include/oembed.php index e5557dc11..426197c5f 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -221,7 +221,11 @@ function oembed_fetch_url($embedurl){ if(strpos(strtolower($embedurl),'.pdf') !== false) { $action = 'allow'; - $j = [ 'html' => '<object data="' . $embedurl . '" type="application/pdf" width="500" height="720">' . '<a href="' . $embedurl . '">' . t('View PDF') . '</a></object>', 'width' => 500, 'height' => 720, 'type' => 'pdf' ]; + $j = [ + 'html' => '<object data="' . $embedurl . '" type="application/pdf" style="width: 100%; height: 300px;"></object>', + 'title' => t('View PDF'), + 'type' => 'pdf' + ]; } diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index b70a13622..9aeb2ef17 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -112,7 +112,7 @@ abstract class photo_driver { public function saveImage($path) { if(!$this->is_valid()) return FALSE; - file_put_contents($path, $this->imageString()); + return (file_put_contents($path, $this->imageString()) ? true : false); } @@ -330,9 +330,9 @@ abstract class photo_driver { } - public function save($arr) { + public function save($arr, $skipcheck = false) { - if(! $this->is_valid()) { + if(! ($skipcheck || $this->is_valid())) { logger('attempt to store invalid photo.'); return false; } @@ -344,6 +344,7 @@ abstract class photo_driver { $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'] : ''); @@ -360,6 +361,7 @@ abstract class photo_driver { $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); @@ -397,17 +399,18 @@ abstract class photo_driver { allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', - deny_gid = '%s' + deny_gid = '%s', + expires = '%s' where id = %d", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), - dbesc($p['created']), - dbesc($p['edited']), + dbescdate($p['created']), + dbescdate($p['edited']), dbesc(basename($p['filename'])), - dbesc($this->getType()), + dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), @@ -424,22 +427,23 @@ abstract class photo_driver { 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 ) - 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' )", + ( 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']), - dbesc($p['created']), - dbesc($p['edited']), + dbescdate($p['created']), + dbescdate($p['edited']), dbesc(basename($p['filename'])), - dbesc($this->getType()), + dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), @@ -455,7 +459,8 @@ abstract class photo_driver { dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), - dbesc($p['deny_gid']) + dbesc($p['deny_gid']), + dbescdate($p['expires']) ); } logger('photo save ' . $p['imgscale'] . ' returned ' . intval($r)); diff --git a/include/photo/photo_imagick.php b/include/photo/photo_imagick.php index 89577e71e..f04c00245 100644 --- a/include/photo/photo_imagick.php +++ b/include/photo/photo_imagick.php @@ -31,8 +31,12 @@ class photo_imagick extends photo_driver { if(! $data) return; - $this->image->readImageBlob($data); - + try { + $this->image->readImageBlob($data); + } + catch (Exception $e) { + logger('imagick readImageBlob() exception:' . print_r($e,true)); + } /** * Setup the image to the format it will be saved to @@ -205,4 +209,4 @@ class photo_imagick extends photo_driver { -}
\ No newline at end of file +} diff --git a/include/text.php b/include/text.php index 646bfe749..bd0d8048d 100644 --- a/include/text.php +++ b/include/text.php @@ -1105,23 +1105,33 @@ function linkify($s, $me = false) { * to a local redirector which uses https and which redirects to the selected content * * @param string $s + * @param int $uid * @returns string */ function sslify($s) { + + // Local photo cache + $str = array( + 'body' => $s, + 'uid' => local_channel() + ); + call_hooks('cache_body_hook', $str); + + $s = $str['body']; + if (strpos(z_root(),'https:') === false) return $s; - + // By default we'll only sslify img tags because media files will probably choke. // You can set sslify_everything if you want - but it will likely white-screen if it hits your php memory limit. // The downside is that http: media files will likely be blocked by your browser // Complain to your browser maker $allow = get_config('system','sslify_everything'); - - $pattern = (($allow) ? "/\<(.*?)src=\"(http\:.*?)\"(.*?)\>/" : "/\<img(.*?)src=\"(http\:.*?)\"(.*?)\>/" ); + $pattern = (($allow) ? "/\<(.*?)src=[\"|'](http\:.*?)[\"|'](.*?)\>/" : "/\<img(.*?)src=[\"|'](http\:.*?)[\"|'](.*?)\>/" ); $matches = null; - $cnt = preg_match_all($pattern,$s,$matches,PREG_SET_ORDER); + $cnt = preg_match_all($pattern, $s, $matches, PREG_SET_ORDER); if ($cnt) { foreach ($matches as $match) { $filename = basename( parse_url($match[2], PHP_URL_PATH) ); @@ -3295,7 +3305,7 @@ function cleanup_bbcode($body) { $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body); - $body = scale_external_images($body,false); + $body = scale_external_images($body, false); return $body; } @@ -3411,7 +3421,7 @@ function get_forum_channels($uid) { $sql_extra = (($xf) ? " and ( xchan_hash in (" . $xf . ") or xchan_pubforum = 1 ) " : " and xchan_pubforum = 1 "); - $r = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 $sql_extra order by xchan_name", + $r = q("select abook_id, xchan_hash, xchan_name, xchan_url, xchan_addr, xchan_photo_s from abook left join xchan on abook_xchan = xchan_hash where xchan_deleted = 0 and abook_channel = %d and abook_pending = 0 and abook_ignored = 0 and abook_blocked = 0 and abook_archived = 0 $sql_extra order by xchan_name", intval($uid) ); @@ -3467,6 +3477,28 @@ function print_val($v) { } +function array_path_exists($str,$arr) { + + $ptr = $arr; + $search = explode('/', $str); + + if($search) { + foreach($search as $s) { + if(array_key_exists($s,$ptr)) { + $ptr = $ptr[$s]; + } + else { + return false; + } + } + return true; + } + + return false; + +} + + /** * @brief Generate a unique ID. * @@ -3482,3 +3514,4 @@ function new_uuid() { return $hash; } + diff --git a/install/schema_mysql.sql b/install/schema_mysql.sql index 3e3e6fe88..49a2c476a 100644 --- a/install/schema_mysql.sql +++ b/install/schema_mysql.sql @@ -947,6 +947,7 @@ CREATE TABLE IF NOT EXISTS `photo` ( `resource_id` char(191) NOT NULL DEFAULT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00', `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00', + `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00', `title` char(191) NOT NULL DEFAULT '', `description` text NOT NULL, `album` char(191) NOT NULL DEFAULT '', @@ -979,6 +980,7 @@ CREATE TABLE IF NOT EXISTS `photo` ( KEY `xchan` (`xchan`), KEY `filesize` (`filesize`), KEY `resource_id` (`resource_id`), + KEY `expires` (`expires`), KEY `is_nsfw` (`is_nsfw`), KEY `os_storage` (`os_storage`), KEY `photo_usage` (`photo_usage`) diff --git a/install/schema_postgres.sql b/install/schema_postgres.sql index 44f98b944..1b6cab51b 100644 --- a/install/schema_postgres.sql +++ b/install/schema_postgres.sql @@ -928,6 +928,7 @@ CREATE TABLE "photo" ( "resource_id" text NOT NULL, "created" timestamp NOT NULL, "edited" timestamp NOT NULL, + "expires" timestamp NOT NULL, "title" text NOT NULL, "description" text NOT NULL, "album" text NOT NULL, @@ -961,6 +962,7 @@ create index "photo_aid" on photo ("aid"); create index "photo_xchan" on photo ("xchan"); create index "photo_filesize" on photo ("filesize"); create index "photo_resource_id" on photo ("resource_id"); +create index "photo_expires_idx" on photo ("expires"); create index "photo_usage" on photo ("photo_usage"); create index "photo_is_nsfw" on photo ("is_nsfw"); create index "photo_os_storage" on photo ("os_storage"); diff --git a/view/es-es/hstrings.php b/view/es-es/hstrings.php index 67f499feb..1dd38b930 100644 --- a/view/es-es/hstrings.php +++ b/view/es-es/hstrings.php @@ -2,10 +2,10 @@ if(! function_exists("string_plural_select_es_es")) { function string_plural_select_es_es($n){ - return ($n != 1 ? 1 : 0);; + return ($n != 1 ? 1 : 0); }} App::$rtl = 0; -App::$strings["plural_function_code"] = "(n != 1 ? 1 : 0);"; +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 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."; diff --git a/view/js/main.js b/view/js/main.js index 085bc8d0d..a69bcfa64 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -544,7 +544,7 @@ function handleNotificationsItems(notifyType, data) { notify_menu.html(''); $(data).each(function() { - html = notifications_tpl.format(this.notify_link,this.photo,this.name,this.message,this.when,this.hclass,this.b64mid,this.notify_id,this.thread_top,this.unseen,this.private_forum); + html = notifications_tpl.format(this.notify_link,this.photo,this.name,this.addr,this.message,this.when,this.hclass,this.b64mid,this.notify_id,this.thread_top,this.unseen,this.private_forum); notify_menu.append(html); }); @@ -558,7 +558,8 @@ function handleNotificationsItems(notifyType, data) { if(filter) { $('#nav-' + notifyType + '-menu .notification').each(function(i, el){ var cn = $(el).data('contact_name').toString().toLowerCase(); - if(cn.indexOf(filter) === -1) + var ca = $(el).data('contact_addr').toString().toLowerCase(); + if(cn.indexOf(filter) === -1 && ca.indexOf(filter) === -1) $(el).addClass('d-none'); else $(el).removeClass('d-none'); diff --git a/view/theme/redbasic/js/redbasic.js b/view/theme/redbasic/js/redbasic.js index 688e92148..8d3b795cc 100644 --- a/view/theme/redbasic/js/redbasic.js +++ b/view/theme/redbasic/js/redbasic.js @@ -121,9 +121,9 @@ function toggleAside() { $('main').addClass('region_1-on') $('<div id="overlay"></div>').appendTo('section'); $('#left_aside_wrapper').stick_in_parent({ - offset_top: $('nav').outerHeight(true) + 10, - parent: '#region_1', - spacer: '#left_aside_spacer' + offset_top: parseInt($('aside').css('padding-top')), + parent: 'main', + spacer: '.aside_spacer' }); } } diff --git a/view/tpl/notifications_widget.tpl b/view/tpl/notifications_widget.tpl index f43d82301..98047f1d9 100644 --- a/view/tpl/notifications_widget.tpl +++ b/view/tpl/notifications_widget.tpl @@ -105,8 +105,9 @@ $("#nav-{{$notification.type}}-menu .notification").each(function(i, el){ var cn = $(el).data('contact_name').toString().toLowerCase(); + var ca = $(el).data('contact_addr').toString().toLowerCase(); - if(cn.indexOf(val) === -1) + if(cn.indexOf(val) === -1 && ca.indexOf(val) === -1) $(this).addClass('d-none'); else $(this).removeClass('d-none'); @@ -134,18 +135,18 @@ {{$no_notifications}}<span class="jumping-dots"><span class="dot-1">.</span><span class="dot-2">.</span><span class="dot-3">.</span></span> </div> <div id="nav-notifications-template" rel="template"> - <a class="list-group-item clearfix notification {5}" href="{0}" title="{2}" data-b64mid="{6}" data-notify_id="{7}" data-thread_top="{8}" data-contact_name="{2}"> + <a class="list-group-item clearfix notification {6}" href="{0}" title="{3}" data-b64mid="{7}" data-notify_id="{8}" data-thread_top="{9}" data-contact_name="{2}" data-contact_addr="{3}"> <img class="menu-img-3" data-src="{1}"> <span class="contactname">{2}</span> - <span class="dropdown-sub-text">{3}<br>{4}</span> + <span class="dropdown-sub-text">{4}<br>{5}</span> </a> </div> <div id="nav-notifications-forums-template" rel="template"> - <a class="list-group-item clearfix notification notification-forum" href="{0}" title="{3}" data-b64mid="{6}" data-notify_id="{7}" data-thread_top="{8}" data-contact_name="{2}"> - <span class="float-right badge badge-{{$notification.severity}}">{9}</span> + <a class="list-group-item clearfix notification notification-forum" href="{0}" title="{4} - {3}" data-b64mid="{7}" data-notify_id="{8}" data-thread_top="{9}" data-contact_name="{2}" data-contact_addr="{3}"> + <span class="float-right badge badge-{{$notification.severity}}">{10}</span> <img class="menu-img-1" data-src="{1}"> <span class="">{2}</span> - <i class="fa fa-{10} text-muted"></i> + <i class="fa fa-{11} text-muted"></i> </a> </div> <div id="notifications" class="navbar-nav"> |