diff --git a/.gitignore b/.gitignore index 050902f17..0e5233eaf 100755 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ *.rej # OSX .DS_Store files .DS_Store -# version scripts (repo master only) +# version scripts (repo master only) .version* Thumbs.db @@ -27,6 +27,7 @@ custom/ /store/ # site apps apps/ +!doc/context/*/apps # default startpage home.html # page header plugin diff --git a/CHANGELOG b/CHANGELOG index 55a35806b..647b84275 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,114 @@ +Hubzilla 2.8 (2017-10-25) + - Redirect to be moderated items to /moderate + - Update notifications if notifications area remains open + - Create an actual logout module instead of relying on internal variables + - Add local_channel as a comanche condition variable + - Implement possibility to pin app-tray apps in the navbar via app category navbar_default + - Introduce custom navbars + - Re-implement single delivery + - Pdledit usability improvements + - Implement next generation notifications in right aside + - Implement single post view for /pubstream + - Make anonymous comments work in mod display + - Introduce notifications for unseen public stream posts (off by default) + - Preperatory work on Zot VI + - Add app for site admin + - Introduce experimental alternate channel_menu navigation (off by default) + - Introduce notifications for shared files + - Bring back notifications for account approvals + - Urlencode hashes from mod_acl + - Don't use chanlink_url() for feed mentions + - Design common friends widget to fit better in the app and move it to left aside + - Allow navbar to be used when cover photo is displayed in mod channel + - Implement admin setting to use imagick converter for large photos + - Process activity deletes from OStatus which for whatever reason do not use the industry standard tombstone mechanism + - Implement new css based spinner + - Move the link header initialisation from Router to Webserver + ⁻ Extend activity_match() to work with arrays + - Updated the trusted CA cert database + - Ostatus - support likes of comments + - Provide ability to mention a forum by using !forumname as well as the traditional red style (@forumname+) + - Encrypt delivery reports (not backward compatible) + - Provide a space between link header params (draft-cavage-http-signatures-08) + - Turn common_friends into a widget + - Update to jquery-3.2.1 + - Wiki pages sorted by name + - Create new hooks for permissions_accept and permissions_reject + - Provide rel=alternate link if no reshare content in post + - Add remote login button to login page + - DB update to add index to item.resource_id + - Implement wiki editing (name and acl) + - Provide a hook for importing a channel photo at channel creation time + - Implement wiki mimetype lock + - Bring back wiki downloads + - Add text/plain mimetype to wiki + - Implement per page mimetype selection for wikis + - Added english context help for apps and appman + - Implement owa (open web auth) + - Ignore diaspora_meta column on item import + - Check code permissions on cloud files + - Remove period from characters allowed in username + - Make comment highlighting more reliable + - Sign zot-info packets with httpsignatures + - Implement server to server magic auth + - Provide support for json-ld signatures + - Rewrite comment form open/close handling to be more reliable + - Radically reduce code duplication in updateConvItems() + - Remove discover tab in favour of the public stream app + - Apply autotime to all autotime classed elements when static loading a page + - Implement cards feature + - Extended support for help page translations including table of contents files at the top level + - Introduce util/dmkdir - a mkdir tool for DAV + - Various doco improvements + - Introduce util/dcp (DAV-copy) - copy file or directory from local system to Hubzilla + - Provide support for HTTPsig + - Implement mechanism for selective network following in protocol connectors (diaspora, ostatus, activitypub, zot, rss) + + Bugfixes + - Fix w2w posts not removed in contact_remove() - github issue #837 + - Fix guests not having a unique (non-existent) url + - Fix mod register re-using the password + - Fix write_storage permission not checked in /display + - Fix discovery of moderated items in enotify + - Fix profile thing image not deleted when thing deleted - github issue #868 + - Fix deletions to comments not synced on wall posts + - Fix community tags not preserved on post edit - github issue #865 + - Fix profile photo propagation issue if the local xchan_photo_[l|m|s] fields were changed from the /photo/profile/l/n form to photo/[hash] form by a clone operation + - Fix lockstate and current permissions not handed over to editor in mod card_edit + - Fix profile edit dropdown for multiple profiles + - Fix affinity slider spinner + - Fix mod pubsites broken + - Fix directory server admin selection includes known dead sites + - Fix sticky-kit issue where the bottom of left aside was not visible when section content was short + - Fix possibility to set bogus my_address + - Fix deleting of wiki pages + - Fix selected theme not appearing selected after change - github issue #855 + - Fix an issue where some encoded mids were not found in /display + - Fix issue with mentions and xchans with @ or / + - Fix webfinger returns invalid XML - github issue #851 + - Fix last remaining task in tasklist was not removed from view when completed + + Plugins/Addon + Hubwall: Remove errant $1 string in sender name + Map federation protocols for zotinfo + Gnusoc: force ostatus profile photos to get refreshed monthly + Gnusoc: fix ostatus mention notifications + Gnusoc: unsubscribe to gnusoc feeds if connector is disabled + Phpmailer: not using load/unload + Gnusoc: don't provide some information if gnusoc is disabled by the channel + Diaspora: add a predelivery interval + Diaspora: support for likes on comments + Introduce the pubcrawl plugin - an unapologetically non-compliant ActivityPub Protocol implemention + Introduce gravatar plugin + Pubsubhubbub: produce much more compact PuSH feeds + Diaspora: support text comments on reshare posts + Diaspora: changes to delivery scenarios for the special handling of profile messages + Diaspora: put diaspora seed_location in json webfinger + Gnusoc: fix mis-attributed comments from mastodon + Gnusoc: allow discovery by url (not just reddress) and permit upgrade from 'unknown' network to gnusoc + Implement mechanism for selective network following in protocol connectors + + Hubzilla 2.6.3 (2017-09-18) - Fix anonymous comments/likes on photos - this is not yet implemented - Fix favicon not displayed on certain pages diff --git a/Zotlabs/Daemon/Deliver.php b/Zotlabs/Daemon/Deliver.php index dbc311cf5..394a7bf3e 100644 --- a/Zotlabs/Daemon/Deliver.php +++ b/Zotlabs/Daemon/Deliver.php @@ -53,6 +53,9 @@ class Deliver { remove_queue_item($r[0]['outq_hash']); if($dresult && is_array($dresult)) { + + // delivery reports for local deliveries do not require encryption + foreach($dresult as $xx) { if(is_array($xx) && array_key_exists('message_id',$xx)) { if(delivery_report_is_storable($xx)) { diff --git a/Zotlabs/Daemon/Gprobe.php b/Zotlabs/Daemon/Gprobe.php index 43cce93c3..f1ffb2d81 100644 --- a/Zotlabs/Daemon/Gprobe.php +++ b/Zotlabs/Daemon/Gprobe.php @@ -17,7 +17,7 @@ class Gprobe { if(! strpos($url,'@')) return; - $r = q("select * from xchan where xchan_addr = '%s' limit 1", + $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", dbesc($url) ); diff --git a/Zotlabs/Daemon/Importfile.php b/Zotlabs/Daemon/Importfile.php new file mode 100644 index 000000000..c68ed21cf --- /dev/null +++ b/Zotlabs/Daemon/Importfile.php @@ -0,0 +1,47 @@ + 3) ? $argv[3] : ''); + $dstname = (($argc > 4) ? $argv[4] : ''); + + $hash = random_string(); + + $arr = [ + 'src' => $srcfile, + 'filename' => (($dstname) ? $dstname : basename($srcfile)), + 'hash' => $hash, + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'], + 'preserve_original' => true, + 'replace' => true + ]; + + if($folder) + $arr['folder'] = $folder; + + attach_store($channel,$channel['channel_hash'],'import',$arr); + + $sync = attach_export_data($channel,$hash); + if($sync) + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + + return; + } +} diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 6bca22025..d0175549b 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -59,14 +59,19 @@ require_once('include/bbcode.php'); * * ZOT * permission_create abook_id + * permission_accept abook_id + * permission_reject abook_id * permission_update abook_id * refresh_all channel_id * purge_all channel_id * expire channel_id * relay item_id (item was relayed to owner, we will deliver it as owner) + * single_activity item_id (deliver to a singleton network from the appropriate clone) + * single_mail mail_id (deliver to a singleton network from the appropriate clone) * location channel_id * request channel_id xchan_hash message_id * rating xlink_id + * keychange channel_id * */ @@ -103,7 +108,7 @@ class Notifier { $normal_mode = true; $packet_type = 'undefined'; - if($cmd === 'mail') { + if($cmd === 'mail' || $cmd === 'single_mail') { $normal_mode = false; $mail = true; $private = true; @@ -144,7 +149,21 @@ class Notifier { $packet_type = 'request'; $normal_mode = false; } - elseif($cmd == 'permission_update' || $cmd == 'permission_create') { + elseif($cmd === 'keychange') { + $channel = channelx_by_n($item_id); + $r = q("select abook_xchan from abook where abook_channel = %d", + intval($item_id) + ); + if($r) { + foreach($r as $rr) { + $recipients[] = $rr['abook_xchan']; + } + } + $private = false; + $packet_type = 'keychange'; + $normal_mode = false; + } + elseif(in_array($cmd, [ 'permission_update', 'permission_reject', 'permission_accept', 'permission_create' ])) { // Get the (single) recipient $r = q("select * from abook left join xchan on abook_xchan = xchan_hash where abook_id = %d and abook_self = 0", intval($item_id) @@ -156,8 +175,12 @@ class Notifier { if($channel) { $perm_update = array('sender' => $channel, 'recipient' => $r[0], 'success' => false, 'deliveries' => ''); - if($cmd == 'permission_create') + if($cmd === 'permission_create') call_hooks('permissions_create',$perm_update); + elseif($cmd === 'permission_accept') + call_hooks('permissions_accept',$perm_update); + elseif($cmd === 'permission_reject') + call_hooks('permissions_reject',$perm_update); else call_hooks('permissions_update',$perm_update); @@ -261,7 +284,7 @@ class Notifier { $deleted_item = true; } - if(intval($target_item['item_type']) != ITEM_TYPE_POST) { + if(! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST ] )) { logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG); return; } @@ -410,7 +433,7 @@ class Notifier { $env_recips = (($private) ? array() : null); - $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); + $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . protect_sprintf(implode(',',$recipients)) . ")"); $recip_list = array(); @@ -445,7 +468,7 @@ class Notifier { 'uplink' => $uplink, 'cmd' => $cmd, 'mail' => $mail, - 'single' => false, + 'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false), 'location' => $location, 'request' => $request, 'normal_mode' => $normal_mode, @@ -477,7 +500,7 @@ class Notifier { // Now we have collected recipients (except for external mentions, FIXME) // Let's reduce this to a set of hubs; checking that the site is not dead. - $r = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . implode(',',$recipients) . ") + $r = q("select hubloc.*, site.site_crypto, site.site_flags from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . protect_sprintf(implode(',',$recipients)) . ") and hubloc_error = 0 and hubloc_deleted = 0 and ( site_dead = 0 OR site_dead is null ) " ); @@ -518,14 +541,14 @@ class Notifier { if($hub['hubloc_network'] == 'zot') { if(! in_array($hub['hubloc_sitekey'],$keys)) { - $hublist[] = $hub['hubloc_host']; + $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; $dhubs[] = $hub; $keys[] = $hub['hubloc_sitekey']; } } else { if(! in_array($hub['hubloc_url'],$urls)) { - $hublist[] = $hub['hubloc_host']; + $hublist[] = $hub['hubloc_host'] . ' ' . $hub['hubloc_network']; $dhubs[] = $hub; $urls[] = $hub['hubloc_url']; } @@ -553,7 +576,7 @@ class Notifier { 'uplink' => $uplink, 'cmd' => $cmd, 'mail' => $mail, - 'single' => false, + 'single' => (($cmd === 'single_mail' || $cmd === 'single_activity') ? true : false), 'location' => $location, 'request' => $request, 'normal_mode' => $normal_mode, @@ -572,12 +595,32 @@ class Notifier { } - $hash = random_string(); + // singleton deliveries by definition 'not got zot'. + // Single deliveries are other federated networks (plugins) and we're essentially + // delivering only to those that have this site url in their abook_instance + // and only from within a sync operation. This means if you post from a clone, + // and a connection is connected to one of your other clones; assuming that hub + // is running it will receive a sync packet. On receipt of this sync packet it + // will invoke a delivery to those connections which are connected to just that + // hub instance. + + if($cmd === 'single_mail' || $cmd === 'single_activity') { + continue; + } + + // default: zot protocol + + $hash = random_string(); $packet = null; + $pmsg = ''; if($packet_type === 'refresh' || $packet_type === 'purge') { $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } + if($packet_type === 'keychange') { + $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); + $pmsg = get_pconfig($channel['channel_id'],'system','keychange'); + } elseif($packet_type === 'request') { $env = (($hub_env && $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']]) ? $hub_env[$hub['hubloc_host'] . $hub['hubloc_sitekey']] : ''); $packet = zot_build_packet($channel,$packet_type,$env,$hub['hubloc_sitekey'],$hub['site_crypto'], @@ -591,7 +634,8 @@ class Notifier { 'account_id' => $channel['channel_account_id'], 'channel_id' => $channel['channel_id'], 'posturl' => $hub['hubloc_callback'], - 'notify' => $packet + 'notify' => $packet, + 'msg' => (($pmsg) ? json_encode($pmsg) : '') )); } else { 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 @@ +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 @@ -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 @@ + 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 @@ + '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 @@ + 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 */ diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index 83fafbdff..e164875e8 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -21,7 +21,7 @@ class Acl extends \Zotlabs\Web\Controller { function init() { - // logger('mod_acl: ' . print_r($_REQUEST,true)); + logger('mod_acl: ' . print_r($_REQUEST,true)); $start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0); $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500); @@ -33,6 +33,7 @@ class Acl extends \Zotlabs\Web\Controller { // $type = // '' => standard ACL request // 'g' => Groups only ACL request + // 'f' => forums only ACL request // 'c' => Connections only ACL request or editor (textarea) mention request // $_REQUEST['search'] contains ACL search text. @@ -56,12 +57,12 @@ class Acl extends \Zotlabs\Web\Controller { $search = $_REQUEST['query']; } - if( (! local_channel()) && (! ($type == 'x' || $type == 'c'))) + if( (! local_channel()) && (! in_array($type, [ 'x', 'c', 'f' ]))) killme(); $permitted = []; - if(in_array($type, [ 'm', 'a', 'c' ])) { + if(in_array($type, [ 'm', 'a', 'c', 'f' ])) { // These queries require permission checking. We'll create a simple array of xchan_hash for those with // the requisite permissions which we can check against. @@ -154,7 +155,7 @@ class Acl extends \Zotlabs\Web\Controller { } } - if($type == '' || $type == 'c') { + if($type == '' || $type == 'c' || $type === 'f') { $extra_channels_sql = ''; @@ -307,7 +308,7 @@ class Acl extends \Zotlabs\Web\Controller { $contacts[] = array( "photo" => $g['photo'], "name" => $g['name'], - "nick" => $g['address'], + "nick" => $g['address'] ); } } @@ -324,18 +325,24 @@ class Acl extends \Zotlabs\Web\Controller { $r = array(); if($r) { - foreach($r as $g){ + foreach($r as $g) { - // remove RSS feeds from ACLs - they are inaccessible - if(strpos($g['hash'],'/') && $type != 'a') + if(($g['network'] === 'rss') && ($type != 'a')) continue; - - if(in_array($g['hash'],$permitted) && $type == 'c' && (! $noforums)) { + + $g['hash'] = urlencode($g['hash']); + + if(! $g['nick']) { + $t = explode(' ',strtolower($g['name'])); + $g['nick'] = $t[0] . '@'; + } + + if(in_array($g['hash'],$permitted) && in_array($type, [ 'c', 'f' ]) && (! $noforums)) { $contacts[] = array( "type" => "c", "photo" => "images/twopeople.png", - "name" => $g['name'] . '+', - "id" => $g['id'] . '+', + "name" => $g['name'] . (($type === 'f') ? '' : '+'), + "id" => urlencode($g['id']) . (($type === 'f') ? '' : '+'), "xid" => $g['hash'], "link" => $g['nick'], "nick" => substr($g['nick'],0,strpos($g['nick'],'@')), @@ -344,18 +351,20 @@ class Acl extends \Zotlabs\Web\Controller { "label" => t('network') ); } - $contacts[] = array( - "type" => "c", - "photo" => $g['micro'], - "name" => $g['name'], - "id" => $g['id'], - "xid" => $g['hash'], - "link" => $g['nick'], - "nick" => (($g['nick']) ? substr($g['nick'],0,strpos($g['nick'],'@')) : t('RSS')), - "self" => (intval($g['abook_self']) ? 'abook-self' : ''), - "taggable" => '', - "label" => '', - ); + if($type !== 'f') { + $contacts[] = array( + "type" => "c", + "photo" => $g['micro'], + "name" => $g['name'], + "id" => urlencode($g['id']), + "xid" => $g['hash'], + "link" => $g['nick'], + "nick" => (($g['nick']) ? substr($g['nick'],0,strpos($g['nick'],'@')) : $g['nick']), + "self" => (intval($g['abook_self']) ? 'abook-self' : ''), + "taggable" => '', + "label" => '', + ); + } } } diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 4c5b82e78..d3d058c53 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -62,6 +62,7 @@ class Site { $feed_contacts = ((x($_POST,'feed_contacts')) ? intval($_POST['feed_contacts']) : 0); $verify_email = ((x($_POST,'verify_email')) ? 1 : 0); $techlevel_lock = ((x($_POST,'techlock')) ? intval($_POST['techlock']) : 0); + $imagick_path = ((x($_POST,'imagick_path')) ? trim($_POST['imagick_path']) : ''); $techlevel = null; if(array_key_exists('techlevel', $_POST)) @@ -82,6 +83,7 @@ class Site { set_config('system', 'reply_address', $reply_address); set_config('system', 'from_email', $from_email); set_config('system', 'from_email_name' , $from_email_name); + set_config('system', 'imagick_convert_path' , $imagick_path); set_config('system', 'techlevel_lock', $techlevel_lock); @@ -206,7 +208,7 @@ class Site { // directory server should not be set or settable unless we are a directory client if($dirmode == DIRECTORY_MODE_NORMAL) { - $x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s'", + $x = q("select site_url from site where site_flags in (%d,%d) and site_realm = '%s' and site_dead = 0", intval(DIRECTORY_MODE_SECONDARY), intval(DIRECTORY_MODE_PRIMARY), dbesc($realm) @@ -317,6 +319,7 @@ class Site { '$delivery_interval' => array('delivery_interval', t("Delivery interval"), (x(get_config('system','delivery_interval'))?get_config('system','delivery_interval'):2), t("Delay background delivery processes by this many seconds to reduce system load. Recommend: 4-5 for shared hosts, 2-3 for virtual private servers. 0-1 for large dedicated servers.")), '$delivery_batch_count' => array('delivery_batch_count', t('Deliveries per process'),(x(get_config('system','delivery_batch_count'))?get_config('system','delivery_batch_count'):1), t("Number of deliveries to attempt in a single operating system process. Adjust if necessary to tune system performance. Recommend: 1-5.")), '$poll_interval' => array('poll_interval', t("Poll interval"), (x(get_config('system','poll_interval'))?get_config('system','poll_interval'):2), t("Delay background polling processes by this many seconds to reduce system load. If 0, use delivery interval.")), + '$imagick_path' => array('imagick_path', t("Path to ImageMagick convert program"), get_config('system','imagick_convert_path'), t("If set, use this program to generate photo thumbnails for huge images ( > 4000 pixels in either dimension), otherwise memory exhaustion may occur. Example: /usr/bin/convert")), '$maxloadavg' => array('maxloadavg', t("Maximum Load Average"), ((intval(get_config('system','maxloadavg')) > 0)?get_config('system','maxloadavg'):50), t("Maximum system load before delivery and poll processes are deferred - default 50.")), '$default_expire_days' => array('default_expire_days', t('Expiration period in days for imported (grid/network) content'), intval(get_config('system','default_expire_days')), t('0 for no expiration of imported content')), '$form_security_token' => get_form_security_token("admin_site"), diff --git a/Zotlabs/Module/Ap_probe.php b/Zotlabs/Module/Ap_probe.php deleted file mode 100644 index 769cd4c4e..000000000 --- a/Zotlabs/Module/Ap_probe.php +++ /dev/null @@ -1,38 +0,0 @@ -ActivityPub Probe Diagnostic'; - - $o .= '
'; - $o .= 'Lookup URI:
'; - $o .= 'Request Signed version:
'; - $o .= '
'; - - $o .= '

