diff --git a/.travis.yml b/.travis.yml
index 94c7b4226..8db58f3dc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -62,28 +62,34 @@ matrix:
fast_finish: true
# Additional check combinations
include:
- # PHP7.1, mariadb 10.1
- - php: '7.1'
- env: DB=mariadb MARIADB_VERSION=10.1 CODECOV=1
+ # PHP7.2, mariadb 10.2
+ - php: '7.2'
+ env: DB=mariadb MARIADB_VERSION=10.2 CODECOV=1
# use mariadb instead of MySQL
addons:
- mariadb: '10.1'
- # PHP7.1, PostgreSQL 9.6
- - php: '7.1'
+ mariadb: '10.2'
+ # PHP7.2, PostgreSQL 9.6
+ - php: '7.2'
env: DB=pgsql POSTGRESQL_VERSION=9.6 PHPUNITFILE=phpunit-pgsql.xml
# Use newer postgres than 9.2 default
addons:
postgresql: '9.6'
services:
- postgresql
- # PHP7.1, old precise distribution with MySQL 5.5
- - php: '7.1'
+ # PostgreSQL 10 with Docker container
+ - php: '7.2'
+ env: DB=pgsql POSTGRESQL_VERSION=10 PHPUNITFILE=phpunit-pgsql.xml
+ sudo: required
+ services:
+ - docker
+ # PHP7.2, old precise distribution with MySQL 5.5
+ - php: '7.2'
env: DB=mysql MYSQL_VERSION=5.5
dist: precise
services:
- mysql
# MySQL 5.7 with Docker container
- - php: '7.1'
+ - php: '7.2'
env: DB=mysql MYSQL_VERSION=5.7
sudo: required
services:
@@ -109,6 +115,8 @@ before_install:
- travis_retry composer self-update
# Start MySQL 5.7 Docker container, needs some time to come up
- if [[ "$MYSQL_VERSION" == "5.7" ]]; then sudo service mysql stop; docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:5.7 && sleep 25 && docker ps; fi
+ # Start PostgreSQL 10 Docker container, needs some time to come up
+ - if [[ "$POSTGRESQL_VERSION" == "10" ]]; then sudo service postgresql stop; docker run -d -p 5432:5432 postgres:10-alpine && sleep 35 && docker ps; fi
# Install composer dev libs
install:
diff --git a/CHANGELOG b/CHANGELOG
index ad5e004c8..62645b2b1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,79 @@
+Hubzilla 3.4 (2018-05-04)
+ - Provide warnings about profile photo and cover photo permissions
+ - Don't duplicate addressbook entries on repeated channel imports
+ - Where possible strip zid parameter from links that get pasted into posts so that they will get a correct zid when rendered
+ - Rename boxy schema to Focus-Boxy
+ - Rename BS-Default schema to Focus-Light
+ - Mark simple_* schemas unmaintained and deprecated - they will be removed in next release if nobody steps up to maintain them.
+ - Implement trending tags for mod pubstream
+ - Relax restrictions to the design tools menu to allow those with write_pages permission
+ - Add alt pager to mod moderate
+ - Show existing cover photo when changing it
+ - Update to bootstrap lib to version 4.1
+ - Provide a higher accuracy method for active channels information
+ - Provide visible star status for starred posts
+ - Move the thread author menu to the wall item photo
+ - Accept system_language through either get or post
+ - Remove recipient name from stored notifications but keep them in emails
+ - Fix issue of being forced to log back in after leaving a delegated channel
+ - Implement last commented expiration setting in mod admin
+ - Create catcloud widget and provide a type option which can include 'cards' or 'articles'
+ - Modified notifications widget to add the public stream when the current user is allowed to see it only
+ - Don't provide a connect button for transient identities
+ - Merge techlevels and features
+ - Implement auto-save posts and comments in browser using localStorage
+ - Display directory server in siteinfo.json
+ - Bring back the dnt policy document
+ - Implement OAuth2/OpenIDConnect server
+ - Add basic structure for additional features documentation
+ - Community tag refactor
+ - Obscurify chats
+ - Provide a way to share wiki pages
+ - Update folder timestamp on uploaded files
+ - Code optimisations and de-duplication on updating parent commented timestamp
+ - Turn newmember widget into a feature
+ - Make list mode work in cards and articles
+ - Make alt pager work for articles and cards
+ - Initial support for alternative sort orders on the cloud pages
+ - Add Ochannel module for testing OStatus bad behaviour
+ - Add the social - federation permission role
+ - Update justified gallery lib from 3.6.3 to 3.6.5
+
+ Bugfixes
+ - Fix regression with forum widget unseen count
+ - Fix issue with imagemagick exif info
+ - Aonymous comments in StdLimits shouldn't be allowed
+ - Fix wiki pages not syncing
+ - Show "Unseen public activity" channel setting when site only public streams are activated
+ - Fix channel import failing to provide channel_password value
+ - Fix permalinks to children of articles and cards
+ - Fix missing year on profile birthday input
+ - Fix missing login/out buttons for medium screensize
+ - Preserve existing categories when updating an app from an embed source
+ - Fix app sellpage not being stored
+ - Fix tagadelic being overly protective of permissions
+ - Fix comments not displayed in single card/article view
+ - Fix anonymous comments bump thread
+ - Fix pending registrations visible in admin accounts
+
+ Addons
+ Pubcrawl: fix issues with "private" messages
+ Pubcrawl: fix issues with postgresql
+ Fuzzloc: new addon to blur your browser location
+ Pubcrawl: implement follow by webfinger
+ Cart: new addon which provides online shop functionalities (experimental)
+ Pubcrawl: implement two-way summary functionality
+ Wordpress: upgrade incutio xmlrpc library to use hubzilla curl wrapper
+ Hzfiles: various fixes
+ Diaspora: support full_name attribute in profile messages
+ Frphotos: deprecate plugin (keep it for reference)
+ Webmention: require html5 parser
+ GNU-Social: provide alternative xchan_url
+ Diaspora: fix wrong callback function
+ Diaspora: fix conversion of forum mentions to markdown by providing a !{forum@host} link syntax
+ Diaspora: fix item title not transferred
+
+
Hubzilla 3.2 (2018-03-09)
- Improve rendering of Readme files in plugin settings
- Add pdl file for mod moderate
diff --git a/Zotlabs/Access/PermissionLimits.php b/Zotlabs/Access/PermissionLimits.php
index 125d11b7b..b8ca3034c 100644
--- a/Zotlabs/Access/PermissionLimits.php
+++ b/Zotlabs/Access/PermissionLimits.php
@@ -72,13 +72,13 @@ class PermissionLimits {
* @param int $channel_id
* @param string $perm (optional)
* @return
- * * \b boolean false if no perm_limits set for this channel
- * * \b int if $perm is set, return one of PERMS_* constants for this permission
+ * * \b false if no perm_limits set for this channel
+ * * \b int if $perm is set, return one of PERMS_* constants for this permission, default 0
* * \b array with all permission limits, if $perm is not set
*/
static public function Get($channel_id, $perm = '') {
if($perm) {
- return PConfig::Get($channel_id, 'perm_limits', $perm);
+ return intval(PConfig::Get($channel_id, 'perm_limits', $perm));
}
PConfig::Load($channel_id);
diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php
index b335bf825..9855a05c4 100644
--- a/Zotlabs/Access/PermissionRoles.php
+++ b/Zotlabs/Access/PermissionRoles.php
@@ -41,6 +41,24 @@ class PermissionRoles {
break;
+ case 'social_federation':
+ $ret['perms_auto'] = false;
+ $ret['default_collection'] = false;
+ $ret['directory_publish'] = true;
+ $ret['online'] = true;
+ $ret['perms_connect'] = [
+ 'view_stream', 'view_profile', 'view_contacts', 'view_storage',
+ 'view_pages', 'view_wiki', 'send_stream', 'post_wall', 'post_comments',
+ 'post_mail', 'chat', 'post_like', 'republish'
+ ];
+ $ret['limits'] = PermissionLimits::Std_Limits();
+ $ret['limits']['post_comments'] = PERMS_AUTHED;
+ $ret['limits']['post_mail'] = PERMS_AUTHED;
+ $ret['limits']['post_like'] = PERMS_AUTHED;
+ $ret['limits']['chat'] = PERMS_AUTHED;
+ break;
+
+
case 'social_restricted':
$ret['perms_auto'] = false;
$ret['default_collection'] = true;
@@ -263,6 +281,7 @@ class PermissionRoles {
static public function roles() {
$roles = [
t('Social Networking') => [
+ 'social_federation' => t('Social - Federation'),
'social' => t('Social - Mostly Public'),
'social_restricted' => t('Social - Restricted'),
'social_private' => t('Social - Private')
diff --git a/Zotlabs/Daemon/Expire.php b/Zotlabs/Daemon/Expire.php
index 215513e87..398425861 100644
--- a/Zotlabs/Daemon/Expire.php
+++ b/Zotlabs/Daemon/Expire.php
@@ -34,7 +34,8 @@ class Expire {
logger('expire: start', LOGGER_DEBUG);
- $site_expire = get_config('system', 'default_expire_days');
+ $site_expire = intval(get_config('system', 'default_expire_days'));
+ $commented_days = intval(get_config('system','active_expire_days'));
logger('site_expire: ' . $site_expire);
@@ -64,7 +65,7 @@ class Expire {
// if the site or service class expiration is non-zero and less than person expiration, use that
logger('Expire: ' . $rr['channel_address'] . ' interval: ' . $expire_days, LOGGER_DEBUG);
- item_expire($rr['channel_id'], $expire_days);
+ item_expire($rr['channel_id'], $expire_days, $commented_days);
}
}
@@ -85,7 +86,7 @@ class Expire {
logger('Expire: sys interval: ' . $expire_days, LOGGER_DEBUG);
if ($expire_days)
- item_expire($x['channel_id'], $expire_days);
+ item_expire($x['channel_id'], $expire_days, $commented_days);
logger('Expire: sys: done', LOGGER_DEBUG);
}
diff --git a/Zotlabs/Lib/Chatroom.php b/Zotlabs/Lib/Chatroom.php
index e762620ae..882c846cd 100644
--- a/Zotlabs/Lib/Chatroom.php
+++ b/Zotlabs/Lib/Chatroom.php
@@ -266,7 +266,7 @@ class Chatroom {
intval($room_id),
dbesc($xchan),
dbesc(datetime_convert()),
- dbesc($arr['chat_text'])
+ dbesc(str_rot47(base64url_encode($arr['chat_text'])))
);
$ret['success'] = true;
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 61c98c881..cfb0bd344 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -115,6 +115,9 @@ class Enotify {
$always_show_in_notices = get_pconfig($recip['channel_id'],'system','always_show_in_notices');
+ $vnotify = get_pconfig($recip['channel_id'],'system','vnotify');
+
+ $salutation = $recip['channel_name'];
// e.g. "your post", "David's photo", etc.
$possess_desc = t('%s ');
@@ -123,7 +126,7 @@ class Enotify {
logger('notification: mail');
$subject = sprintf( t('[$Projectname:Notify] New mail received at %s'),$sitename);
- $preamble = sprintf( t('%1$s, %2$s sent you a new private message at %3$s.'),$recip['channel_name'], $sender['xchan_name'],$sitename);
+ $preamble = sprintf( t('%1$s sent you a new private message at %2$s.'), $sender['xchan_name'],$sitename);
$epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]');
$sitelink = t('Please visit %s to view and/or reply to your private messages.');
$tsitelink = sprintf( $sitelink, $siteurl . '/mail/' . $params['item']['id'] );
@@ -142,7 +145,7 @@ class Enotify {
if(array_key_exists('item',$params) && in_array($params['item']['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) {
- if(! $always_show_in_notices) {
+ if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
return;
@@ -195,8 +198,7 @@ class Enotify {
//$possess_desc = str_replace('',$possess_desc);
// "a post"
- $dest_str = sprintf(t('%1$s, %2$s %3$s [zrl=%4$s]a %5$s[/zrl]'),
- $recip['channel_name'],
+ $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]a %4$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
@@ -204,8 +206,7 @@ class Enotify {
// "George Bull's post"
if($p)
- $dest_str = sprintf(t('%1$s, %2$s %3$s [zrl=%4$s]%5$s\'s %6$s[/zrl]'),
- $recip['channel_name'],
+ $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]%4$s\'s %5$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
@@ -214,8 +215,7 @@ class Enotify {
// "your post"
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
- $dest_str = sprintf(t('%1$s, %2$s %3$s [zrl=%4$s]your %5$s[/zrl]'),
- $recip['channel_name'],
+ $dest_str = sprintf(t('%1$s %2$s [zrl=%3$s]your %4$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$action,
$itemlink,
@@ -230,7 +230,7 @@ class Enotify {
$subject = sprintf( t('[$Projectname:Notify] Moderated Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
else
$subject = sprintf( t('[$Projectname:Notify] Comment to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s commented on an item/conversation you have been following.'), $recip['channel_name'], $sender['xchan_name']);
+ $preamble = sprintf( t('%1$s commented on an item/conversation you have been following.'), $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
@@ -249,7 +249,7 @@ class Enotify {
$itemlink = $params['link'];
if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) {
- if(! $always_show_in_notices) {
+ if(! $always_show_in_notices || !($vnotify & VNOTIFY_LIKE)) {
logger('notification: not a visible activity. Ignoring.');
pop_lang();
return;
@@ -296,8 +296,7 @@ class Enotify {
// "your post"
if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall']))
- $dest_str = sprintf(t('%1$s, %2$s liked [zrl=%3$s]your %4$s[/zrl]'),
- $recip['channel_name'],
+ $dest_str = sprintf(t('%1$s liked [zrl=%2$s]your %3$s[/zrl]'),
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$itemlink,
$item_post_type);
@@ -312,7 +311,7 @@ class Enotify {
// differents subjects for messages on the same thread.
$subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s liked an item/conversation you created.'), $recip['channel_name'], $sender['xchan_name']);
+ $preamble = sprintf( t('%1$s liked an item/conversation you created.'), $sender['xchan_name']);
$epreamble = $dest_str;
$sitelink = t('Please visit %s to view and/or reply to the conversation.');
@@ -325,10 +324,9 @@ class Enotify {
if($params['type'] == NOTIFY_WALL) {
$subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s posted to your profile wall at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
+ $preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, %2$s posted to [zrl=%3$s]your wall[/zrl]') ,
- $recip['channel_name'],
+ $epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -352,9 +350,8 @@ class Enotify {
}
$subject = sprintf( t('[$Projectname:Notify] %s tagged you') , $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s tagged you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, %2$s [zrl=%3$s]tagged you[/zrl].') ,
- $recip['channel_name'],
+ $preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename);
+ $epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -366,9 +363,8 @@ class Enotify {
if ($params['type'] == NOTIFY_POKE) {
$subject = sprintf( t('[$Projectname:Notify] %1$s poked you') , $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s poked you at %3$s') , $recip['channel_name'], $sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, %2$s [zrl=%2$s]poked you[/zrl].') ,
- $recip['channel_name'],
+ $preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename);
+ $epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$params['link']);
@@ -384,9 +380,8 @@ class Enotify {
if ($params['type'] == NOTIFY_TAGSHARE) {
$subject = sprintf( t('[$Projectname:Notify] %s tagged your post') , $sender['xchan_name']);
- $preamble = sprintf( t('%1$s, %2$s tagged your post at %3$s') , $recip['channel_name'],$sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, %2$s tagged [zrl=%3$s]your post[/zrl]') ,
- $recip['channel_name'],
+ $preamble = sprintf( t('%1$s tagged your post at %2$s'),$sender['xchan_name'], $sitename);
+ $epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') ,
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]',
$itemlink);
@@ -398,9 +393,8 @@ class Enotify {
if ($params['type'] == NOTIFY_INTRO) {
$subject = sprintf( t('[$Projectname:Notify] Introduction received'));
- $preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'),
- $recip['channel_name'],
+ $preamble = sprintf( t('You\'ve received an new connection request from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
+ $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a new connection request[/zrl] from %2$s.'),
$siteurl . '/connections/ifpending',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
$body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']);
@@ -413,9 +407,8 @@ class Enotify {
if ($params['type'] == NOTIFY_SUGGEST) {
$subject = sprintf( t('[$Projectname:Notify] Friend suggestion received'));
- $preamble = sprintf( t('%1$s, you\'ve received a friend suggestion from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename);
- $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a friend suggestion[/zrl] for %3$s from %4$s.'),
- $recip['channel_name'],
+ $preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename);
+ $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'),
$itemlink,
'[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]',
'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]');
@@ -664,7 +657,7 @@ class Enotify {
'$banner' => $datarray['banner'],
'$notify_icon' => \Zotlabs\Lib\System::get_notify_icon(),
'$product' => $datarray['product'],
- '$preamble' => $datarray['preamble'],
+ '$preamble' => $salutation . '
' . $datarray['preamble'],
'$sitename' => $datarray['sitename'],
'$siteurl' => $datarray['siteurl'],
'$source_name' => $datarray['source_name'],
@@ -686,7 +679,7 @@ class Enotify {
$email_text_body = replace_macros($tpl, array(
'$banner' => $datarray['banner'],
'$product' => $datarray['product'],
- '$preamble' => $datarray['preamble'],
+ '$preamble' => $salutation . "\n\n" . $datarray['preamble'],
'$sitename' => $datarray['sitename'],
'$siteurl' => $datarray['siteurl'],
'$source_name' => $datarray['source_name'],
@@ -754,8 +747,8 @@ class Enotify {
// generate a mime boundary
$mimeBoundary = rand(0, 9) . "-"
- .rand(10000000000, 9999999999) . "-"
- .rand(10000000000, 9999999999) . "=:"
+ .rand(100000000, 999999999) . "-"
+ .rand(100000000, 999999999) . "=:"
.rand(10000, 99999);
// generate a multipart/alternative message header
diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php
index 7642dbb3e..6f916216e 100644
--- a/Zotlabs/Lib/NativeWiki.php
+++ b/Zotlabs/Lib/NativeWiki.php
@@ -171,16 +171,23 @@ class NativeWiki {
dbesc(NWIKI_ITEM_RESOURCE_TYPE),
dbesc($resource_id)
);
+
if($r) {
$q = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s'",
- dbesc($r[0]['resource_type'])
+ dbesc($r[0]['resource_id'])
);
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))));
+ if($sync_item) {
+ $pkt = [];
+ foreach($sync_item as $w) {
+ $pkt[] = encode_item($w,true);
+ }
+ build_sync_packet($uid,array('wiki' => $pkt));
+ }
}
}
diff --git a/Zotlabs/Lib/Share.php b/Zotlabs/Lib/Share.php
index b5341e662..e9e4d23c4 100644
--- a/Zotlabs/Lib/Share.php
+++ b/Zotlabs/Lib/Share.php
@@ -123,11 +123,13 @@ class Share {
$bb = substr($this->item['body'], $pos);
} else {
$bb = "[share author='".urlencode($this->item['author']['xchan_name']).
- "' profile='".$this->item['author']['xchan_url'] .
- "' avatar='".$this->item['author']['xchan_photo_s'].
- "' link='".$this->item['plink'].
- "' posted='".$this->item['created'].
- "' message_id='".$this->item['mid']."']";
+ "' profile='" . $this->item['author']['xchan_url'] .
+ "' avatar='" . $this->item['author']['xchan_photo_s'] .
+ "' link='" . $this->item['plink'] .
+ "' auth='" . (($this->item['author']['network'] === 'zot') ? 'true' : 'false') .
+ "' posted='" . $this->item['created'] .
+ "' message_id='" . $this->item['mid'] .
+ "']";
if($this->item['title'])
$bb .= '[b]'.$this->item['title'].'[/b]'."\r\n";
$bb .= (($is_photo) ? $photo_bb . "\r\n" . $this->item['body'] : $this->item['body']);
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index d35d4732a..61a012f9d 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -238,9 +238,9 @@ class ThreadItem {
'do' => t("Add Star"),
'undo' => t("Remove Star"),
'toggle' => t("Toggle Star Status"),
- 'classdo' => (intval($item['item_starred']) ? "hidden" : ""),
- 'classundo' => (intval($item['item_starred']) ? "" : "hidden"),
- 'isstarred' => (intval($item['item_starred']) ? "starred fa-star" : "unstarred fa-star-o"),
+ 'classdo' => ((intval($item['item_starred'])) ? "hidden" : ""),
+ 'classundo' => ((intval($item['item_starred'])) ? "" : "hidden"),
+ 'isstarred' => ((intval($item['item_starred'])) ? true : false),
'starred' => t('starred'),
);
@@ -733,6 +733,8 @@ class ThreadItem {
$arr = array('comment_buttons' => '','id' => $this->get_id());
call_hooks('comment_buttons',$arr);
$comment_buttons = $arr['comment_buttons'];
+
+ $feature_auto_save_draft = ((feature_enabled($conv->get_profile_owner(), 'auto_save_draft')) ? "true" : "false");
$comment_box = replace_macros($template,array(
'$return_path' => '',
@@ -768,7 +770,8 @@ class ThreadItem {
'$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)') ]
+ '$anonurl' => [ 'anonurl', t('Your website URL (optional)') ],
+ '$auto_save_draft' => $feature_auto_save_draft,
));
return $comment_box;
diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php
index fae7e2e44..4c5883e88 100644
--- a/Zotlabs/Module/Acl.php
+++ b/Zotlabs/Module/Acl.php
@@ -24,7 +24,7 @@ class Acl extends \Zotlabs\Web\Controller {
function init() {
- logger('mod_acl: ' . print_r($_REQUEST,true));
+ logger('mod_acl: ' . print_r($_REQUEST,true),LOGGER_DATA);
$start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0);
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 500);
@@ -82,7 +82,7 @@ class Acl extends \Zotlabs\Web\Controller {
if($search) {
$sql_extra = " AND groups.gname LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
- $sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc($search) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
+ $sql_extra2 = "AND ( xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " OR xchan_addr LIKE " . protect_sprintf( "'%" . dbesc(punify($search)) . ((strpos($search,'@') === false) ? "%@%'" : "%'")) . ") ";
// This horrible mess is needed because position also returns 0 if nothing is found.
// Would be MUCH easier if it instead returned a very large value
@@ -92,10 +92,10 @@ class Acl extends \Zotlabs\Web\Controller {
$order_extra2 = "CASE WHEN xchan_name LIKE "
. protect_sprintf( "'%" . dbesc($search) . "%'" )
. " then POSITION('" . protect_sprintf(dbesc($search))
- . "' IN xchan_name) else position('" . protect_sprintf(dbesc($search)) . "' IN xchan_addr) end, ";
+ . "' IN xchan_name) else position('" . protect_sprintf(dbesc(punify($search))) . "' IN xchan_addr) end, ";
$col = ((strpos($search,'@') !== false) ? 'xchan_addr' : 'xchan_name' );
- $sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " ";
+ $sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc(($col === 'xchan_addr') ? punify($search) : $search) . "%'" ) . " ";
}
else {
@@ -435,7 +435,7 @@ class Acl extends \Zotlabs\Web\Controller {
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
if($url) {
$query = $url . '?f=' . (($token) ? '&t=' . urlencode($token) : '');
- $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode($search) : '');
+ $query .= '&name=' . urlencode($search) . "&limit=$count" . (($address) ? '&address=' . urlencode(punify($search)) : '');
$x = z_fetch_url($query);
if($x['success']) {
diff --git a/Zotlabs/Module/Admin/Accounts.php b/Zotlabs/Module/Admin/Accounts.php
index 2e417edd1..0c7e089be 100644
--- a/Zotlabs/Module/Admin/Accounts.php
+++ b/Zotlabs/Module/Admin/Accounts.php
@@ -133,12 +133,13 @@ class Accounts {
$base = z_root() . '/admin/accounts?f=';
$odir = (($dir === 'asc') ? '0' : '1');
-
+
$users = q("SELECT account_id , account_email, account_lastlog, account_created, account_expires, account_service_class, ( account_flags & %d ) > 0 as blocked,
(SELECT %s FROM channel as ch WHERE ch.channel_account_id = ac.account_id and ch.channel_removed = 0 ) as channels FROM account as ac
- where true $serviceclass order by $key $dir limit %d offset %d ",
+ where true $serviceclass and account_flags != %d order by $key $dir limit %d offset %d ",
intval(ACCOUNT_BLOCKED),
db_concat('ch.channel_address', ' '),
+ intval(ACCOUNT_BLOCKED | ACCOUNT_PENDING),
intval(\App::$pager['itemspage']),
intval(\App::$pager['start'])
);
@@ -203,4 +204,4 @@ class Accounts {
}
-}
\ No newline at end of file
+}
diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php
index 6d5747380..656770ad9 100644
--- a/Zotlabs/Module/Admin/Site.php
+++ b/Zotlabs/Module/Admin/Site.php
@@ -39,9 +39,10 @@ class Site {
$site_location = ((x($_POST,'site_location')) ? notags(trim($_POST['site_location'])) : '');
$frontpage = ((x($_POST,'frontpage')) ? notags(trim($_POST['frontpage'])) : '');
$firstpage = ((x($_POST,'firstpage')) ? notags(trim($_POST['firstpage'])) : 'profiles');
+ $first_page = ((x($_POST,'first_page')) ? notags(trim($_POST['first_page'])) : 'profiles');
// check value after trim
- if(! $firstpage) {
- $firstpage = 'profiles';
+ if(! $first_page) {
+ $first_page = 'profiles';
}
$mirror_frontpage = ((x($_POST,'mirror_frontpage')) ? intval(trim($_POST['mirror_frontpage'])) : 0);
$directory_server = ((x($_POST,'directory_server')) ? trim($_POST['directory_server']) : '');
@@ -55,6 +56,7 @@ class Site {
$global_directory = ((x($_POST,'directory_submit_url')) ? notags(trim($_POST['directory_submit_url'])) : '');
$no_community_page = !((x($_POST,'no_community_page')) ? True : False);
$default_expire_days = ((array_key_exists('default_expire_days',$_POST)) ? intval($_POST['default_expire_days']) : 0);
+ $active_expire_days = ((array_key_exists('active_expire_days',$_POST)) ? intval($_POST['active_expire_days']) : 7);
$reply_address = ((array_key_exists('reply_address',$_POST) && trim($_POST['reply_address'])) ? trim($_POST['reply_address']) : 'noreply@' . \App::get_hostname());
$from_email = ((array_key_exists('from_email',$_POST) && trim($_POST['from_email'])) ? trim($_POST['from_email']) : 'Administrator@' . \App::get_hostname());
@@ -86,7 +88,7 @@ class Site {
set_config('system', 'maxloadavg', $maxloadavg);
set_config('system', 'frontpage', $frontpage);
set_config('system', 'sellpage', $site_sellpage);
- set_config('system', 'workflow_channel_next', $firstpage);
+ set_config('system', 'workflow_channel_next', $first_page);
set_config('system', 'site_location', $site_location);
set_config('system', 'mirror_frontpage', $mirror_frontpage);
set_config('system', 'sitename', $sitename);
@@ -94,6 +96,7 @@ class Site {
set_config('system', 'enable_context_help', $enable_context_help);
set_config('system', 'verify_email', $verify_email);
set_config('system', 'default_expire_days', $default_expire_days);
+ set_config('system', 'active_expire_days', $active_expire_days);
set_config('system', 'reply_address', $reply_address);
set_config('system', 'from_email', $from_email);
set_config('system', 'from_email_name' , $from_email_name);
@@ -347,9 +350,10 @@ class Site {
'$thumbnail_security' => array('thumbnail_security', t("Allow SVG thumbnails in file browser"), get_config('system','thumbnail_security',0), t("WARNING: SVG images may contain malicious code.")),
'$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')),
+ '$active_expire_days' => array('active_expire_days', t('Do not expire any posts which have comments less than this many days ago'), intval(get_config('system','active_expire_days',7)), ''),
'$sellpage' => array('site_sellpage', t('Public servers: Optional landing (marketing) webpage for new registrants'), get_config('system','sellpage',''), sprintf( t('Create this page first. Default is %s/register'),z_root())),
- '$firstpage' => array('firstpage', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Recommend: profiles, go, or settings')),
+ '$first_page' => array('first_page', t('Page to display after creating a new channel'), get_config('system','workflow_channel_next','profiles'), t('Recommend: profiles, go, or settings')),
'$location' => array('site_location', t('Optional: site location'), get_config('system','site_location',''), t('Region or country')),
diff --git a/Zotlabs/Module/Articles.php b/Zotlabs/Module/Articles.php
index 62ce1cb9c..284868241 100644
--- a/Zotlabs/Module/Articles.php
+++ b/Zotlabs/Module/Articles.php
@@ -191,7 +191,7 @@ class Articles extends \Zotlabs\Web\Controller {
'$title' => t('Articles'),
'$editor' => $editor,
'$content' => $content,
- '$pager' => alt_pager($a,$pager_total)
+ '$pager' => alt_pager($pager_total)
]);
return $o;
diff --git a/Zotlabs/Module/Authorize.php b/Zotlabs/Module/Authorize.php
index 254700b4e..bfb76150f 100644
--- a/Zotlabs/Module/Authorize.php
+++ b/Zotlabs/Module/Authorize.php
@@ -4,60 +4,89 @@ namespace Zotlabs\Module;
use Zotlabs\Identity\OAuth2Storage;
-
class Authorize extends \Zotlabs\Web\Controller {
- function init() {
+ function get() {
+ if (!local_channel()) {
+ return login();
+ } else {
+ // TODO: Fully implement the dynamic client registration protocol:
+ // OpenID Connect Dynamic Client Registration 1.0 Client Metadata
+ // http://openid.net/specs/openid-connect-registration-1_0.html
+ $app = array(
+ 'name' => (x($_REQUEST, 'client_name') ? urldecode($_REQUEST['client_name']) : t('Unknown App')),
+ 'icon' => (x($_REQUEST, 'logo_uri') ? urldecode($_REQUEST['logo_uri']) : z_root() . '/images/icons/plugin.png'),
+ 'url' => (x($_REQUEST, 'client_uri') ? urldecode($_REQUEST['client_uri']) : ''),
+ );
+ $o .= replace_macros(get_markup_template('oauth_authorize.tpl'), array(
+ '$title' => t('Authorize'),
+ '$authorize' => sprintf( t('Do you authorize the app %s to access your channel data?'), '' . $app['name'] . ' '),
+ '$app' => $app,
+ '$yes' => t('Allow'),
+ '$no' => t('Deny'),
+ '$client_id' => (x($_REQUEST, 'client_id') ? $_REQUEST['client_id'] : ''),
+ '$redirect_uri' => (x($_REQUEST, 'redirect_uri') ? $_REQUEST['redirect_uri'] : ''),
+ '$state' => (x($_REQUEST, 'state') ? $_REQUEST['state'] : ''),
+ ));
+ return $o;
+ }
+ }
- // 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;
- }
+ function post() {
+ if (! local_channel()) {
+ return;
}
- 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;
- }
- }
+ $storage = new OAuth2Storage(\DBA::$dba->db);
+ $s = new \Zotlabs\Identity\OAuth2Server($storage);
- $s = new \Zotlabs\Identity\OAuth2Server(new OAuth2Storage(\DBA::$dba->db));
+ // TODO: The automatic client registration protocol below should adhere more
+ // closely to "OAuth 2.0 Dynamic Client Registration Protocol" defined
+ // at https://tools.ietf.org/html/rfc7591
+
+ // If no client_id was provided, generate a new one.
+ if (x($_POST, 'client_id')) {
+ $client_id = $_POST['client_id'];
+ } else {
+ $client_id = $_POST['client_id'] = random_string(16);
+ }
+ // If no redirect_uri was provided, generate a fake one.
+ if (x($_POST, 'redirect_uri')) {
+ $redirect_uri = $_POST['redirect_uri'];
+ } else {
+ $redirect_uri = $_POST['redirect_uri'] = 'https://fake.example.com/oauth';
+ }
$request = \OAuth2\Request::createFromGlobals();
$response = new \OAuth2\Response();
+ // If the client is not registered, add to the database
+ if (!$client = $storage->getClientDetails($client_id)) {
+ $client_secret = random_string(16);
+ // Client apps are registered per channel
+ $user_id = local_channel();
+ $storage->setClientDetails($client_id, $client_secret, $redirect_uri, 'authorization_code', null, $user_id);
+
+ }
+ if (!$client = $storage->getClientDetails($client_id)) {
+ // There was an error registering the client.
+ $response->send();
+ killme();
+ }
+ $response->setParameter('client_secret', $client['client_secret']);
+
// validate the authorize request
- if (! $s->validateAuthorizeRequest($request, $response)) {
+ if (!$s->validateAuthorizeRequest($request, $response)) {
$response->send();
killme();
}
- // display an authorization form
- if (empty($_POST)) {
-
- return '
-
'; $o .= str_replace("\n",''; diff --git a/Zotlabs/Module/Well_known.php b/Zotlabs/Module/Well_known.php index b57666bff..177de2323 100644 --- a/Zotlabs/Module/Well_known.php +++ b/Zotlabs/Module/Well_known.php @@ -26,7 +26,6 @@ class Well_known extends \Zotlabs\Web\Controller { killme(); } - switch(argv(1)) { case 'zot-info': \App::$argc -= 1; @@ -52,6 +51,10 @@ class Well_known extends \Zotlabs\Web\Controller { $module->init(); break; + case 'dnt-policy.txt': + echo file_get_contents('doc/dnt-policy.txt'); + killme(); + default: if(file_exists(\App::$cmd)) { echo file_get_contents(\App::$cmd); diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index ae543eb98..7dc8eb1bc 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -284,6 +284,8 @@ class Wiki extends \Zotlabs\Web\Controller { $wikiheaderPage = urldecode($pageUrlName); $renamePage = (($wikiheaderPage === 'Home') ? '' : t('Rename page')); + $sharePage = t('Share'); + $p = []; if(! $ignore_language) { @@ -354,6 +356,8 @@ class Wiki extends \Zotlabs\Web\Controller { '$wikiheaderName' => $wikiheaderName, '$wikiheaderPage' => $wikiheaderPage, '$renamePage' => $renamePage, + '$sharePage' => $sharePage, + '$shareLink' => urlencode('#^[zrl=' . z_root() . '/wiki/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName . ']' . '[ ' . $owner['channel_name'] . ' ] ' . $wikiheaderName . ' - ' . $wikiheaderPage . '[/zrl]'), '$showPageControls' => $showPageControls, '$editOrSourceLabel' => (($showPageControls) ? t('Edit') : t('Source')), '$tools_label' => 'Page Tools', diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php index f1c95802b..508c39d22 100644 --- a/Zotlabs/Storage/Browser.php +++ b/Zotlabs/Storage/Browser.php @@ -274,7 +274,7 @@ class Browser extends DAV\Browser\Plugin { '$actionspanel' => $output, '$shared' => t('Shared'), '$create' => t('Create'), - '$upload' => t('Upload'), + '$upload' => t('Add Files'), '$is_owner' => $is_owner, '$parentpath' => $parentpath, '$cpath' => bin2hex(\App::$query_string), diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php index 510d463c1..7068ee15a 100644 --- a/Zotlabs/Storage/Directory.php +++ b/Zotlabs/Storage/Directory.php @@ -169,7 +169,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo $x = attach_syspaths($this->auth->owner_id,$this->folder_hash); - $y = q("update attach set display_path = '%s where hash = '%s' and uid = %d", + $y = q("update attach set display_path = '%s' where hash = '%s' and uid = %d", dbesc($x['path']), dbesc($this->folder_hash), intval($this->auth->owner_id) @@ -389,8 +389,12 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo ); if ($r) { + + // When initiated from DAV, set the 'force' flag on attach_mkdir(). This will cause the operation to report success even if the + // folder already exists. + require_once('include/attach.php'); - $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); + $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash, 'force' => true)); if($result['success']) { $sync = attach_export_data($r[0],$result['data']['hash']); @@ -680,7 +684,7 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo throw new DAV\Exception\Forbidden('Permission denied.'); } else { - throw new DAV\Exception\NotFound('A component of the request file path could not be found.'); + throw new DAV\Exception\NotFound('A component of the requested file path could not be found.'); } } @@ -691,7 +695,23 @@ class Directory extends DAV\Node implements DAV\ICollection, DAV\IQuota, DAV\IMo } $prefix = ''; - $suffix = ' order by is_dir desc, filename asc '; + + if(! array_key_exists('cloud_sort',$_SESSION)) + $_SESSION['cloud_sort'] = 'name'; + + switch($_SESSION['cloud_sort']) { + case 'size': + $suffix = ' order by is_dir desc, filesize asc '; + break; + // The following provides inconsistent results for directories because we re-calculate the date for directories based on the most recent change + case 'date': + $suffix = ' order by is_dir desc, edited asc '; + break; + case 'name': + default: + $suffix = ' order by is_dir desc, filename asc '; + break; + } $r = q("select $prefix id, uid, hash, filename, filetype, filesize, revision, folder, flags, is_dir, created, edited from attach where folder = '%s' and uid = %d $perms $suffix", dbesc($folder), diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php index 53d5d3476..4610aceb7 100644 --- a/Zotlabs/Storage/File.php +++ b/Zotlabs/Storage/File.php @@ -49,7 +49,7 @@ class File extends DAV\Node implements DAV\IFile { $this->data = $data; $this->auth = $auth; - logger(print_r($this->data, true), LOGGER_DATA); + // logger(print_r($this->data, true), LOGGER_DATA); } /** diff --git a/Zotlabs/Storage/ZotOauth2Pdo.php b/Zotlabs/Storage/ZotOauth2Pdo.php new file mode 100644 index 000000000..b2c3ce228 --- /dev/null +++ b/Zotlabs/Storage/ZotOauth2Pdo.php @@ -0,0 +1,10 @@ +config; + } +} diff --git a/Zotlabs/Update/_1208.php b/Zotlabs/Update/_1208.php new file mode 100644 index 000000000..840252694 --- /dev/null +++ b/Zotlabs/Update/_1208.php @@ -0,0 +1,26 @@ +fetcharr(); $body = $data['body']; @@ -47,9 +56,11 @@ class HTTPSig { else { $headers = []; - $headers['(request-target)'] = + $headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI']; + $headers['content-type'] = $_SERVER['CONTENT_TYPE']; + foreach($_SERVER as $k => $v) { if(strpos($k,'HTTP_') === 0) { $field = str_replace('_','-',strtolower(substr($k,5))); @@ -58,6 +69,10 @@ class HTTPSig { } } + // logger('SERVER: ' . print_r($_SERVER,true), LOGGER_ALL); + + // logger('headers: ' . print_r($headers,true), LOGGER_ALL); + $sig_block = null; if(array_key_exists('signature',$headers)) { @@ -78,7 +93,7 @@ class HTTPSig { $result['header_signed'] = true; $signed_headers = $sig_block['headers']; - if(! $signed_headers) + if(! $signed_headers) $signed_headers = [ 'date' ]; $signed_data = ''; @@ -131,7 +146,7 @@ class HTTPSig { if($digest[0] === 'SHA-512') $hashalg = 'sha512'; - // The explode operation will have stripped the '=' padding, so compare against unpadded base64 + // 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; } @@ -146,7 +161,7 @@ class HTTPSig { if($digest[0] === 'SHA-512') $hashalg = 'sha512'; - // The explode operation will have stripped the '=' padding, so compare against unpadded base64 + // The explode operation will have stripped the '=' padding, so compare against unpadded base64 if(rtrim(base64_encode(hash($hashalg,$_POST['data'],true)),'=') === $digest[1]) { $result['content_valid'] = true; } @@ -155,9 +170,15 @@ class HTTPSig { logger('Content_Valid: ' . (($result['content_valid']) ? 'true' : 'false')); return $result; - } + /** + * @brief + * + * @param string $id + * @return boolean|string + * false if no pub key found, otherwise return the pub key + */ function get_activitypub_key($id) { if(strpos($id,'acct:') === 0) { @@ -174,25 +195,41 @@ class HTTPSig { if($x && $x[0]['xchan_pubkey']) { return ($x[0]['xchan_pubkey']); } - $r = as_fetch($id); + + if(function_exists('as_fetch')) + $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'])) { + if((array_key_exists('id',$j['publicKey']) && $j['publicKey']['id'] !== $id) && $j['id'] !== $id) + return false; + 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') { + /** + * @brief + * + * @param string $request + * @param array $head + * @param string $prvkey + * @param string $keyid (optional, default 'Key') + * @param boolean $send_headers (optional, default false) + * If set send a HTTP header + * @param boolean $auth (optional, default false) + * @param string $alg (optional, default 'sha256') + * @param string $crypt_key (optional, default null) + * @param string $crypt_algo (optional, default 'aes256ctr') + * @return array + */ + static function create_sig($request, $head, $prvkey, $keyid = 'Key', $send_headers = false, $auth = false, + $alg = 'sha256', $crypt_key = null, $crypt_algo = 'aes256ctr') { $return_headers = []; @@ -212,7 +249,7 @@ class HTTPSig { $x = crypto_encapsulate($headerval,$crypt_key,$crypt_algo); $headerval = 'iv="' . $x['iv'] . '",key="' . $x['key'] . '",alg="' . $x['alg'] . '",data="' . $x['data'] . '"'; } - + if($auth) { $sighead = 'Authorization: Signature ' . $headerval; } @@ -236,12 +273,20 @@ class HTTPSig { else { $return_headers[] = $sighead; } + return $return_headers; } - - - static function sign($request,$head,$prvkey,$alg = 'sha256') { + /** + * @brief + * + * @param string $request + * @param array $head + * @param string $prvkey + * @param string $alg (optional) default 'sha256' + * @return array + */ + static function sign($request, $head, $prvkey, $alg = 'sha256') { $ret = []; @@ -250,27 +295,38 @@ class HTTPSig { 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)); + $sig = base64_encode(rsa_sign($headers,$prvkey,$alg)); $ret['headers'] = $fields; $ret['signature'] = $sig; - + return $ret; } + /** + * @brief + * + * @param string $header + * @return array associate array with + * - \e string \b keyID + * - \e string \b algorithm + * - \e array \b headers + * - \e string \b signature + */ static function parse_sigheader($header) { $ret = []; @@ -297,12 +353,23 @@ class HTTPSig { } - static function decrypt_sigheader($header,$prvkey = null) { + /** + * @brief + * + * @param string $header + * @param string $prvkey (optional), if not set use site private key + * @return array|string associative array, empty string if failue + * - \e string \b iv + * - \e string \b key + * - \e string \b alg + * - \e string \b data + */ + static function decrypt_sigheader($header, $prvkey = null) { $iv = $key = $alg = $data = null; if(! $prvkey) { - $prvkey = get_config('system','prvkey'); + $prvkey = get_config('system', 'prvkey'); } $matches = []; @@ -319,10 +386,8 @@ class HTTPSig { if($iv && $key && $alg && $data) { return crypto_unencapsulate([ 'iv' => $iv, 'key' => $key, 'alg' => $alg, 'data' => $data ] , $prvkey); } - return ''; + return ''; } } - - diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index 5183fb2b0..1c3ea29d0 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -44,9 +44,9 @@ class WebServer { * We have to do it here because the session was just now opened. */ - if(array_key_exists('system_language',$_POST)) { - if(strlen($_POST['system_language'])) - $_SESSION['language'] = $_POST['system_language']; + if(array_key_exists('system_language',$_REQUEST)) { + if(strlen($_REQUEST['system_language'])) + $_SESSION['language'] = $_REQUEST['system_language']; else unset($_SESSION['language']); } diff --git a/Zotlabs/Widget/Catcloud.php b/Zotlabs/Widget/Catcloud.php new file mode 100644 index 000000000..c53f9bbf6 --- /dev/null +++ b/Zotlabs/Widget/Catcloud.php @@ -0,0 +1,46 @@ + 'pubs', 'icon' => 'globe', diff --git a/Zotlabs/Widget/Pubtagcloud.php b/Zotlabs/Widget/Pubtagcloud.php new file mode 100644 index 000000000..826e3e6ae --- /dev/null +++ b/Zotlabs/Widget/Pubtagcloud.php @@ -0,0 +1,41 @@ + t('Connected apps'), + 'label' => t('OAuth1 apps'), 'url' => z_root() . '/settings/oauth', 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), ); } + if(feature_enabled(local_channel(),'oauth2_clients')) { + $tabs[] = array( + 'label' => t('OAuth2 apps'), + 'url' => z_root() . '/settings/oauth2', + 'selected' => ((argv(1) === 'oauth2') ? 'active' : ''), + ); + } + if(feature_enabled(local_channel(),'access_tokens')) { $tabs[] = array( 'label' => t('Guest Access Tokens'), diff --git a/Zotlabs/Widget/Tagcloud.php b/Zotlabs/Widget/Tagcloud.php index cf7a4932e..f79bd59ad 100644 --- a/Zotlabs/Widget/Tagcloud.php +++ b/Zotlabs/Widget/Tagcloud.php @@ -2,9 +2,6 @@ namespace Zotlabs\Widget; -// @FIXME The problem with this widget is that we don't have a search function for webpages -// that we can send the links to. Then we should also provide an option to search webpages -// and conversations. class Tagcloud { @@ -14,15 +11,15 @@ class Tagcloud { $uid = \App::$profile_uid; $count = ((x($args,'count')) ? intval($args['count']) : 24); $flags = 0; - $type = TERM_CATEGORY; + $type = TERM_HASHTAG; // @FIXME there exists no $authors variable - $r = tagadelic($uid, $count, $authors, $owner, $flags, ITEM_TYPE_WEBPAGE, $type); + $r = tagadelic($uid, $count, $authors, $owner, $flags, 0, $type); // @FIXME this should use a template if($r) { - $o = '
',print_r($res,true)); $o .= '