diff options
author | Mario <mario@mariovavti.com> | 2017-10-25 13:29:19 +0200 |
---|---|---|
committer | Mario <mario@mariovavti.com> | 2017-10-25 13:29:19 +0200 |
commit | 344aa13c64f0fa7a246f3284bf9cdb267e6101d3 (patch) | |
tree | 943809c01164dda7b6e4a26bc3d6c7e8c48bd6eb /Zotlabs/Lib | |
parent | 61c86212b944efa0d78dcc0364b81bfb8a0d19bc (diff) | |
parent | 69b22e3f7916b5ba19b5ed03a55ad72bf5995291 (diff) | |
download | volse-hubzilla-344aa13c64f0fa7a246f3284bf9cdb267e6101d3.tar.gz volse-hubzilla-344aa13c64f0fa7a246f3284bf9cdb267e6101d3.tar.bz2 volse-hubzilla-344aa13c64f0fa7a246f3284bf9cdb267e6101d3.zip |
Merge branch '2.8RC'
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r-- | Zotlabs/Lib/ActivityStreams.php | 199 | ||||
-rw-r--r-- | Zotlabs/Lib/ActivityStreams2.php | 86 | ||||
-rw-r--r-- | Zotlabs/Lib/Apps.php | 41 | ||||
-rw-r--r-- | Zotlabs/Lib/Enotify.php | 6 | ||||
-rw-r--r-- | Zotlabs/Lib/JSalmon.php | 38 | ||||
-rw-r--r-- | Zotlabs/Lib/LDSignatures.php | 135 | ||||
-rw-r--r-- | Zotlabs/Lib/MarkdownSoap.php | 2 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWiki.php | 84 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWikiPage.php | 36 | ||||
-rw-r--r-- | Zotlabs/Lib/PConfig.php | 9 | ||||
-rw-r--r-- | Zotlabs/Lib/SConfig.php | 25 | ||||
-rw-r--r-- | Zotlabs/Lib/System.php | 7 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 32 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadStream.php | 15 |
14 files changed, 592 insertions, 123 deletions
diff --git a/Zotlabs/Lib/ActivityStreams.php b/Zotlabs/Lib/ActivityStreams.php new file mode 100644 index 000000000..379e78a59 --- /dev/null +++ b/Zotlabs/Lib/ActivityStreams.php @@ -0,0 +1,199 @@ +<?php + +namespace Zotlabs\Lib; + +class ActivityStreams { + + public $data; + public $valid = false; + public $id = ''; + public $type = ''; + public $actor = null; + public $obj = null; + public $tgt = null; + public $origin = null; + public $owner = null; + public $signer = null; + public $ldsig = null; + public $sigok = false; + public $recips = null; + public $raw_recips = null; + + function __construct($string) { + + $this->data = json_decode($string,true); + if($this->data) { + $this->valid = true; + } + + if($this->is_valid()) { + $this->id = $this->get_property_obj('id'); + $this->type = $this->get_primary_type(); + $this->actor = $this->get_compound_property('actor'); + $this->obj = $this->get_compound_property('object'); + $this->tgt = $this->get_compound_property('target'); + $this->origin = $this->get_compound_property('origin'); + $this->recips = $this->collect_recips(); + + $this->ldsig = $this->get_compound_property('signature'); + if($this->ldsig) { + $this->signer = $this->get_compound_property('creator',$this->ldsig); + if($this->signer && $this->signer['publicKey'] && $this->signer['publicKey']['publicKeyPem']) { + $this->sigok = \Zotlabs\Lib\LDSignatures::verify($this->data,$this->signer['publicKey']['publicKeyPem']); + } + } + + if(($this->type === 'Note') && (! $this->obj)) { + $this->obj = $this->data; + $this->type = 'Create'; + } + } + } + + function is_valid() { + return $this->valid; + } + + function set_recips($arr) { + $this->saved_recips = $arr; + } + + function collect_recips($base = '',$namespace = '') { + $x = []; + $fields = [ 'to','cc','bto','bcc','audience']; + foreach($fields as $f) { + $y = $this->get_compound_property($f,$base,$namespace); + if($y) { + $x = array_merge($x,$y); + if(! is_array($this->raw_recips)) + $this->raw_recips = []; + $this->raw_recips[$f] = $x; + } + } +// not yet ready for prime time +// $x = $this->expand($x,$base,$namespace); + return $x; + } + + function expand($arr,$base = '',$namespace = '') { + $ret = []; + + // right now use a hardwired recursion depth of 5 + + for($z = 0; $z < 5; $z ++) { + if(is_array($arr) && $arr) { + foreach($arr as $a) { + if(is_array($a)) { + $ret[] = $a; + } + else { + $x = $this->get_compound_property($a,$base,$namespace); + if($x) { + $ret = array_merge($ret,$x); + } + } + } + } + } + + // @fixme de-duplicate + + return $ret; + } + + function get_namespace($base,$namespace) { + + if(! $namespace) + return ''; + + $key = null; + + + foreach( [ $this->data, $base ] as $b ) { + if(! $b) + continue; + if(array_key_exists('@context',$b)) { + if(is_array($b['@context'])) { + foreach($b['@context'] as $ns) { + if(is_array($ns)) { + foreach($ns as $k => $v) { + if($namespace === $v) + $key = $k; + } + } + else { + if($namespace === $ns) { + $key = ''; + } + } + } + } + else { + if($namespace === $b['@context']) { + $key = ''; + } + } + } + } + return $key; + } + + + function get_property_obj($property,$base = '',$namespace = '' ) { + $prefix = $this->get_namespace($base,$namespace); + if($prefix === null) + return null; + $base = (($base) ? $base : $this->data); + $propname = (($prefix) ? $prefix . ':' : '') . $property; + return ((array_key_exists($propname,$base)) ? $base[$propname] : null); + } + + function fetch_property($url) { + $redirects = 0; + if(! check_siteallowed($url)) { + logger('blacklisted: ' . $url); + return null; + } + + $x = z_fetch_url($url,true,$redirects, + ['headers' => [ 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json' ]]); + if($x['success']) + return json_decode($x['body'],true); + return null; + } + + function get_compound_property($property,$base = '',$namespace = '') { + $x = $this->get_property_obj($property,$base,$namespace); + if($this->is_url($x)) { + $x = $this->fetch_property($x); + } + return $x; + } + + function is_url($url) { + if(($url) && (! is_array($url)) && (strpos($url,'http') === 0)) { + return true; + } + return false; + } + + function get_primary_type($base = '',$namespace = '') { + if(! $base) + $base = $this->data; + $x = $this->get_property_obj('type',$base,$namespace); + if(is_array($x)) { + foreach($x as $y) { + if(strpos($y,':') === false) { + return $y; + } + } + } + return $x; + } + + function debug() { + $x = var_export($this,true); + return $x; + } + +}
\ No newline at end of file diff --git a/Zotlabs/Lib/ActivityStreams2.php b/Zotlabs/Lib/ActivityStreams2.php deleted file mode 100644 index 904782bf7..000000000 --- a/Zotlabs/Lib/ActivityStreams2.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -namespace Zotlabs\Lib; - - -class ActivityStreams2 { - - public $data; - public $valid = false; - public $id = ''; - public $type = ''; - public $actor = null; - public $obj = null; - public $tgt = null; - - function __construct($string) { - - $this->data = json_decode($string,true); - if($this->data) { - $this->valid = true; - } - - if($this->is_valid()) { - $this->id = $this->get_property_obj('id'); - $this->type = $this->get_primary_type(); - $this->actor = $this->get_compound_property('actor'); - $this->obj = $this->get_compound_property('object'); - $this->tgt = $this->get_compound_property('target'); - } - } - - function is_valid() { - return $this->valid; - } - - function get_property_obj($property,$base = '') { - if(! $base) { - $base = $this->data; - } - return $base[$property]; - } - - function fetch_property($url) { - $redirects = 0; - $x = z_fetch_url($url,true,$redirects, - ['headers' => [ 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"']]); - if($x['success']) - return json_decode($x['body'],true); - return null; - } - - function get_compound_property($property,$base = '') { - $x = $this->get_property_obj($property,$base); - if($this->is_url($x)) { - $x = $this->fetch_property($x); - } - return $x; - } - - function is_url($url) { - if(($url) && (! is_array($url)) && (strpos($url,'http') === 0)) { - return true; - } - return false; - } - - function get_primary_type($base = '') { - if(! $base) - $base = $this->data; - $x = $this->get_property_obj('type',$base); - if(is_array($x)) { - foreach($x as $y) { - if(strpos($y,':') === false) { - return $y; - } - } - } - return $x; - } - - function debug() { - $x = var_export($this,true); - return $x; - } - -}
\ No newline at end of file diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 68587df49..f13fbe362 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -169,6 +169,14 @@ class Apps { $requires = explode(',',$ret['requires']); foreach($requires as $require) { $require = trim(strtolower($require)); + $config = false; + + if(substr($require, 0, 7) == 'config:') { + $config = true; + $require = ltrim($require, 'config:'); + $require = explode('=', $require); + } + switch($require) { case 'nologin': if(local_channel()) @@ -191,10 +199,13 @@ class Apps { unset($ret); break; default: - if(! (local_channel() && feature_enabled(local_channel(),$require))) + if($config) + $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); + else + $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); + if($unset) unset($ret); break; - } } } @@ -210,7 +221,8 @@ class Apps { static public function translate_system_apps(&$arr) { $apps = array( 'Apps' => t('Apps'), - 'Site Admin' => t('Site Admin'), + 'Cards' => t('Cards'), + 'Admin' => t('Site Admin'), 'Report Bug' => t('Report Bug'), 'View Bookmarks' => t('View Bookmarks'), 'My Chatrooms' => t('My Chatrooms'), @@ -305,8 +317,17 @@ class Apps { if($k === 'requires') { $requires = explode(',',$v); + foreach($requires as $require) { $require = trim(strtolower($require)); + $config = false; + + if(substr($require, 0, 7) == 'config:') { + $config = true; + $require = ltrim($require, 'config:'); + $require = explode('=', $require); + } + switch($require) { case 'nologin': if(local_channel()) @@ -330,10 +351,13 @@ class Apps { return ''; break; default: - if(! (local_channel() && feature_enabled(local_channel(),$require))) + if($config) + $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); + else + $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); + if($unset) return ''; break; - } } } @@ -359,6 +383,13 @@ class Apps { $install_action = (($installed) ? t('Update') : t('Install')); $icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : ''); + if($mode === 'navbar') { + return replace_macros(get_markup_template('app_nav.tpl'),array( + '$app' => $papp, + '$icon' => $icon, + )); + } + return replace_macros(get_markup_template('app.tpl'),array( '$app' => $papp, '$icon' => $icon, diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 9f3347d19..e82c11a35 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -130,7 +130,9 @@ class Enotify { if ($params['type'] == NOTIFY_COMMENT) { // logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); - $itemlink = $params['link']; + $moderated = (($params['item']['item_blocked'] == ITEM_MODERATED) ? true : false); + + $itemlink = $params['link']; // ignore like/unlike activity on posts - they probably require a separate notification preference @@ -170,8 +172,6 @@ class Enotify { xchan_query($p); - $moderated = (($p[0]['item_blocked'] == ITEM_MODERATED) ? true : false); - $item_post_type = item_post_type($p[0]); // $private = $p[0]['item_private']; $parent_id = $p[0]['id']; diff --git a/Zotlabs/Lib/JSalmon.php b/Zotlabs/Lib/JSalmon.php new file mode 100644 index 000000000..43d5f9d09 --- /dev/null +++ b/Zotlabs/Lib/JSalmon.php @@ -0,0 +1,38 @@ +<?php + +namespace Zotlabs\Lib; + + +class JSalmon { + + static function sign($data,$key_id,$key) { + + $arr = $data; + $data = json_encode($data,JSON_UNESCAPED_SLASHES); + $data = base64url_encode($data, false); // do not strip padding + $data_type = 'application/x-zot+json'; + $encoding = 'base64url'; + $algorithm = 'RSA-SHA256'; + + $data = preg_replace('/\s+/','',$data); + + // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods + + $precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; + + $signature = base64url_encode(rsa_sign($data . $precomputed, $key), false); + + return ([ + 'signed' => true, + 'data' => $data, + 'data_type' => $data_type, + 'encoding' => $encoding, + 'alg' => $algorithm, + 'sigs' => [ + 'value' => $signature, + 'key_id' => base64url_encode($key_id) + ] + ]); + + } +}
\ No newline at end of file diff --git a/Zotlabs/Lib/LDSignatures.php b/Zotlabs/Lib/LDSignatures.php new file mode 100644 index 000000000..6d7127cde --- /dev/null +++ b/Zotlabs/Lib/LDSignatures.php @@ -0,0 +1,135 @@ +<?php + +namespace Zotlabs\Lib; + +require_once('library/jsonld/jsonld.php'); + +class LDSignatures { + + + static function verify($data,$pubkey) { + + $ohash = self::hash(self::signable_options($data['signature'])); + $dhash = self::hash(self::signable_data($data)); + + $x = rsa_verify($ohash . $dhash,base64_decode($data['signature']['signatureValue']), $pubkey); + logger('LD-verify: ' . intval($x)); + + return $x; + } + + static function dopplesign(&$data,$channel) { + // remove for the time being - performance issues + // $data['magicEnv'] = self::salmon_sign($data,$channel); + return self::sign($data,$channel); + } + + static function sign($data,$channel) { + + $options = [ + 'type' => 'RsaSignature2017', + 'nonce' => random_string(64), + 'creator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem', + 'created' => datetime_convert('UTC','UTC', 'now', 'Y-m-d\Th:i:s\Z') + ]; + + $ohash = self::hash(self::signable_options($options)); + $dhash = self::hash(self::signable_data($data)); + $options['signatureValue'] = base64_encode(rsa_sign($ohash . $dhash,$channel['channel_prvkey'])); + + $signed = array_merge([ + '@context' => [ + ACTIVITYSTREAMS_JSONLD_REV, + 'https://w3id.org/security/v1' ], + ],$options); + + return $signed; + } + + + static function signable_data($data) { + + $newdata = []; + if($data) { + foreach($data as $k => $v) { + if(! in_array($k,[ 'signature' ])) { + $newdata[$k] = $v; + } + } + } + return json_encode($newdata,JSON_UNESCAPED_SLASHES); + } + + + static function signable_options($options) { + + $newopts = [ '@context' => 'https://w3id.org/identity/v1' ]; + if($options) { + foreach($options as $k => $v) { + if(! in_array($k,[ 'type','id','signatureValue' ])) { + $newopts[$k] = $v; + } + } + } + return json_encode($newopts,JSON_UNESCAPED_SLASHES); + } + + static function hash($obj) { + + return hash('sha256',self::normalise($obj)); + } + + static function normalise($data) { + if(is_string($data)) { + $data = json_decode($data); + } + + if(! is_object($data)) + return ''; + + jsonld_set_document_loader('jsonld_document_loader'); + + try { + $d = jsonld_normalize($data,[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]); + } + catch (\Exception $e) { + logger('normalise error:' . print_r($e,true)); + logger('normalise error: ' . print_r($data,true)); + } + + return $d; + } + + static function salmon_sign($data,$channel) { + + $arr = $data; + $data = json_encode($data,JSON_UNESCAPED_SLASHES); + $data = base64url_encode($data, false); // do not strip padding + $data_type = 'application/activity+json'; + $encoding = 'base64url'; + $algorithm = 'RSA-SHA256'; + $keyhash = base64url_encode(z_root() . '/channel/' . $channel['channel_address']); + + $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$data); + + // precomputed base64url encoding of data_type, encoding, algorithm concatenated with periods + + $precomputed = '.' . base64url_encode($data_type,false) . '.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; + + $signature = base64url_encode(rsa_sign($data . $precomputed,$channel['channel_prvkey'])); + + return ([ + 'id' => $arr['id'], + 'meData' => $data, + 'meDataType' => $data_type, + 'meEncoding' => $encoding, + 'meAlgorithm' => $algorithm, + 'meCreator' => z_root() . '/channel/' . $channel['channel_address'] . '/public_key_pem', + 'meSignatureValue' => $signature + ]); + + } + + + +}
\ No newline at end of file diff --git a/Zotlabs/Lib/MarkdownSoap.php b/Zotlabs/Lib/MarkdownSoap.php index 534ad819f..fa279b07c 100644 --- a/Zotlabs/Lib/MarkdownSoap.php +++ b/Zotlabs/Lib/MarkdownSoap.php @@ -94,7 +94,7 @@ class MarkdownSoap { } function escape($s) { - return htmlspecialchars($s,ENT_QUOTES); + return htmlspecialchars($s,ENT_QUOTES,'UTF-8',false); } static public function unescape($s) { diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php index 4301feaa0..7642dbb3e 100644 --- a/Zotlabs/Lib/NativeWiki.php +++ b/Zotlabs/Lib/NativeWiki.php @@ -18,11 +18,18 @@ class NativeWiki { if($wikis) { foreach($wikis as &$w) { + + $w['json_allow_cid'] = acl2json($w['allow_cid']); + $w['json_allow_gid'] = acl2json($w['allow_gid']); + $w['json_deny_cid'] = acl2json($w['deny_cid']); + $w['json_deny_gid'] = acl2json($w['deny_gid']); + $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); $w['htmlName'] = escape_tags($w['rawName']); $w['urlName'] = urlencode(urlencode($w['rawName'])); $w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType'); - $w['lock'] = (($w['item_private'] || $w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? true : false); + $w['typelock'] = get_iconfig($w, 'wiki', 'typelock'); + $w['lockstate'] = (($w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? 'lock' : 'unlock'); } } // TODO: query db for wikis the observer can access. Return with two lists, for read and write access @@ -84,7 +91,9 @@ class NativeWiki { if(! set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) { return array('item' => null, 'success' => false); } - + + set_iconfig($arr,'wiki','typelock',$wiki['typelock'],true); + $post = item_store($arr); $item_id = $post['item_id']; @@ -98,6 +107,61 @@ class NativeWiki { } } + function update_wiki($channel_id, $observer_hash, $arr, $acl) { + + $w = self::get_wiki($channel_id, $observer_hash, $arr['resource_id']); + $item = $w['wiki']; + + if(! $item) { + return array('item' => null, 'success' => false); + } + + $x = $acl->get(); + + $item['allow_cid'] = $x['allow_cid']; + $item['allow_gid'] = $x['allow_gid']; + $item['deny_cid'] = $x['deny_cid']; + $item['deny_gid'] = $x['deny_gid']; + $item['item_private'] = intval($acl->is_private()); + + $update_title = false; + + if($item['title'] !== $arr['updateRawName']) { + $update_title = true; + $item['title'] = $arr['updateRawName']; + } + + $update = item_store_update($item); + + $item_id = $update['item_id']; + + // update acl for any existing wiki pages + + q("update item set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s', item_private = %d where resource_type = 'nwikipage' and resource_id = '%s'", + dbesc($item['allow_cid']), + dbesc($item['allow_gid']), + dbesc($item['deny_cid']), + dbesc($item['deny_gid']), + dbesc($item['item_private']), + dbesc($arr['resource_id']) + ); + + + if($update['item_id']) { + info( t('Wiki updated successfully')); + if($update_title) { + // Update the wiki name information using iconfig. + if(! set_iconfig($update['item_id'], 'wiki', 'rawName', $arr['updateRawName'], true)) { + return array('item' => null, 'success' => false); + } + } + return array('item' => $update['item'], 'item_id' => $update['item_id'], 'success' => $update['success']); + } + else { + return array('item' => null, 'success' => false); + } + } + static public function sync_a_wiki_item($uid,$id,$resource_id) { @@ -108,6 +172,12 @@ class NativeWiki { dbesc($resource_id) ); if($r) { + $q = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s'", + dbesc($r[0]['resource_type']) + ); + if($q) { + $r = array_merge($r,$q); + } xchan_query($r); $sync_item = fetch_post_tags($r); build_sync_packet($uid,array('wiki' => array(encode_item($sync_item[0],true)))); @@ -150,13 +220,15 @@ class NativeWiki { // Get wiki metadata $rawName = get_iconfig($w, 'wiki', 'rawName'); $mimeType = get_iconfig($w, 'wiki', 'mimeType'); + $typelock = get_iconfig($w, 'wiki', 'typelock'); return array( - 'wiki' => $w, - 'rawName' => $rawName, + 'wiki' => $w, + 'rawName' => $rawName, 'htmlName' => escape_tags($rawName), - 'urlName' => urlencode(urlencode($rawName)), - 'mimeType' => $mimeType + 'urlName' => urlencode(urlencode($rawName)), + 'mimeType' => $mimeType, + 'typelock' => $typelock ); } } diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index 78b54ebda..209a5ef3c 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -21,7 +21,7 @@ class NativeWikiPage { $sql_extra = item_permissions_sql($channel_id,$observer_hash); $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0 - $sql_extra order by created asc", + $sql_extra order by title asc", dbesc($resource_id), intval($channel_id) ); @@ -55,7 +55,12 @@ class NativeWikiPage { } - static public function create_page($channel_id, $observer_hash, $name, $resource_id) { + static public function create_page($channel_id, $observer_hash, $name, $resource_id, $mimetype = 'text/bbcode') { + + logger('mimetype: ' . $mimetype); + + if(! in_array($mimetype,[ 'text/markdown','text/bbcode','text/plain','text/html' ])) + $mimetype = 'text/markdown'; $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); @@ -68,6 +73,8 @@ class NativeWikiPage { $arr = []; $arr['uid'] = $channel_id; $arr['author_xchan'] = $observer_hash; + $arr['mimetype'] = $mimetype; + $arr['title'] = $name; $arr['resource_type'] = 'nwikipage'; $arr['resource_id'] = $resource_id; $arr['allow_cid'] = $w['wiki']['allow_cid']; @@ -133,8 +140,14 @@ class NativeWikiPage { if($ic) { foreach($ic as $c) { set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName); + $ids[] = $c['item_id']; } + $str_ids = implode(',', $ids); + q("update item set title = '%s' where id in ($str_ids)", + dbesc($pageNewName) + ); + $page = [ 'rawName' => $pageNewName, 'htmlName' => escape_tags($pageNewName), @@ -167,10 +180,11 @@ class NativeWikiPage { $content = $item['body']; return [ - 'content' => $content, - 'mimeType' => $w['mimeType'], - 'message' => '', - 'success' => true + 'content' => $content, + 'mimeType' => $w['mimeType'], + 'pageMimeType' => $item['mimetype'], + 'message' => '', + 'success' => true ]; } @@ -333,7 +347,6 @@ class NativeWikiPage { return array('message' => t('Error reading wiki'), 'success' => false); } - $mimetype = $w['mimeType']; // fetch the most recently saved revision. @@ -342,6 +355,8 @@ class NativeWikiPage { return array('message' => t('Page not found'), 'success' => false); } + $mimetype = $item['mimetype']; + // change just the fields we need to change to create a revision; unset($item['id']); @@ -599,10 +614,13 @@ class NativeWikiPage { } static public function get_file_ext($arr) { - if($arr['mimeType'] == 'text/bbcode') + if($arr['mimetype'] === 'text/bbcode') return '.bb'; - else + elseif($arr['mimetype'] === 'text/markdown') return '.md'; + elseif($arr['mimetype'] === 'text/plain') + return '.txt'; + } // This function is derived from diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 25478e764..2a0b18aac 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -20,11 +20,12 @@ class PConfig { if(is_null($uid) || $uid === false) return false; - if(! array_key_exists($uid, \App::$config)) - \App::$config[$uid] = array(); - if(! is_array(\App::$config)) { - btlogger('App::$config not an array: ' . $uid); + btlogger('App::$config not an array'); + } + + if(! array_key_exists($uid, \App::$config)) { + \App::$config[$uid] = array(); } if(! is_array(\App::$config[$uid])) { diff --git a/Zotlabs/Lib/SConfig.php b/Zotlabs/Lib/SConfig.php new file mode 100644 index 000000000..ca0d133b2 --- /dev/null +++ b/Zotlabs/Lib/SConfig.php @@ -0,0 +1,25 @@ +<?php + +namespace Zotlabs\Lib; + +// account configuration storage is built on top of the under-utilised xconfig + +class SConfig { + + static public function Load($server_id) { + return XConfig::Load('s_' . $server_id); + } + + static public function Get($server_id,$family,$key,$default = false) { + return XConfig::Get('s_' . $server_id,$family,$key, $default); + } + + static public function Set($server_id,$family,$key,$value) { + return XConfig::Set('s_' . $server_id,$family,$key,$value); + } + + static public function Delete($server_id,$family,$key) { + return XConfig::Delete('s_' . $server_id,$family,$key); + } + +} diff --git a/Zotlabs/Lib/System.php b/Zotlabs/Lib/System.php index a5790fb07..c3e11eb6a 100644 --- a/Zotlabs/Lib/System.php +++ b/Zotlabs/Lib/System.php @@ -61,6 +61,13 @@ class System { return 'pro'; } + + static public function get_zot_revision() { + $x = [ 'revision' => ZOT_REVISION ]; + call_hooks('zot_revision',$x); + return $x['revision']; + } + static public function get_std_version() { if(defined('STD_VERSION')) return STD_VERSION; diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 17d65dbc7..67a507025 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -29,6 +29,7 @@ class ThreadItem { private $visiting = false; private $channel = null; private $display_mode = 'normal'; + private $reload = ''; public function __construct($data) { @@ -101,10 +102,13 @@ class ThreadItem { if($item['author']['xchan_network'] === 'rss') $shareable = true; + $mode = $conv->get_mode(); + $edlink = (($item['item_type'] == ITEM_TYPE_CARD) ? 'card_edit' : 'editpost'); + if(local_channel() && $observer['xchan_hash'] === $item['author_xchan']) - $edpost = array(z_root()."/editpost/".$item['id'], t("Edit")); + $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit')); else $edpost = false; @@ -309,7 +313,8 @@ class ThreadItem { $tmp_item = array( 'template' => $this->get_template(), - 'mode' => $mode, + 'mode' => $mode, + 'item_type' => intval($item['item_type']), 'type' => implode("",array_slice(explode("/",$item['verb']),-1)), 'body' => $body['html'], 'tags' => $body['tags'], @@ -407,8 +412,9 @@ class ThreadItem { 'showdislike' => $showdislike, 'comment' => $this->get_comment_box($indent), 'previewing' => ($conv->is_preview() ? true : false ), + 'preview_lbl' => t('This is an unsaved preview'), 'wait' => t('Please wait'), - 'submid' => str_replace(['+','='], ['',''], base64_encode(substr($item['mid'],0,32))), + 'submid' => str_replace(['+','='], ['',''], base64_encode($item['mid'])), 'thread_level' => $thread_level ); @@ -479,6 +485,14 @@ class ThreadItem { return $this->threaded; } + public function set_reload($val) { + $this->reload = $val; + } + + public function get_reload() { + return $this->reload; + } + public function set_commentable($val) { $this->commentable = $val; foreach($this->get_children() as $child) @@ -715,7 +729,7 @@ class ThreadItem { $comment_box = replace_macros($template,array( '$return_path' => '', '$threaded' => $this->is_threaded(), - '$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), + '$jsreload' => $conv->reload, '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), '$id' => $this->get_id(), '$parent' => $this->get_id(), @@ -733,19 +747,21 @@ class ThreadItem { '$edquote' => t('Quote'), '$edcode' => t('Code'), '$edimg' => t('Image'), + '$edatt' => t('Attach File'), '$edurl' => t('Insert Link'), '$edvideo' => t('Video'), '$preview' => t('Preview'), // ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''), '$indent' => $indent, + '$can_upload' => (perm_is_allowed($conv->get_profile_owner(),get_observer_hash(),'write_storage') && $conv->is_uploadable()), '$feature_encrypt' => ((feature_enabled($conv->get_profile_owner(),'content_encrypt')) ? true : false), '$encrypt' => t('Encrypt text'), '$cipher' => $conv->get_cipher(), '$sourceapp' => \App::$sourcename, '$observer' => get_observer_hash(), - '$anoncomments' => (($conv->get_mode() === 'channel' && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), - '$anonname' => [ 'anonname', t('Your full name (required)'),'','','','onBlur="commentCloseUI(this,\'' . $this->get_id() . '\')"' ], - '$anonmail' => [ 'anonmail', t('Your email address (required)'),'','','','onBlur="commentCloseUI(this,\'' . $this->get_id() . '\')"' ], - '$anonurl' => [ 'anonurl', t('Your website URL (optional)'),'','','','onBlur="commentCloseUI(this,\'' . $this->get_id() . '\')"' ] + '$anoncomments' => ((($conv->get_mode() === 'channel' || $conv->get_mode() === 'display') && perm_is_allowed($conv->get_profile_owner(),'','post_comments')) ? true : false), + '$anonname' => [ 'anonname', t('Your full name (required)') ], + '$anonmail' => [ 'anonmail', t('Your email address (required)') ], + '$anonurl' => [ 'anonurl', t('Your website URL (optional)') ] )); return $comment_box; diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php index 35ccf4fdb..436723f8c 100644 --- a/Zotlabs/Lib/ThreadStream.php +++ b/Zotlabs/Lib/ThreadStream.php @@ -22,15 +22,17 @@ class ThreadStream { private $profile_owner = 0; private $preview = false; private $prepared_item = ''; + public $reload = ''; private $cipher = 'aes256'; // $prepared_item is for use by alternate conversation structures such as photos // wherein we've already prepared a top level item which doesn't look anything like // a normal "post" item - public function __construct($mode, $preview, $prepared_item = '') { + public function __construct($mode, $preview, $uploadable, $prepared_item = '') { $this->set_mode($mode); $this->preview = $preview; + $this->uploadable = $uploadable; $this->prepared_item = $prepared_item; $c = ((local_channel()) ? get_pconfig(local_channel(),'system','default_cipher') : ''); if($c) @@ -56,11 +58,17 @@ class ThreadStream { $this->profile_owner = \App::$profile['profile_uid']; $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); break; + case 'cards': + $this->profile_owner = \App::$profile['profile_uid']; + $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); + $this->reload = $_SESSION['return_url']; + break; case 'display': // in this mode we set profile_owner after initialisation (from conversation()) and then // pull some trickery which allows us to re-invoke this function afterward // it's an ugly hack so @FIXME $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); + $this->uploadable = perm_is_allowed($this->profile_owner,$ob_hash,'write_storage'); break; case 'page': $this->profile_owner = \App::$profile['uid']; @@ -92,6 +100,11 @@ class ThreadStream { return $this->commentable; } + public function is_uploadable() { + return $this->uploadable; + } + + /** * Check if page is a preview */ |