'; - - if(x($_GET,'addr')) { - $addr = $_GET['addr']; - - if($_GET['magenv']) { - $headers = 'Accept: application/magic-envelope+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; - } - else { - $headers = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"'; - } - - $redirects = 0; - $x = z_fetch_url($addr,true,$redirects, [ 'headers' => [ $headers ]]); - if($x['success']) - $o .= '
' . str_replace(['\\n','\\'],["\n",''],jindent($x['body'])) . '
'; - } - return $o; - } - -} diff --git a/Zotlabs/Module/Apporder.php b/Zotlabs/Module/Apporder.php index 1097a01eb..956548d1f 100644 --- a/Zotlabs/Module/Apporder.php +++ b/Zotlabs/Module/Apporder.php @@ -11,30 +11,35 @@ class Apporder extends \Zotlabs\Web\Controller { } function get() { - $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), false, 'nav_featured_app'); - if($list) { - foreach($list as $li) { - $syslist[] = Zlib\Apps::app_encode($li); - } - } - Zlib\Apps::translate_system_apps($syslist); - usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); + if(! local_channel()) + return; - $syslist = Zlib\Apps::app_order(local_channel(),$syslist); + nav_set_selected('Order Apps'); - foreach($syslist as $app) { - $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); + $syslist = array(); + $list = Zlib\Apps::app_list(local_channel(), false, 'nav_featured_app'); + if($list) { + foreach($list as $li) { + $syslist[] = Zlib\Apps::app_encode($li); + } + } + Zlib\Apps::translate_system_apps($syslist); + usort($syslist,'Zotlabs\\Lib\\Apps::app_name_compare'); + + $syslist = Zlib\Apps::app_order(local_channel(),$syslist); + + foreach($syslist as $app) { + $nav_apps[] = Zlib\Apps::app_render($app,'nav-order'); } - return replace_macros(get_markup_template('apporder.tpl'), - [ - '$header' => t('Change Order of Navigation Apps'), - '$desc' => t('Use arrows to move the corresponding app up or down in the display list'), - '$nav_apps' => $nav_apps - ] - ); + return replace_macros(get_markup_template('apporder.tpl'), + [ + '$header' => t('Change Order of Navigation Apps'), + '$desc' => t('Use arrows to move the corresponding app up or down in the display list'), + '$nav_apps' => $nav_apps + ] + ); } } diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 261615997..2f61f2932 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -7,6 +7,8 @@ use \Zotlabs\Lib as Zlib; class Apps extends \Zotlabs\Web\Controller { function get() { + + nav_set_selected('Apps'); if(argc() == 2 && argv(1) == 'edit') $mode = 'edit'; diff --git a/Zotlabs/Module/Attach.php b/Zotlabs/Module/Attach.php index 94f46978a..490d5edd0 100644 --- a/Zotlabs/Module/Attach.php +++ b/Zotlabs/Module/Attach.php @@ -31,7 +31,7 @@ class Attach extends \Zotlabs\Web\Controller { $unsafe_types = array('text/html','text/css','application/javascript'); - if(in_array($r['data']['filetype'],$unsafe_types)) { + if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($r['data']['uid']))) { header('Content-type: text/plain'); } else { diff --git a/Zotlabs/Module/Bookmarks.php b/Zotlabs/Module/Bookmarks.php index e62f5ce96..e147ffe6c 100644 --- a/Zotlabs/Module/Bookmarks.php +++ b/Zotlabs/Module/Bookmarks.php @@ -8,7 +8,7 @@ class Bookmarks extends \Zotlabs\Web\Controller { if(! local_channel()) return; - nav_set_selected(t('View Bookmarks')); + nav_set_selected('View Bookmarks'); $item_id = intval($_REQUEST['item']); $burl = trim($_REQUEST['burl']); diff --git a/Zotlabs/Module/Card_edit.php b/Zotlabs/Module/Card_edit.php new file mode 100644 index 000000000..7cc563fd2 --- /dev/null +++ b/Zotlabs/Module/Card_edit.php @@ -0,0 +1,138 @@ + 1) ? intval(argv(1)) : 0); + + if(! $post_id) { + notice( t('Item not found') . EOL); + return; + } + + $itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1", + intval($post_id), + intval(ITEM_TYPE_CARD) + ); + if($itm) { + $item_id = q("select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1", + intval($itm[0]['id']) + ); + if($item_id) + $card_title = $item_id[0]['v']; + } + else { + notice( t('Item not found') . EOL); + return; + } + + $owner = $itm[0]['uid']; + $uid = local_channel(); + + $observer = \App::get_observer(); + + $channel = channelx_by_n($owner); + if(! $channel) { + notice( t('Channel not found.') . EOL); + return; + } + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'write_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = (($uid && $uid == $owner) ? true : false); + + $o = ''; + + + + $category = ''; + $catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : ''); + + if ($catsenabled){ + $itm = fetch_post_tags($itm); + + $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY); + + foreach ($cats as $cat) { + if (strlen($category)) + $category .= ', '; + $category .= $cat['term']; + } + } + + if($itm[0]['attach']) { + $j = json_decode($itm[0]['attach'],true); + if($j) { + foreach($j as $jj) { + $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n"; + } + } + } + + + $mimetype = $itm[0]['mimetype']; + + $content = $itm[0]['body']; + + + + $rp = 'cards/' . $channel['channel_address']; + + $x = array( + 'nickname' => $channel['channel_address'], + 'bbco_autocomplete'=> 'bbcode', + 'return_path' => $rp, + 'webpage' => ITEM_TYPE_CARD, + 'button' => t('Edit'), + 'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'), + 'weblink' => t('Insert web link'), + 'hide_voting' => false, + 'hide_future' => false, + 'hide_location' => false, + 'hide_expire' => false, + 'showacl' => true, + 'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')), + 'permissions' => $itm[0], + 'lockstate' => (($itm[0]['allow_cid'] || $itm[0]['allow_gid'] || $itm[0]['deny_cid'] || $itm[0]['deny_gid']) ? 'lock' : 'unlock'), + 'ptyp' => $itm[0]['type'], + 'mimeselect' => false, + 'mimetype' => $itm[0]['mimetype'], + 'body' => undo_post_tagging($content), + 'post_id' => $post_id, + 'visitor' => true, + 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'), + 'placeholdertitle' => t('Title (optional)'), + 'pagetitle' => $card_title, + 'profile_uid' => (intval($channel['channel_id'])), + 'catsenabled' => $catsenabled, + 'category' => $category, + 'bbcode' => (($mimetype == 'text/bbcode') ? true : false) + ); + + $editor = status_editor($a, $x); + + $o .= replace_macros(get_markup_template('edpost_head.tpl'), array( + '$title' => t('Edit Card'), + '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false), + '$id' => $itm[0]['id'], + '$editor' => $editor + )); + + return $o; + + } + +} diff --git a/Zotlabs/Module/Cards.php b/Zotlabs/Module/Cards.php new file mode 100644 index 000000000..22c5d673c --- /dev/null +++ b/Zotlabs/Module/Cards.php @@ -0,0 +1,187 @@ + 1) + $which = argv(1); + else + return; + + profile_load($which); + + } + + function get($update = 0, $load = false) { + + if(observer_prohibited(true)) { + return login(); + } + + if(! \App::$profile) { + notice( t('Requested profile is not available.') . EOL ); + \App::$error = 404; + return; + } + + if(! feature_enabled(\App::$profile_uid,'cards')) { + return; + } + + nav_set_selected(t('Cards')); + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); + + + $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : ''); + + if($category) { + $sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY)); + } + + + $which = argv(1); + + $selected_card = ((argc() > 2) ? argv(2) : ''); + + $_SESSION['return_url'] = \App::$query_string; + + $uid = local_channel(); + $owner = \App::$profile_uid; + $observer = \App::get_observer(); + + $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($owner,$ob_hash,'view_pages')) { + notice( t('Permission denied.') . EOL); + return; + } + + $is_owner = ($uid && $uid == $owner); + + $channel = channelx_by_n($owner); + + if($channel) { + $channel_acl = array( + 'allow_cid' => $channel['channel_allow_cid'], + 'allow_gid' => $channel['channel_allow_gid'], + 'deny_cid' => $channel['channel_deny_cid'], + 'deny_gid' => $channel['channel_deny_gid'] + ); + } + else { + $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ]; + } + + + + if(perm_is_allowed($owner,$ob_hash,'write_pages')) { + + $x = [ + 'webpage' => ITEM_TYPE_CARD, + 'is_owner' => true, + 'content_label' => t('Add Card'), + 'button' => t('Create'), + 'nickname' => $channel['channel_address'], + 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid'] + || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'), + 'acl' => (($is_owner) ? populate_acl($channel_acl, false, + \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''), + 'permissions' => $channel_acl, + 'showacl' => (($is_owner) ? true : false), + 'visitor' => true, + 'hide_location' => false, + 'hide_voting' => false, + 'profile_uid' => intval($owner), + 'mimetype' => 'text/bbcode', + 'mimeselect' => false, + 'layoutselect' => false, + 'expanded' => false, + 'novoting' => false, + 'catsenabled' => feature_enabled($owner,'categories'), + 'bbco_autocomplete' => 'bbcode', + 'bbcode' => true + ]; + + if($_REQUEST['title']) + $x['title'] = $_REQUEST['title']; + if($_REQUEST['body']) + $x['body'] = $_REQUEST['body']; + $editor = status_editor($a,$x); + + } + else { + $editor = ''; + } + + + $sql_extra = item_permissions_sql($owner); + + if($selected_card) { + $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", + dbesc($selected_card) + ); + if($r) { + $sql_extra .= "and item.id = " . intval($r[0]['iid']) . " "; + } + } + + $r = q("select * from item + where item.uid = %d and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_CARD) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; + + if($r) { + + $parents_str = ids_to_querystr($r,'id'); + + $items = q("SELECT item.*, item.id AS item_id + FROM item + WHERE item.uid = %d $item_normal + AND item.parent IN ( %s ) + $sql_extra $sql_extra2 ", + intval(\App::$profile['profile_uid']), + dbesc($parents_str) + ); + if($items) { + xchan_query($items); + $items = fetch_post_tags($items, true); + $items = conv_sort($items,'updated'); + } + else + $items = []; + } + + $mode = 'cards'; + + $content = conversation($items,$mode,false,'traditional'); + + $o = replace_macros(get_markup_template('cards.tpl'), [ + '$title' => t('Cards'), + '$editor' => $editor, + '$content' => $content, + '$pager' => alt_pager($a,count($items)) + ]); + + return $o; + } + +} diff --git a/Zotlabs/Module/Cdav.php b/Zotlabs/Module/Cdav.php index d0619ef0b..77052f97c 100644 --- a/Zotlabs/Module/Cdav.php +++ b/Zotlabs/Module/Cdav.php @@ -3,30 +3,80 @@ namespace Zotlabs\Module; require_once('include/event.php'); +require_once('include/auth.php'); +require_once('include/security.php'); + class Cdav extends \Zotlabs\Web\Controller { function init() { + $record = null; + $channel_login = false; + if((argv(1) !== 'calendar') && (argv(1) !== 'addressbook')) { - // workaround for HTTP-auth in CGI mode - if (x($_SERVER, 'REDIRECT_REMOTE_USER')) { - $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; + foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { + + /* Basic authentication */ + + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,5) === 'Basic') { + $userpass = @base64_decode(substr(trim($_SERVER[$head]),6)) ; + if(strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + break; + } + + /* Signature authentication */ + + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { + if($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } + + $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); + if($sigblock) { + $keyId = $sigblock['keyId']; + if($keyId) { + $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", + dbesc($keyId) + ); + if($r) { + $c = channelx_by_hash($r[0]['hubloc_hash']); + if($c) { + $a = q("select * from account where account_id = %d limit 1", + intval($c['channel_account_id']) + ); + if($a) { + $record = [ 'channel' => $c, 'account' => $a[0] ]; + $channel_login = $c['channel_id']; + } + } + } + if(! $record) + continue; + + if($record) { + $verified = \Zotlabs\Web\HTTPSig::verify('',$record['channel']['channel_pubkey']); + if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) { + $record = null; + } + if($record['account']) { + authenticate_success($record['account']); + if($channel_login) { + change_channel($channel_login); + } + } + break; + } + } + } } } - if (x($_SERVER, 'HTTP_AUTHORIZATION')) { - $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; - } - } /** * This server combines both CardDAV and CalDAV functionality into a single @@ -774,7 +824,7 @@ class Cdav extends \Zotlabs\Web\Controller { } if(argv(1) === 'calendar') { - nav_set_selected(t('CalDAV')); + nav_set_selected('CalDAV'); $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $calendars = $caldavBackend->getCalendarsForUser($principalUri); } @@ -975,7 +1025,7 @@ class Cdav extends \Zotlabs\Web\Controller { if(argv(1) === 'addressbook') { - nav_set_selected(t('CardDAV')); + nav_set_selected('CardDAV'); $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); } diff --git a/Zotlabs/Module/Changeaddr.php b/Zotlabs/Module/Changeaddr.php new file mode 100644 index 000000000..5cd236394 --- /dev/null +++ b/Zotlabs/Module/Changeaddr.php @@ -0,0 +1,88 @@ + NULL_DATE) { + $d1 = datetime_convert('UTC','UTC','now - 48 hours'); + if($account['account_password_changed'] > d1) { + notice( t('Channel name changes are not allowed within 48 hours of changing the account password.') . EOL); + return; + } + } + + $new_address = trim($_POST['newname']); + + if($new_address === $channel['channel_address']) + return; + + if($new_address === 'sys') { + notice( t('Reserved nickname. Please choose another.') . EOL); + return; + } + + if(check_webbie(array($new_address)) !== $new_address) { + notice( t('Nickname has unsupported characters or is already being used on this site.') . EOL); + return $ret; + } + + channel_change_address($channel,$new_address); + + goaway(z_root() . '/changeaddr'); + + } + + + function get() { + + if(! local_channel()) + goaway(z_root()); + + $channel = \App::get_channel(); + + $hash = random_string(); + + $_SESSION['remove_account_verify'] = $hash; + + $tpl = get_markup_template('channel_rename.tpl'); + $o .= replace_macros($tpl, array( + '$basedir' => z_root(), + '$hash' => $hash, + '$title' => t('Change channel nickname/address'), + '$desc' => array(t('WARNING: '), t('Any/all connections on other networks will be lost!')), + '$passwd' => t('Please enter your password for verification:'), + '$newname' => array('newname', t('New channel address'),$channel['channel_address'], ''), + '$submit' => t('Rename Channel') + )); + + return $o; + + } + +} diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php index a44984c97..14d02d873 100644 --- a/Zotlabs/Module/Channel.php +++ b/Zotlabs/Module/Channel.php @@ -92,11 +92,6 @@ class Channel extends \Zotlabs\Web\Controller { // Ensure we've got a profile owner if updating. \App::$profile['profile_uid'] = \App::$profile_uid = $update; } - else { - if(\App::$profile['profile_uid'] == local_channel()) { - nav_set_selected(t('Channel Home')); - } - } $is_owner = (((local_channel()) && (\App::$profile['profile_uid'] == local_channel())) ? true : false); @@ -119,11 +114,13 @@ class Channel extends \Zotlabs\Web\Controller { if(! $update) { + nav_set_selected('Channel Home'); + $static = channel_manual_conv_update(\App::$profile['profile_uid']); //$o .= profile_tabs($a, $is_owner, \App::$profile['channel_address']); - $o .= common_friends_visitor_widget(\App::$profile['profile_uid']); + // $o .= common_friends_visitor_widget(\App::$profile['profile_uid']); if($channel && $is_owner) { $channel_acl = array( @@ -169,6 +166,7 @@ class Channel extends \Zotlabs\Web\Controller { */ $item_normal = item_normal(); + $item_normal_update = item_normal_update(); $sql_extra = item_permissions_sql(\App::$profile['profile_uid']); if(get_pconfig(\App::$profile['profile_uid'],'system','channel_list_mode') && (! $mid)) @@ -180,7 +178,12 @@ class Channel extends \Zotlabs\Web\Controller { $simple_update = (($update) ? " AND item_unseen = 1 " : ''); - \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); if($update && $_SESSION['loadtime']) $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) "; @@ -188,12 +191,12 @@ class Channel extends \Zotlabs\Web\Controller { $simple_update = ''; if($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; if(($update) && (! $load)) { if($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal + $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal_update AND item_wall = 1 $simple_update $sql_extra limit 1", dbesc($mid . '%'), intval(\App::$profile['profile_uid']) @@ -203,7 +206,7 @@ class Channel extends \Zotlabs\Web\Controller { else { $r = q("SELECT distinct parent AS item_id, created from item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) - WHERE uid = %d $item_normal + WHERE uid = %d $item_normal_update AND item_wall = 1 $simple_update AND (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra @@ -236,7 +239,7 @@ class Channel extends \Zotlabs\Web\Controller { if($load || ($checkjs->disabled())) { if($mid) { - $r = q("SELECT parent AS item_id from item where mid like '%s' and uid = %d $item_normal + $r = q("SELECT distinct parent AS item_id from item where mid like '%s' and uid = %d $item_normal AND item_wall = 1 $sql_extra limit 1", dbesc($mid . '%'), intval(\App::$profile['profile_uid']) @@ -358,9 +361,13 @@ class Channel extends \Zotlabs\Web\Controller { } if($is_owner && $update_unseen) { - $r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen", - intval(local_channel()) - ); + $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; + call_hooks('update_unseen',$x); + if($x['update'] === 'unset' || intval($x['update'])) { + $r = q("UPDATE item SET item_unseen = 0 where item_unseen = 1 and item_wall = 1 AND uid = %d $update_unseen", + intval(local_channel()) + ); + } } diff --git a/Zotlabs/Module/Chat.php b/Zotlabs/Module/Chat.php index 138ca1cb5..378c9f4dd 100644 --- a/Zotlabs/Module/Chat.php +++ b/Zotlabs/Module/Chat.php @@ -91,7 +91,7 @@ class Chat extends \Zotlabs\Web\Controller { if(local_channel()) { $channel = \App::get_channel(); - nav_set_selected(t('My Chatrooms')); + nav_set_selected('My Chatrooms'); } $ob = \App::get_observer(); diff --git a/Zotlabs/Module/Common.php b/Zotlabs/Module/Common.php index 2f3c57267..eebc56d2b 100644 --- a/Zotlabs/Module/Common.php +++ b/Zotlabs/Module/Common.php @@ -25,7 +25,7 @@ class Common extends \Zotlabs\Web\Controller { } - function get() { + function get() { $o = ''; @@ -34,38 +34,37 @@ class Common extends \Zotlabs\Web\Controller { $observer_hash = get_observer_hash(); - if(! perm_is_allowed(\App::$profile['profile_uid'],$observer_hash,'view_contacts')) { notice( t('Permission denied.') . EOL); return; } - $o .= '

' . t('Common connections') . '

'; - $t = count_common_friends(\App::$profile['profile_uid'],$observer_hash); if(! $t) { notice( t('No connections in common.') . EOL); - return $o; + return; } $r = common_friends(\App::$profile['profile_uid'],$observer_hash); if($r) { - - $tpl = get_markup_template('common_friends.tpl'); - foreach($r as $rr) { - $o .= replace_macros($tpl,array( - '$url' => $rr['xchan_url'], - '$name' => $rr['xchan_name'], - '$photo' => $rr['xchan_photo_m'], - '$tags' => '' - )); + $items[] = [ + 'url' => $rr['xchan_url'], + 'name' => $rr['xchan_name'], + 'photo' => $rr['xchan_photo_m'], + 'tags' => '' + ]; } - - $o .= cleardiv(); } + + $tpl = get_markup_template('common_friends.tpl'); + + $o = replace_macros($tpl, [ + '$title' => t('View Common Connections'), + '$items' => $items + ]); return $o; } diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index 6ad1e9528..f42ff9b84 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -30,7 +30,7 @@ class Connections extends \Zotlabs\Web\Controller { return login(); } - nav_set_selected(t('Connections')); + nav_set_selected('Connections'); $blocked = false; $hidden = false; diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index d301c2d45..23c5282e3 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -248,6 +248,10 @@ class Connedit extends \Zotlabs\Web\Controller { notice( t('Failed to update connection record.') . EOL); if(! intval(\App::$poi['abook_self'])) { + if($new_friend) { + \Zotlabs\Daemon\Master::Summon( [ 'Notifier', 'permission_accept', $contact_id ] ); + } + \Zotlabs\Daemon\Master::Summon( [ 'Notifier', (($new_friend) ? 'permission_create' : 'permission_update'), @@ -841,7 +845,7 @@ class Connedit extends \Zotlabs\Web\Controller { } } else - $locstr = t('none'); + $locstr = $contact['xchan_url']; $clone_warn = ''; $clonable = (in_array($contact['xchan_network'],['zot','rss']) ? true : false); @@ -866,6 +870,7 @@ class Connedit extends \Zotlabs\Web\Controller { '$permcat_new' => t('Add permission role'), '$permcat_enable' => feature_enabled(local_channel(),'permcats'), '$addr' => $contact['xchan_addr'], + '$primeurl' => $contact['xchan_url'], '$section' => $section, '$sections' => $sections, '$vcard' => $vcard, diff --git a/Zotlabs/Module/Dav.php b/Zotlabs/Module/Dav.php index 8ae2e8991..d506fe9f5 100644 --- a/Zotlabs/Module/Dav.php +++ b/Zotlabs/Module/Dav.php @@ -12,6 +12,9 @@ use \Sabre\DAV as SDAV; use \Zotlabs\Storage; require_once('include/attach.php'); +require_once('include/auth.php'); +require_once('include/security.php'); + class Dav extends \Zotlabs\Web\Controller { @@ -21,22 +24,65 @@ class Dav extends \Zotlabs\Web\Controller { */ function init() { - // workaround for HTTP-auth in CGI mode - if (x($_SERVER, 'REDIRECT_REMOTE_USER')) { - $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; - } - } + foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { - if (x($_SERVER, 'HTTP_AUTHORIZATION')) { - $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)) ; - if(strlen($userpass)) { - list($name, $password) = explode(':', $userpass); - $_SERVER['PHP_AUTH_USER'] = $name; - $_SERVER['PHP_AUTH_PW'] = $password; + /* Basic authentication */ + + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,5) === 'Basic') { + $userpass = @base64_decode(substr(trim($_SERVER[$head]),6)) ; + if(strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + break; + } + + /* Signature authentication */ + + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { + if($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } + + $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); + if($sigblock) { + $keyId = $sigblock['keyId']; + if($keyId) { + $r = q("select * from hubloc where hubloc_addr = '%s' limit 1", + dbesc($keyId) + ); + if($r) { + $c = channelx_by_hash($r[0]['hubloc_hash']); + if($c) { + $a = q("select * from account where account_id = %d limit 1", + intval($c['channel_account_id']) + ); + if($a) { + $record = [ 'channel' => $c, 'account' => $a[0] ]; + $channel_login = $c['channel_id']; + } + } + } + if(! $record) + continue; + + if($record) { + $verified = \Zotlabs\Web\HTTPSig::verify('',$record['channel']['channel_pubkey']); + if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) { + $record = null; + } + if($record['account']) { + authenticate_success($record['account']); + if($channel_login) { + change_channel($channel_login); + } + } + break; + } + } + } } } diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index 6268e99c6..caf0190ae 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -77,7 +77,7 @@ class Directory extends \Zotlabs\Web\Controller { $pubforums = get_directory_setting($observer, 'pubforums'); $o = ''; - nav_set_selected(t('Directory')); + nav_set_selected('Directory'); if(x($_POST,'search')) $search = notags(trim($_POST['search'])); diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index df3cb1e2b..d5afdd787 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -17,20 +17,18 @@ class Display extends \Zotlabs\Web\Controller { if($load) $_SESSION['loadtime'] = datetime_convert(); - if(observer_prohibited()) { notice( t('Public access denied.') . EOL); return; } - if(argc() > 1 && argv(1) !== 'load') $item_hash = argv(1); if($_REQUEST['mid']) $item_hash = $_REQUEST['mid']; - if(! $item_hash) { + if(! $item_hash) { \App::$error = 404; notice( t('Item not found.') . EOL); return; @@ -38,21 +36,18 @@ class Display extends \Zotlabs\Web\Controller { $observer_is_owner = false; $updateable = false; - - + if(local_channel() && (! $update)) { $channel = \App::get_channel(); - - + $channel_acl = array( 'allow_cid' => $channel['channel_allow_cid'], 'allow_gid' => $channel['channel_allow_gid'], 'deny_cid' => $channel['channel_deny_cid'], 'deny_gid' => $channel['channel_deny_gid'] ); - - + $x = array( 'is_owner' => true, 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), @@ -76,7 +71,6 @@ class Display extends \Zotlabs\Web\Controller { $o = '
'; $o .= status_editor($a,$x); $o .= '
'; - } // This page can be viewed by anybody so the query could be complicated @@ -95,14 +89,18 @@ class Display extends \Zotlabs\Web\Controller { if($decoded) $item_hash = $decoded; - $r = q("select id, uid, mid, parent_mid, item_type, item_deleted from item where mid like '%s' limit 1", - dbesc($item_hash . '%'), - dbesc($decoded . '%') + $r = q("select id, uid, mid, parent_mid, thr_parent, verb, item_type, item_deleted, item_blocked from item where mid like '%s' limit 1", + dbesc($item_hash . '%') ); if($r) { $target_item = $r[0]; } + + //if the item is to be moderated redirect to /moderate + if($target_item['item_blocked'] == ITEM_MODERATED) { + goaway(z_root() . '/moderate/' . $target_item['id']); + } $r = null; @@ -138,10 +136,16 @@ class Display extends \Zotlabs\Web\Controller { $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; if((! $update) && (! $load)) { - - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 0); - + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); + + //if the target item is not a post (eg a like) we want to address its thread parent + $mid = ((($target_item['verb'] == ACTIVITY_LIKE) || ($target_item['verb'] == ACTIVITY_DISLIKE)) ? $target_item['thr_parent'] : $target_item['mid']); + + //if we got a decoded hash we must encode it again before handing to javascript + if($decoded) + $mid = 'b64.' . base64url_encode($mid); + $o .= '
' . "\r\n"; $o .= "\r\n"; @@ -173,25 +177,31 @@ class Display extends \Zotlabs\Web\Controller { '$dend' => '', '$dbegin' => '', '$verb' => '', - '$mid' => $item_hash + '$mid' => $mid )); - - + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); + } - + $observer_hash = get_observer_hash(); $item_normal = item_normal(); - + $item_normal_update = item_normal_update(); + $sql_extra = public_permissions_sql($observer_hash); if(($update && $load) || ($checkjs->disabled())) { - - + $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']),intval(\App::$pager['start'])); - + if($load || ($checkjs->disabled())) { $r = null; - + require_once('include/channel.php'); $sys = get_sys_channel(); $sysid = $sys['channel_id']; @@ -207,25 +217,22 @@ class Display extends \Zotlabs\Web\Controller { ); if($r) { $updateable = true; - } - } if($r === null) { - + // in case somebody turned off public access to sys channel content using permissions - // make that content unsearchable by ensuring the owner_xchan can't match - + // make that content unsearchable by ensuring the owner uid can't match + if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) $sysid = 0; - - + $r = q("SELECT item.id as item_id from item WHERE mid = '%s' AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND item.deny_gid = '' AND item_private = 0 ) - and owner_xchan in ( " . stream_perms_xchans(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) + and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) OR uid = %d ) $sql_extra ) $item_normal @@ -233,7 +240,6 @@ class Display extends \Zotlabs\Web\Controller { dbesc($target_item['parent_mid']), intval($sysid) ); - } } } @@ -249,7 +255,7 @@ class Display extends \Zotlabs\Web\Controller { $r = q("SELECT item.parent AS item_id from item WHERE uid = %d and parent_mid = '%s' - $item_normal + $item_normal_update $simple_update limit 1", intval(local_channel()), @@ -265,15 +271,15 @@ class Display extends \Zotlabs\Web\Controller { // make that content unsearchable by ensuring the owner_xchan can't match if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) $sysid = 0; - + $r = q("SELECT item.parent AS item_id from item WHERE parent_mid = '%s' AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = '' AND item.deny_gid = '' AND item_private = 0 ) - and owner_xchan in ( " . stream_perms_xchans(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) + and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " )) OR uid = %d ) $sql_extra ) - $item_normal + $item_normal_update $simple_update limit 1", dbesc($target_item['parent_mid']), @@ -288,10 +294,8 @@ class Display extends \Zotlabs\Web\Controller { } if($r) { - $parents_str = ids_to_querystr($r,'item_id'); if($parents_str) { - $items = q("SELECT item.*, item.id AS item_id FROM item WHERE parent in ( %s ) $item_normal ", @@ -302,11 +306,11 @@ class Display extends \Zotlabs\Web\Controller { $items = fetch_post_tags($items,true); $items = conv_sort($items,'created'); } - } else { + } + else { $items = array(); } - if ($checkjs->disabled()) { $o .= conversation($items, 'display', $update, 'traditional'); if ($items[0]['title']) @@ -324,17 +328,13 @@ class Display extends \Zotlabs\Web\Controller { } $o .= '
'; - - return $o; - - - /* - elseif((! $update) && (! { + + if((($update && $load) || $checkjs->disabled()) && (! $items)) { - $r = q("SELECT id, item_flags FROM item WHERE id = '%s' OR mid = '%s' LIMIT 1", - dbesc($item_hash), + $r = q("SELECT id, item_deleted FROM item WHERE mid = '%s' LIMIT 1", dbesc($item_hash) ); + if($r) { if(intval($r[0]['item_deleted'])) { notice( t('Item has been removed.') . EOL ); @@ -348,8 +348,9 @@ class Display extends \Zotlabs\Web\Controller { } } - */ + + return $o; + } - - + } diff --git a/Zotlabs/Module/Events.php b/Zotlabs/Module/Events.php index 0541f5e9b..33c8b8249 100644 --- a/Zotlabs/Module/Events.php +++ b/Zotlabs/Module/Events.php @@ -272,7 +272,7 @@ class Events extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Events')); + nav_set_selected('Events'); if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) { $r = q("update event set dismissed = 1 where id = %d and uid = %d", diff --git a/Zotlabs/Module/File_upload.php b/Zotlabs/Module/File_upload.php index 29a7bd137..5c4b9a502 100644 --- a/Zotlabs/Module/File_upload.php +++ b/Zotlabs/Module/File_upload.php @@ -34,10 +34,26 @@ class File_upload extends \Zotlabs\Web\Controller { $_REQUEST['deny_gid'] = perms2str($_REQUEST['group_deny']); if($_REQUEST['filename']) { - $r = attach_mkdir($channel,get_observer_hash(),$_REQUEST); + $r = attach_mkdir($channel, get_observer_hash(), $_REQUEST); + if($r['success']) { + $hash = $r['data']['hash']; + + $sync = attach_export_data($channel,$hash); + if($sync) { + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + } + goaway(z_root() . '/cloud/' . $channel['channel_address'] . '/' . $r['data']['display_path']); + + } } else { - $r = attach_store($channel,get_observer_hash(), '', $_REQUEST); + $r = attach_store($channel, get_observer_hash(), '', $_REQUEST); + if($r['success']) { + $sync = attach_export_data($channel,$r['data']['hash']); + if($sync) + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + + } } goaway(z_root() . '/' . $_REQUEST['return_url']); diff --git a/Zotlabs/Module/Filestorage.php b/Zotlabs/Module/Filestorage.php index d6f363e77..55713027a 100644 --- a/Zotlabs/Module/Filestorage.php +++ b/Zotlabs/Module/Filestorage.php @@ -18,7 +18,7 @@ class Filestorage extends \Zotlabs\Web\Controller { $recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0); $resource = ((x($_POST, 'filehash')) ? notags($_POST['filehash']) : ''); - $notify = ((x($_POST, 'notify')) ? intval($_POST['notify']) : 0); + $notify = ((x($_POST, 'notify_edit')) ? intval($_POST['notify_edit']) : 0); if(! $resource) { notice(t('Item not found.') . EOL); @@ -31,16 +31,16 @@ class Filestorage extends \Zotlabs\Web\Controller { $acl->set_from_array($_POST); $x = $acl->get(); - $cloudPath = get_parent_cloudpath($channel_id, $channel['channel_address'], $resource); + $url = get_cloud_url($channel_id, $channel['channel_address'], $resource); //get the object before permissions change so we can catch eventual former allowed members - $object = get_file_activity_object($channel_id, $resource, $cloudPath); + $object = get_file_activity_object($channel_id, $resource, $url); attach_change_permissions($channel_id, $resource, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], $recurse, true); file_activity($channel_id, $object, $x['allow_cid'], $x['allow_gid'], $x['deny_cid'], $x['deny_gid'], 'post', $notify); - goaway($cloudPath); + goaway(dirname($url)); } function get() { @@ -99,11 +99,11 @@ class Filestorage extends \Zotlabs\Web\Controller { $f = $r[0]; $channel = \App::get_channel(); - $parentpath = get_parent_cloudpath($channel['channel_id'], $channel['channel_address'], $f['hash']); + $url = get_cloud_url($channel['channel_id'], $channel['channel_address'], $f['hash']); attach_delete($owner, $f['hash']); - goaway($parentpath); + goaway(dirname($url)); } if(argc() > 3 && argv(3) === 'edit') { @@ -123,7 +123,6 @@ class Filestorage extends \Zotlabs\Web\Controller { $channel = \App::get_channel(); $cloudpath = get_cloudpath($f); - $parentpath = get_parent_cloudpath($channel['channel_id'], $channel['channel_address'], $f['hash']); $aclselect_e = populate_acl($f, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_storage')); $is_a_dir = (intval($f['is_dir']) ? true : false); @@ -138,7 +137,6 @@ class Filestorage extends \Zotlabs\Web\Controller { '$header' => t('Edit file permissions'), '$file' => $f, '$cloudpath' => z_root() . '/' . $encoded_path, - '$parentpath' => $parentpath, '$uid' => $channel['channel_id'], '$channelnick' => $channel['channel_address'], '$permissions' => t('Permissions'), @@ -157,7 +155,7 @@ class Filestorage extends \Zotlabs\Web\Controller { '$submit' => t('Submit'), '$attach_btn_title' => t('Share this file'), '$link_btn_title' => t('Show URL to this file'), - '$notify' => array('notify', t('Notify your contacts about this file'), 0, '', array(t('No'), t('Yes'))), + '$notify' => array('notify_edit', t('Show in your contacts shared folder'), 0, '', array(t('No'), t('Yes'))), )); echo $o; diff --git a/Zotlabs/Module/Getfile.php b/Zotlabs/Module/Getfile.php index 0b05d78a4..413a68e0c 100644 --- a/Zotlabs/Module/Getfile.php +++ b/Zotlabs/Module/Getfile.php @@ -108,7 +108,7 @@ class Getfile extends \Zotlabs\Web\Controller { $unsafe_types = array('text/html','text/css','application/javascript'); - if(in_array($r['data']['filetype'],$unsafe_types)) { + if(in_array($r['data']['filetype'],$unsafe_types) && (! channel_codeallowed($channel['channel_id']))) { header('Content-type: text/plain'); } else { diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index e98cb9d4d..f1b1acaef 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -15,7 +15,7 @@ require_once('include/help.php'); class Help extends \Zotlabs\Web\Controller { function get() { - nav_set_selected(t('Help')); + nav_set_selected('Help'); if($_REQUEST['search']) { $o .= '
'; @@ -88,12 +88,15 @@ class Help extends \Zotlabs\Web\Controller { $heading = $headings[argv(1)]; $content = get_help_content(); + + $language = determine_help_language()['language']; return replace_macros(get_markup_template('help.tpl'), array( '$title' => t('$Projectname Documentation'), '$tocHeading' => t('Contents'), '$content' => $content, - '$heading' => $heading + '$heading' => $heading, + '$language' => $language )); } diff --git a/Zotlabs/Module/Import.php b/Zotlabs/Module/Import.php index 40ce8f6d1..2b16ff4e1 100644 --- a/Zotlabs/Module/Import.php +++ b/Zotlabs/Module/Import.php @@ -278,20 +278,31 @@ class Import extends \Zotlabs\Web\Controller { create_table_from_array('xchan',$xchan); require_once('include/photo/photo_driver.php'); - $photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']); - if($photos[4]) - $photodate = NULL_DATE; - else - $photodate = $xchan['xchan_photo_date']; - $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", - dbesc($photos[0]), - dbesc($photos[1]), - dbesc($photos[2]), - dbesc($photos[3]), - dbesc($photodate), - dbesc($xchan['xchan_hash']) - ); + if($xchan['xchan_hash'] === $channel['channel_hash']) { + $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", + dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), + dbesc($xchan['xchan_hash']) + ); + } + else { + $photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']); + if($photos[4]) + $photodate = NULL_DATE; + else + $photodate = $xchan['xchan_photo_date']; + + $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s', xchan_photo_date = '%s' where xchan_hash = '%s'", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($photodate), + dbesc($xchan['xchan_hash']) + ); + } } logger('import step 7'); diff --git a/Zotlabs/Module/Invite.php b/Zotlabs/Module/Invite.php index bbd98150d..0bcd1c1fa 100644 --- a/Zotlabs/Module/Invite.php +++ b/Zotlabs/Module/Invite.php @@ -95,7 +95,7 @@ class Invite extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Invite')); + nav_set_selected('Invite'); $tpl = get_markup_template('invite.tpl'); $invonly = false; diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index eb8b60931..9e5dcfaff 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -659,14 +659,23 @@ class Item extends \Zotlabs\Web\Controller { // BBCODE end alert if(strlen($categories)) { + $cats = explode(',',$categories); foreach($cats as $cat) { + + if($webpage == ITEM_TYPE_CARD) { + $catlink = z_root() . '/cards/' . $channel['channel_address'] . '?f=&cat=' . urlencode(trim($cat)); + } + else { + $catlink = $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)); + } + $post_tags[] = array( 'uid' => $profile_uid, 'ttype' => TERM_CATEGORY, 'otype' => TERM_OBJ_POST, 'term' => trim($cat), - 'url' => $owner_xchan['xchan_url'] . '?f=&cat=' . urlencode(trim($cat)) + 'url' => $catlink ); } } @@ -734,7 +743,9 @@ class Item extends \Zotlabs\Web\Controller { if($parent_item) $parent_mid = $parent_item['mid']; - + + + // Fallback so that we alway have a thr_parent if(!$thr_parent) @@ -744,6 +755,21 @@ class Item extends \Zotlabs\Web\Controller { $item_thread_top = ((! $parent) ? 1 : 0); + + // fix permalinks for cards + + if($webpage == ITEM_TYPE_CARD) { + $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . (($pagetitle) ? $pagetitle : substr($mid,0,16)); + } + 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", + intval($parent_item['id']) + ); + if($r) { + $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . $r[0]['v']; + } + } + if ((! $plink) && ($item_thread_top)) { $plink = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $mid; } @@ -1079,21 +1105,28 @@ class Item extends \Zotlabs\Web\Controller { // if this is a different page type or it's just a local delete // but not by the item author or owner, do a simple deletion - + + $complex = false; + if(intval($i[0]['item_type']) || ($local_delete && (! $can_delete))) { drop_item($i[0]['id']); } else { // complex deletion that needs to propagate and be performed in phases drop_item($i[0]['id'],true,DROPITEM_PHASE1); - $r = q("select * from item where id = %d", - intval($i[0]['id']) - ); - if($r) { - xchan_query($r); - $sync_item = fetch_post_tags($r); - build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); - } + $complex = true; + } + + $r = q("select * from item where id = %d", + intval($i[0]['id']) + ); + if($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); + } + + if($complex) { tag_deliver($i[0]['uid'],$i[0]['id']); } } diff --git a/Zotlabs/Module/Lang.php b/Zotlabs/Module/Lang.php index 84776c3ea..0e5d85d05 100644 --- a/Zotlabs/Module/Lang.php +++ b/Zotlabs/Module/Lang.php @@ -5,7 +5,7 @@ namespace Zotlabs\Module; class Lang extends \Zotlabs\Web\Controller { function get() { - nav_set_selected(t('Language')); + nav_set_selected('Language'); return lang_selector(); } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index c995079ce..b104a5f5f 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -255,7 +255,7 @@ class Like extends \Zotlabs\Web\Controller { // get the item. Allow linked photos (which are normally hidden) to be liked $r = q("SELECT * FROM item WHERE id = %d - and item_type = 0 and item_deleted = 0 and item_unpublished = 0 + and (item_type = 0 or item_type = 6) and item_deleted = 0 and item_unpublished = 0 and item_delayed = 0 and item_pending_remove = 0 and item_blocked = 0 LIMIT 1", intval($item_id) ); diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php index 8f8231c49..78c34583e 100644 --- a/Zotlabs/Module/Linkinfo.php +++ b/Zotlabs/Module/Linkinfo.php @@ -95,7 +95,7 @@ class Linkinfo extends \Zotlabs\Web\Controller { echo $arr['text']; killme(); } - + if($process_oembed) { $x = oembed_process($url); if($x) { diff --git a/Zotlabs/Module/Logout.php b/Zotlabs/Module/Logout.php new file mode 100644 index 000000000..6aa11d110 --- /dev/null +++ b/Zotlabs/Module/Logout.php @@ -0,0 +1,12 @@ +nuke(); + goaway(z_root()); + + } +} \ No newline at end of file diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index 9ee5f9324..879085f96 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -17,6 +17,7 @@ class Magic extends \Zotlabs\Web\Controller { $dest = ((x($_REQUEST,'dest')) ? $_REQUEST['dest'] : ''); $test = ((x($_REQUEST,'test')) ? intval($_REQUEST['test']) : 0); $rev = ((x($_REQUEST,'rev')) ? intval($_REQUEST['rev']) : 0); + $owa = ((x($_REQUEST,'owa')) ? intval($_REQUEST['owa']) : 0); $delegate = ((x($_REQUEST,'delegate')) ? $_REQUEST['delegate'] : ''); $parsed = parse_url($dest); @@ -132,12 +133,32 @@ class Magic extends \Zotlabs\Web\Controller { if(local_channel()) { $channel = \App::get_channel(); + // OpenWebAuth + + if($owa) { + + $headers = []; + $headers['Accept'] = 'application/x-zot+json' ; + $headers['X-Open-Web-Auth'] = random_string(); + $headers = \Zotlabs\Web\HTTPSig::create_sig('',$headers,$channel['channel_prvkey'], + 'acct:' . $channel['channel_address'] . '@' . \App::get_hostname(),false,true,'sha512'); + $x = z_fetch_url($basepath . '/owa',false,$redirects,[ 'headers' => $headers ]); + + if($x['success']) { + $j = json_decode($x['body'],true); + if($j['success'] && $j['token']) { + $x = strpbrk($dest,'?&'); + $args = (($x) ? '&owt=' . $j['token'] : '?f=&owt=' . $j['token']) . (($delegate) ? '&delegate=1' : ''); + + goaway($dest . $args); + } + } + goaway($dest); + } + + $token = random_string(); - $token_sig = base64url_encode(rsa_sign($token,$channel['channel_prvkey'])); - - $channel['token'] = $token; - $channel['token_sig'] = $token_sig; - + \Zotlabs\Zot\Verify::create('auth',$channel['channel_id'],$token,$x[0]['hubloc_url']); $target_url = $x[0]['hubloc_callback'] . '/?f=&auth=' . urlencode(channel_reddress($channel)) diff --git a/Zotlabs/Module/Mail.php b/Zotlabs/Module/Mail.php index e5509961a..12f3b8152 100644 --- a/Zotlabs/Module/Mail.php +++ b/Zotlabs/Module/Mail.php @@ -140,7 +140,7 @@ class Mail extends \Zotlabs\Web\Controller { function get() { $o = ''; - nav_set_selected(t('Mail')); + nav_set_selected('Mail'); if(! local_channel()) { notice( t('Permission denied.') . EOL); diff --git a/Zotlabs/Module/Manage.php b/Zotlabs/Module/Manage.php index 2d8f39ded..9c5c32294 100644 --- a/Zotlabs/Module/Manage.php +++ b/Zotlabs/Module/Manage.php @@ -11,7 +11,7 @@ class Manage extends \Zotlabs\Web\Controller { return; } - nav_set_selected('Manage'); + nav_set_selected('Channel Manager'); require_once('include/security.php'); diff --git a/Zotlabs/Module/Moderate.php b/Zotlabs/Module/Moderate.php index 9af43420d..cf1625a6b 100644 --- a/Zotlabs/Module/Moderate.php +++ b/Zotlabs/Module/Moderate.php @@ -14,6 +14,24 @@ class Moderate extends \Zotlabs\Web\Controller { return; } + //show all items + if(argc() == 1) { + $r = q("select item.id as item_id, item.* from item where item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc limit 60", + intval(local_channel()), + intval(ITEM_MODERATED) + ); + } + + //show a single item + if(argc() == 2) { + $post_id = intval(argv(1)); + + $r = q("select item.id as item_id, item.* from item where item.id = %d and item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc limit 60", + intval($post_id), + intval(local_channel()), + intval(ITEM_MODERATED) + ); + } if(argc() > 2) { $post_id = intval(argv(1)); @@ -55,10 +73,6 @@ class Moderate extends \Zotlabs\Web\Controller { goaway(z_root() . '/moderate'); } } - $r = q("select item.id as item_id, item.* from item where item.uid = %d and item_blocked = %d and item_deleted = 0 order by created desc limit 60", - intval(local_channel()), - intval(ITEM_MODERATED) - ); if($r) { xchan_query($r); @@ -73,4 +87,4 @@ class Moderate extends \Zotlabs\Web\Controller { } -} \ No newline at end of file +} diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php index 85c8a3042..ad29ec7e8 100644 --- a/Zotlabs/Module/Mood.php +++ b/Zotlabs/Module/Mood.php @@ -117,7 +117,7 @@ class Mood extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Mood')); + nav_set_selected('Mood'); $parent = ((x($_GET,'parent')) ? intval($_GET['parent']) : '0'); diff --git a/Zotlabs/Module/Network.php b/Zotlabs/Module/Network.php index e5c059af5..ee736ff42 100644 --- a/Zotlabs/Module/Network.php +++ b/Zotlabs/Module/Network.php @@ -44,6 +44,7 @@ class Network extends \Zotlabs\Web\Controller { $channel = \App::get_channel(); $item_normal = item_normal(); + $item_normal_update = item_normal_update(); $datequery = $datequery2 = ''; @@ -116,7 +117,6 @@ class Network extends \Zotlabs\Web\Controller { $spam = ((x($_GET,'spam')) ? intval($_GET['spam']) : 0); $cmin = ((x($_GET,'cmin')) ? intval($_GET['cmin']) : 0); $cmax = ((x($_GET,'cmax')) ? intval($_GET['cmax']) : 99); - $firehose = ((x($_GET,'fh')) ? intval($_GET['fh']) : 0); $file = ((x($_GET,'file')) ? $_GET['file'] : ''); $xchan = ((x($_GET,'xchan')) ? $_GET['xchan'] : ''); @@ -154,7 +154,7 @@ class Network extends \Zotlabs\Web\Controller { )); } - nav_set_selected(t('Activity')); + nav_set_selected('Grid'); $channel_acl = array( 'allow_cid' => $channel['channel_allow_cid'], @@ -290,9 +290,6 @@ class Network extends \Zotlabs\Web\Controller { // We only launch liveUpdate if you aren't filtering in some incompatible // way and also you aren't writing a comment (discovered in javascript). - if($gid || $cid || $cmin || ($cmax != 99) || $star || $liked || $conv || $spam || $nouveau || $list) - $firehose = 0; - $maxheight = get_pconfig(local_channel(),'system','network_divmore_height'); if(! $maxheight) $maxheight = 400; @@ -315,7 +312,7 @@ class Network extends \Zotlabs\Web\Controller { '$liked' => (($liked) ? $liked : '0'), '$conv' => (($conv) ? $conv : '0'), '$spam' => (($spam) ? $spam : '0'), - '$fh' => (($firehose) ? $firehose : '0'), + '$fh' => '0', '$nouveau' => (($nouveau) ? $nouveau : '0'), '$wall' => '0', '$static' => $static, @@ -409,17 +406,7 @@ class Network extends \Zotlabs\Web\Controller { } $abook_uids = " and abook.abook_channel = " . local_channel() . " "; - - $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; - if($firehose && (! $disable_discover_tab)) { - require_once('include/channel.php'); - $sys = get_sys_channel(); - $uids = " and item.uid = " . intval($sys['channel_id']) . " "; - \App::$data['firehose'] = intval($sys['channel_id']); - } - else { - $uids = " and item.uid = " . local_channel() . " "; - } + $uids = " and item.uid = " . local_channel() . " "; if(get_pconfig(local_channel(),'system','network_list_mode')) $page_mode = 'list'; @@ -491,10 +478,11 @@ class Network extends \Zotlabs\Web\Controller { } else { + // this is an update $r = q("SELECT item.parent AS item_id FROM item left join abook on ( item.owner_xchan = abook.abook_xchan $abook_uids ) - WHERE true $uids $item_normal $simple_update + WHERE true $uids $item_normal_update $simple_update and (abook.abook_blocked = 0 or abook.abook_flags is null) $sql_extra3 $sql_extra $sql_nets " ); @@ -516,14 +504,14 @@ class Network extends \Zotlabs\Web\Controller { dbesc($parents_str) ); - xchan_query($items,true,(($firehose) ? local_channel() : 0)); + xchan_query($items,true); $items = fetch_post_tags($items,true); $items = conv_sort($items,$ordering); } else { $items = array(); } - + if($page_mode === 'list') { /** @@ -535,20 +523,26 @@ class Network extends \Zotlabs\Web\Controller { if($parents_str) { $update_unseen = " AND ( id IN ( " . dbesc($parents_str) . " )"; + $update_unseen .= " AND obj_type != '" . dbesc(ACTIVITY_OBJ_FILE) . "'"; $update_unseen .= " OR ( parent IN ( " . dbesc($parents_str) . " ) AND verb in ( '" . dbesc(ACTIVITY_LIKE) . "','" . dbesc(ACTIVITY_DISLIKE) . "' ))) "; } } else { if($parents_str) { - $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " )"; + $update_unseen = " AND parent IN ( " . dbesc($parents_str) . " ) AND obj_type != '" . dbesc(ACTIVITY_OBJ_FILE) . "'"; } } } - if(($update_unseen) && (! $firehose)) - $r = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d $update_unseen ", - intval(local_channel()) - ); + if($update_unseen) { + $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; + call_hooks('update_unseen',$x); + if($x['update'] === 'unset' || intval($x['update'])) { + $r = q("UPDATE item SET item_unseen = 0 WHERE item_unseen = 1 AND uid = %d $update_unseen ", + intval(local_channel()) + ); + } + } $mode = (($nouveau) ? 'network-new' : 'network'); diff --git a/Zotlabs/Module/New_channel.php b/Zotlabs/Module/New_channel.php index cfd45e909..2b73fa191 100644 --- a/Zotlabs/Module/New_channel.php +++ b/Zotlabs/Module/New_channel.php @@ -9,7 +9,7 @@ require_once('include/permissions.php'); class New_channel extends \Zotlabs\Web\Controller { function init() { - + $cmd = ((argc() > 1) ? argv(1) : ''); if($cmd === 'autofill.json') { diff --git a/Zotlabs/Module/Notifications.php b/Zotlabs/Module/Notifications.php index 652648701..dfa007548 100644 --- a/Zotlabs/Module/Notifications.php +++ b/Zotlabs/Module/Notifications.php @@ -12,25 +12,44 @@ class Notifications extends \Zotlabs\Web\Controller { return; } - nav_set_selected('notifications'); + nav_set_selected('Notifications'); $o = ''; - - $r = q("SELECT * from notify where uid = %d and seen = 0 order by created desc", + + $r = q("select count(*) as total from notify where uid = %d and seen = 0", intval(local_channel()) ); + if($r && intval($t[0]['total']) > 49) { + $r = q("select * from notify where uid = %d + and seen = 0 order by created desc limit 50", + intval(local_channel()) + ); + } else { + $r1 = q("select * from notify where uid = %d + and seen = 0 order by created desc limit 50", + intval(local_channel()) + ); + $r2 = q("select * from notify where uid = %d + and seen = 1 order by created desc limit %d", + intval(local_channel()), + intval(50 - intval($t[0]['total'])) + ); + $r = array_merge($r1,$r2); + } if($r) { $notifications_available = 1; - foreach ($r as $it) { - $x = strip_tags(bbcode($it['msg'])); + foreach ($r as $rr) { + $x = strip_tags(bbcode($rr['msg'])); if(strpos($x,',')) $x = substr($x,strpos($x,',')+1); $notif_content .= replace_macros(get_markup_template('notify.tpl'),array( - '$item_link' => z_root().'/notify/view/'. $it['id'], - '$item_image' => $it['photo'], + '$item_link' => z_root().'/notify/view/'. $rr['id'], + '$item_image' => $rr['photo'], '$item_text' => $x, - '$item_when' => relative_date($it['created']) + '$item_when' => relative_date($rr['created']), + '$item_seen' => (($rr['seen']) ? true : false), + '$new' => t('New') )); } } diff --git a/Zotlabs/Module/Notify.php b/Zotlabs/Module/Notify.php index f592f6f37..3d6e1c2e7 100644 --- a/Zotlabs/Module/Notify.php +++ b/Zotlabs/Module/Notify.php @@ -15,12 +15,16 @@ class Notify extends \Zotlabs\Web\Controller { intval(local_channel()) ); if($r) { - q("update notify set seen = 1 where (( parent != '' and parent = '%s' and otype = '%s' ) or link = '%s' ) and uid = %d", - dbesc($r[0]['parent']), - dbesc($r[0]['otype']), - dbesc($r[0]['link']), - intval(local_channel()) - ); + $x = [ 'channel_id' => local_channel(), 'update' => 'unset' ]; + call_hooks('update_unseen',$x); + if($x['update'] === 'unset' || intval($x['update'])) { + q("update notify set seen = 1 where (( parent != '' and parent = '%s' and otype = '%s' ) or link = '%s' ) and uid = %d", + dbesc($r[0]['parent']), + dbesc($r[0]['otype']), + dbesc($r[0]['link']), + intval(local_channel()) + ); + } goaway($r[0]['link']); } goaway(z_root()); diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php index 9c05f5e3f..5e06d3540 100644 --- a/Zotlabs/Module/Oep.php +++ b/Zotlabs/Module/Oep.php @@ -1,6 +1,8 @@ oep_profile_reply($_REQUEST); elseif(fnmatch('*/profile/*',$url)) $arr = $this->oep_profile_reply($_REQUEST); + elseif(fnmatch('*/cards/*',$url)) + $arr = $this->oep_cards_reply($_REQUEST); if($arr) { if($html) { @@ -66,45 +70,70 @@ class Oep extends \Zotlabs\Web\Controller { $url = $args['url']; $maxwidth = intval($args['maxwidth']); $maxheight = intval($args['maxheight']); - - if(preg_match('#//(.*?)/(.*?)/(.*?)/(.*?)mid\=(.*?)(&|$)#',$url,$matches)) { - $chn = $matches[3]; - $res = $matches[5]; + logger('processing display'); + if(preg_match('#//(.*?)/display/(.*?)(&|\?|$)#',$url,$matches)) { + $res = $matches[2]; } - - if(! ($chn && $res)) - return; - $c = q("select * from channel where channel_address = '%s' limit 1", - dbesc($chn) + + if(strpos($res,'b64.') === 0) { + $res = base64url_decode(substr($res,4)); + } + + $item_normal = item_normal(); + + $p = q("select * from item where mid like '%s' limit 1", + dbesc($res . '%') ); - - if(! $c) + + if(! $p) return; + + $c = channelx_by_n($p[0]['uid']); + - $sql_extra = item_permissions_sql($c[0]['channel_id']); + if(! ($c && $res)) + return; + + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream')) + return; + + $sql_extra = item_permissions_sql($c['channel_id']); - $p = q("select * from item where mid = '%s' and uid = %d $sql_extra limit 1", - dbesc($res), - intval($c[0]['channel_id']) + $p = q("select * from item where mid like '%s' and uid = %d $sql_extra $item_normal limit 1", + dbesc($res . '%'), + intval($c['channel_id']) ); + if(! $p) return; xchan_query($p,true); $p = fetch_post_tags($p,true); + + // This function can get tripped up if the item is already a reshare + // (the multiple share declarations do not parse cleanly if nested) + // So build a template with a known nonsense string as the content, and then + // replace that known string with the actual rendered content, sending + // each content layer through bbcode() separately. + + $x = '2eGriplW^*Jmf4'; + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $p[0]['body']; - $o .= "[/share]"; + $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + + $o .= $x; + $o .= "[/share]"; $o = bbcode($o); + $o = str_replace($x,bbcode($p[0]['body']),$o); + $ret['type'] = 'rich'; $w = (($maxwidth) ? $maxwidth : 640); @@ -118,6 +147,91 @@ class Oep extends \Zotlabs\Web\Controller { return $ret; } + + + function oep_cards_reply($args) { + + $ret = []; + $url = $args['url']; + $maxwidth = intval($args['maxwidth']); + $maxheight = intval($args['maxheight']); + + if(preg_match('#//(.*?)/cards/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) { + $nick = $matches[2]; + $res = $matches[3]; + } + if(! ($nick && $res)) + return $ret; + + $channel = channelx_by_nick($nick); + + if(! $channel) + return $ret; + + + if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) + return $ret; + + $sql_extra = item_permissions_sql($channel['channel_id'],get_observer_hash()); + + $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1", + dbesc($res) + ); + if($r) { + $sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; + } + else { + return $ret; + } + + $r = q("select * from item + where item.uid = %d and item_type = %d + $sql_extra order by item.created desc", + intval($channel['channel_id']), + intval(ITEM_TYPE_CARD) + ); + + $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 + and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 + and item.item_blocked = 0 "; + + if($r) { + xchan_query($r); + $p = fetch_post_tags($r, true); + } + + $x = '2eGriplW^*Jmf4'; + + + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; + if($p[0]['title']) + $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + + $o .= $x; + $o .= "[/share]"; + $o = bbcode($o); + + $o = str_replace($x,bbcode($p[0]['body']),$o); + + $ret['type'] = 'rich'; + + $w = (($maxwidth) ? $maxwidth : 640); + $h = (($maxheight) ? $maxheight : intval($w * 2 / 3)); + + $ret['html'] = '
' . $o . '
'; + + $ret['width'] = $w; + $ret['height'] = $h; + + return $ret; + + } + function oep_mid_reply($args) { @@ -139,6 +253,9 @@ class Oep extends \Zotlabs\Web\Controller { if(! $c) return; + + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream')) + return; $sql_extra = item_permissions_sql($c[0]['channel_id']); @@ -151,19 +268,29 @@ class Oep extends \Zotlabs\Web\Controller { xchan_query($p,true); $p = fetch_post_tags($p,true); - + + // This function can get tripped up if the item is already a reshare + // (the multiple share declarations do not parse cleanly if nested) + // So build a template with a known nonsense string as the content, and then + // replace that known string with the actual rendered content, sending + // each content layer through bbcode() separately. + + $x = '2eGriplW^*Jmf4'; + $o = "[share author='".urlencode($p[0]['author']['xchan_name']). - "' profile='".$p[0]['author']['xchan_url'] . - "' avatar='".$p[0]['author']['xchan_photo_s']. - "' link='".$p[0]['plink']. - "' posted='".$p[0]['created']. - "' message_id='".$p[0]['mid']."']"; - if($p[0]['title']) - $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; - $o .= $p[0]['body']; - $o .= "[/share]"; + "' profile='".$p[0]['author']['xchan_url'] . + "' avatar='".$p[0]['author']['xchan_photo_s']. + "' link='".$p[0]['plink']. + "' posted='".$p[0]['created']. + "' message_id='".$p[0]['mid']."']"; + if($p[0]['title']) + $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n"; + $o .= $x; + $o .= "[/share]"; $o = bbcode($o); + $o = str_replace($x,bbcode($p[0]['body']),$o); + $ret['type'] = 'rich'; $w = (($maxwidth) ? $maxwidth : 640); @@ -247,6 +374,9 @@ class Oep extends \Zotlabs\Web\Controller { if(! $c) return; + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) + return; + $sql_extra = permissions_sql($c[0]['channel_id']); $p = q("select resource_id from photo where album = '%s' and uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", @@ -308,6 +438,9 @@ class Oep extends \Zotlabs\Web\Controller { if(! $c) return; + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) + return; + $sql_extra = permissions_sql($c[0]['channel_id']); $p = q("select resource_id from photo where uid = %d and imgscale = 0 $sql_extra order by created desc limit 1", @@ -368,7 +501,10 @@ class Oep extends \Zotlabs\Web\Controller { if(! $c) return; - + + if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files')) + return; + $sql_extra = permissions_sql($c[0]['channel_id']); diff --git a/Zotlabs/Module/Ofeed.php b/Zotlabs/Module/Ofeed.php index 2b7acff99..58488d4af 100644 --- a/Zotlabs/Module/Ofeed.php +++ b/Zotlabs/Module/Ofeed.php @@ -18,7 +18,7 @@ class Ofeed extends \Zotlabs\Web\Controller { $params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0); $params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0); $params['start'] = ((x($params,'start')) ? intval($params['start']) : 0); - $params['records'] = ((x($params,'records')) ? intval($params['records']) : 40); + $params['records'] = ((x($params,'records')) ? intval($params['records']) : 10); $params['direction'] = ((x($params,'direction')) ? dbesc($params['direction']) : 'desc'); $params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : ''); $params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1); diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php new file mode 100644 index 000000000..4b0d855c5 --- /dev/null +++ b/Zotlabs/Module/Owa.php @@ -0,0 +1,53 @@ + false ]; + + foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) { + if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') { + if($head !== 'HTTP_AUTHORIZATION') { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head]; + continue; + } + + $sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); + if($sigblock) { + $keyId = $sigblock['keyId']; + + if($keyId) { + $r = q("select * from hubloc left join xchan on hubloc_hash = xchan_hash + where hubloc_addr = '%s' limit 1", + dbesc(str_replace('acct:','',$keyId)) + ); + if($r) { + $hubloc = $r[0]; + $verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']); + if($verified && $verified['header_signed'] && $verified['header_valid']) { + $ret['success'] = true; + $token = random_string(32); + \Zotlabs\Zot\Verify::create('owt',0,$token,$r[0]['hubloc_addr']); + $ret['token'] = $token; + } + } + } + } + } + } + json_return_and_die($ret,'application/x-zot+json'); + } +} diff --git a/Zotlabs/Module/Pdledit.php b/Zotlabs/Module/Pdledit.php index 618444480..f8af470ac 100644 --- a/Zotlabs/Module/Pdledit.php +++ b/Zotlabs/Module/Pdledit.php @@ -14,7 +14,7 @@ class Pdledit extends \Zotlabs\Web\Controller { if(! trim($_REQUEST['content'])) { del_pconfig(local_channel(),'system','mod_' . $_REQUEST['module'] . '.pdl'); - goaway(z_root() . '/pdledit/' . $_REQUEST['module']); + goaway(z_root() . '/pdledit'); } set_pconfig(local_channel(),'system','mod_' . $_REQUEST['module'] . '.pdl',escape_tags($_REQUEST['content'])); build_sync_packet(); @@ -34,19 +34,38 @@ class Pdledit extends \Zotlabs\Web\Controller { notice( t('Feature disabled.') . EOL); return; } - + + if(argc() > 2 && argv(2) === 'reset') { + del_pconfig(local_channel(),'system','mod_' . argv(1) . '.pdl'); + goaway(z_root() . '/pdledit'); + } + if(argc() > 1) $module = 'mod_' . argv(1) . '.pdl'; else { $o .= '
'; $o .= '

' . t('Edit System Page Description') . '

'; + + $edited = []; + + $r = q("select k from pconfig where uid = %d and cat = 'system' and k like '%s' ", + intval(local_channel()), + dbesc('mod_%.pdl') + ); + + if($r) { + foreach($r as $rv) { + $edited[] = substr(str_replace('.pdl','',$rv['k']),4); + } + } + $files = glob('Zotlabs/Module/*.php'); if($files) { foreach($files as $f) { $name = lcfirst(basename($f,'.php')); $x = theme_include('mod_' . $name . '.pdl'); if($x) { - $o .= '' . $name . '
'; + $o .= '' . $name . '' . ((in_array($name,$edited)) ? ' ' . t('(modified)') . ' ' . t('Reset') . '': '' ) . '
'; } } } @@ -69,6 +88,7 @@ class Pdledit extends \Zotlabs\Web\Controller { '$header' => t('Edit System Page Description'), '$mname' => t('Module Name:'), '$help' => t('Layout Help'), + '$another' => t('Edit another layout'), '$module' => argv(1), '$content' => htmlspecialchars($t,ENT_COMPAT,'UTF-8'), '$submit' => t('Submit') diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index 85dae46a5..caef45d98 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -555,7 +555,7 @@ class Photos extends \Zotlabs\Web\Controller { $sql_extra = permissions_sql($owner_uid,get_observer_hash(),'photo'); $sql_attach = permissions_sql($owner_uid,get_observer_hash(),'attach'); - nav_set_selected(t('Photos')); + nav_set_selected('Photos'); $o = ""; @@ -671,8 +671,13 @@ class Photos extends \Zotlabs\Web\Controller { */ if($datatype === 'album') { - - \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); if($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) { \App::set_pager_itemspage(60); @@ -765,7 +770,7 @@ class Photos extends \Zotlabs\Web\Controller { if($photos) { $o = replace_macros(get_markup_template('photosajax.tpl'),array( '$photos' => $photos, - '$album_id' => bin2hex($album) + '$album_id' => $datum )); } else { diff --git a/Zotlabs/Module/Ping.php b/Zotlabs/Module/Ping.php index bf2fa5cc9..c91659f2f 100644 --- a/Zotlabs/Module/Ping.php +++ b/Zotlabs/Module/Ping.php @@ -19,6 +19,7 @@ class Ping extends \Zotlabs\Web\Controller { * @result JSON */ function init() { + $result = array(); $notifs = array(); @@ -36,6 +37,11 @@ class Ping extends \Zotlabs\Web\Controller { $result['all_events_today'] = 0; $result['notice'] = array(); $result['info'] = array(); + $result['pubs'] = 0; + $result['files'] = 0; + + if(! $_SESSION['static_loadtime']) + $_SESSION['static_loadtime'] = datetime_convert(); $t0 = dba_timer(); @@ -134,6 +140,61 @@ class Ping extends \Zotlabs\Web\Controller { db_utcnow(), db_quoteinterval('3 MINUTE') ); + $discover_tab_on = ((get_config('system','disable_discover_tab') != 1) ? true : false); + $notify_pubs = ((local_channel()) ? ($vnotify & VNOTIFY_PUBS) && $discover_tab_on : $discover_tab_on); + + if($notify_pubs) { + $sys = get_sys_channel(); + + $pubs = q("SELECT count(id) as total from item + WHERE uid = %d + AND author_xchan != '%s' + AND obj_type != '%s' + AND item_unseen = 1 + AND created > '" . datetime_convert('UTC','UTC',$_SESSION['static_loadtime']) . "' + $item_normal", + intval($sys['channel_id']), + dbesc(get_observer_hash()), + dbesc(ACTIVITY_OBJ_FILE) + ); + + if($pubs) + $result['pubs'] = intval($pubs[0]['total']); + } + + if((argc() > 1) && (argv(1) === 'pubs') && ($notify_pubs)) { + $sys = get_sys_channel(); + $result = array(); + + $r = q("SELECT * FROM item + WHERE uid = %d + AND author_xchan != '%s' + AND obj_type != '%s' + AND item_unseen = 1 + AND created > '" . datetime_convert('UTC','UTC',$_SESSION['static_loadtime']) . "' + $item_normal + ORDER BY created DESC + LIMIT 300", + intval($sys['channel_id']), + dbesc(get_observer_hash()), + dbesc(ACTIVITY_OBJ_FILE) + ); + + if($r) { + xchan_query($r); + foreach($r as $rr) { + $rr['llink'] = str_replace('display/', 'pubstream/?f=&mid=', $rr['llink']); + $result[] = \Zotlabs\Lib\Enotify::format($rr); + } + } + +// logger('ping (network||home): ' . print_r($result, true), LOGGER_DATA); + echo json_encode(array('notify' => $result)); + killme(); + } + + $t1 = dba_timer(); + if((! local_channel()) || ($result['invalid'])) { echo json_encode($result); killme(); @@ -177,6 +238,9 @@ class Ping extends \Zotlabs\Web\Controller { intval(local_channel()) ); break; + case 'pubs': + unset($_SESSION['static_loadtime']); + break; default: break; } @@ -194,37 +258,20 @@ class Ping extends \Zotlabs\Web\Controller { * dropdown menu. */ if(argc() > 1 && argv(1) === 'notify') { - $t = q("select count(*) as total from notify where uid = %d and seen = 0", + $t = q("select * from notify where uid = %d and seen = 0 order by created desc", intval(local_channel()) ); - if($t && intval($t[0]['total']) > 49) { - $z = q("select * from notify where uid = %d - and seen = 0 order by created desc limit 50", - intval(local_channel()) - ); - } else { - $z1 = q("select * from notify where uid = %d - and seen = 0 order by created desc limit 50", - intval(local_channel()) - ); - $z2 = q("select * from notify where uid = %d - and seen = 1 order by created desc limit %d", - intval(local_channel()), - intval(50 - intval($t[0]['total'])) - ); - $z = array_merge($z1,$z2); - } - if(count($z)) { - foreach($z as $zz) { + if($t) { + foreach($t as $tt) { $notifs[] = array( - 'notify_link' => z_root() . '/notify/view/' . $zz['id'], - 'name' => $zz['xname'], - 'url' => $zz['url'], - 'photo' => $zz['photo'], - 'when' => relative_date($zz['created']), - 'hclass' => (($zz['seen']) ? 'notify-seen' : 'notify-unseen'), - 'message' => strip_tags(bbcode($zz['msg'])) + 'notify_link' => z_root() . '/notify/view/' . $tt['id'], + 'name' => $tt['xname'], + 'url' => $tt['url'], + 'photo' => $tt['photo'], + 'when' => relative_date($tt['created']), + 'hclass' => (($tt['seen']) ? 'notify-seen' : 'notify-unseen'), + 'message' => strip_tags(bbcode($tt['msg'])) ); } } @@ -233,7 +280,7 @@ class Ping extends \Zotlabs\Web\Controller { killme(); } - if(argc() > 1 && argv(1) === 'messages') { + if(argc() > 1 && argv(1) === 'mail') { $channel = \App::get_channel(); $t = q("select mail.*, xchan.* from mail left join xchan on xchan_hash = from_xchan where channel_id = %d and mail_seen = 0 and mail_deleted = 0 @@ -265,9 +312,12 @@ class Ping extends \Zotlabs\Web\Controller { $r = q("SELECT * FROM item WHERE item_unseen = 1 and uid = %d $item_normal - and author_xchan != '%s' ORDER BY created DESC limit 300", + AND author_xchan != '%s' + AND obj_type != '%s' + ORDER BY created DESC limit 300", intval(local_channel()), - dbesc($ob_hash) + dbesc($ob_hash), + dbesc(ACTIVITY_OBJ_FILE) ); if($r) { @@ -308,6 +358,30 @@ class Ping extends \Zotlabs\Web\Controller { killme(); } + if((argc() > 1 && (argv(1) === 'register')) && is_site_admin()) { + $result = array(); + + $r = q("SELECT account_email, account_created from account where (account_flags & %d) > 0", + intval(ACCOUNT_PENDING) + ); + if($r) { + foreach($r as $rr) { + $result[] = array( + 'notify_link' => z_root() . '/admin/accounts', + 'name' => $rr['account_email'], + 'url' => '', + 'photo' => get_default_profile_photo(48), + 'when' => relative_date($rr['account_created']), + 'hclass' => ('notify-unseen'), + 'message' => t('requires approval') + ); + } + } + logger('ping (register): ' . print_r($result, true), LOGGER_DATA); + echo json_encode(array('notify' => $result)); + killme(); + } + if(argc() > 1 && (argv(1) === 'all_events')) { $bd_format = t('g A l F d') ; // 8 AM Friday January 18 @@ -345,6 +419,39 @@ class Ping extends \Zotlabs\Web\Controller { killme(); } + 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 + LEFT JOIN xchan on author_xchan = xchan_hash + WHERE item.verb = '%s' + AND item.obj_type = '%s' + AND item.uid = %d + AND item.owner_xchan != '%s' + AND item.item_unseen = 1", + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()), + dbesc($ob_hash) + ); + if($r) { + foreach($r as $rr) { + $result[] = array( + 'notify_link' => z_root() . '/sharedwithme', + 'name' => $rr['xchan_name'], + 'url' => $rr['xchan_url'], + 'photo' => $rr['xchan_photo_s'], + 'when' => relative_date($rr['created']), + 'hclass' => ('notify-unseen'), + 'message' => t('shared a file with you') + ); + } + } + logger('ping (files): ' . print_r($result, true), LOGGER_DATA); + echo json_encode(array('notify' => $result)); + killme(); + } + /** * Normal ping - just the counts, no detail */ @@ -356,15 +463,35 @@ class Ping extends \Zotlabs\Web\Controller { $result['notify'] = intval($t[0]['total']); } - $t1 = dba_timer(); + $t2 = dba_timer(); + + if($vnotify & VNOTIFY_FILES) { + $files = q("SELECT count(id) as total FROM item + WHERE verb = '%s' + AND obj_type = '%s' + AND uid = %d + AND owner_xchan != '%s' + AND item_unseen = 1", + dbesc(ACTIVITY_POST), + dbesc(ACTIVITY_OBJ_FILE), + intval(local_channel()), + dbesc($ob_hash) + ); + if($files) + $result['files'] = intval($files[0]['total']); + } + + $t3 = dba_timer(); if($vnotify & (VNOTIFY_NETWORK|VNOTIFY_CHANNEL)) { $r = q("SELECT id, item_wall FROM item WHERE item_unseen = 1 and uid = %d $item_normal - and author_xchan != '%s'", + AND author_xchan != '%s' + AND obj_type != '%s'", intval(local_channel()), - dbesc($ob_hash) + dbesc($ob_hash), + dbesc(ACTIVITY_OBJ_FILE) ); if($r) { @@ -384,20 +511,20 @@ class Ping extends \Zotlabs\Web\Controller { if(! ($vnotify & VNOTIFY_CHANNEL)) $result['home'] = 0; - $t2 = dba_timer(); + $t4 = dba_timer(); if($vnotify & VNOTIFY_INTRO) { $intr = q("SELECT COUNT(abook.abook_id) AS total FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash where abook_channel = %d and abook_pending = 1 and abook_self = 0 and abook_ignored = 0 and xchan_deleted = 0 and xchan_orphan = 0 ", intval(local_channel()) ); - $t3 = dba_timer(); + $t5 = dba_timer(); if($intr) $result['intros'] = intval($intr[0]['total']); } - $t4 = dba_timer(); + $t6 = dba_timer(); $channel = \App::get_channel(); if($vnotify & VNOTIFY_MAIL) { @@ -420,7 +547,7 @@ class Ping extends \Zotlabs\Web\Controller { } } - $t5 = dba_timer(); + $t7 = dba_timer(); if($vnotify & (VNOTIFY_EVENT|VNOTIFY_EVENTTODAY|VNOTIFY_BIRTHDAY)) { $events = q("SELECT etype, dtstart, adjust FROM event @@ -466,9 +593,9 @@ class Ping extends \Zotlabs\Web\Controller { $x = json_encode($result); - $t6 = dba_timer(); + $t8 = dba_timer(); -// logger('ping timer: ' . sprintf('%01.4f %01.4f %01.4f %01.4f %01.4f %01.4f',$t6 - $t5, $t5 - $t4, $t4 - $t3, $t3 - $t2, $t2 - $t1, $t1 - $t0)); +// logger('ping timer: ' . sprintf('%01.4f %01.4f %01.4f %01.4f %01.4f %01.4f %01.4f %01.4f',$t8 - $t7, $t7 - $t6, $t6 - $t5, $t5 - $t4, $t4 - $t3, $t3 - $t2, $t2 - $t1, $t1 - $t0)); echo $x; killme(); diff --git a/Zotlabs/Module/Poke.php b/Zotlabs/Module/Poke.php index 0bd1187c7..d13ec5ced 100644 --- a/Zotlabs/Module/Poke.php +++ b/Zotlabs/Module/Poke.php @@ -150,7 +150,7 @@ class Poke extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Poke')); + nav_set_selected('Poke'); $name = ''; $id = ''; diff --git a/Zotlabs/Module/Post.php b/Zotlabs/Module/Post.php index c78484a45..f67cbf020 100644 --- a/Zotlabs/Module/Post.php +++ b/Zotlabs/Module/Post.php @@ -19,16 +19,16 @@ class Post extends \Zotlabs\Web\Controller { function init() { if(array_key_exists('auth', $_REQUEST)) { $x = new \Zotlabs\Zot\Auth($_REQUEST); - exit; } } function post() { - $z = new \Zotlabs\Zot\Receiver($_REQUEST['data'], get_config('system', 'prvkey'), new \Zotlabs\Zot\ZotHandler()); + if(array_key_exists('data',$_REQUEST)) { + $z = new \Zotlabs\Zot\Receiver($_REQUEST['data'], get_config('system', 'prvkey'), new \Zotlabs\Zot\ZotHandler()); + exit; + } - // notreached; - exit; } } diff --git a/Zotlabs/Module/Probe.php b/Zotlabs/Module/Probe.php index 859bed315..2e65f107c 100644 --- a/Zotlabs/Module/Probe.php +++ b/Zotlabs/Module/Probe.php @@ -8,7 +8,7 @@ class Probe extends \Zotlabs\Web\Controller { function get() { - nav_set_selected(t('Remote Diagnostics')); + nav_set_selected('Remote Diagnostics'); $o .= '

Probe Diagnostic

'; diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php index 6930d50ca..43106e3af 100644 --- a/Zotlabs/Module/Profile.php +++ b/Zotlabs/Module/Profile.php @@ -94,7 +94,6 @@ class Profile extends \Zotlabs\Web\Controller { echo \App::$profile['profile_vcard']; killme(); } - $is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false); @@ -102,11 +101,14 @@ class Profile extends \Zotlabs\Web\Controller { notice( t('Permission denied.') . EOL); return; } - - //$o .= profile_tabs($a, $is_owner, \App::$profile['channel_address']); - - \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; - + + head_add_link([ + 'rel' => 'alternate', + 'type' => 'application/json+oembed', + 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string), + 'title' => 'oembed' + ]); + $o .= advanced_profile($a); call_hooks('profile_advanced',$o); return $o; diff --git a/Zotlabs/Module/Profile_photo.php b/Zotlabs/Module/Profile_photo.php index 47b627015..27e6bc445 100644 --- a/Zotlabs/Module/Profile_photo.php +++ b/Zotlabs/Module/Profile_photo.php @@ -158,6 +158,9 @@ class Profile_photo extends \Zotlabs\Web\Controller { intval(local_channel()) ); + + + send_profile_photo_activity($channel,$base_image,$profile); } @@ -174,12 +177,17 @@ class Profile_photo extends \Zotlabs\Web\Controller { // We'll set the updated profile-photo timestamp even if it isn't the default profile, // so that browsers will do a cache update unconditionally + // Also set links back to site-specific profile photo url in case it was + // changed to a generic URL by a clone operation. Otherwise the new photo may + // not get pushed to other sites correctly. - - $r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s' + $r = q("UPDATE xchan set xchan_photo_mimetype = '%s', xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'", dbesc($im->getType()), dbesc(datetime_convert()), + dbesc(z_root() . '/photo/profile/l/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/m/' . $channel['channel_id']), + dbesc(z_root() . '/photo/profile/s/' . $channel['channel_id']), dbesc($channel['xchan_hash']) ); diff --git a/Zotlabs/Module/Pubsites.php b/Zotlabs/Module/Pubsites.php index d87967189..daec5dde3 100644 --- a/Zotlabs/Module/Pubsites.php +++ b/Zotlabs/Module/Pubsites.php @@ -30,13 +30,14 @@ class Pubsites extends \Zotlabs\Web\Controller { if($ret['success']) { $j = json_decode($ret['body'],true); if($j) { - $o .= ''; + $o .= '
' . t('Hub URL') . '' . t('Access Type') . '' . t('Registration Policy') . '' . t('Stats') . '' . t('Software') . '
'; if($rating_enabled) $o .= ''; $o .= ''; if($j['sites']) { foreach($j['sites'] as $jj) { - if(! \Zotlabs\Lib\System::compatible_project($jj['project'])) + $projectname = explode(' ',$jj['project']); + if(! \Zotlabs\Lib\System::compatible_project($projectname[0])) continue; if(strpos($jj['version'],' ')) { $x = explode(' ', $jj['version']); @@ -54,7 +55,7 @@ class Pubsites extends \Zotlabs\Web\Controller { $location = '
 '; } $urltext = str_replace(array('https://'), '', $jj['url']); - $o .= ''; + $o .= ''; if($rating_enabled) $o .= '' . $rate_links ; $o .= ''; diff --git a/Zotlabs/Module/Pubstream.php b/Zotlabs/Module/Pubstream.php index 42aa2b51b..15e2d8a74 100644 --- a/Zotlabs/Module/Pubstream.php +++ b/Zotlabs/Module/Pubstream.php @@ -7,10 +7,10 @@ require_once('include/conversation.php'); class Pubstream extends \Zotlabs\Web\Controller { function get($update = 0, $load = false) { - + if($load) $_SESSION['loadtime'] = datetime_convert(); - + if(observer_prohibited(true)) { return login(); @@ -19,15 +19,28 @@ class Pubstream extends \Zotlabs\Web\Controller { $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; if($disable_discover_tab) return; - + + $mid = ((x($_REQUEST,'mid')) ? $_REQUEST['mid'] : ''); + + if(strpos($mid,'b64.') === 0) + $decoded = @base64url_decode(substr($mid,4)); + if($decoded) + $mid = $decoded; + $item_normal = item_normal(); + $item_normal_update = item_normal_update(); $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); - if(! $update) { + if(! $update && !$load) { - $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 0); + nav_set_selected(t('Public Stream')); + + if(!$mid) + $_SESSION['static_loadtime'] = datetime_convert(); + + $static = ((local_channel()) ? channel_manual_conv_update(local_channel()) : 1); $maxheight = get_config('system','home_divmore_height'); if(! $maxheight) @@ -38,6 +51,10 @@ class Pubstream extends \Zotlabs\Web\Controller { . "; var profile_page = " . \App::$pager['page'] . "; divmore_height = " . intval($maxheight) . "; \r\n"; + //if we got a decoded hash we must encode it again before handing to javascript + if($decoded) + $mid = 'b64.' . base64url_encode($mid); + \App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( '$baseurl' => z_root(), '$pgtype' => 'pubstream', @@ -63,7 +80,7 @@ class Pubstream extends \Zotlabs\Web\Controller { '$cats' => '', '$tags' => '', '$dend' => '', - '$mid' => '', + '$mid' => $mid, '$verb' => '', '$dbegin' => '' )); @@ -105,7 +122,7 @@ class Pubstream extends \Zotlabs\Web\Controller { $simple_update = ''; if($static && $simple_update) - $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; + $simple_update .= " and item_thread_top = 0 and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; //logger('update: ' . $update . ' load: ' . $load); @@ -114,29 +131,46 @@ class Pubstream extends \Zotlabs\Web\Controller { $ordering = "commented"; if($load) { - - // Fetch a page full of parent items for this page - - $r = q("SELECT distinct item.id AS item_id, $ordering FROM item - left join abook on item.author_xchan = abook.abook_xchan - WHERE true $uids $item_normal - AND item.parent = item.id - and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets - ORDER BY $ordering DESC $pager_sql " - ); - - + if($mid) { + $r = q("SELECT parent AS item_id FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE mid like '%s' $uids $item_normal + and (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets LIMIT 1", + dbesc($mid . '%') + ); + } + else { + // Fetch a page full of parent items for this page + $r = q("SELECT distinct item.id AS item_id, $ordering FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE true $uids $item_normal + AND item.parent = item.id + and (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets + ORDER BY $ordering DESC $pager_sql " + ); + } } elseif($update) { - - $r = q("SELECT distinct item.id AS item_id, $ordering FROM item - left join abook on item.author_xchan = abook.abook_xchan - WHERE true $uids $item_normal - AND item.parent = item.id $simple_update - and (abook.abook_blocked = 0 or abook.abook_flags is null) - $sql_extra3 $sql_extra $sql_nets" - ); + if($mid) { + $r = q("SELECT parent AS item_id FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE mid like '%s' $uids $item_normal_update $simple_update + and (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets LIMIT 1", + dbesc($mid . '%') + ); + } + else { + $r = q("SELECT distinct item.id AS item_id, $ordering FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE true $uids $item_normal_update + AND item.parent = item.id $simple_update + and (abook.abook_blocked = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets" + ); + } $_SESSION['loadtime'] = datetime_convert(); } // Then fetch all the children of the parents that are on this page @@ -168,6 +202,9 @@ class Pubstream extends \Zotlabs\Web\Controller { $mode = ('network'); $o .= conversation($items,$mode,$update,$page_mode); + + if($mid) + $o .= '
'; if(($items) && (! $update)) $o .= alt_pager($a,count($items)); diff --git a/Zotlabs/Module/Register.php b/Zotlabs/Module/Register.php index d4573156c..95e3ca96f 100644 --- a/Zotlabs/Module/Register.php +++ b/Zotlabs/Module/Register.php @@ -27,7 +27,7 @@ class Register extends \Zotlabs\Web\Controller { $result = check_account_email($_REQUEST['email']); break; case 'password_check.json': - $result = check_account_password($_REQUEST['password']); + $result = check_account_password($_REQUEST['password1']); break; default: break; @@ -123,12 +123,19 @@ class Register extends \Zotlabs\Web\Controller { if($policy == REGISTER_OPEN ) { if($email_verify) { $res = verify_email_address($result); + if($res) { + info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ; + } } else { $res = send_register_success_email($result['email'],$result['password']); } if($res) { - info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ; + if($invite_code) { + info( t('Registration successful. Continue to create your first channel...') . EOL ) ; + } else { + info( t('Registration successful. Please check your email for validation instructions.') . EOL ) ; + } } } elseif($policy == REGISTER_APPROVE) { @@ -167,7 +174,8 @@ class Register extends \Zotlabs\Web\Controller { $next_page = $x; $_SESSION['workflow'] = true; } - + + unset($_SESSION['login_return_url']); goaway(z_root() . '/' . $next_page); } @@ -231,8 +239,8 @@ class Register extends \Zotlabs\Web\Controller { $enable_tos = 1 - intval(get_config('system','no_termsofservice')); $email = array('email', t('Your email address'), ((x($_REQUEST,'email')) ? strip_tags(trim($_REQUEST['email'])) : "")); - $password = array('password', t('Choose a password'), ((x($_REQUEST,'password')) ? trim($_REQUEST['password']) : "")); - $password2 = array('password2', t('Please re-enter your password'), ((x($_REQUEST,'password2')) ? trim($_REQUEST['password2']) : "")); + $password = array('password', t('Choose a password'), ''); + $password2 = array('password2', t('Please re-enter your password'), ''); $invite_code = array('invite_code', t('Please enter your invitation code'), ((x($_REQUEST,'invite_code')) ? strip_tags(trim($_REQUEST['invite_code'])) : "")); $name = array('name', t('Name or caption'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Examples: "Bob Jameson", "Lisa and her Horses", "Soccer", "Aviation Group"')); $nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl')); diff --git a/Zotlabs/Module/Rmagic.php b/Zotlabs/Module/Rmagic.php index 9fcc72441..bfc03f6ec 100644 --- a/Zotlabs/Module/Rmagic.php +++ b/Zotlabs/Module/Rmagic.php @@ -17,8 +17,8 @@ class Rmagic extends \Zotlabs\Web\Controller { if($r) { if($r[0]['hubloc_url'] === z_root()) goaway(z_root() . '/login'); - $dest = z_root() . '/' . str_replace('zid=','zid_=',\App::$query_string); - goaway($r[0]['hubloc_url'] . '/magic' . '?f=&dest=' . $dest); + $dest = z_root() . '/' . str_replace(['rmagic','zid='],['','zid_='],\App::$query_string); + goaway($r[0]['hubloc_url'] . '/magic' . '?f=&owa=1&dest=' . $dest); } } } @@ -61,9 +61,9 @@ class Rmagic extends \Zotlabs\Web\Controller { if($_SESSION['return_url']) $dest = urlencode(z_root() . '/' . str_replace('zid=','zid_=',$_SESSION['return_url'])); else - $dest = urlencode(z_root() . '/' . str_replace('zid=','zid_=',\App::$query_string)); + $dest = urlencode(z_root() . '/' . str_replace([ 'rmagic', 'zid=' ] ,[ '', 'zid_='],\App::$query_string)); - goaway($url . '/magic' . '?f=&dest=' . $dest); + goaway($url . '/magic' . '?f=&owa=1&dest=' . $dest); } } } diff --git a/Zotlabs/Module/Rpost.php b/Zotlabs/Module/Rpost.php index 731eab82e..5d2f0d7e8 100644 --- a/Zotlabs/Module/Rpost.php +++ b/Zotlabs/Module/Rpost.php @@ -20,6 +20,7 @@ require_once('include/zot.php'); * body= Body of post * url= URL which will be parsed and the results appended to the body * source= Source application + * post_id= post_id of post to 'share' (local use only) * remote_return= absolute URL to return after posting is finished * type= choices are 'html' or 'bbcode', default is 'bbcode' * @@ -60,7 +61,7 @@ class Rpost extends \Zotlabs\Web\Controller { return login(); } - nav_set_selected(t('Post')); + nav_set_selected('Post'); // If we have saved rpost session variables, but nothing in the current $_REQUEST, recover the saved variables @@ -90,8 +91,6 @@ class Rpost extends \Zotlabs\Web\Controller { } $plaintext = true; - // if(feature_enabled(local_channel(),'richtext')) - // $plaintext = false; if(array_key_exists('type', $_REQUEST) && $_REQUEST['type'] === 'html') { require_once('include/html2bbcode.php'); @@ -110,28 +109,67 @@ class Rpost extends \Zotlabs\Web\Controller { if($x['success']) $_REQUEST['body'] = $_REQUEST['body'] . $x['body']; } + + if($_REQUEST['post_id']) { + $r = q("SELECT * from item WHERE id = %d LIMIT 1", + intval($_REQUEST['post_id']) + ); + if(($r) && (! intval($r[0]['item_private']))) { + $sql_extra = item_permissions_sql($r[0]['uid']); + + $r = q("select * from item where id = %d $sql_extra", + intval($_REQUEST['post_id']) + ); + if($r && $r[0]['mimetype'] === 'text/bbcode') { + + xchan_query($r); + + $is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false); + if($is_photo) { + $object = json_decode($r[0]['obj'],true); + $photo_bb = $object['body']; + } + + if (strpos($r[0]['body'], "[/share]") !== false) { + $pos = strpos($r[0]['body'], "[share"); + $i = substr($r[0]['body'], $pos); + } else { + $i = "[share author='".urlencode($r[0]['author']['xchan_name']). + "' profile='".$r[0]['author']['xchan_url'] . + "' avatar='".$r[0]['author']['xchan_photo_s']. + "' link='".$r[0]['plink']. + "' posted='".$r[0]['created']. + "' message_id='".$r[0]['mid']."']"; + if($r[0]['title']) + $i .= '[b]'.$r[0]['title'].'[/b]'."\r\n"; + $i .= (($is_photo) ? $photo_bb . "\r\n" . $r[0]['body'] : $r[0]['body']); + $i .= "[/share]"; + } + } + } + $_REQUEST['body'] = $_REQUEST['body'] . $i; + } $x = array( - 'is_owner' => true, - 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), - 'default_location' => $channel['channel_location'], - 'nickname' => $channel['channel_address'], - 'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), - 'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), - 'permissions' => $channel_acl, - 'bang' => '', - 'visitor' => true, - 'profile_uid' => local_channel(), - 'title' => $_REQUEST['title'], - 'body' => $_REQUEST['body'], - 'attachment' => $_REQUEST['attachment'], - 'source' => ((x($_REQUEST,'source')) ? strip_tags($_REQUEST['source']) : ''), - 'return_path' => 'rpost/return', - 'bbco_autocomplete' => 'bbcode', - 'editor_autocomplete'=> true, - 'bbcode' => true, - 'jotnets' => true - + 'is_owner' => true, + 'allow_location' => ((intval(get_pconfig($channel['channel_id'],'system','use_browser_location'))) ? '1' : ''), + 'default_location' => $channel['channel_location'], + 'nickname' => $channel['channel_address'], + 'lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), + 'acl' => populate_acl($channel_acl, true, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'), get_post_aclDialogDescription(), 'acl_dialog_post'), + 'permissions' => $channel_acl, + 'bang' => '', + 'visitor' => true, + 'profile_uid' => local_channel(), + 'title' => $_REQUEST['title'], + 'body' => $_REQUEST['body'], + 'attachment' => $_REQUEST['attachment'], + 'source' => ((x($_REQUEST,'source')) ? strip_tags($_REQUEST['source']) : ''), + 'return_path' => 'rpost/return', + 'bbco_autocomplete' => 'bbcode', + 'editor_autocomplete' => true, + 'bbcode' => true, + 'jotnets' => true ); $editor = status_editor($a,$x); diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index e1d35b879..37e9a336f 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -22,7 +22,7 @@ class Search extends \Zotlabs\Web\Controller { if($load) $_SESSION['loadtime'] = datetime_convert(); - nav_set_selected(t('Search')); + nav_set_selected('Search'); require_once("include/bbcode.php"); require_once('include/security.php'); diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index 3e6adcf8d..41e23b717 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -199,6 +199,10 @@ class Channel { $vnotify += intval($_POST['vnotify10']); if(x($_POST,'vnotify11')) $vnotify += intval($_POST['vnotify11']); + if(x($_POST,'vnotify12')) + $vnotify += intval($_POST['vnotify12']); + if(x($_POST,'vnotify13') && (get_config('system', 'disable_discover_tab') != 1)) + $vnotify += intval($_POST['vnotify13']); $always_show_in_notices = x($_POST,'always_show_in_notices') ? 1 : 0; @@ -505,7 +509,7 @@ class Channel { '$expire' => array('expire',t('Expire other channel content after this many days'),$expire, t('0 or blank to use the website limit.') . ' ' . ((intval($sys_expire)) ? sprintf( t('This website expires after %d days.'),intval($sys_expire)) : t('This website does not expire imported content.')) . ' ' . t('The website limit takes precedence if lower than your limit.')), '$maxreq' => array('maxreq', t('Maximum Friend Requests/Day:'), intval($channel['channel_max_friend_req']) , t('May reduce spam activity')), - '$permissions' => t('Default Access Control List (ACL)'), + '$permissions' => t('Default Privacy Group'), '$permdesc' => t("\x28click to open/close\x29"), '$aclselect' => populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromDescription(t('Use my default audience setting for the type of object published'))), '$allow_cid' => acl2json($perm_defaults['allow_cid']), @@ -555,6 +559,8 @@ class Channel { '$vnotify9' => array('vnotify9', t('System critical alerts'), ($vnotify & VNOTIFY_ALERT), VNOTIFY_ALERT, t('Recommended'), $yes_no), '$vnotify10' => array('vnotify10', t('New connections'), ($vnotify & VNOTIFY_INTRO), VNOTIFY_INTRO, t('Recommended'), $yes_no), '$vnotify11' => array('vnotify11', t('System Registrations'), ($vnotify & VNOTIFY_REGISTER), VNOTIFY_REGISTER, '', $yes_no), + '$vnotify12' => array('vnotify12', t('Unseen shared files'), ($vnotify & VNOTIFY_FILES), VNOTIFY_FILES, '', $yes_no), + '$vnotify13' => ((get_config('system', 'disable_discover_tab') != 1) ? array('vnotify13', t('Unseen public activity'), ($vnotify & VNOTIFY_PUBS), VNOTIFY_PUBS, '', $yes_no) : array()), '$always_show_in_notices' => array('always_show_in_notices', t('Also show new wall posts, private messages and connections under Notices'), $always_show_in_notices, 1, '', $yes_no), '$evdays' => array('evdays', t('Notify me of events this many days in advance'), $evdays, t('Must be greater than 0')), diff --git a/Zotlabs/Module/Suggest.php b/Zotlabs/Module/Suggest.php index c3f4a6d5a..f79e4e245 100644 --- a/Zotlabs/Module/Suggest.php +++ b/Zotlabs/Module/Suggest.php @@ -29,7 +29,7 @@ class Suggest extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Suggest Channels')); + nav_set_selected('Suggest Channels'); $_SESSION['return_url'] = z_root() . '/' . \App::$cmd; diff --git a/Zotlabs/Module/Tasks.php b/Zotlabs/Module/Tasks.php index c8deb11bf..0709f31f6 100644 --- a/Zotlabs/Module/Tasks.php +++ b/Zotlabs/Module/Tasks.php @@ -19,8 +19,8 @@ class Tasks extends \Zotlabs\Web\Controller { $arr['all'] = 1; $x = tasks_fetch($arr); + $x['html'] = ''; if($x['tasks']) { - $x['html'] = ''; foreach($x['tasks'] as $y) { $x['html'] .= '
' . $y['summary'] . '
'; } @@ -69,6 +69,7 @@ class Tasks extends \Zotlabs\Web\Controller { if($x) $ret['success'] = true; } + json_return_and_die($ret); } diff --git a/Zotlabs/Module/Thing.php b/Zotlabs/Module/Thing.php index 95c6c5636..f816632ab 100644 --- a/Zotlabs/Module/Thing.php +++ b/Zotlabs/Module/Thing.php @@ -91,6 +91,7 @@ class Thing extends \Zotlabs\Web\Controller { } $orig_record = $t[0]; if($photo != $orig_record['obj_imgurl']) { + delete_thing_photo($orig_record['obj_imgurl'],get_observer_hash()); $arr = import_xchan_photo($photo,get_observer_hash(),true); $local_photo = $arr[0]; $local_photo_type = $arr[3]; @@ -336,6 +337,9 @@ class Thing extends \Zotlabs\Web\Controller { return ''; } + + delete_thing_photo($r[0]['obj_imgurl'],get_observer_hash()); + $x = q("delete from obj where obj_obj = '%s' and obj_type = %d and obj_channel = %d", dbesc($thing_hash), intval(TERM_OBJ_THING), diff --git a/Zotlabs/Module/Update_cards.php b/Zotlabs/Module/Update_cards.php new file mode 100644 index 000000000..bb87357e8 --- /dev/null +++ b/Zotlabs/Module/Update_cards.php @@ -0,0 +1,39 @@ + 1) && (argv(1) == 'load')) ? 1 : 0); + + header("Content-type: text/html"); + echo "
\r\n"; + + killme(); + + + $mod = new Cards(); + + $text = $mod->get($profile_uid,$load); + + /** + * reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well + */ + + echo str_replace("\t",' ',$text); + echo (($_GET['msie'] == 1) ? '' : ''); + echo "\r\n"; + killme(); + +} +} diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index 03d4cb37b..e001ad929 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -97,6 +97,12 @@ class Wall_attach extends \Zotlabs\Web\Controller { $s = "\n\n" . '[attachment]' . $r['data']['hash'] . ',' . $r['data']['revision'] . '[/attachment]' . "\n"; } + + $sync = attach_export_data($channel,$r['data']['hash']); + if($sync) { + build_sync_packet($channel['channel_id'],array('file' => array($sync))); + } + if($using_api) return $s; diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index e449a790f..97ec55ba3 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -35,7 +35,7 @@ class Webpages extends \Zotlabs\Web\Controller { return; } - nav_set_selected(t('Webpages')); + nav_set_selected('Webpages'); $which = argv(1); diff --git a/Zotlabs/Module/Wfinger.php b/Zotlabs/Module/Wfinger.php index 07a7b7735..2e9307196 100644 --- a/Zotlabs/Module/Wfinger.php +++ b/Zotlabs/Module/Wfinger.php @@ -30,9 +30,15 @@ class Wfinger extends \Zotlabs\Web\Controller { $resource = $_REQUEST['resource']; logger('webfinger: ' . $resource,LOGGER_DEBUG); + + $root_resource = false; + + if(strcasecmp(rtrim($resource,'/'),z_root()) === 0) + $root_resource = true; + $r = null; - if($resource) { + if(($resource) && (! $root_resource)) { if(strpos($resource,'acct:') === 0) { $channel = str_replace('acct:','',$resource); @@ -60,7 +66,25 @@ class Wfinger extends \Zotlabs\Web\Controller { header('Access-Control-Allow-Origin: *'); + + if($root_resource) { + $result['subject'] = $resource; + $result['properties'] = [ + 'https://w3id.org/security/v1#publicKeyPem' => get_config('system','pubkey') + ]; + $result['links'] = [ + [ + 'rel' => 'http://purl.org/openwebauth/v1', + 'type' => 'application/x-zot+json', + 'href' => z_root() . '/owa', + ], + ]; + + + + } + if($resource && $r) { $h = q("select hubloc_addr from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0", @@ -84,7 +108,8 @@ class Wfinger extends \Zotlabs\Web\Controller { $result['properties'] = [ 'http://webfinger.net/ns/name' => $r[0]['channel_name'], - 'http://xmlns.com/foaf/0.1/name' => $r[0]['channel_name'] + 'http://xmlns.com/foaf/0.1/name' => $r[0]['channel_name'], + 'https://w3id.org/security/v1#publicKeyPem' => $r[0]['xchan_pubkey'] ]; foreach($aliases as $alias) @@ -98,7 +123,13 @@ class Wfinger extends \Zotlabs\Web\Controller { 'type' => $r[0]['xchan_photo_mimetype'], 'href' => $r[0]['xchan_photo_l'] ], - + + [ + 'rel' => 'http://microformats.org/profile/hcard', + 'type' => 'text/html', + 'href' => z_root() . '/hcard/' . $r[0]['channel_address'] + ], + [ 'rel' => 'http://webfinger.net/rel/profile-page', 'href' => z_root() . '/profile/' . $r[0]['channel_address'], @@ -124,6 +155,13 @@ class Wfinger extends \Zotlabs\Web\Controller { 'rel' => 'http://purl.org/zot/protocol', 'href' => z_root() . '/.well-known/zot-info' . '?address=' . $r[0]['xchan_addr'], ], + + [ + 'rel' => 'http://purl.org/openwebauth/v1', + 'type' => 'application/x-zot+json', + 'href' => z_root() . '/owa', + ], + [ 'rel' => 'magic-public-key', @@ -136,14 +174,16 @@ class Wfinger extends \Zotlabs\Web\Controller { $result['zot'] = zotinfo( [ 'address' => $r[0]['xchan_addr'] ]); } } - else { + + if(! $result) { header($_SERVER["SERVER_PROTOCOL"] . ' ' . 400 . ' ' . 'Bad Request'); killme(); } $arr = [ 'channel' => $r[0], 'request' => $_REQUEST, 'result' => $result ]; call_hooks('webfinger',$arr); - + + json_return_and_die($arr['result'],'application/jrd+json'); } diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index 4dc11c683..d6a01af11 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -76,7 +76,7 @@ class Wiki extends \Zotlabs\Web\Controller { $wiki_owner = true; - nav_set_selected(t('Wiki')); + nav_set_selected('Wiki'); // Obtain the default permission settings of the channel $owner_acl = array( @@ -113,12 +113,13 @@ class Wiki extends \Zotlabs\Web\Controller { $o = ''; // Download a wiki -/* + if((argc() > 3) && (argv(2) === 'download') && (argv(3) === 'wiki')) { $resource_id = argv(4); + $w = Zlib\NativeWiki::get_wiki($owner['channel_id'],$observer_hash,$resource_id); - $w = Zlib\NativeWiki::get_wiki($owner,$observer_hash,$resource_id); +// $w = Zlib\NativeWiki::get_wiki($owner,$observer_hash,$resource_id); if(! $w['htmlName']) { notice(t('Error retrieving wiki') . EOL); } @@ -133,8 +134,41 @@ class Wiki extends \Zotlabs\Web\Controller { $zip_filename = $w['urlName']; $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + // Generate the zip file - ZLib\ExtendedZip::zipTree($w['path'], $zip_filepath, \ZipArchive::CREATE); + + $zip = new \ZipArchive; + $r = $zip->open($zip_filepath, \ZipArchive::CREATE); + if($r === true) { + $pages = []; + $i = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' order by revision desc", + dbesc($resource_id) + ); + + if($i) { + foreach($i as $iv) { + if(in_array($iv['mid'],$pages)) + continue; + + if($iv['mimetype'] === 'text/plain') { + $content = html_entity_decode($iv['body'],ENT_COMPAT,'UTF-8'); + } + elseif($iv['mimetype'] === 'text/bbcode') { + $content = html_entity_decode($iv['body'],ENT_COMPAT,'UTF-8'); + } + elseif($iv['mimetype'] === 'text/markdown') { + $content = html_entity_decode(Zlib\MarkdownSoap::unescape($iv['body']),ENT_COMPAT,'UTF-8'); + } + $fname = get_iconfig($iv['id'],'nwikipage','pagetitle') . Zlib\NativeWikiPage::get_file_ext($iv); + $zip->addFromString($fname,$content); + $pages[] = $iv['mid']; + } + + + } + + } + $zip->close(); // Output the file for download @@ -153,10 +187,11 @@ class Wiki extends \Zotlabs\Web\Controller { killme(); } -*/ + switch(argc()) { case 2: $wikis = Zlib\NativeWiki::listwikis($owner, get_observer_hash()); + if($wikis) { $o .= replace_macros(get_markup_template('wikilist.tpl'), array( '$header' => t('Wikis'), @@ -170,16 +205,19 @@ class Wiki extends \Zotlabs\Web\Controller { '$create' => t('Create New'), '$submit' => t('Submit'), '$wikiName' => array('wikiName', t('Wiki name')), - '$mimeType' => array('mimeType', t('Content type'), '', '', ['text/markdown' => 'Markdown', 'text/bbcode' => 'BB Code']), + '$mimeType' => array('mimeType', t('Content type'), '', '', ['text/markdown' => t('Markdown'), 'text/bbcode' => t('BBcode'), 'text/plain' => t('Text') ]), '$name' => t('Name'), '$type' => t('Type'), + '$unlocked' => t('Any type'), '$lockstate' => $x['lockstate'], '$acl' => $x['acl'], '$allow_cid' => $x['allow_cid'], '$allow_gid' => $x['allow_gid'], '$deny_cid' => $x['deny_cid'], '$deny_gid' => $x['deny_gid'], - '$notify' => array('postVisible', t('Create a status post for this wiki'), '', '', array(t('No'), t('Yes'))) + '$typelock' => array('typelock', t('Lock content type'), '', '', array(t('No'), t('Yes'))), + '$notify' => array('postVisible', t('Create a status post for this wiki'), '', '', array(t('No'), t('Yes'))), + '$edit_wiki_name' => t('Edit Wiki Name') )); return $o; @@ -259,9 +297,11 @@ class Wiki extends \Zotlabs\Web\Controller { goaway(z_root() . '/' . argv(0) . '/' . argv(1) ); } - $mimeType = $p['mimeType']; + $mimeType = $p['pageMimeType']; $sampleContent = (($mimeType == 'text/bbcode') ? '[h3]' . t('New page') . '[/h3]' : '### ' . t('New page')); + if($mimeType === 'text/plain') + $sampleContent = t('New page'); $content = (($p['content'] == '') ? $sampleContent : $p['content']); @@ -269,7 +309,10 @@ class Wiki extends \Zotlabs\Web\Controller { if($mimeType == 'text/bbcode') { $renderedContent = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))), argv(0) . '/' . argv(1) . '/' . $wikiUrlName); } - else { + elseif($mimeType === 'text/plain') { + $renderedContent = str_replace(["\n",' ',"\t"],[EOL,' ','    '],htmlentities($content,ENT_COMPAT,'UTF-8',false)); + } + elseif($mimeType === 'text/markdown') { $content = Zlib\MarkdownSoap::unescape($content); $html = Zlib\NativeWikiPage::generate_toc(zidify_text(MarkdownExtra::defaultTransform(Zlib\NativeWikiPage::bbcode($content)))); $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); @@ -290,6 +333,9 @@ class Wiki extends \Zotlabs\Web\Controller { '$cancel' => t('Cancel') )); + $types = [ 'text/bbcode' => t('BBcode'), 'text/markdown' => t('Markdown'), 'text/plain' => 'Text' ]; + $currenttype = $types[$mimeType]; + $placeholder = t('Short description of your changes (optional)'); $o .= replace_macros(get_markup_template('wiki.tpl'),array( @@ -304,6 +350,7 @@ class Wiki extends \Zotlabs\Web\Controller { '$resource_id' => $resource_id, '$page' => $pageUrlName, '$mimeType' => $mimeType, + '$typename' => $currenttype, '$content' => $content, '$renderedContent' => $renderedContent, '$pageRename' => array('pageRename', t('New page name'), '', ''), @@ -323,7 +370,7 @@ class Wiki extends \Zotlabs\Web\Controller { '$modalerroralbum' => t('Error getting album'), )); - if($p['mimeType'] != 'text/bbcode') + if($p['pageMimeType'] === 'text/markdown') head_add_js('/library/ace/ace.js'); // Ace Code Editor return $o; @@ -347,17 +394,17 @@ class Wiki extends \Zotlabs\Web\Controller { if((argc() > 2) && (argv(2) === 'preview')) { $content = $_POST['content']; $resource_id = $_POST['resource_id']; + $w = Zlib\NativeWiki::get_wiki($owner['channel_id'],$observer_hash,$resource_id); $wikiURL = argv(0) . '/' . argv(1) . '/' . $w['urlName']; - $mimeType = $w['mimeType']; + $mimeType = $_POST['mimetype']; - if($mimeType == 'text/bbcode') { + if($mimeType === 'text/bbcode') { $html = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))),$wikiURL); } - else { - + elseif($mimeType === 'text/markdown') { $bb = Zlib\NativeWikiPage::bbcode($content); $x = new ZLib\MarkdownSoap($bb); $md = $x->clean(); @@ -366,6 +413,9 @@ class Wiki extends \Zotlabs\Web\Controller { $html = Zlib\NativeWikiPage::generate_toc(zidify_text($html)); $html = Zlib\NativeWikiPage::convert_links($html,$wikiURL); } + elseif($mimeType === 'text/plain') { + $html = str_replace(["\n",' ',"\t"],[EOL,' ','    '],htmlentities($content,ENT_COMPAT,'UTF-8',false)); + } json_return_and_die(array('html' => $html, 'success' => true)); } @@ -386,6 +436,7 @@ class Wiki extends \Zotlabs\Web\Controller { $wiki['htmlName'] = escape_tags($_POST['wikiName']); $wiki['urlName'] = urlencode(urlencode($_POST['wikiName'])); $wiki['mimeType'] = $_POST['mimeType']; + $wiki['typelock'] = $_POST['typelock']; if($wiki['urlName'] === '') { notice( t('Error creating wiki. Invalid name.') . EOL); @@ -406,7 +457,7 @@ class Wiki extends \Zotlabs\Web\Controller { $r = Zlib\NativeWiki::create_wiki($owner, $observer_hash, $wiki, $acl); if($r['success']) { Zlib\NativeWiki::sync_a_wiki_item($owner['channel_id'],$r['item_id'],$r['item']['resource_id']); - $homePage = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash,'Home', $r['item']['resource_id']); + $homePage = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash,'Home', $r['item']['resource_id'], $wiki['mimeType']); if(! $homePage['success']) { notice( t('Wiki created, but error creating Home page.')); goaway(z_root() . '/wiki/' . $nick . '/' . $wiki['urlName']); @@ -420,6 +471,52 @@ class Wiki extends \Zotlabs\Web\Controller { } } + // Update a wiki + // /wiki/channel/update/wiki + if ((argc() > 3) && (argv(2) === 'update') && (argv(3) === 'wiki')) { + // Only the channel owner can update a wiki, at least until we create a + // more detail permissions framework + + if (local_channel() !== intval($owner['channel_id'])) { + goaway('/' . argv(0) . '/' . $nick . '/'); + } + + $arr = []; + + $arr['urlName'] = urlencode(urlencode($_POST['origRawName'])); + + if($_POST['updateRawName']) + $arr['updateRawName'] = $_POST['updateRawName']; + + if(($arr['urlName'] || $arr['updateRawName']) === '') { + notice( t('Error updating wiki. Invalid name.') . EOL); + goaway('/wiki'); + return; //not reached + } + + $wiki = Zlib\NativeWiki::exists_by_name($owner['channel_id'], $arr['urlName']); + + if($wiki['resource_id']) { + + $arr['resource_id'] = $wiki['resource_id']; + + $acl = new \Zotlabs\Access\AccessList($owner); + $acl->set_from_array($_POST); + + $r = Zlib\NativeWiki::update_wiki($owner['channel_id'], $observer_hash, $arr, $acl); + if($r['success']) { + Zlib\NativeWiki::sync_a_wiki_item($owner['channel_id'],$r['item_id'],$r['item']['resource_id']); + goaway(z_root() . '/wiki/' . $nick); + } + else { + notice( t('Error updating wiki')); + goaway(z_root() . '/wiki'); + } + + } + goaway(z_root() . '/wiki'); + } + // Delete a wiki if ((argc() > 3) && (argv(2) === 'delete') && (argv(3) === 'wiki')) { @@ -445,11 +542,13 @@ class Wiki extends \Zotlabs\Web\Controller { // Create a page if ((argc() === 4) && (argv(2) === 'create') && (argv(3) === 'page')) { + $mimetype = $_POST['mimetype']; + $resource_id = $_POST['resource_id']; // Determine if observer has permission to create a page + - - $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash, $mimetype); if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); @@ -459,7 +558,7 @@ class Wiki extends \Zotlabs\Web\Controller { if(urlencode(escape_tags($_POST['pageName'])) === '') { json_return_and_die(array('message' => 'Error creating page. Invalid name.', 'success' => false)); } - $page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id); + $page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id, $mimetype); if($page['item_id']) { $commit = Zlib\NativeWikiPage::commit(array( diff --git a/Zotlabs/Module/Xrd.php b/Zotlabs/Module/Xrd.php index 60a8f58fa..959e31cbe 100644 --- a/Zotlabs/Module/Xrd.php +++ b/Zotlabs/Module/Xrd.php @@ -50,6 +50,7 @@ class Xrd extends \Zotlabs\Web\Controller { '$accturi' => $resource, '$subject' => $subject, '$aliases' => $aliases, + '$channel_url' => z_root() . '/channel/' . $r[0]['channel_address'], '$profile_url' => z_root() . '/channel/' . $r[0]['channel_address'], '$hcard_url' => z_root() . '/hcard/' . $r[0]['channel_address'], '$atom' => z_root() . '/ofeed/' . $r[0]['channel_address'], diff --git a/Zotlabs/Module/Zfinger.php b/Zotlabs/Module/Zfinger.php index 2ff605fc9..0f7f6a64b 100644 --- a/Zotlabs/Module/Zfinger.php +++ b/Zotlabs/Module/Zfinger.php @@ -9,8 +9,36 @@ class Zfinger extends \Zotlabs\Web\Controller { require_once('include/zot.php'); require_once('include/crypto.php'); - $x = zotinfo($_REQUEST); + + if($x && $x['guid'] && $x['guid_sig']) { + $chan_hash = make_xchan_hash($x['guid'],$x['guid_sig']); + if($chan_hash) { + $chan = channelx_by_hash($chan_hash); + } + } + + $headers = []; + $headers['Content-Type'] = 'application/json' ; + $ret = json_encode($x); + + if($chan) { + $hash = \Zotlabs\Web\HTTPSig::generate_digest($ret,false); + $headers['Digest'] = 'SHA-256=' . $hash; + \Zotlabs\Web\HTTPSig::create_sig('',$headers,$chan['channel_prvkey'], + 'acct:' . $chan['channel_address'] . '@' . \App::get_hostname(),true); + } + else { + foreach($headers as $k => $v) { + header($k . ': ' . $v); + } + } + + echo $ret; + killme(); + + + json_return_and_die($x); } diff --git a/Zotlabs/Render/Comanche.php b/Zotlabs/Render/Comanche.php index beee9796e..8831bd117 100644 --- a/Zotlabs/Render/Comanche.php +++ b/Zotlabs/Render/Comanche.php @@ -121,6 +121,11 @@ class Comanche { if($cnt) \App::$layout['theme'] = trim($matches[1]); + $cnt = preg_match("/\[navbar\](.*?)\[\/navbar\]/ism", $s, $matches); + if($cnt) + \App::$layout['navbar'] = trim($matches[1]); + + $cnt = preg_match_all("/\[webpage\](.*?)\[\/webpage\]/ism", $s, $matches, PREG_SET_ORDER); if($cnt) { // only the last webpage definition is used if there is more than one @@ -148,6 +153,7 @@ class Comanche { * $observer.address - xchan_addr or false * $observer.name - xchan_name or false * $observer - xchan_hash of observer or empty string + * $local_channel - logged in channel_id or false */ function get_condition_var($v) { @@ -157,6 +163,9 @@ class Comanche { return get_config($x[1],$x[2]); elseif($x[0] === 'request') return $_SERVER['REQUEST_URI']; + elseif($x[0] === 'local_channel') { + return local_channel(); + } elseif($x[0] === 'observer') { if(count($x) > 1) { if($x[1] == 'language') @@ -168,6 +177,8 @@ class Comanche { return $y['xchan_addr']; elseif($x[1] == 'name') return $y['xchan_name']; + elseif($x[1] == 'webname') + return substr($y['xchan_addr'],0,strpos($y['xchan_addr'],'@')); return false; } return get_observer_hash(); @@ -450,6 +461,9 @@ class Comanche { } } + if(! purify_filename($name)) + return ''; + $clsname = ucfirst($name); $nsname = "\\Zotlabs\\Widget\\" . $clsname; diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index 3af9fcab1..6f6f4a292 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -244,7 +244,7 @@ class Browser extends DAV\Browser\Plugin { $a = false; - nav_set_selected(t('Files')); + nav_set_selected('Files'); \App::$page['content'] = $html; load_pdl(); @@ -347,7 +347,8 @@ class Browser extends DAV\Browser\Plugin { '$return_url' => \App::$cmd, '$path' => $path, '$folder' => find_folder_hash_by_path($this->auth->owner_id, $path), - '$dragdroptext' => t('Drop files here to immediately upload') + '$dragdroptext' => t('Drop files here to immediately upload'), + '$notify' => ['notify', t('Show in your contacts shared folder'), 0, '', [t('No'), t('Yes')]] )); } diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php index 7a102134f..332bf6896 100644 --- a/Zotlabs/Storage/File.php +++ b/Zotlabs/Storage/File.php @@ -254,7 +254,7 @@ class File extends DAV\Node implements DAV\IFile { // @todo this should be a global definition $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - if (in_array($r[0]['filetype'], $unsafe_types)) { + if (in_array($r[0]['filetype'], $unsafe_types) && (! channel_codeallowed($this->data['uid']))) { header('Content-disposition: attachment; filename="' . $r[0]['filename'] . '"'); header('Content-type: text/plain'); } @@ -300,7 +300,7 @@ class File extends DAV\Node implements DAV\IFile { public function getContentType() { // @todo this should be a global definition. $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - if (in_array($this->data['filetype'], $unsafe_types)) { + if (in_array($this->data['filetype'], $unsafe_types) && (! channel_codeallowed($this->data['uid']))) { return 'text/plain'; } return $this->data['filetype']; diff --git a/Zotlabs/Web/CheckJS.php b/Zotlabs/Web/CheckJS.php index 109790fa5..8179ceb15 100644 --- a/Zotlabs/Web/CheckJS.php +++ b/Zotlabs/Web/CheckJS.php @@ -21,9 +21,9 @@ class CheckJS { $page = urlencode(\App::$query_string); if($test) { - self::$jsdisabled = 1; + $this->jsdisabled = 1; if(array_key_exists('jsdisabled',$_COOKIE)) - self::$jsdisabled = $_COOKIE['jsdisabled']; + $this->jsdisabled = $_COOKIE['jsdisabled']; if(! array_key_exists('jsdisabled',$_COOKIE)) { \App::$page['htmlhead'] .= "\r\n" . '' . "\r\n"; @@ -41,7 +41,7 @@ class CheckJS { } function disabled() { - return self::$jsdisabled; + return $this->jsdisabled; } diff --git a/Zotlabs/Web/HTTPHeaders.php b/Zotlabs/Web/HTTPHeaders.php index 1e4c1bf84..4be51a8f3 100644 --- a/Zotlabs/Web/HTTPHeaders.php +++ b/Zotlabs/Web/HTTPHeaders.php @@ -40,6 +40,20 @@ class HTTPHeaders { function fetch() { return $this->parsed; } + + function fetcharr() { + $ret = []; + if($this->parsed) { + foreach($this->parsed as $x) { + foreach($x as $y => $z) { + $ret[$y] = $z; + } + } + } + return $ret; + } + + } diff --git a/Zotlabs/Web/HTTPSig.php b/Zotlabs/Web/HTTPSig.php new file mode 100644 index 000000000..1c66b8cf4 --- /dev/null +++ b/Zotlabs/Web/HTTPSig.php @@ -0,0 +1,313 @@ + '', + 'header_signed' => false, + 'header_valid' => false, + 'content_signed' => false, + 'content_valid' => false + ]; + + // decide if $data arrived via controller submission or curl + if(is_array($data) && $data['header']) { + if(! $data['success']) + return $result; + $h = new \Zotlabs\Web\HTTPHeaders($data['header']); + $headers = $h->fetcharr(); + $body = $data['body']; + } + + else { + $headers = []; + $headers['(request-target)'] = + strtolower($_SERVER['REQUEST_METHOD']) . ' ' . + $_SERVER['REQUEST_URI']; + foreach($_SERVER as $k => $v) { + if(strpos($k,'HTTP_') === 0) { + $field = str_replace('_','-',strtolower(substr($k,5))); + $headers[$field] = $v; + } + } + } + + $sig_block = null; + + if(array_key_exists('signature',$headers)) { + $sig_block = self::parse_sigheader($headers['signature']); + } + elseif(array_key_exists('authorization',$headers)) { + $sig_block = self::parse_sigheader($headers['authorization']); + } + + if(! $sig_block) { + logger('no signature provided.'); + return $result; + } + + // Warning: This log statement includes binary data + // logger('sig_block: ' . print_r($sig_block,true), LOGGER_DATA); + + $result['header_signed'] = true; + + $signed_headers = $sig_block['headers']; + if(! $signed_headers) + $signed_headers = [ 'date' ]; + + $signed_data = ''; + foreach($signed_headers as $h) { + if(array_key_exists($h,$headers)) { + $signed_data .= $h . ': ' . $headers[$h] . "\n"; + } + if(strpos($h,'.')) { + $spoofable = true; + } + } + $signed_data = rtrim($signed_data,"\n"); + + $algorithm = null; + if($sig_block['algorithm'] === 'rsa-sha256') { + $algorithm = 'sha256'; + } + if($sig_block['algorithm'] === 'rsa-sha512') { + $algorithm = 'sha512'; + } + + if($key && function_exists($key)) { + $result['signer'] = $sig_block['keyId']; + $key = $key($sig_block['keyId']); + } + + if(! $key) { + $result['signer'] = $sig_block['keyId']; + $key = self::get_activitypub_key($sig_block['keyId']); + } + + if(! $key) + return $result; + + $x = rsa_verify($signed_data,$sig_block['signature'],$key,$algorithm); + + logger('verified: ' . $x, LOGGER_DEBUG); + + if($x === false) + return $result; + + if(! $spoofable) + $result['header_valid'] = true; + + if(in_array('digest',$signed_headers)) { + $result['content_signed'] = true; + $digest = explode('=', $headers['digest']); + if($digest[0] === 'SHA-256') + $hashalg = 'sha256'; + if($digest[0] === 'SHA-512') + $hashalg = 'sha512'; + + // The explode operation will have stripped the '=' padding, so compare against unpadded base64 + if(rtrim(base64_encode(hash($hashalg,$body,true)),'=') === $digest[1]) { + $result['content_valid'] = true; + } + } + + logger('Content_Valid: ' . $result['content_valid']); + + return $result; + + } + + function get_activitypub_key($id) { + + if(strpos($id,'acct:') === 0) { + $x = q("select xchan_pubkey from xchan left join hubloc on xchan_hash = hubloc_hash where hubloc_addr = '%s' limit 1", + dbesc(str_replace('acct:','',$id)) + ); + } + else { + $x = q("select xchan_pubkey from xchan where xchan_hash = '%s' and xchan_network = 'activitypub' ", + dbesc($id) + ); + } + + if($x && $x[0]['xchan_pubkey']) { + return ($x[0]['xchan_pubkey']); + } + $r = as_fetch($id); + + if($r) { + $j = json_decode($r,true); + + if($j['id'] !== $id) + return false; + if(array_key_exists('publicKey',$j) && array_key_exists('publicKeyPem',$j['publicKey'])) { + return($j['publicKey']['publicKeyPem']); + } + } + return false; + } + + + + + static function create_sig($request,$head,$prvkey,$keyid = 'Key',$send_headers = false,$auth = false,$alg = 'sha256', + $crypt_key = null, $crypt_algo = 'aes256ctr') { + + $return_headers = []; + + if($alg === 'sha256') { + $algorithm = 'rsa-sha256'; + } + if($alg === 'sha512') { + $algorithm = 'rsa-sha512'; + } + + $x = self::sign($request,$head,$prvkey,$alg); + + $headerval = 'keyId="' . $keyid . '",algorithm="' . $algorithm + . '",headers="' . $x['headers'] . '",signature="' . $x['signature'] . '"'; + + if($crypt_key) { + $x = crypto_encapsulate($headerval,$crypt_key,$crypt_alg); + $headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data']; + } + + if($auth) { + $sighead = 'Authorization: Signature ' . $headerval; + } + else { + $sighead = 'Signature: ' . $headerval; + } + + if($head) { + foreach($head as $k => $v) { + if($send_headers) { + header($k . ': ' . $v); + } + else { + $return_headers[] = $k . ': ' . $v; + } + } + } + if($send_headers) { + header($sighead); + } + else { + $return_headers[] = $sighead; + } + return $return_headers; + } + + + + static function sign($request,$head,$prvkey,$alg = 'sha256') { + + $ret = []; + + $headers = ''; + $fields = ''; + if($request) { + $headers = '(request-target)' . ': ' . trim($request) . "\n"; + $fields = '(request-target)'; + } + + if(head) { + foreach($head as $k => $v) { + $headers .= strtolower($k) . ': ' . trim($v) . "\n"; + if($fields) + $fields .= ' '; + $fields .= strtolower($k); + } + // strip the trailing linefeed + $headers = rtrim($headers,"\n"); + } + + $sig = base64_encode(rsa_sign($headers,$prvkey,$alg)); + + $ret['headers'] = $fields; + $ret['signature'] = $sig; + + return $ret; + } + + static function parse_sigheader($header) { + + $ret = []; + $matches = []; + + // if the header is encrypted, decrypt with (default) site private key and continue + + if(preg_match('/iv="(.*?)"/ism',$header,$matches)) + $header = self::decrypt_sigheader($header); + + if(preg_match('/keyId="(.*?)"/ism',$header,$matches)) + $ret['keyId'] = $matches[1]; + if(preg_match('/algorithm="(.*?)"/ism',$header,$matches)) + $ret['algorithm'] = $matches[1]; + if(preg_match('/headers="(.*?)"/ism',$header,$matches)) + $ret['headers'] = explode(' ', $matches[1]); + if(preg_match('/signature="(.*?)"/ism',$header,$matches)) + $ret['signature'] = base64_decode(preg_replace('/\s+/','',$matches[1])); + + if(($ret['signature']) && ($ret['algorithm']) && (! $ret['headers'])) + $ret['headers'] = [ 'date' ]; + + return $ret; + } + + + static function decrypt_sigheader($header,$prvkey = null) { + + $iv = $key = $alg = $data = null; + + if(! $prvkey) { + $prvkey = get_config('system','prvkey'); + } + + $matches = []; + + if(preg_match('/iv="(.*?)"/ism',$header,$matches)) + $iv = $matches[1]; + if(preg_match('/key="(.*?)"/ism',$header,$matches)) + $key = $matches[1]; + if(preg_match('/alg="(.*?)"/ism',$header,$matches)) + $alg = $matches[1]; + if(preg_match('/data="(.*?)"/ism',$header,$matches)) + $data = $matches[1]; + + if($iv && $key && $alg && $data) { + return crypto_unencapsulate([ 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); + } + return ''; + + } + +} + + diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index 3190369c8..9486130cb 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -119,6 +119,18 @@ class Router { if(! (\App::$module_loaded)) { + // undo the setting of a letsencrypt acme-challenge rewrite rule + // which blocks access to our .well-known routes. + // Also provide a config setting for sites that have a legitimate need + // for a custom .htaccess in the .well-known directory; but they should + // make the file read-only so letsencrypt doesn't modify it + + if(strpos($_SERVER['REQUEST_URI'],'/.well-known/') === 0) { + if(file_exists('.well-known/.htaccess') && get_config('system','fix_apache_acme',true)) { + rename('.well-known/.htaccess','.well-known/.htaccess.old'); + } + } + $x = [ 'module' => $module, 'installed' => \App::$module_loaded, @@ -166,6 +178,7 @@ class Router { */ if(\App::$module_loaded) { + \App::$page['page_title'] = \App::$module; $placeholder = ''; diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index 4e8dc6786..9e6af8c4c 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -58,7 +58,11 @@ class WebServer { if((x($_GET,'zid')) && (! \App::$install)) { \App::$query_string = strip_zids(\App::$query_string); if(! local_channel()) { - $_SESSION['my_address'] = $_GET['zid']; + if ($_SESSION['my_address']!=$_GET['zid']) + { + $_SESSION['my_address'] = $_GET['zid']; + $_SESSION['authenticated'] = 0; + } zid_init(); } } @@ -70,6 +74,12 @@ class WebServer { } } + if((x($_REQUEST,'owt')) && (! \App::$install)) { + $token = $_REQUEST['owt']; + \App::$query_string = strip_query_param(\App::$query_string,'owt'); + owt_init($token); + } + if((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (\App::$module === 'login')) require('include/auth.php'); @@ -97,10 +107,38 @@ class WebServer { check_config(); } - nav_set_selected('nothing'); + //nav_set_selected('nothing'); $Router = new Router($a); + /* Initialise the Link: response header if this is a channel page. + * This cannot be done inside the channel module because some protocol + * addons over-ride the module functions and these links are common + * to all protocol drivers; thus doing it here avoids duplication. + */ + + if (( \App::$module === 'channel' ) && argc() > 1) { + \App::$channel_links = [ + [ + 'rel' => 'lrdd', + 'type' => 'application/xrd+xml', + 'url' => z_root() . '/xrd?f=&uri=acct%3A' . argv(1) . '%40' . \App::get_hostname() + ], + [ + 'rel' => 'jrd', + 'type' => 'application/jrd+json', + 'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . \App::get_hostname() + ], + ]; + $x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ]; + call_hooks('channel_links', $x ); + \App::$channel_links = $x['channel_links']; + header('Link: ' . \App::get_channel_links()); + } + + + + /* initialise content region */ if(! x(\App::$page, 'content')) @@ -137,4 +175,4 @@ class WebServer { killme(); } -} \ No newline at end of file +} diff --git a/Zotlabs/Widget/Categories.php b/Zotlabs/Widget/Categories.php index d1dcfda93..305869706 100644 --- a/Zotlabs/Widget/Categories.php +++ b/Zotlabs/Widget/Categories.php @@ -8,16 +8,25 @@ class Categories { function widget($arr) { + $cards = ((array_key_exists('cards',$arr) && $arr['cards']) ? true : false); + + if(($cards) && (! feature_enabled(\App::$profile['profile_uid'],'cards'))) + return ''; + if((! \App::$profile['profile_uid']) - || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'view_stream'))) { + || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards) ? 'view_pages' : 'view_stream')))) { return ''; } $cat = ((x($_REQUEST,'cat')) ? htmlspecialchars($_REQUEST['cat'],ENT_COMPAT,'UTF-8') : ''); - $srchurl = \App::$query_string; + $srchurl = (($cards) ? \App::$argv[0] . '/' . \App::$argv[1] : \App::$query_string); $srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&'); $srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl); - return categories_widget($srchurl, $cat); + if($cards) + return cardcategories_widget($srchurl, $cat); + else + return categories_widget($srchurl, $cat); + } } diff --git a/Zotlabs/Widget/Common_friends.php b/Zotlabs/Widget/Common_friends.php new file mode 100644 index 000000000..a67b9312c --- /dev/null +++ b/Zotlabs/Widget/Common_friends.php @@ -0,0 +1,19 @@ +'; $level_0 = get_help_content('sitetoc'); - if(! $level_0) - $level_0 = get_help_content('toc'); + if(! $level_0) { + $path = 'toc'; + $x = determine_help_language(); + $lang = $x['language']; + if($lang !== 'en') { + $path = $lang . '/toc'; + } + $level_0 = get_help_content($path); + } $level_0 = preg_replace('/\/','
' . t('Hub URL') . '' . t('Access Type') . '' . t('Registration Policy') . '' . t('Software') . '' . t('Ratings') . '
' . $urltext . '' . $location . '' . $jj['access'] . '' . $jj['register'] . '' . '' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . '
' . $urltext . '' . $location . '' . $jj['access'] . '' . $jj['register'] . '' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . ' ' . t('View') . '
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/\s*$/g;function Ca(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Da(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ea(a){var b=Aa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&za.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(m&&(e=oa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(la(e,"script"),Da),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=la(h),f=la(a),d=0,e=f.length;d0&&ma(g,!i&&la(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(la(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!ya.test(a)&&!ka[(ia.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Xa(a,b,c,d,e){return new Xa.prototype.init(a,b,c,d,e)}r.Tween=Xa,Xa.prototype={constructor:Xa,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Xa.propHooks[this.prop];return a&&a.get?a.get(this):Xa.propHooks._default.get(this)},run:function(a){var b,c=Xa.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Xa.propHooks._default.set(this),this}},Xa.prototype.init.prototype=Xa.prototype,Xa.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Xa.propHooks.scrollTop=Xa.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Xa.prototype.init,r.fx.step={};var Ya,Za,$a=/^(?:toggle|show|hide)$/,_a=/queueHooks$/;function ab(){Za&&(a.requestAnimationFrame(ab),r.fx.tick())}function bb(){return a.setTimeout(function(){Ya=void 0}),Ya=r.now()}function cb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=aa[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function db(a,b,c){for(var d,e=(gb.tweeners[b]||[]).concat(gb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?hb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K); -if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),hb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ib[b]||r.find.attr;ib[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ib[g],ib[g]=e,e=null!=c(a,b,d)?g:null,ib[g]=f),e}});var jb=/^(?:input|select|textarea|button)$/i,kb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):jb.test(a.nodeName)||kb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});var lb=/[\t\r\n\f]/g;function mb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,mb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,mb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,mb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=mb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(c)+" ").replace(lb," ").indexOf(b)>-1)return!0;return!1}});var nb=/\r/g,ob=/[\x20\t\r\n\f]+/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(nb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:r.trim(r.text(a)).replace(ob," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type,g=f?null:[],h=f?e+1:d.length,i=e<0?h:f?e:0;i-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ha.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,""),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" diff --git a/view/js/mod_help.js b/view/js/mod_help.js new file mode 100644 index 000000000..8ee89dd61 --- /dev/null +++ b/view/js/mod_help.js @@ -0,0 +1,124 @@ +function docoTocToggle() { + if ($('#doco-top-toc').is(':visible')) { + $('#doco-toc-toggle').removeClass('fa-cog').addClass('fa-caret-right'); + } else { + $('#doco-toc-toggle').removeClass('fa-caret-right').addClass('fa-caret-down'); + } + $('#doco-top-toc').toggle(); + + return false; +} + +toc = {}; +// Generate the table of contents in the side nav menu (see view/tpl/help.tpl) +$(document).ready(function () { + // Generate the table of contents in the side nav menu (see view/tpl/help.tpl) + $('#doco-top-toc').toc({content: "#doco-content", headings: "h3,h4,h5,h6"}); + + $(".doco-section").find('a').each(function () { + var url = document.createElement('a'); + url.href = window.location; + var pageName = url.href.split('/').pop().split('#').shift().split('?').shift(); + var linkName = $(this).attr('href').split('/').pop(); + if (pageName === linkName) { + var tocUl = $(this).closest('a').append('
    ').find('ul'); + tocUl.removeClass(); // Classes are automatically added to
      elements by something else + tocUl.toc({content: "#doco-content", headings: "h3"}); + tocUl.addClass('toc-content'); + tocUl.addClass('list-unstyled'); + tocUl.attr('id', 'doco-side-toc'); + + } + }); + + $(document.body).trigger("sticky_kit:recalc"); + + toc.contentTop = []; + toc.edgeMargin = 20; // margin above the top or margin from the end of the page + toc.topRange = 200; // measure from the top of the viewport to X pixels down + // Set up content an array of locations + $('#doco-side-toc').find('a').each(function () { + toc.contentTop.push($('#' + $(this).attr('href').split('#').pop()).offset().top); + }); + + + // adjust side menu + $(window).scroll(function () { + var winTop = $(window).scrollTop(), + bodyHt = $(document).height(), + vpHt = $(window).height() + toc.edgeMargin; // viewport height + margin + $.each(toc.contentTop, function (i, loc) { + if ((loc > winTop - toc.edgeMargin && (loc < winTop + toc.topRange || (winTop + vpHt) >= bodyHt))) { + $('#doco-side-toc li') + .removeClass('selected-doco-nav') + .eq(i).addClass('selected-doco-nav'); + if (typeof ($('#doco-side-toc li').eq(i).find('a').attr('href').split('#')[1]) !== 'undefined') { + window.history.pushState({}, '', location.href.split('#')[0] + '#' + $('#doco-side-toc li').eq(i).find('a').attr('href').split('#')[1]); + } + } + }); + }); + + // When the page loads, it does not scroll to the section specified in the URL because it + // has not been constructed yet by the script. This will reload the URL + if (typeof (location.href.split('#')[1]) !== 'undefined') { + var p = document.createElement('a'); + p.href = location.href; + var portstr = ''; + if (p.port !== '') { + portstr = ':' + p.port; + } + var newref = p.protocol + '//' + p.hostname + portstr + p.pathname + p.hash.split('?').shift(); + location.replace(newref) + } + + + // Determine language translations available from the language selector menu itself + var langChoices = []; + $('.lang-selector').find('.lang-choice').each(function (idx, a) { + langChoices.push($(a).html()); + }); + // Parse the URL and insert the language code for the loaded language, based + // on the variable "help_language" that is declared in the help.tpl page template + var path = window.location.pathname.split('/'); + var pathParts = []; + var pick_me = true; + for (var i = 0; i < path.length; i++) { + if(i === 2 && pick_me ) { + if(path[i].length > 0) { + pathParts.push(help_language); + pick_me = false; + if($.inArray(path[i], langChoices) < 0) { + i--; + } + } + } else { + if(path[i].length > 0) { + pathParts.push(path[i]); + } + } + + } + // Update the address bar to reflect the loaded language + window.history.pushState({}, '', '/' + pathParts.join('/')); + + // Highlight the language in the language selector that is currently viewed + $('.lang-selector').find('.lang-choice:contains("' + help_language + '")').addClass('active'); + + // Construct the links to the available translations based and populate the selector menu + $('.lang-selector').find('.lang-choice').each(function (idx, a) { + var langLink = []; + + for (var i = 0; i < pathParts.length; i++) { + + if(i === 1) { + langLink.push($(a).html()); + } else { + langLink.push(pathParts[i]); + } + + } + $(a).attr('href', '/' + langLink.join('/')); + }); + +}); diff --git a/view/js/mod_new_channel.js b/view/js/mod_new_channel.js index 17b354a4b..e670e1a35 100644 --- a/view/js/mod_new_channel.js +++ b/view/js/mod_new_channel.js @@ -1,7 +1,7 @@ $(document).ready(function() { // $("#id_permissions_role").sSelect(); $("#id_name").blur(function() { - $("#name-spinner").spin('small'); + $("#name-spinner").show(); var zreg_name = $("#id_name").val(); $.get("new_channel/autofill.json?f=&name=" + encodeURIComponent(zreg_name),function(data) { $("#id_nickname").val(data); @@ -9,12 +9,12 @@ $("#help_name").html(""); zFormError("#help_name",data.error); } - $("#name-spinner").spin(false); + $("#name-spinner").hide(); }); }); $("#id_nickname").blur(function() { - $("#nick-spinner").spin('small'); + $("#nick-spinner").show(); var zreg_nick = $("#id_nickname").val(); $.get("new_channel/checkaddr.json?f=&nick=" + encodeURIComponent(zreg_nick),function(data) { $("#id_nickname").val(data); @@ -22,7 +22,7 @@ $("#help_nickname").html(""); zFormError("#help_nickname",data.error); } - $("#nick-spinner").spin(false); + $("#nick-spinner").hide(); }); }); diff --git a/view/js/mod_register.js b/view/js/mod_register.js index f1f3e7f71..6607579a2 100644 --- a/view/js/mod_register.js +++ b/view/js/mod_register.js @@ -28,7 +28,7 @@ $(document).ready(function() { }); $("#id_name").blur(function() { - $("#name-spinner").spin('small'); + $("#name-spinner").show(); var zreg_name = $("#id_name").val(); $.get("new_channel/autofill.json?f=&name=" + encodeURIComponent(zreg_name),function(data) { $("#id_nickname").val(data); @@ -36,12 +36,12 @@ $(document).ready(function() { $("#help_name").html(""); zFormError("#help_name",data.error); } - $("#name-spinner").spin(false); + $("#name-spinner").hide(); }); }); $("#id_nickname").blur(function() { - $("#nick-spinner").spin('small'); + $("#nick-spinner").show(); var zreg_nick = $("#id_nickname").val(); $.get("new_channel/checkaddr.json?f=&nick=" + encodeURIComponent(zreg_nick),function(data) { $("#id_nickname").val(data); @@ -49,7 +49,7 @@ $(document).ready(function() { $("#help_nickname").html(""); zFormError("#help_nickname",data.error); } - $("#nick-spinner").spin(false); + $("#nick-spinner").hide(); }); }); diff --git a/view/js/spin.js b/view/js/spin.js deleted file mode 100644 index 145ce1baf..000000000 --- a/view/js/spin.js +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Copyright (c) 2011-2014 Felix Gnass - * Licensed under the MIT license - * http://spin.js.org/ - * - * Example: - var opts = { - lines: 12, // The number of lines to draw - , length: 7, // The length of each line - , width: 5, // The line thickness - , radius: 10 // The radius of the inner circle - , scale: 1.0 // Scales overall size of the spinner - , corners: 1 // Roundness (0..1) - , color: '#000' // #rgb or #rrggbb - , opacity: 1/4 // Opacity of the lines - , rotate: 0 // Rotation offset - , direction: 1 // 1: clockwise, -1: counterclockwise - , speed: 1 // Rounds per second - , trail: 100 // Afterglow percentage - , fps: 20 // Frames per second when using setTimeout() - , zIndex: 2e9 // Use a high z-index by default - , className: 'spinner' // CSS class to assign to the element - , top: '50%' // center vertically - , left: '50%' // center horizontally - , shadow: false // Whether to render a shadow - , hwaccel: false // Whether to use hardware acceleration (might be buggy) - , position: 'absolute' // Element positioning - } - var target = document.getElementById('foo') - var spinner = new Spinner(opts).spin(target) - */ -;(function (root, factory) { - - /* CommonJS */ - if (typeof exports == 'object') module.exports = factory() - - /* AMD module */ - else if (typeof define == 'function' && define.amd) define(factory) - - /* Browser global */ - else root.Spinner = factory() -}(this, function () { - "use strict" - - var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */ - , animations = {} /* Animation rules keyed by their name */ - , useCssAnimations /* Whether to use CSS animations or setTimeout */ - , sheet /* A stylesheet to hold the @keyframe or VML rules. */ - - /** - * Utility function to create elements. If no tag name is given, - * a DIV is created. Optionally properties can be passed. - */ - function createEl (tag, prop) { - var el = document.createElement(tag || 'div') - , n - - for (n in prop) el[n] = prop[n] - return el - } - - /** - * Appends children and returns the parent. - */ - function ins (parent /* child1, child2, ...*/) { - for (var i = 1, n = arguments.length; i < n; i++) { - parent.appendChild(arguments[i]) - } - - return parent - } - - /** - * Creates an opacity keyframe animation rule and returns its name. - * Since most mobile Webkits have timing issues with animation-delay, - * we create separate rules for each line/segment. - */ - function addAnimation (alpha, trail, i, lines) { - var name = ['opacity', trail, ~~(alpha * 100), i, lines].join('-') - , start = 0.01 + i/lines * 100 - , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha) - , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase() - , pre = prefix && '-' + prefix + '-' || '' - - if (!animations[name]) { - sheet.insertRule( - '@' + pre + 'keyframes ' + name + '{' + - '0%{opacity:' + z + '}' + - start + '%{opacity:' + alpha + '}' + - (start+0.01) + '%{opacity:1}' + - (start+trail) % 100 + '%{opacity:' + alpha + '}' + - '100%{opacity:' + z + '}' + - '}', sheet.cssRules.length) - - animations[name] = 1 - } - - return name - } - - /** - * Tries various vendor prefixes and returns the first supported property. - */ - function vendor (el, prop) { - var s = el.style - , pp - , i - - prop = prop.charAt(0).toUpperCase() + prop.slice(1) - if (s[prop] !== undefined) return prop - for (i = 0; i < prefixes.length; i++) { - pp = prefixes[i]+prop - if (s[pp] !== undefined) return pp - } - } - - /** - * Sets multiple style properties at once. - */ - function css (el, prop) { - for (var n in prop) { - el.style[vendor(el, n) || n] = prop[n] - } - - return el - } - - /** - * Fills in default values. - */ - function merge (obj) { - for (var i = 1; i < arguments.length; i++) { - var def = arguments[i] - for (var n in def) { - if (obj[n] === undefined) obj[n] = def[n] - } - } - return obj - } - - /** - * Returns the line color from the given string or array. - */ - function getColor (color, idx) { - return typeof color == 'string' ? color : color[idx % color.length] - } - - // Built-in defaults - - var defaults = { - lines: 12 // The number of lines to draw - , length: 7 // The length of each line - , width: 5 // The line thickness - , radius: 10 // The radius of the inner circle - , scale: 1.0 // Scales overall size of the spinner - , corners: 1 // Roundness (0..1) - , color: '#000' // #rgb or #rrggbb - , opacity: 1/4 // Opacity of the lines - , rotate: 0 // Rotation offset - , direction: 1 // 1: clockwise, -1: counterclockwise - , speed: 1 // Rounds per second - , trail: 100 // Afterglow percentage - , fps: 20 // Frames per second when using setTimeout() - , zIndex: 2e9 // Use a high z-index by default - , className: 'spinner' // CSS class to assign to the element - , top: '50%' // center vertically - , left: '50%' // center horizontally - , shadow: false // Whether to render a shadow - , hwaccel: false // Whether to use hardware acceleration (might be buggy) - , position: 'absolute' // Element positioning - } - - /** The constructor */ - function Spinner (o) { - this.opts = merge(o || {}, Spinner.defaults, defaults) - } - - // Global defaults that override the built-ins: - Spinner.defaults = {} - - merge(Spinner.prototype, { - /** - * Adds the spinner to the given target element. If this instance is already - * spinning, it is automatically removed from its previous target b calling - * stop() internally. - */ - spin: function (target) { - this.stop() - - var self = this - , o = self.opts - , el = self.el = createEl(null, {className: o.className}) - - css(el, { - position: o.position - , width: 0 - , zIndex: o.zIndex - , left: o.left - , top: o.top - }) - - if (target) { - target.insertBefore(el, target.firstChild || null) - } - - el.setAttribute('role', 'progressbar') - self.lines(el, self.opts) - - if (!useCssAnimations) { - // No CSS animation support, use setTimeout() instead - var i = 0 - , start = (o.lines - 1) * (1 - o.direction) / 2 - , alpha - , fps = o.fps - , f = fps / o.speed - , ostep = (1 - o.opacity) / (f * o.trail / 100) - , astep = f / o.lines - - ;(function anim () { - i++ - for (var j = 0; j < o.lines; j++) { - alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity) - - self.opacity(el, j * o.direction + start, alpha, o) - } - self.timeout = self.el && setTimeout(anim, ~~(1000 / fps)) - })() - } - return self - } - - /** - * Stops and removes the Spinner. - */ - , stop: function () { - var el = this.el - if (el) { - clearTimeout(this.timeout) - if (el.parentNode) el.parentNode.removeChild(el) - this.el = undefined - } - return this - } - - /** - * Internal method that draws the individual lines. Will be overwritten - * in VML fallback mode below. - */ - , lines: function (el, o) { - var i = 0 - , start = (o.lines - 1) * (1 - o.direction) / 2 - , seg - - function fill (color, shadow) { - return css(createEl(), { - position: 'absolute' - , width: o.scale * (o.length + o.width) + 'px' - , height: o.scale * o.width + 'px' - , background: color - , boxShadow: shadow - , transformOrigin: 'left' - , transform: 'rotate(' + ~~(360/o.lines*i + o.rotate) + 'deg) translate(' + o.scale*o.radius + 'px' + ',0)' - , borderRadius: (o.corners * o.scale * o.width >> 1) + 'px' - }) - } - - for (; i < o.lines; i++) { - seg = css(createEl(), { - position: 'absolute' - , top: 1 + ~(o.scale * o.width / 2) + 'px' - , transform: o.hwaccel ? 'translate3d(0,0,0)' : '' - , opacity: o.opacity - , animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1 / o.speed + 's linear infinite' - }) - - if (o.shadow) ins(seg, css(fill('#000', '0 0 4px #000'), {top: '2px'})) - ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)'))) - } - return el - } - - /** - * Internal method that adjusts the opacity of a single line. - * Will be overwritten in VML fallback mode below. - */ - , opacity: function (el, i, val) { - if (i < el.childNodes.length) el.childNodes[i].style.opacity = val - } - - }) - - - function initVML () { - - /* Utility function to create a VML tag */ - function vml (tag, attr) { - return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr) - } - - // No CSS transforms but VML support, add a CSS rule for VML elements: - sheet.addRule('.spin-vml', 'behavior:url(#default#VML)') - - Spinner.prototype.lines = function (el, o) { - var r = o.scale * (o.length + o.width) - , s = o.scale * 2 * r - - function grp () { - return css( - vml('group', { - coordsize: s + ' ' + s - , coordorigin: -r + ' ' + -r - }) - , { width: s, height: s } - ) - } - - var margin = -(o.width + o.length) * o.scale * 2 + 'px' - , g = css(grp(), {position: 'absolute', top: margin, left: margin}) - , i - - function seg (i, dx, filter) { - ins( - g - , ins( - css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}) - , ins( - css( - vml('roundrect', {arcsize: o.corners}) - , { width: r - , height: o.scale * o.width - , left: o.scale * o.radius - , top: -o.scale * o.width >> 1 - , filter: filter - } - ) - , vml('fill', {color: getColor(o.color, i), opacity: o.opacity}) - , vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change - ) - ) - ) - } - - if (o.shadow) - for (i = 1; i <= o.lines; i++) { - seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)') - } - - for (i = 1; i <= o.lines; i++) seg(i) - return ins(el, g) - } - - Spinner.prototype.opacity = function (el, i, val, o) { - var c = el.firstChild - o = o.shadow && o.lines || 0 - if (c && i + o < c.childNodes.length) { - c = c.childNodes[i + o]; c = c && c.firstChild; c = c && c.firstChild - if (c) c.opacity = val - } - } - } - - if (typeof document !== 'undefined') { - sheet = (function () { - var el = createEl('style', {type : 'text/css'}) - ins(document.getElementsByTagName('head')[0], el) - return el.sheet || el.styleSheet - }()) - - var probe = css(createEl('group'), {behavior: 'url(#default#VML)'}) - - if (!vendor(probe, 'transform') && probe.adj) initVML() - else useCssAnimations = vendor(probe, 'animation') - } - - return Spinner - -})); diff --git a/view/pdl/mod_admin.pdl b/view/pdl/mod_admin.pdl index 5bd47ea37..deee4551b 100644 --- a/view/pdl/mod_admin.pdl +++ b/view/pdl/mod_admin.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=admin][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_appman.pdl b/view/pdl/mod_appman.pdl index 1209d85b9..d2b1379a5 100644 --- a/view/pdl/mod_appman.pdl +++ b/view/pdl/mod_appman.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=appcategories][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_apps.pdl b/view/pdl/mod_apps.pdl index 1209d85b9..d2b1379a5 100644 --- a/view/pdl/mod_apps.pdl +++ b/view/pdl/mod_apps.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=appcategories][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_blocks.pdl b/view/pdl/mod_blocks.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_blocks.pdl +++ b/view/pdl/mod_blocks.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_cal.pdl b/view/pdl/mod_cal.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_cal.pdl +++ b/view/pdl/mod_cal.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_cards.pdl b/view/pdl/mod_cards.pdl new file mode 100644 index 000000000..f5606dcb8 --- /dev/null +++ b/view/pdl/mod_cards.pdl @@ -0,0 +1,8 @@ +[region=aside] +[widget=categories][var=cards]1[/var][/widget] +[widget=tasklist][/widget] +[widget=notes][/widget] +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_cdav.pdl b/view/pdl/mod_cdav.pdl index ae26810c4..d31308d90 100644 --- a/view/pdl/mod_cdav.pdl +++ b/view/pdl/mod_cdav.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=cdav][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_channel.pdl b/view/pdl/mod_channel.pdl index 98dc71915..f7ac0b4ef 100644 --- a/view/pdl/mod_channel.pdl +++ b/view/pdl/mod_channel.pdl @@ -3,8 +3,11 @@ [/region] [region=aside] [widget=fullprofile][/widget] +[widget=common_friends][/widget] [widget=archive][var=wall]1[/var][/widget] [widget=categories][/widget] [widget=tagcloud_wall][var=limit]24[/var][/widget] [/region] - +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_chanview.pdl b/view/pdl/mod_chanview.pdl index d8f50ad7a..5c8ca77d5 100644 --- a/view/pdl/mod_chanview.pdl +++ b/view/pdl/mod_chanview.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=vcard][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_chat.pdl b/view/pdl/mod_chat.pdl index 664e77f53..2f1f5c8d1 100644 --- a/view/pdl/mod_chat.pdl +++ b/view/pdl/mod_chat.pdl @@ -5,3 +5,6 @@ [widget=suggestedchats][/widget] [widget=chatroom_members][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_cloud.pdl b/view/pdl/mod_cloud.pdl index d8f50ad7a..5c8ca77d5 100644 --- a/view/pdl/mod_cloud.pdl +++ b/view/pdl/mod_cloud.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=vcard][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_common.pdl b/view/pdl/mod_common.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_common.pdl +++ b/view/pdl/mod_common.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_connect.pdl b/view/pdl/mod_connect.pdl index 6b1d2a15e..23b8d9f71 100644 --- a/view/pdl/mod_connect.pdl +++ b/view/pdl/mod_connect.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=profile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_connections.pdl b/view/pdl/mod_connections.pdl index fd3f25483..7cead4fe8 100644 --- a/view/pdl/mod_connections.pdl +++ b/view/pdl/mod_connections.pdl @@ -4,3 +4,6 @@ [widget=suggestions][/widget] [widget=findpeople][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_connedit.pdl b/view/pdl/mod_connedit.pdl index 4b468e34c..3f57ed87b 100644 --- a/view/pdl/mod_connedit.pdl +++ b/view/pdl/mod_connedit.pdl @@ -4,3 +4,6 @@ [widget=suggestions][/widget] [widget=findpeople][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_directory.pdl b/view/pdl/mod_directory.pdl index 452ab66c7..7b430738b 100644 --- a/view/pdl/mod_directory.pdl +++ b/view/pdl/mod_directory.pdl @@ -4,3 +4,6 @@ [widget=dirtags][/widget] [widget=suggestions][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_display.pdl b/view/pdl/mod_display.pdl new file mode 100644 index 000000000..e657fa88b --- /dev/null +++ b/view/pdl/mod_display.pdl @@ -0,0 +1,3 @@ +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_editblock.pdl b/view/pdl/mod_editblock.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_editblock.pdl +++ b/view/pdl/mod_editblock.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_editlayout.pdl b/view/pdl/mod_editlayout.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_editlayout.pdl +++ b/view/pdl/mod_editlayout.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_editwebpage.pdl b/view/pdl/mod_editwebpage.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_editwebpage.pdl +++ b/view/pdl/mod_editwebpage.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_events.pdl b/view/pdl/mod_events.pdl index 8347d4c59..b26f3b0d0 100644 --- a/view/pdl/mod_events.pdl +++ b/view/pdl/mod_events.pdl @@ -2,3 +2,6 @@ [widget=eventstools][/widget] [widget=tasklist][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_group.pdl b/view/pdl/mod_group.pdl index 8db29cf78..0a31e17d9 100644 --- a/view/pdl/mod_group.pdl +++ b/view/pdl/mod_group.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=collections][var=mode]groups[/var][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_help.pdl b/view/pdl/mod_help.pdl index bafad3839..2eab7aa63 100644 --- a/view/pdl/mod_help.pdl +++ b/view/pdl/mod_help.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=helpindex][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_id.pdl b/view/pdl/mod_id.pdl index 71ef7f898..c34898dd5 100644 --- a/view/pdl/mod_id.pdl +++ b/view/pdl/mod_id.pdl @@ -1,4 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] - +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_layouts.pdl b/view/pdl/mod_layouts.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_layouts.pdl +++ b/view/pdl/mod_layouts.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_locs.pdl b/view/pdl/mod_locs.pdl index 0b0a99638..53a3ac9e5 100644 --- a/view/pdl/mod_locs.pdl +++ b/view/pdl/mod_locs.pdl @@ -1,4 +1,6 @@ [region=aside] [widget=settings_menu][/widget] [/region] - +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_mail.pdl b/view/pdl/mod_mail.pdl index 67632619e..52f908919 100644 --- a/view/pdl/mod_mail.pdl +++ b/view/pdl/mod_mail.pdl @@ -2,3 +2,6 @@ [widget=mailmenu][/widget] [widget=conversations][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_menu.pdl b/view/pdl/mod_menu.pdl index cef69f194..6ef7993b5 100644 --- a/view/pdl/mod_menu.pdl +++ b/view/pdl/mod_menu.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=design_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_message.pdl b/view/pdl/mod_message.pdl index 2efb3de79..f9dd8f623 100644 --- a/view/pdl/mod_message.pdl +++ b/view/pdl/mod_message.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=mailmenu][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_mitem.pdl b/view/pdl/mod_mitem.pdl index c210606d0..4db06cb49 100644 --- a/view/pdl/mod_mitem.pdl +++ b/view/pdl/mod_mitem.pdl @@ -1,4 +1,7 @@ [region=aside] [widget=design_tools][/widget] [widget=menu_preview][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_network.pdl b/view/pdl/mod_network.pdl index b8817fd99..44e29ffbd 100644 --- a/view/pdl/mod_network.pdl +++ b/view/pdl/mod_network.pdl @@ -1,5 +1,3 @@ -[region=nav]$nav[/region] - [region=aside] [widget=collections][/widget] [widget=forums][/widget] @@ -14,3 +12,6 @@ $content [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_photos.pdl b/view/pdl/mod_photos.pdl index c37cf02fe..4d1a5b2ea 100644 --- a/view/pdl/mod_photos.pdl +++ b/view/pdl/mod_photos.pdl @@ -2,3 +2,6 @@ [widget=vcard][/widget] [widget=photo_albums][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_profile.pdl b/view/pdl/mod_profile.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_profile.pdl +++ b/view/pdl/mod_profile.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_profile_photo.pdl b/view/pdl/mod_profile_photo.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_profile_photo.pdl +++ b/view/pdl/mod_profile_photo.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_profiles.pdl b/view/pdl/mod_profiles.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_profiles.pdl +++ b/view/pdl/mod_profiles.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_profperm.pdl b/view/pdl/mod_profperm.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_profperm.pdl +++ b/view/pdl/mod_profperm.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_pubstream.pdl b/view/pdl/mod_pubstream.pdl new file mode 100644 index 000000000..e657fa88b --- /dev/null +++ b/view/pdl/mod_pubstream.pdl @@ -0,0 +1,3 @@ +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_rate.pdl b/view/pdl/mod_rate.pdl index d8f50ad7a..5c8ca77d5 100644 --- a/view/pdl/mod_rate.pdl +++ b/view/pdl/mod_rate.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=vcard][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_ratings.pdl b/view/pdl/mod_ratings.pdl index df1486c9b..cfa39e408 100644 --- a/view/pdl/mod_ratings.pdl +++ b/view/pdl/mod_ratings.pdl @@ -5,3 +5,6 @@ [widget=suggestions][/widget] [widget=findpeople][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_search.pdl b/view/pdl/mod_search.pdl index 5096508fa..8353b4571 100644 --- a/view/pdl/mod_search.pdl +++ b/view/pdl/mod_search.pdl @@ -1,3 +1,6 @@ [region=aside] [comment][widget=sitesearch][/widget][/comment] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_settings.pdl b/view/pdl/mod_settings.pdl index 0b0a99638..53a3ac9e5 100644 --- a/view/pdl/mod_settings.pdl +++ b/view/pdl/mod_settings.pdl @@ -1,4 +1,6 @@ [region=aside] [widget=settings_menu][/widget] [/region] - +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_sharedwithme.pdl b/view/pdl/mod_sharedwithme.pdl new file mode 100644 index 000000000..e657fa88b --- /dev/null +++ b/view/pdl/mod_sharedwithme.pdl @@ -0,0 +1,3 @@ +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_suggest.pdl b/view/pdl/mod_suggest.pdl index c2889f2fe..392a36dd6 100644 --- a/view/pdl/mod_suggest.pdl +++ b/view/pdl/mod_suggest.pdl @@ -1,4 +1,7 @@ [region=aside] [widget=follow][/widget] [widget=findpeople][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_uexport.pdl b/view/pdl/mod_uexport.pdl index 0b0a99638..53a3ac9e5 100644 --- a/view/pdl/mod_uexport.pdl +++ b/view/pdl/mod_uexport.pdl @@ -1,4 +1,6 @@ [region=aside] [widget=settings_menu][/widget] [/region] - +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_viewconnections.pdl b/view/pdl/mod_viewconnections.pdl index f12bf39c3..c34898dd5 100644 --- a/view/pdl/mod_viewconnections.pdl +++ b/view/pdl/mod_viewconnections.pdl @@ -1,3 +1,6 @@ [region=aside] [widget=fullprofile][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_webpages.pdl b/view/pdl/mod_webpages.pdl index 9e4d604ba..4e60dc2a7 100644 --- a/view/pdl/mod_webpages.pdl +++ b/view/pdl/mod_webpages.pdl @@ -1,4 +1,7 @@ [region=aside] [widget=design_tools][/widget] [widget=website_portation_tools][/widget] -[/region] \ No newline at end of file +[/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/pdl/mod_wiki.pdl b/view/pdl/mod_wiki.pdl index 052ae61a1..1b98b6379 100644 --- a/view/pdl/mod_wiki.pdl +++ b/view/pdl/mod_wiki.pdl @@ -2,3 +2,6 @@ [widget=vcard][/widget] [widget=wiki_pages][/widget] [/region] +[region=right_aside] +[widget=notifications][/widget] +[/region] diff --git a/view/php/default.php b/view/php/default.php index bd4d7dc22..b180a6eb8 100644 --- a/view/php/default.php +++ b/view/php/default.php @@ -8,14 +8,14 @@ >
      - +
      - +
      diff --git a/view/php/theme_init.php b/view/php/theme_init.php index ff37023cf..1e066c570 100644 --- a/view/php/theme_init.php +++ b/view/php/theme_init.php @@ -15,15 +15,13 @@ head_add_js('jquery.js'); head_add_js('/library/justifiedGallery/jquery.justifiedGallery.min.js'); head_add_js('/library/sprintf.js/dist/sprintf.min.js'); -head_add_js('spin.js'); -head_add_js('jquery.spin.js'); head_add_js('jquery.textinputs.js'); head_add_js('autocomplete.js'); head_add_js('/library/jquery-textcomplete/jquery.textcomplete.js'); head_add_js('/library/jquery.timeago.js'); head_add_js('/library/readmore.js/readmore.js'); -head_add_js('/library/sticky-kit/sticky-kit.js'); +head_add_js('/library/sticky-kit/sticky-kit.min.js'); head_add_js('/library/jgrowl/jquery.jgrowl_minimized.js'); head_add_js('/library/cryptojs/components/core-min.js'); head_add_js('/library/cryptojs/rollups/aes.js'); diff --git a/view/theme/redbasic/css/align_left.css b/view/theme/redbasic/css/align_left.css deleted file mode 100644 index acb2893e3..000000000 --- a/view/theme/redbasic/css/align_left.css +++ /dev/null @@ -1,8 +0,0 @@ -main { - margin-left: 0px; -} - -aside#region_3 { - width: auto; - padding: 0px 0px 0px 0px; -} diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index fd2ceb2c9..aea75df65 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -7,6 +7,7 @@ /* generals */ + html { font-size: $font_size; } @@ -49,7 +50,7 @@ main { height: 100%; background: rgba(0, 0, 0, .5); cursor: pointer; - z-index: 1029; + z-index: 1028; } h1, .h1 { @@ -136,6 +137,7 @@ input, optgroup, select, textarea { pre code { border: none; + padding: 1em 1.5em; } code { @@ -147,6 +149,7 @@ pre { background: #F5F5F5; color: #333; border:1px solid #ccc; + border-radius: $radius; } .heart { @@ -158,6 +161,10 @@ nav { filter:alpha(opacity=$nav_percent_min_opacity); } +#nav-app-link { + white-space: nowrap; +} + #powered-by { font-size: 0.5rem; position: absolute; @@ -226,6 +233,81 @@ nav { /* contextual help end */ +/* spinner */ + +.spinner-wrapper { + display: none; +} + +.spinner.s { + height: 1rem; + width: 1rem; +} + +.spinner.m { + height: 2rem; + width: 2rem; +} + +.spinner.l { + height: 3rem; + width: 3rem; +} + +.spinner.s, +.spinner.m, +.spinner.l { + margin: 0 auto; + position: relative; + -webkit-animation: rotation 1s infinite linear; + -moz-animation: rotation 1s infinite linear; + -o-animation: rotation 1s infinite linear; + animation: rotation 1s infinite linear; + border-left: .2rem solid rgba(77, 77, 77, .15); + border-right: .2rem solid rgba(77, 77, 77, .15); + border-bottom: .2rem solid rgba(77, 77, 77, .15); + border-top: .2rem solid rgba(77, 77, 77, .5); + border-radius: 100%; +} + +@-webkit-keyframes rotation { + from { + -webkit-transform: rotate(0deg); + } + to { + -webkit-transform: rotate(359deg); + } +} + +@-moz-keyframes rotation { + from { + -moz-transform: rotate(0deg); + } + to { + -moz-transform: rotate(359deg); + } +} + +@-o-keyframes rotation { + from { + -o-transform: rotate(0deg); + } + to { + -o-transform: rotate(359deg); + } +} + +@keyframes rotation { + from { + transform: rotate(0deg); + } + to { + transform: rotate(359deg); + } +} + +/* spinner end */ + /* footer */ footer { @@ -251,8 +333,7 @@ footer { margin-top: 15px; } -.preview { - background: url('../img/gray_and_white_diagonal_stripes_background_seamless.gif'); +.preview-indicator { } #theme-preview { @@ -477,8 +558,8 @@ footer { #nav-search-spinner { float: right; - margin-top: -0.85rem; - margin-right: 1.6rem; + margin-top: -1.4rem; + margin-right: 1rem; } @@ -706,23 +787,21 @@ div.jGrowl div.jGrowl-notification { .contactname { font-weight: bold; + color: $font_colour; display: block; overflow: hidden; text-overflow: ellipsis; } .dropdown-notification, +.notification, .member-item { line-height: 1.1em; font-size: 0.75rem; overflow: hidden; text-overflow: ellipsis; display: block; -} - -#page-spinner { - color: #777; - margin: 24px; + white-space: nowrap; } #acl-search::-webkit-input-placeholder { @@ -972,10 +1051,6 @@ img.mail-conv-sender-photo { display: none; } -#profile-rotator { - color: #777; -} - .jot-icons.jot-lock-warn { color: darkorange; } @@ -1074,16 +1149,11 @@ img.mail-conv-sender-photo { } -.comment-edit-text-empty, -.comment-edit-text-full { +.comment-edit-text { border: 1px solid #ccc; border-radius: $radius; } -.comment-edit-text-empty { - color: gray; -} - .divgrow-showmore { display: block; border-top: 1px dashed #ccc; @@ -1198,7 +1268,8 @@ img.mail-conv-sender-photo { .generic-content-wrapper { border: 1px solid #ccc; box-shadow: 0px 0px 5px 1px rgba(0,0,0,0.2); - border-radius: $radius; + border-radius: $radius; + margin-bottom: 1.5rem; } .section-title-wrapper { @@ -1390,6 +1461,10 @@ blockquote { background-color: $nav_bg !important; } +.navbar { + z-index:1030; +} + .navbar-dark .navbar-nav .nav-link, .usermenu i { color: $nav_icon_colour; diff --git a/view/theme/redbasic/img/bbedit.png b/view/theme/redbasic/img/bbedit.png deleted file mode 100644 index b89f2f7a8..000000000 Binary files a/view/theme/redbasic/img/bbedit.png and /dev/null differ diff --git a/view/theme/redbasic/img/border.jpg b/view/theme/redbasic/img/border.jpg deleted file mode 100644 index 034a1cb63..000000000 Binary files a/view/theme/redbasic/img/border.jpg and /dev/null differ diff --git a/view/theme/redbasic/img/editicons.png b/view/theme/redbasic/img/editicons.png deleted file mode 100644 index 171a40876..000000000 Binary files a/view/theme/redbasic/img/editicons.png and /dev/null differ diff --git a/view/theme/redbasic/img/ff-16.jpg b/view/theme/redbasic/img/ff-16.jpg deleted file mode 100644 index 3621f5914..000000000 Binary files a/view/theme/redbasic/img/ff-16.jpg and /dev/null differ diff --git a/view/theme/redbasic/img/file.gif b/view/theme/redbasic/img/file.gif deleted file mode 100644 index c532335ed..000000000 Binary files a/view/theme/redbasic/img/file.gif and /dev/null differ diff --git a/view/theme/redbasic/img/friendika-16.png b/view/theme/redbasic/img/friendika-16.png deleted file mode 100644 index 1a742ecdc..000000000 Binary files a/view/theme/redbasic/img/friendika-16.png and /dev/null differ diff --git a/view/theme/redbasic/img/jotperms.png b/view/theme/redbasic/img/jotperms.png deleted file mode 100644 index d133a6e01..000000000 Binary files a/view/theme/redbasic/img/jotperms.png and /dev/null differ diff --git a/view/theme/redbasic/img/login-bg.gif b/view/theme/redbasic/img/login-bg.gif deleted file mode 100644 index cde836c89..000000000 Binary files a/view/theme/redbasic/img/login-bg.gif and /dev/null differ diff --git a/view/theme/redbasic/img/notify_on.png b/view/theme/redbasic/img/notify_on.png deleted file mode 100644 index 18002e15c..000000000 Binary files a/view/theme/redbasic/img/notify_on.png and /dev/null differ diff --git a/view/theme/redbasic/img/photo-menu.jpg b/view/theme/redbasic/img/photo-menu.jpg deleted file mode 100644 index fde5eb535..000000000 Binary files a/view/theme/redbasic/img/photo-menu.jpg and /dev/null differ diff --git a/view/theme/redbasic/img/shiny.png b/view/theme/redbasic/img/shiny.png deleted file mode 100644 index 994c0d05d..000000000 Binary files a/view/theme/redbasic/img/shiny.png and /dev/null differ diff --git a/view/theme/redbasic/js/redbasic.js b/view/theme/redbasic/js/redbasic.js index 5a586b9f9..04199ea95 100644 --- a/view/theme/redbasic/js/redbasic.js +++ b/view/theme/redbasic/js/redbasic.js @@ -16,20 +16,26 @@ $(document).ready(function() { } $('#css3-calc').remove(); // Remove the test element - - - if($(window).width() > 767) { + if($(window).width() >= 767) { $('#left_aside_wrapper').stick_in_parent({ - offset_top: $('nav').outerHeight(true) + 10, - parent: '#region_1', + offset_top: parseInt($('aside').css('padding-top')), + parent: 'main', spacer: '#left_aside_spacer' }); } - if(($(window).width() < 767) && ($('#left_aside_wrapper .widget, #left_aside_wrapper .vcard').length > 0)) - $('#expand-aside').show(); - else - $('#expand-aside').hide(); + if($(window).width() >= 1200) { + $('#right_aside_wrapper').stick_in_parent({ + offset_top: parseInt($('aside').css('padding-top')), + parent: 'main', + spacer: '#right_aside_spacer' + }); + } + + + $('#notifications_wrapper.fs #notifications').stick_in_parent({ + parent: '#notifications_wrapper' + }); $('#expand-aside').on('click', toggleAside); @@ -48,6 +54,16 @@ $(document).ready(function() { } }); + + var right_aside_height = $('#rightt_aside_wrapper').height(); + + $('#right_aside_wrapper').on('click', function() { + if(right_aside_height != $('#right_aside_wrapper').height()) { + $(document.body).trigger("sticky_kit:recalc"); + right_aside_height = $('#right_aside_wrapper').height(); + } + }); + $('.usermenu').click(function() { if($('#navbar-collapse-1, #navbar-collapse-2').hasClass('show')){ $('#navbar-collapse-1, #navbar-collapse-2').removeClass('show'); @@ -66,6 +82,19 @@ $(document).ready(function() { } }); + var notifications_parent = $('#notifications_wrapper')[0].parentElement.id; + $('#notifications-btn').click(function() { + if($('#notifications_wrapper').hasClass('fs')) + $('#notifications_wrapper').prependTo('#' + notifications_parent); + else + $('#notifications_wrapper').prependTo('section'); + + $('#notifications_wrapper').toggleClass('fs'); + if($('#navbar-collapse-2').hasClass('show')){ + $('#navbar-collapse-2').removeClass('show'); + } + }); + $("input[data-role=cat-tagsinput]").tagsinput({ tagClass: 'badge badge-pill badge-warning text-dark' }); diff --git a/view/theme/redbasic/php/config.php b/view/theme/redbasic/php/config.php index 8dbe1be9c..f98182739 100644 --- a/view/theme/redbasic/php/config.php +++ b/view/theme/redbasic/php/config.php @@ -52,7 +52,6 @@ class RedbasicConfig { $arr['radius'] = get_pconfig(local_channel(),'redbasic', 'radius' ); $arr['shadow'] = get_pconfig(local_channel(),'redbasic', 'photo_shadow' ); $arr['converse_width']=get_pconfig(local_channel(),"redbasic","converse_width"); - $arr['align_left']=get_pconfig(local_channel(),"redbasic","align_left"); $arr['top_photo']=get_pconfig(local_channel(),"redbasic","top_photo"); $arr['reply_photo']=get_pconfig(local_channel(),"redbasic","reply_photo"); return $this->form($arr); @@ -79,7 +78,6 @@ class RedbasicConfig { set_pconfig(local_channel(), 'redbasic', 'radius', $_POST['redbasic_radius']); set_pconfig(local_channel(), 'redbasic', 'photo_shadow', $_POST['redbasic_shadow']); set_pconfig(local_channel(), 'redbasic', 'converse_width', $_POST['redbasic_converse_width']); - set_pconfig(local_channel(), 'redbasic', 'align_left', $_POST['redbasic_align_left']); set_pconfig(local_channel(), 'redbasic', 'top_photo', $_POST['redbasic_top_photo']); set_pconfig(local_channel(), 'redbasic', 'reply_photo', $_POST['redbasic_reply_photo']); } @@ -112,7 +110,6 @@ class RedbasicConfig { '$radius' => array('redbasic_radius', t('Set radius of corners'), $arr['radius'], t('Example: 4px')), '$shadow' => array('redbasic_shadow', t('Set shadow depth of photos'), $arr['shadow']), '$converse_width' => array('redbasic_converse_width',t('Set maximum width of content region in pixel'),$arr['converse_width'], t('Leave empty for default width')), - '$align_left' => array('redbasic_align_left',t('Left align page content'),$arr['align_left'], '', array(t('No'),t('Yes'))), '$top_photo' => array('redbasic_top_photo', t('Set size of conversation author photo'), $arr['top_photo']), '$reply_photo' => array('redbasic_reply_photo', t('Set size of followup author photos'), $arr['reply_photo']), )); diff --git a/view/theme/redbasic/php/style.php b/view/theme/redbasic/php/style.php index e5a6eb0cd..91cc0b85b 100644 --- a/view/theme/redbasic/php/style.php +++ b/view/theme/redbasic/php/style.php @@ -26,7 +26,6 @@ if(! App::$install) { $radius = get_pconfig($uid, 'redbasic', 'radius'); $shadow = get_pconfig($uid,'redbasic','photo_shadow'); $converse_width=get_pconfig($uid,'redbasic','converse_width'); - $align_left=get_pconfig($uid,'redbasic','align_left'); $top_photo=get_pconfig($uid,'redbasic','top_photo'); $reply_photo=get_pconfig($uid,'redbasic','reply_photo'); } @@ -117,10 +116,6 @@ if(file_exists('view/theme/redbasic/css/style.css')) { $x .= file_get_contents('view/theme/redbasic/css/narrow_navbar.css'); } - if($align_left && file_exists('view/theme/redbasic/css/align_left.css')) { - $x .= file_get_contents('view/theme/redbasic/css/align_left.css'); - } - if($schemecss) { $x .= $schemecss; } @@ -128,12 +123,8 @@ if(file_exists('view/theme/redbasic/css/style.css')) { $aside_width = 288; // left aside and right aside are 285px + converse width - if($align_left) { - $main_width = (($aside_width) + intval($converse_width)); - } - else { - $main_width = (($aside_width * 2) + intval($converse_width)); - } + $main_width = (($aside_width * 2) + intval($converse_width)); + // prevent main_width smaller than 768px $main_width = (($main_width < 768) ? 768 : $main_width); diff --git a/view/theme/redbasic/tpl/theme_settings.tpl b/view/theme/redbasic/tpl/theme_settings.tpl index f90e37ac3..7c552b49e 100644 --- a/view/theme/redbasic/tpl/theme_settings.tpl +++ b/view/theme/redbasic/tpl/theme_settings.tpl @@ -1,4 +1,3 @@ -{{include file="field_checkbox.tpl" field=$align_left}} {{include file="field_checkbox.tpl" field=$narrow_navbar}} {{include file="field_input.tpl" field=$converse_width}} {{include file="field_input.tpl" field=$font_size}} diff --git a/view/tpl/abook_edit.tpl b/view/tpl/abook_edit.tpl index 146030494..13b94a560 100755 --- a/view/tpl/abook_edit.tpl +++ b/view/tpl/abook_edit.tpl @@ -52,7 +52,7 @@ {{/foreach}}
    - {{if $canadd}} - - {{/if}} {{if ! $refresh}}
{{/if}} @@ -33,7 +43,7 @@ {{if $canadd}}