Merge branch 'dev'

This commit is contained in:
zotlabs 2019-06-03 17:59:38 -07:00
commit 35db84a703
766 changed files with 32380 additions and 20476 deletions

View file

@ -1,10 +1,11 @@
ZAP ZAP
=== ===
Zap is a full featured social network application running under the Zot6 protocol. It provides enhanced privacy modes and identity/content mirroring across multiple servers ("nomadic identity"). It does not "federate" with non-nomadic servers, protocols, or projects. Zap is a decentralised social network server application with a number of powerful features, yet very easy to use.
Installation Installation
============ ============
Read `/install/INSTALL.txt` for installation instructions. Read `install/INSTALL.txt` for installation instructions.

View file

@ -52,7 +52,8 @@ class Cron_weekly {
Master::Summon(array('Checksites')); Master::Summon(array('Checksites'));
// update searchable doc indexes // update searchable doc indexes
Master::Summon(array('Importdoc')); // disabled until help system regenerated
// Master::Summon(array('Importdoc'));
/** /**
* End Cron Weekly * End Cron Weekly

View file

@ -61,6 +61,7 @@ require_once('include/bbcode.php');
* permissions_reject abook_id * permissions_reject abook_id
* permissions_update abook_id * permissions_update abook_id
* refresh_all channel_id * refresh_all channel_id
* purge xchan_hash
* purge_all channel_id * purge_all channel_id
* expire channel_id * expire channel_id
* relay item_id (item was relayed to owner, we will deliver it as owner) * relay item_id (item was relayed to owner, we will deliver it as owner)
@ -228,13 +229,21 @@ class Notifier {
self::$private = false; self::$private = false;
self::$packet_type = 'refresh'; self::$packet_type = 'refresh';
} }
elseif($cmd === 'purge') {
$xchan = argv(3);
logger('notifier: purge: ' . $item_id . ' => ' . $xchan);
if (! $xchan) {
return;
}
self::$channel = channelx_by_n($item_id);
self::$recipients = [ $xchan ];
self::$private = true;
self::$packet_type = 'purge';
}
elseif($cmd === 'purge_all') { elseif($cmd === 'purge_all') {
logger('notifier: purge_all: ' . $item_id); logger('notifier: purge_all: ' . $item_id);
$s = q("select * from channel where channel_id = %d limit 1", self::$channel = channelx_by_n($item_id);
intval($item_id)
);
if($s)
self::$channel = $s[0];
self::$recipients = array(); self::$recipients = array();
$r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0", $r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 0",

View file

@ -91,11 +91,11 @@ class Activity {
); );
} }
if(! $r) if (! $r) {
return []; return [];
}
return self::encode_person($r[0],false); return self::encode_person($r[0],false);
} }
static function fetch_thing($x) { static function fetch_thing($x) {
@ -105,8 +105,9 @@ class Activity {
dbesc($x['id']) dbesc($x['id'])
); );
if(! $r) if (! $r) {
return []; return [];
}
$x = [ $x = [
'type' => 'Object', 'type' => 'Object',
@ -114,9 +115,9 @@ class Activity {
'name' => $r[0]['obj_term'] 'name' => $r[0]['obj_term']
]; ];
if($r[0]['obj_image']) if ($r[0]['obj_image']) {
$x['image'] = $r[0]['obj_image']; $x['image'] = $r[0]['obj_image'];
}
return $x; return $x;
} }
@ -156,14 +157,17 @@ class Activity {
else { else {
$t = self::encode_activity($i,$activitypub); $t = self::encode_activity($i,$activitypub);
} }
if($t) if ($t) {
$x[] = $t; $x[] = $t;
} }
if($type === 'OrderedCollection') }
if ($type === 'OrderedCollection') {
$ret['orderedItems'] = $x; $ret['orderedItems'] = $x;
else }
else {
$ret['items'] = $x; $ret['items'] = $x;
} }
}
return $ret; return $ret;
} }
@ -175,8 +179,9 @@ class Activity {
'type' => $type, 'type' => $type,
'totalItems' => count($items), 'totalItems' => count($items),
]; ];
if($extra) if ($extra) {
$ret = array_merge($ret,$extra); $ret = array_merge($ret,$extra);
}
if ($items) { if ($items) {
$x = []; $x = [];
@ -186,11 +191,13 @@ class Activity {
} }
} }
if($type === 'OrderedCollection') if ($type === 'OrderedCollection') {
$ret['orderedItems'] = $x; $ret['orderedItems'] = $x;
else }
else {
$ret['items'] = $x; $ret['items'] = $x;
} }
}
return $ret; return $ret;
} }
@ -257,8 +264,11 @@ class Activity {
if ($i['created'] !== $i['edited']) { if ($i['created'] !== $i['edited']) {
$ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME);
} }
if ($i['expires'] <= NULL_DATE) {
$ret['expires'] = datetime_convert('UTC','UTC',$i['expires'],ATOM_TIME);
}
if ($i['app']) { if ($i['app']) {
$ret['instrument'] = [ 'type' => 'Service', 'name' => $i['app'] ]; $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ];
} }
if ($i['location'] || $i['coord']) { if ($i['location'] || $i['coord']) {
$ret['location'] = [ 'type' => 'Place' ]; $ret['location'] = [ 'type' => 'Place' ];
@ -310,7 +320,6 @@ class Activity {
else { else {
$reply_url = z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@')); $reply_url = z_root() . '/followers/' . substr($i['author']['xchan_addr'],0,strpos($i['author']['xchan_addr'],'@'));
} }
$reply_addr = (($d[0]['xchan_addr']) ? $d[0]['xchan_addr'] : $d[0]['xchan_name']); $reply_addr = (($d[0]['xchan_addr']) ? $d[0]['xchan_addr'] : $d[0]['xchan_name']);
} }
} }
@ -323,19 +332,23 @@ class Activity {
} }
if ($i['mimetype'] === 'text/bbcode') { if ($i['mimetype'] === 'text/bbcode') {
if($i['title']) if ($i['title']) {
$ret['name'] = $i['title']; $ret['name'] = $i['title'];
if($i['summary']) }
if ($i['summary']) {
$ret['summary'] = bbcode($i['summary'], [ 'export' => true ]); $ret['summary'] = bbcode($i['summary'], [ 'export' => true ]);
}
$ret['content'] = bbcode($i['body'], [ 'export' => true ]); $ret['content'] = bbcode($i['body'], [ 'export' => true ]);
$ret['source'] = [ 'content' => $i['body'], 'summary' => $i['summary'], 'mediaType' => 'text/bbcode' ]; $ret['source'] = [ 'content' => $i['body'], 'summary' => $i['summary'], 'mediaType' => 'text/bbcode' ];
} }
$actor = self::encode_person($i['author'],false); $actor = self::encode_person($i['author'],false);
if($actor) if ($actor) {
$ret['actor'] = $actor; $ret['actor'] = $actor;
else }
else {
return []; return [];
}
$t = self::encode_taxonomy($i); $t = self::encode_taxonomy($i);
if ($t) { if ($t) {
@ -352,9 +365,9 @@ class Activity {
foreach ($images as $match) { foreach ($images as $match) {
$img[] = [ 'type' => 'Image', 'url' => $match[2] ]; $img[] = [ 'type' => 'Image', 'url' => $match[2] ];
} }
if(! $ret['attachment']) if (! $ret['attachment']) {
$ret['attachment'] = []; $ret['attachment'] = [];
}
$ret['attachment'] = array_merge($img,$ret['attachment']); $ret['attachment'] = array_merge($img,$ret['attachment']);
} }
@ -661,7 +674,7 @@ class Activity {
if ($i['created'] !== $i['edited']) if ($i['created'] !== $i['edited'])
$ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME); $ret['updated'] = datetime_convert('UTC','UTC',$i['edited'],ATOM_TIME);
if ($i['app']) { if ($i['app']) {
$ret['instrument'] = [ 'type' => 'Service', 'name' => $i['app'] ]; $ret['generator'] = [ 'type' => 'Application', 'name' => $i['app'] ];
} }
if ($i['location'] || $i['coord']) { if ($i['location'] || $i['coord']) {
$ret['location'] = [ 'type' => 'Place' ]; $ret['location'] = [ 'type' => 'Place' ];
@ -946,7 +959,12 @@ class Activity {
else { else {
$ret['inbox'] = z_root() . '/nullbox'; $ret['inbox'] = z_root() . '/nullbox';
if ($c) {
$ret['outbox'] = z_root() . '/outbox/' . $c['channel_address'];
}
else {
$ret['outbox'] = z_root() . '/nullbox'; $ret['outbox'] = z_root() . '/nullbox';
}
$ret['publicKey'] = [ $ret['publicKey'] = [
'id' => $p['xchan_url'] . '/public_key_pem', 'id' => $p['xchan_url'] . '/public_key_pem',
'owner' => $p['xchan_url'], 'owner' => $p['xchan_url'],
@ -1384,7 +1402,6 @@ class Activity {
} }
if (isset($person_obj['publicKey']['publicKeyPem'])) { if (isset($person_obj['publicKey']['publicKeyPem'])) {
//if(array_key_exists('publicKey',$person_obj) && array_key_exists('publicKeyPem',$person_obj['publicKey'])) {
if ($person_obj['id'] === $person_obj['publicKey']['owner']) { if ($person_obj['id'] === $person_obj['publicKey']['owner']) {
$pubkey = $person_obj['publicKey']['publicKeyPem']; $pubkey = $person_obj['publicKey']['publicKeyPem'];
if (strstr($pubkey,'RSA ')) { if (strstr($pubkey,'RSA ')) {
@ -1649,13 +1666,13 @@ class Activity {
$s['verb'] = ACTIVITY_POST; $s['verb'] = ACTIVITY_POST;
$s['obj_type'] = ACTIVITY_OBJ_NOTE; $s['obj_type'] = ACTIVITY_OBJ_NOTE;
$instrument = $act->get_property_obj('instrument'); $generator = $act->get_property_obj('generator');
if(! $instrument) if (! $generator)
$instrument = $act->get_property_obj('instrument',$act->obj); $generator = $act->get_property_obj('generator',$act->obj);
if($instrument && array_key_exists('type',$instrument) if ($generator && array_key_exists('type',$generator)
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) { && in_array($generator['type'], ['Application', 'Service' ] ) && array_key_exists('name',$generator)) {
$s['app'] = escape_tags($instrument['name']); $s['app'] = escape_tags($generator['name']);
} }
if ($channel['channel_system']) { if ($channel['channel_system']) {
@ -1833,9 +1850,16 @@ class Activity {
elseif ($act->obj['updated']) { elseif ($act->obj['updated']) {
$s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']); $s['edited'] = datetime_convert('UTC','UTC',$act->obj['updated']);
} }
if ($act->data['expires']) {
$s['expires'] = datetime_convert('UTC','UTC',$act->data['expires']);
}
elseif ($act->obj['expires']) {
$s['expires'] = datetime_convert('UTC','UTC',$act->obj['expires']);
}
if(in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'emojiReaction' ])) { if (in_array($act->type, [ 'Like', 'Dislike', 'Flag', 'Block', 'Announce', 'Accept', 'Reject',
'TentativeAccept', 'TentativeReject', 'emojiReaction' ])) {
$response_activity = true; $response_activity = true;
@ -1873,6 +1897,9 @@ class Activity {
if ($act->type === 'TentativeAccept' && $act->obj['type'] === 'Event' ) { if ($act->type === 'TentativeAccept' && $act->obj['type'] === 'Event' ) {
$content['content'] = sprintf( t('May attend %1$s\'s %2$s'),$mention,$act->obj['type']) . EOL . EOL . $content['content']; $content['content'] = sprintf( t('May attend %1$s\'s %2$s'),$mention,$act->obj['type']) . EOL . EOL . $content['content'];
} }
if ($act->type === 'TentativeReject' && $act->obj['type'] === 'Event' ) {
$content['content'] = sprintf( t('May not attend %1$s\'s %2$s'),$mention,$act->obj['type']) . EOL . EOL . $content['content'];
}
if ($act->type === 'Announce') { if ($act->type === 'Announce') {
$content['content'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']); $content['content'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, $act->obj['type']);
} }
@ -1891,7 +1918,17 @@ class Activity {
$s['summary'] = self::bb_content($content,'summary'); $s['summary'] = self::bb_content($content,'summary');
$s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content')); $s['body'] = ((self::bb_content($content,'bbcode') && (! $response_activity)) ? self::bb_content($content,'bbcode') : self::bb_content($content,'content'));
if($act->type === 'Tombstone' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) { // handle some of the more widely used of the numerous and varied ways of deleting something
if ($act->type === 'Tombstone') {
$s['item_deleted'] = 1;
}
if ($act->type === 'Create' && $act->obj['type'] === 'Tombstone') {
$s['item_deleted'] = 1;
}
if ($act->type === 'Delete' && $act->obj['type'] === 'Tombstone') {
$s['item_deleted'] = 1; $s['item_deleted'] = 1;
} }
@ -1905,14 +1942,14 @@ class Activity {
// @todo add target if present // @todo add target if present
$instrument = $act->get_property_obj('instrument'); $generator = $act->get_property_obj('generator');
if((! $instrument) && (! $response_activity)) { if ((! $generator) && (! $response_activity)) {
$instrument = $act->get_property_obj('instrument',$act->obj); $generator = $act->get_property_obj('generator',$act->obj);
} }
if($instrument && array_key_exists('type',$instrument) if ($generator && array_key_exists('type',$generator)
&& $instrument['type'] === 'Service' && array_key_exists('name',$instrument)) { && in_array($generator['type'], [ 'Application','Service' ] ) && array_key_exists('name',$generator)) {
$s['app'] = escape_tags($instrument['name']); $s['app'] = escape_tags($generator['name']);
} }

View file

@ -2,6 +2,7 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use App;
use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Libsync;
/** /**
@ -22,11 +23,13 @@ class Apps {
static public function get_system_apps($translate = true) { static public function get_system_apps($translate = true) {
$ret = array(); $ret = [];
if(is_dir('apps')) if (is_dir('apps')) {
$files = glob('apps/*.apd'); $files = glob('apps/*.apd');
else }
else {
$files = glob('app/*.apd'); $files = glob('app/*.apd');
}
if ($files) { if ($files) {
foreach ($files as $f) { foreach ($files as $f) {
$x = self::parse_app_description($f,$translate); $x = self::parse_app_description($f,$translate);
@ -66,10 +69,12 @@ class Apps {
'Channel Home', 'Channel Home',
'View Profile', 'View Profile',
'Photos', 'Photos',
'Events', // 'Calendar',
'Directory', 'Directory',
'Events',
'Search', 'Search',
'Profile Photo' 'Profile Photo',
'Access Lists'
]); ]);
call_hooks('get_base_apps',$x); call_hooks('get_base_apps',$x);
return $x; return $x;
@ -78,8 +83,9 @@ class Apps {
static public function import_system_apps() { static public function import_system_apps() {
if(! local_channel()) if (! local_channel()) {
return; return;
}
self::$base_apps = self::get_base_apps(); self::$base_apps = self::get_base_apps();
@ -105,22 +111,17 @@ class Apps {
$id = self::check_install_personal_app($app); $id = self::check_install_personal_app($app);
// $id will be boolean true or false to install an app, or an integer id to update an existing app // $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id === false) if ($id === false) {
continue; continue;
}
if ($id !== true) { if ($id !== true) {
// if we already installed this app, but it changed, preserve any categories we created // if we already installed this app, but it changed, preserve any categories we created
$s = EMPTY_STR;
$r = q("select term from term where otype = %d and oid = %d", $r = q("select term from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($id) intval($id)
); );
if ($r) { if ($r) {
foreach($r as $t) { $app['categories'] = array_elm_to_str($r,'term');
if($s)
$s .= ',';
$s .= $t['term'];
}
$app['categories'] = $s;
} }
} }
$app['uid'] = local_channel(); $app['uid'] = local_channel();
@ -194,12 +195,12 @@ class Apps {
$ret = array(); $ret = array();
$baseurl = z_root(); $baseurl = z_root();
$channel = \App::get_channel(); $channel = App::get_channel();
$address = (($channel) ? $channel['channel_address'] : ''); $address = (($channel) ? $channel['channel_address'] : '');
//future expansion //future expansion
$observer = \App::get_observer(); $observer = App::get_observer();
$lines = @file($f); $lines = @file($f);
@ -211,8 +212,9 @@ class Apps {
} }
} }
if(! $ret['photo']) if (! $ret['photo']) {
$ret['photo'] = $baseurl . '/' . get_default_profile_photo(80); $ret['photo'] = $baseurl . '/' . get_default_profile_photo(80);
}
$ret['type'] = 'system'; $ret['type'] = 'system';
@ -224,18 +226,18 @@ class Apps {
} }
} }
if(array_key_exists('desc',$ret)) if (array_key_exists('desc',$ret)) {
$ret['desc'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['desc']); $ret['desc'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['desc']);
}
if(array_key_exists('target',$ret)) if (array_key_exists('target',$ret)) {
$ret['target'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['target']); $ret['target'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['target']);
}
if(array_key_exists('version',$ret)) if (array_key_exists('version',$ret)) {
$ret['version'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['version']); $ret['version'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['version']);
}
if(array_key_exists('categories',$ret)) if (array_key_exists('categories',$ret)) {
$ret['categories'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['categories']); $ret['categories'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$ret['categories']);
}
if (array_key_exists('requires',$ret)) { if (array_key_exists('requires',$ret)) {
$requires = explode(',',$ret['requires']); $requires = explode(',',$ret['requires']);
foreach ($requires as $require) { foreach ($requires as $require) {
@ -250,47 +252,58 @@ class Apps {
switch ($require) { switch ($require) {
case 'nologin': case 'nologin':
if(local_channel()) if (local_channel()) {
unset($ret); unset($ret);
}
break; break;
case 'admin': case 'admin':
if(! is_site_admin()) if (! is_site_admin()) {
unset($ret); unset($ret);
}
break; break;
case 'local_channel': case 'local_channel':
if(! local_channel()) if (! local_channel()) {
unset($ret); unset($ret);
}
break; break;
case 'public_profile': case 'public_profile':
if(! is_public_profile()) if (! is_public_profile()) {
unset($ret); unset($ret);
}
break; break;
case 'public_stream': case 'public_stream':
if(! can_view_public_stream()) if (! can_view_public_stream()) {
unset($ret); unset($ret);
}
break; break;
case 'custom_role': case 'custom_role':
if(get_pconfig(local_channel(),'system','permissions_role') !== 'custom') if (get_pconfig(local_channel(),'system','permissions_role') !== 'custom') {
unset($ret); unset($ret);
}
break; break;
case 'observer': case 'observer':
if(! $observer) if (! $observer) {
unset($ret); unset($ret);
}
break; break;
default: default:
if($config) if ($config) {
$unset = ((get_config('system', $require[0]) == $require[1]) ? false : true); $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true);
else }
else {
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset) }
if ($unset) {
unset($ret); unset($ret);
}
break; break;
} }
} }
} }
if ($ret) { if ($ret) {
if($translate) if ($translate) {
self::translate_system_apps($ret); self::translate_system_apps($ret);
}
return $ret; return $ret;
} }
return false; return false;
@ -303,6 +316,7 @@ class Apps {
'Affinity Tool' => t('Affinity Tool'), 'Affinity Tool' => t('Affinity Tool'),
'Articles' => t('Articles'), 'Articles' => t('Articles'),
'Cards' => t('Cards'), 'Cards' => t('Cards'),
'Calendar' => t('Calendar'),
'Categories' => t('Categories'), 'Categories' => t('Categories'),
'Admin' => t('Site Admin'), 'Admin' => t('Site Admin'),
'Content Filter' => t('Content Filter'), 'Content Filter' => t('Content Filter'),
@ -345,7 +359,7 @@ class Apps {
'Profile Photo' => t('Profile Photo'), 'Profile Photo' => t('Profile Photo'),
'Profile' => t('Profile'), 'Profile' => t('Profile'),
'Profiles' => t('Profiles'), 'Profiles' => t('Profiles'),
'Privacy Groups' => t('Access Lists'), 'Access Lists' => t('Access Lists'),
'Notifications' => t('Notifications'), 'Notifications' => t('Notifications'),
'Order Apps' => t('Order Apps'), 'Order Apps' => t('Order Apps'),
'CalDAV' => t('CalDAV'), 'CalDAV' => t('CalDAV'),
@ -399,16 +413,19 @@ class Apps {
$installed = false; $installed = false;
if(! $papp) if (! $papp) {
return; return;
}
if(! $papp['photo']) if (! $papp['photo']) {
$papp['photo'] = 'icon:gear'; $papp['photo'] = 'icon:gear';
}
self::translate_system_apps($papp); self::translate_system_apps($papp);
if(trim($papp['plugin']) && (! plugin_is_installed(trim($papp['plugin'])))) if (trim($papp['plugin']) && (! plugin_is_installed(trim($papp['plugin'])))) {
return ''; return '';
}
$papp['papp'] = self::papp_encode($papp); $papp['papp'] = self::papp_encode($papp);
@ -431,9 +448,9 @@ class Apps {
$papp['settings_url'] = trim($urls[1]); $papp['settings_url'] = trim($urls[1]);
} }
if(! strstr($papp['url'],'://')) if (! strstr($papp['url'],'://')) {
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
}
foreach ($papp as $k => $v) { foreach ($papp as $k => $v) {
@ -442,8 +459,9 @@ class Apps {
$papp[$k] = zid($v); $papp[$k] = zid($v);
} }
} }
if($k === 'desc') if ($k === 'desc') {
$papp['desc'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$papp['desc']); $papp['desc'] = str_replace(array('\'','"'),array('&#39;','&dquot;'),$papp['desc']);
}
if ($k === 'requires') { if ($k === 'requires') {
$requires = explode(',',$v); $requires = explode(',',$v);
@ -460,41 +478,51 @@ class Apps {
switch ($require) { switch ($require) {
case 'nologin': case 'nologin':
if(local_channel()) if (local_channel()) {
return ''; return '';
}
break; break;
case 'admin': case 'admin':
if(! is_site_admin()) if (! is_site_admin()) {
return ''; return '';
}
break; break;
case 'local_channel': case 'local_channel':
if(! local_channel()) if (! local_channel()) {
return ''; return '';
}
break; break;
case 'public_profile': case 'public_profile':
if(! is_public_profile()) if (! is_public_profile()) {
return ''; return '';
}
break; break;
case 'public_stream': case 'public_stream':
if(! can_view_public_stream()) if (! can_view_public_stream()) {
return ''; return '';
}
break; break;
case 'custom_role': case 'custom_role':
if(get_pconfig(local_channel(),'system','permissions_role') != 'custom') if (get_pconfig(local_channel(),'system','permissions_role') != 'custom') {
return ''; return '';
}
break; break;
case 'observer': case 'observer':
$observer = \App::get_observer(); $observer = App::get_observer();
if(! $observer) if (! $observer) {
return ''; return '';
}
break; break;
default: default:
if($config) if ($config) {
$unset = ((get_config('system', $require[0]) === $require[1]) ? false : true); $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
else }
else {
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true); $unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset) }
if ($unset) {
return ''; return '';
}
break; break;
} }
} }
@ -504,13 +532,14 @@ class Apps {
$hosturl = ''; $hosturl = '';
if (local_channel()) { if (local_channel()) {
if(self::app_installed(local_channel(),$papp) && !$papp['deleted']) if (self::app_installed(local_channel(),$papp) && !$papp['deleted']) {
$installed = true; $installed = true;
}
$hosturl = z_root() . '/'; $hosturl = z_root() . '/';
} }
elseif (remote_channel()) { elseif (remote_channel()) {
$observer = \App::get_observer(); $observer = App::get_observer();
if ($observer && $observer['xchan_network'] === 'zot6') { if ($observer && $observer['xchan_network'] === 'zot6') {
// some folks might have xchan_url redirected offsite, use the connurl // some folks might have xchan_url redirected offsite, use the connurl
$x = parse_url($observer['xchan_connurl']); $x = parse_url($observer['xchan_connurl']);
@ -524,16 +553,16 @@ class Apps {
$icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : ''); $icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : '');
if ($mode === 'navbar') { if ($mode === 'navbar') {
return replace_macros(get_markup_template('app_nav.tpl'),array( return replace_macros(get_markup_template('app_nav.tpl'), [
'$app' => $papp, '$app' => $papp,
'$icon' => $icon, '$icon' => $icon,
)); ]);
} }
if ($mode === 'install') { if ($mode === 'install') {
$papp['embed'] = true; $papp['embed'] = true;
} }
return replace_macros(get_markup_template('app.tpl'),array( return replace_macros(get_markup_template('app.tpl'), [
'$app' => $papp, '$app' => $papp,
'$icon' => $icon, '$icon' => $icon,
'$hosturl' => $hosturl, '$hosturl' => $hosturl,
@ -555,7 +584,7 @@ class Apps {
'$remove' => t('Remove from app-tray'), '$remove' => t('Remove from app-tray'),
'$add_nav' => t('Pin to navbar'), '$add_nav' => t('Pin to navbar'),
'$remove_nav' => t('Unpin from navbar') '$remove_nav' => t('Unpin from navbar')
)); ]);
} }
static public function app_install($uid,$app) { static public function app_install($uid,$app) {
@ -564,18 +593,21 @@ class Apps {
$r = q("select * from app where app_name = '%s' and app_channel = 0", $r = q("select * from app where app_name = '%s' and app_channel = 0",
dbesc($app) dbesc($app)
); );
if(! $r) if (! $r) {
return false; return false;
}
$app = self::app_encode($r[0]); $app = self::app_encode($r[0]);
} }
$app['uid'] = $uid; $app['uid'] = $uid;
if(self::app_installed($uid,$app,true)) if (self::app_installed($uid,$app,true)) {
$x = self::app_update($app); $x = self::app_update($app);
else }
else {
$x = self::app_store($app); $x = self::app_store($app);
}
if ($x['success']) { if ($x['success']) {
$r = q("select * from app where app_id = '%s' and app_channel = %d limit 1", $r = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
@ -766,15 +798,15 @@ class Apps {
} }
return (($r) ? true : false); return (($r) ? true : false);
} }
static public function app_list($uid, $deleted = false, $cats = []) { static public function app_list($uid, $deleted = false, $cats = []) {
if($deleted) if ($deleted) {
$sql_extra = ""; $sql_extra = "";
else }
else {
$sql_extra = " and app_deleted = 0 "; $sql_extra = " and app_deleted = 0 ";
}
if ($cats) { if ($cats) {
$cat_sql_extra = " and ( "; $cat_sql_extra = " and ( ";
@ -791,16 +823,10 @@ class Apps {
$r = q("select oid from term where otype = %d $cat_sql_extra", $r = q("select oid from term where otype = %d $cat_sql_extra",
intval(TERM_OBJ_APP) intval(TERM_OBJ_APP)
); );
if(! $r) if (! $r) {
return $r; return $r;
$sql_extra .= " and app.id in ( ";
$s = '';
foreach($r as $rr) {
if($s)
$s .= ',';
$s .= intval($rr['oid']);
} }
$sql_extra .= $s . ') '; $sql_extra .= " and app.id in ( " . array_elm_to_str($r,'oid') . ') ';
} }
$r = q("select * from app where app_channel = %d $sql_extra order by app_name asc", $r = q("select * from app where app_channel = %d $sql_extra order by app_name asc",
@ -812,8 +838,9 @@ class Apps {
call_hooks('app_list',$hookinfo); call_hooks('app_list',$hookinfo);
$r = $hookinfo['apps']; $r = $hookinfo['apps'];
for ($x = 0; $x < count($r); $x ++) { for ($x = 0; $x < count($r); $x ++) {
if(! $r[$x]['app_system']) if (! $r[$x]['app_system']) {
$r[$x]['type'] = 'personal'; $r[$x]['type'] = 'personal';
}
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d", $r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($r[$x]['id']) intval($r[$x]['id'])
@ -824,6 +851,21 @@ class Apps {
return ($r); return ($r);
} }
static public function app_search_available($str) {
// not yet finished
// somehow need to account for translations
$r = q("select * from app where app_channel = 0 $sql_extra order by app_name asc",
intval($uid)
);
return ($r);
}
static public function app_order($uid,$apps,$menu) { static public function app_order($uid,$apps,$menu) {
if (! $apps) if (! $apps)
@ -838,8 +880,9 @@ class Apps {
$x = $y; $x = $y;
} }
if(! (is_array($x) && ($x))) if (! (is_array($x) && ($x))) {
return $apps; return $apps;
}
$ret = []; $ret = [];
foreach ($x as $xx) { foreach ($x as $xx) {
@ -858,8 +901,9 @@ class Apps {
} }
static function find_app_in_array($name,$arr) { static function find_app_in_array($name,$arr) {
if(! $arr) if (! $arr) {
return false; return false;
}
foreach ($arr as $x) { foreach ($arr as $x) {
if ($x['name'] === $name) { if ($x['name'] === $name) {
return $x; return $x;
@ -869,7 +913,7 @@ class Apps {
} }
static function moveup($uid,$guid,$menu) { static function moveup($uid,$guid,$menu) {
$syslist = array(); $syslist = [];
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
@ -888,8 +932,9 @@ class Apps {
$syslist = self::app_order($uid,$syslist,$menu); $syslist = self::app_order($uid,$syslist,$menu);
if(! $syslist) if (! $syslist) {
return; return;
}
$newlist = []; $newlist = [];
@ -899,8 +944,9 @@ class Apps {
break; break;
} }
} }
if(! $position) if (! $position) {
return; return;
}
$dest_position = $position - 1; $dest_position = $position - 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -924,8 +970,9 @@ class Apps {
if ($list) { if ($list) {
foreach ($list as $li) { foreach ($list as $li) {
$papp = self::app_encode($li); $papp = self::app_encode($li);
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) if ($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) {
continue; continue;
}
$syslist[] = $papp; $syslist[] = $papp;
} }
} }
@ -935,8 +982,9 @@ class Apps {
$syslist = self::app_order($uid,$syslist,$menu); $syslist = self::app_order($uid,$syslist,$menu);
if(! $syslist) if (! $syslist) {
return; return;
}
$newlist = []; $newlist = [];
@ -946,8 +994,9 @@ class Apps {
break; break;
} }
} }
if($position >= count($syslist) - 1) if ($position >= count($syslist) - 1) {
return; return;
}
$dest_position = $position + 1; $dest_position = $position + 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -970,8 +1019,9 @@ class Apps {
static public function app_macros($uid,&$arr) { static public function app_macros($uid,&$arr) {
if(! intval($uid)) if (! intval($uid)) {
return; return;
}
$baseurl = z_root(); $baseurl = z_root();
$channel = channelx_by_n($uid); $channel = channelx_by_n($uid);
@ -979,7 +1029,7 @@ class Apps {
// future expansion // future expansion
$observer = \App::get_observer(); $observer = App::get_observer();
$arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']);
$arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']);
@ -987,16 +1037,12 @@ class Apps {
} }
static public function app_store($arr) { static public function app_store($arr) {
// logger('app_store: ' . print_r($arr,true)); // logger('app_store: ' . print_r($arr,true));
$darray = array(); $darray = [];
$ret = array('success' => false); $ret = [ 'success' => false ];
$sys = get_sys_channel(); $sys = get_sys_channel();
@ -1005,8 +1051,9 @@ class Apps {
$darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : ''); $darray['app_url'] = ((x($arr,'url')) ? $arr['url'] : '');
$darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0);
if(! $darray['app_url']) if (! $darray['app_url']) {
return $ret; return $ret;
}
if ((! $arr['uid']) && (! $arr['author'])) { if ((! $arr['uid']) && (! $arr['author'])) {
$arr['author'] = $sys['channel_hash']; $arr['author'] = $sys['channel_hash'];
@ -1086,8 +1133,8 @@ class Apps {
static public function app_update($arr) { static public function app_update($arr) {
// logger('app_update: ' . print_r($arr,true)); // logger('app_update: ' . print_r($arr,true));
$darray = array(); $darray = [];
$ret = array('success' => false); $ret = [ 'success' => false ];
self::app_macros($arr['uid'],$arr); self::app_macros($arr['uid'],$arr);
@ -1096,8 +1143,9 @@ class Apps {
$darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0); $darray['app_channel'] = ((x($arr,'uid')) ? $arr['uid'] : 0);
$darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : 0); $darray['app_id'] = ((x($arr,'guid')) ? $arr['guid'] : 0);
if((! $darray['app_url']) || (! $darray['app_id'])) if ((! $darray['app_url']) || (! $darray['app_id'])) {
return $ret; return $ret;
}
if ($arr['photo'] && (strpos($arr['photo'],'icon:') === false) && (strpos($arr['photo'],z_root()) !== false)) { if ($arr['photo'] && (strpos($arr['photo'],'icon:') === false) && (strpos($arr['photo'],z_root()) !== false)) {
$x = import_xchan_photo(str_replace('$baseurl',z_root(),$arr['photo']),get_observer_hash(),true); $x = import_xchan_photo(str_replace('$baseurl',z_root(),$arr['photo']),get_observer_hash(),true);
@ -1185,75 +1233,70 @@ class Apps {
$ret['type'] = 'personal'; $ret['type'] = 'personal';
if($app['app_id']) if ($app['app_id']) {
$ret['guid'] = $app['app_id']; $ret['guid'] = $app['app_id'];
}
if($app['app_sig']) if ($app['app_sig']) {
$ret['sig'] = $app['app_sig']; $ret['sig'] = $app['app_sig'];
}
if($app['app_author']) if ($app['app_author']) {
$ret['author'] = $app['app_author']; $ret['author'] = $app['app_author'];
}
if($app['app_name']) if ($app['app_name']) {
$ret['name'] = $app['app_name']; $ret['name'] = $app['app_name'];
}
if($app['app_desc']) if ($app['app_desc']) {
$ret['desc'] = $app['app_desc']; $ret['desc'] = $app['app_desc'];
}
if($app['app_url']) if ($app['app_url']) {
$ret['url'] = $app['app_url']; $ret['url'] = $app['app_url'];
}
if($app['app_photo']) if ($app['app_photo']) {
$ret['photo'] = $app['app_photo']; $ret['photo'] = $app['app_photo'];
}
if($app['app_icon']) if ($app['app_icon']) {
$ret['icon'] = $app['app_icon']; $ret['icon'] = $app['app_icon'];
}
if($app['app_version']) if ($app['app_version']) {
$ret['version'] = $app['app_version']; $ret['version'] = $app['app_version'];
}
if($app['app_addr']) if ($app['app_addr']) {
$ret['addr'] = $app['app_addr']; $ret['addr'] = $app['app_addr'];
}
if($app['app_price']) if ($app['app_price']) {
$ret['price'] = $app['app_price']; $ret['price'] = $app['app_price'];
}
if($app['app_page']) if ($app['app_page']) {
$ret['page'] = $app['app_page']; $ret['page'] = $app['app_page'];
}
if($app['app_requires']) if ($app['app_requires']) {
$ret['requires'] = $app['app_requires']; $ret['requires'] = $app['app_requires'];
}
if($app['app_system']) if ($app['app_system']) {
$ret['system'] = $app['app_system']; $ret['system'] = $app['app_system'];
}
if($app['app_options']) if ($app['app_options']) {
$ret['options'] = $app['app_options']; $ret['options'] = $app['app_options'];
}
if($app['app_plugin']) if ($app['app_plugin']) {
$ret['plugin'] = trim($app['app_plugin']); $ret['plugin'] = trim($app['app_plugin']);
}
if($app['app_deleted']) if ($app['app_deleted']) {
$ret['deleted'] = $app['app_deleted']; $ret['deleted'] = $app['app_deleted'];
}
if ($app['term']) { if ($app['term']) {
$s = ''; $ret['categories'] = array_elm_to_str($app['term'],'term');
foreach($app['term'] as $t) {
if($s)
$s .= ',';
$s .= $t['term'];
}
$ret['categories'] = $s;
} }
if(! $embed) if (! $embed) {
return $ret; return $ret;
}
$ret['embed'] = true; $ret['embed'] = true;
if(array_key_exists('categories',$ret)) if (array_key_exists('categories',$ret)) {
unset($ret['categories']); unset($ret['categories']);
}
$j = json_encode($ret); $j = json_encode($ret);
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]'; return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
@ -1263,7 +1306,6 @@ class Apps {
static public function papp_encode($papp) { static public function papp_encode($papp) {
return chunk_split(base64_encode(json_encode($papp)),72,"\n"); return chunk_split(base64_encode(json_encode($papp)),72,"\n");
} }
} }

View file

@ -35,13 +35,6 @@ class Connect {
$protocol = substr($url,1,$x-1); $protocol = substr($url,1,$x-1);
$url = substr($url,$x+1); $url = substr($url,$x+1);
} }
}
$category = null;
if(strpos($url,'$') !== false) {
} }
if(! allowed_url($url)) { if(! allowed_url($url)) {

View file

@ -12,7 +12,7 @@ class Enotify {
/** /**
* @brief * @brief
* *
* @param array $params an assoziative array with: * @param array $params an associative array with:
* * \e string \b from_xchan sender xchan hash * * \e string \b from_xchan sender xchan hash
* * \e string \b to_xchan recipient xchan hash * * \e string \b to_xchan recipient xchan hash
* * \e array \b item an assoziative array * * \e array \b item an assoziative array
@ -53,6 +53,8 @@ class Enotify {
return; return;
} }
// from here on everything is in the recipients language // from here on everything is in the recipients language
push_lang($recip['account_language']); // should probably have a channel language push_lang($recip['account_language']); // should probably have a channel language
@ -840,6 +842,9 @@ class Enotify {
$ret = ''; $ret = '';
$expire = intval(get_config('system','default_expire_days'));
$expire_date = (($expire) ? datetime_convert('UTC','UTC','now - ' . $expire . ' days') : NULL_DATE);
require_once('include/conversation.php'); require_once('include/conversation.php');
// Call localize_item to get a one line status for activities. // Call localize_item to get a one line status for activities.
@ -898,6 +903,11 @@ class Enotify {
'display' => true 'display' => true
); );
$post_date = (($edit)? $item['edited'] : $item['created']);
if($post_date && $post_date < $expire_date) {
return [];
}
call_hooks('enotify_format',$x); call_hooks('enotify_format',$x);
if(! $x['display']) { if(! $x['display']) {
return []; return [];

View file

@ -40,7 +40,9 @@ class LDSignatures {
$signed = array_merge([ $signed = array_merge([
'@context' => [ '@context' => [
ACTIVITYSTREAMS_JSONLD_REV, ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1' ], 'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
],
],$options); ],$options);
return $signed; return $signed;

View file

@ -166,8 +166,9 @@ class Libzot {
call_hooks('zot_best_algorithm', $x); call_hooks('zot_best_algorithm', $x);
if($x['result']) if ($x['result']) {
return $x['result']; return $x['result'];
}
if ($methods) { if ($methods) {
$x = explode(',', $methods); $x = explode(',', $methods);
@ -254,8 +255,9 @@ class Libzot {
static function refresh($them, $channel = null, $force = false) { static function refresh($them, $channel = null, $force = false) {
logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG);
if ($channel) if ($channel) {
logger('channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG); logger('channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG);
}
$url = null; $url = null;
@ -311,8 +313,9 @@ class Libzot {
// Check the HTTP signature // Check the HTTP signature
$hsig = $record['signature']; $hsig = $record['signature'];
if($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) if ($hsig && $hsig['signer'] === $url && $hsig['header_valid'] === true && $hsig['content_valid'] === true) {
$hsig_valid = true; $hsig_valid = true;
}
if (! $hsig_valid) { if (! $hsig_valid) {
logger('http signature not valid: ' . print_r($hsig,true)); logger('http signature not valid: ' . print_r($hsig,true));
@ -367,8 +370,9 @@ class Libzot {
intval($channel['channel_id']) intval($channel['channel_id'])
); );
if(! $y) if (! $y) {
logger('abook update failed'); logger('abook update failed');
}
else { else {
// if we were just granted read stream permission and didn't have it before, try to pull in some posts // if we were just granted read stream permission and didn't have it before, try to pull in some posts
if ((! $old_read_stream_perm) && (intval($permissions['view_stream']))) if ((! $old_read_stream_perm) && (intval($permissions['view_stream'])))
@ -405,7 +409,6 @@ class Libzot {
$closeness = 10; $closeness = 10;
} }
$y = abook_store_lowlevel( $y = abook_store_lowlevel(
[ [
'abook_account' => intval($channel['channel_account_id']), 'abook_account' => intval($channel['channel_account_id']),
@ -432,8 +435,9 @@ class Libzot {
); );
if ($new_connection) { if ($new_connection) {
if(! Permissions::PermsCompare($new_perms,$previous_perms)) if (! Permissions::PermsCompare($new_perms,$previous_perms)) {
Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]); Master::Summon([ 'Notifier', 'permissions_create', $new_connection[0]['abook_id'] ]);
}
if (! $is_collection) { if (! $is_collection) {
Enotify::submit( Enotify::submit(
@ -460,18 +464,19 @@ class Libzot {
$default_group = $channel['channel_default_group']; $default_group = $channel['channel_default_group'];
if ($default_group) { if ($default_group) {
$g = AccessList::rec_byhash($channel['channel_id'],$default_group); $g = AccessList::rec_byhash($channel['channel_id'],$default_group);
if($g) if ($g) {
AccessList::member_add($channel['channel_id'],'',$x['hash'],$g['id']); AccessList::member_add($channel['channel_id'],'',$x['hash'],$g['id']);
} }
} }
}
unset($new_connection[0]['abook_id']); unset($new_connection[0]['abook_id']);
unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_account']);
unset($new_connection[0]['abook_channel']); unset($new_connection[0]['abook_channel']);
$abconfig = load_abconfig($channel['channel_id'],$new_connection['abook_xchan']); $abconfig = load_abconfig($channel['channel_id'],$new_connection['abook_xchan']);
if($abconfig) if ($abconfig) {
$new_connection['abconfig'] = $abconfig; $new_connection['abconfig'] = $abconfig;
}
Libsync::build_sync_packet($channel['channel_id'], array('abook' => $new_connection)); Libsync::build_sync_packet($channel['channel_id'], array('abook' => $new_connection));
} }
} }
@ -558,7 +563,6 @@ class Libzot {
} }
return $r[0]; return $r[0];
} }
/** /**
@ -687,8 +691,9 @@ class Libzot {
dbesc($xchan_hash) dbesc($xchan_hash)
); );
if(! array_key_exists('connect_url', $arr)) if (! array_key_exists('connect_url', $arr)) {
$arr['connect_url'] = ''; $arr['connect_url'] = '';
}
if ($r) { if ($r) {
if ($arr['photo'] && array_key_exists('updated',$arr['photo']) && $r[0]['xchan_photo_date'] != $arr['photo']['updated']) { if ($arr['photo'] && array_key_exists('updated',$arr['photo']) && $r[0]['xchan_photo_date'] != $arr['photo']['updated']) {
@ -700,19 +705,23 @@ class Libzot {
$dirmode = get_config('system','directory_mode'); $dirmode = get_config('system','directory_mode');
if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) if ((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) && ($arr['site']['url'] != z_root())) {
$arr['searchable'] = false; $arr['searchable'] = false;
}
$hidden = (1 - intval($arr['searchable'])); $hidden = (1 - intval($arr['searchable']));
$hidden_changed = $adult_changed = $deleted_changed = $type_changed = 0; $hidden_changed = $adult_changed = $deleted_changed = $type_changed = 0;
if(intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) if (intval($r[0]['xchan_hidden']) != (1 - intval($arr['searchable']))) {
$hidden_changed = 1; $hidden_changed = 1;
if(intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) }
if (intval($r[0]['xchan_selfcensored']) != intval($arr['adult_content'])) {
$adult_changed = 1; $adult_changed = 1;
if(intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) }
if (intval($r[0]['xchan_deleted']) != intval($arr['deleted'])) {
$deleted_changed = 1; $deleted_changed = 1;
}
if ($arr['channel_type'] === 'collection') { if ($arr['channel_type'] === 'collection') {
$px = 2; $px = 2;
@ -776,8 +785,9 @@ class Libzot {
if ((($arr['site']['directory_mode'] === 'standalone') if ((($arr['site']['directory_mode'] === 'standalone')
|| ($dirmode & DIRECTORY_MODE_STANDALONE)) || ($dirmode & DIRECTORY_MODE_STANDALONE))
&& ($arr['site']['url'] != z_root())) && ($arr['site']['url'] != z_root())) {
$arr['searchable'] = false; $arr['searchable'] = false;
}
if ($arr['channel_type'] === 'collection') { if ($arr['channel_type'] === 'collection') {
@ -818,8 +828,6 @@ class Libzot {
] ]
); );
$what .= 'new_xchan'; $what .= 'new_xchan';
$changed = true; $changed = true;
} }
@ -836,7 +844,6 @@ class Libzot {
if ($local) { if ($local) {
$ph = z_fetch_url($arr['photo']['url'], true); $ph = z_fetch_url($arr['photo']['url'], true);
if ($ph['success']) { if ($ph['success']) {
$hash = import_channel_photo($ph['body'], $arr['photo']['type'], $local[0]['channel_account_id'], $local[0]['channel_id']); $hash = import_channel_photo($ph['body'], $arr['photo']['type'], $local[0]['channel_account_id'], $local[0]['channel_id']);
if ($hash) { if ($hash) {
@ -848,9 +855,10 @@ class Libzot {
intval($local[0]['channel_id']) intval($local[0]['channel_id'])
); );
if ($profile) { if ($profile) {
if(! intval($profile[0]['is_default'])) if (! intval($profile[0]['is_default'])) {
$is_default_profile = 0; $is_default_profile = 0;
} }
}
// If setting for the default profile, unset the profile photo flag from any other photos I own // If setting for the default profile, unset the profile photo flag from any other photos I own
if ($is_default_profile) { if ($is_default_profile) {
@ -912,13 +920,16 @@ class Libzot {
$s = Libsync::sync_locations($arr, $arr); $s = Libsync::sync_locations($arr, $arr);
if ($s) { if ($s) {
if($s['change_message']) if ($s['change_message']) {
$what .= $s['change_message']; $what .= $s['change_message'];
if($s['changed']) }
if ($s['changed']) {
$changed = $s['changed']; $changed = $s['changed'];
if($s['message']) }
if ($s['message']) {
$ret['message'] .= $s['message']; $ret['message'] .= $s['message'];
} }
}
// Which entries in the update table are we interested in updating? // Which entries in the update table are we interested in updating?
@ -970,7 +981,7 @@ class Libzot {
} }
if (($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) { if (($changed) || ($ud_flags == UPDATE_FLAGS_FORCED)) {
$guid = random_string() . '@' . \App::get_hostname(); $guid = random_string() . '@' . App::get_hostname();
Libzotdir::update_modtime($xchan_hash,$guid,$address,$ud_flags); Libzotdir::update_modtime($xchan_hash,$guid,$address,$ud_flags);
logger('Changed: ' . $what,LOGGER_DEBUG); logger('Changed: ' . $what,LOGGER_DEBUG);
} }
@ -1073,8 +1084,9 @@ class Libzot {
// synchronous message types are handled immediately // synchronous message types are handled immediately
// async messages remain in the queue until processed. // async messages remain in the queue until processed.
if(intval($outq['outq_async'])) if (intval($outq['outq_async'])) {
Queue::remove($outq['outq_hash'],$outq['outq_channel']); Queue::remove($outq['outq_hash'],$outq['outq_channel']);
}
logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG); logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG);
} }
@ -1177,8 +1189,9 @@ class Libzot {
// at the present time (2019/02) during the Hubzilla transition to zot6 it is likely to cause lots of duplicates for // at the present time (2019/02) during the Hubzilla transition to zot6 it is likely to cause lots of duplicates for
// messages arriving from different protocols and sources with different message-id semantics. This // messages arriving from different protocols and sources with different message-id semantics. This
// restriction can be relaxed once most Hubzilla sites are upgraded to > 4.0. // restriction can be relaxed once most Hubzilla sites are upgraded to > 4.0.
// Don't check sync packets since they have a different encoding
if($arr) { if ($arr && $env['type'] !== 'sync') {
if (strpos($arr['mid'],'http') === false && strpos($arr['mid'],'x-zot') === false) { if (strpos($arr['mid'],'http') === false && strpos($arr['mid'],'x-zot') === false) {
logger('activity rejected: legacy message-id'); logger('activity rejected: legacy message-id');
return; return;
@ -1218,8 +1231,9 @@ class Libzot {
// Response messages will inherit the privacy of the parent // Response messages will inherit the privacy of the parent
if($env['type'] !== 'response') if ($env['type'] !== 'response') {
$private = true; $private = true;
}
$deliveries = ids_to_array($r,'hash'); $deliveries = ids_to_array($r,'hash');
@ -1235,8 +1249,6 @@ class Libzot {
// @fixme; // @fixme;
$deliveries = self::public_recips($env,$AS); $deliveries = self::public_recips($env,$AS);
} }
$deliveries = array_unique($deliveries); $deliveries = array_unique($deliveries);
@ -1292,7 +1304,6 @@ class Libzot {
} }
} }
} }
// @fixme - spoofable
if ($AS->data['hubloc']) { if ($AS->data['hubloc']) {
$arr['item_verified'] = true; $arr['item_verified'] = true;
@ -1391,15 +1402,14 @@ class Libzot {
static function public_recips($msg, $act) { static function public_recips($msg, $act) {
require_once('include/channel.php');
$check_mentions = false; $check_mentions = false;
$include_sys = false; $include_sys = false;
if ($msg['type'] === 'activity') { if ($msg['type'] === 'activity') {
$disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false; $disable_discover_tab = get_config('system','disable_discover_tab') || get_config('system','disable_discover_tab') === false;
if(! $disable_discover_tab) if (! $disable_discover_tab) {
$include_sys = true; $include_sys = true;
}
$perm = 'send_stream'; $perm = 'send_stream';
@ -1407,8 +1417,9 @@ class Libzot {
$check_mentions = true; $check_mentions = true;
} }
} }
elseif($msg['type'] === 'mail') elseif ($msg['type'] === 'mail') {
$perm = 'post_mail'; $perm = 'post_mail';
}
$r = []; $r = [];
@ -1432,9 +1443,10 @@ class Libzot {
if ($include_sys) { if ($include_sys) {
$sys = get_sys_channel(); $sys = get_sys_channel();
if($sys) if ($sys) {
$r[] = $sys['channel_hash']; $r[] = $sys['channel_hash'];
} }
}
// look for any public mentions on this site // look for any public mentions on this site
// They will get filtered by tgroup_check() so we don't need to check permissions now // They will get filtered by tgroup_check() so we don't need to check permissions now
@ -1775,8 +1787,9 @@ class Libzot {
$last_prior_route = ''; $last_prior_route = '';
} }
if(in_array('undefined',$existing_route) || $last_hop == 'undefined' || $sender == 'undefined') if (in_array('undefined',$existing_route) || $last_hop == 'undefined' || $sender == 'undefined') {
$last_hop = ''; $last_hop = '';
}
$current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender; $current_route = (($arr['route']) ? $arr['route'] . ',' : '') . $sender;
@ -1822,7 +1835,6 @@ class Libzot {
$DR->update('relayed'); $DR->update('relayed');
$result[] = $DR->get(); $result[] = $DR->get();
} }
continue; continue;
} }
@ -1916,9 +1928,10 @@ class Libzot {
*/ */
call_hooks('activity_received', $parr); call_hooks('activity_received', $parr);
// don't add a source route if it's a relay or later recipients will get a route mismatch // don't add a source route if it's a relay or later recipients will get a route mismatch
if(! $relay) if (! $relay) {
add_source_route($item_id,$sender); add_source_route($item_id,$sender);
} }
}
$DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']); $DR->update(($item_id) ? 'posted' : 'storage failed: ' . $item_result['message']);
$result[] = $DR->get(); $result[] = $DR->get();
} }
@ -1940,8 +1953,9 @@ class Libzot {
} }
} }
if(! $deliveries) if (! $deliveries) {
$result[] = array('', 'no recipients', '', $arr['mid']); $result[] = array('', 'no recipients', '', $arr['mid']);
}
logger('Local results: ' . print_r($result, true), LOGGER_DEBUG); logger('Local results: ' . print_r($result, true), LOGGER_DEBUG);
@ -1965,7 +1979,6 @@ class Libzot {
logger('fetching conversation: ' . $mid, LOGGER_DEBUG); logger('fetching conversation: ' . $mid, LOGGER_DEBUG);
$a = Zotfinger::exec($mid,$channel); $a = Zotfinger::exec($mid,$channel);
logger('received conversation: ' . print_r($a,true), LOGGER_DATA); logger('received conversation: ' . print_r($a,true), LOGGER_DATA);
@ -2001,7 +2014,6 @@ class Libzot {
// logger($AS->debug()); // logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
@ -2032,7 +2044,6 @@ class Libzot {
$arr['author_xchan'] = $r[0]['hubloc_hash']; $arr['author_xchan'] = $r[0]['hubloc_hash'];
} }
if ($signer) { if ($signer) {
$arr['owner_xchan'] = $signer[0]['hubloc_hash']; $arr['owner_xchan'] = $signer[0]['hubloc_hash'];
} }
@ -2117,11 +2128,12 @@ class Libzot {
$i = $r[0]; $i = $r[0];
if($i['target']) if ($i['target']) {
$i['target'] = json_decode($i['target'],true); $i['target'] = json_decode($i['target'],true);
if($i['object']) }
if ($i['object']) {
$i['object'] = json_decode($i['object'],true); $i['object'] = json_decode($i['object'],true);
}
if (! ($i['target'] && $i['object'])) { if (! ($i['target'] && $i['object'])) {
logger('No target/object'); logger('No target/object');
return; return;
@ -2190,14 +2202,17 @@ class Libzot {
if ($orig['resource_type'] === 'event') { if ($orig['resource_type'] === 'event') {
$res = event_addtocal($orig['id'], $uid); $res = event_addtocal($orig['id'], $uid);
if(! $res) if (! $res) {
logger('update event: failed'); logger('update event: failed');
} }
}
if(! $x['item_id']) if (! $x['item_id']) {
logger('update_imported_item: failed: ' . $x['message']); logger('update_imported_item: failed: ' . $x['message']);
else }
else {
logger('update_imported_item'); logger('update_imported_item');
}
return $x; return $x;
} }
@ -2231,8 +2246,9 @@ class Libzot {
); );
if ($r) { if ($r) {
if($r[0]['author_xchan'] === $sender || $r[0]['owner_xchan'] === $sender || $r[0]['source_xchan'] === $sender) if ($r[0]['author_xchan'] === $sender || $r[0]['owner_xchan'] === $sender || $r[0]['source_xchan'] === $sender) {
$ownership_valid = true; $ownership_valid = true;
}
$post_id = $r[0]['id']; $post_id = $r[0]['id'];
$item_found = true; $item_found = true;
@ -2261,8 +2277,9 @@ class Libzot {
if ($item_found) { if ($item_found) {
if (intval($r[0]['item_deleted'])) { if (intval($r[0]['item_deleted'])) {
logger('delete_imported_item: item was already deleted'); logger('delete_imported_item: item was already deleted');
if(! $relay) if (! $relay) {
return false; return false;
}
// This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised // This is a bit hackish, but may have to suffice until the notification/delivery loop is optimised
// a bit further. We're going to strip the ITEM_ORIGIN on this item if it's a comment, because // a bit further. We're going to strip the ITEM_ORIGIN on this item if it's a comment, because
@ -2450,11 +2467,13 @@ class Libzot {
static function check_location_move($sender_hash, $locations) { static function check_location_move($sender_hash, $locations) {
if(! $locations) if (! $locations) {
return; return;
}
if(count($locations) != 1) if (count($locations) != 1) {
return; return;
}
$loc = $locations[0]; $loc = $locations[0];
@ -2462,8 +2481,9 @@ class Libzot {
dbesc($sender_hash) dbesc($sender_hash)
); );
if(! $r) if (! $r) {
return; return;
}
if ($loc['url'] !== z_root()) { if ($loc['url'] !== z_root()) {
$x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1", $x = q("update channel set channel_moved = '%s' where channel_hash = '%s' limit 1",
@ -2510,8 +2530,9 @@ class Libzot {
// if this is a local channel that has been deleted, the hubloc is no good - make sure it is marked deleted // if this is a local channel that has been deleted, the hubloc is no good - make sure it is marked deleted
// so that nobody tries to use it. // so that nobody tries to use it.
if(intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) if (intval($channel['channel_removed']) && $hub['hubloc_url'] === z_root()) {
$hub['hubloc_deleted'] = 1; $hub['hubloc_deleted'] = 1;
}
$ret[] = [ $ret[] = [
'host' => $hub['hubloc_host'], 'host' => $hub['hubloc_host'],
@ -2542,8 +2563,9 @@ class Libzot {
static function import_site($arr) { static function import_site($arr) {
if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) if ( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) {
return false; return false;
}
if (! self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) { if (! self::verify($arr['url'], $arr['site_sig'], $arr['sitekey'])) {
logger('Bad url_sig'); logger('Bad url_sig');
@ -2562,39 +2584,51 @@ class Libzot {
} }
$site_directory = 0; $site_directory = 0;
if($arr['directory_mode'] == 'normal') if ($arr['directory_mode'] == 'normal') {
$site_directory = DIRECTORY_MODE_NORMAL; $site_directory = DIRECTORY_MODE_NORMAL;
if($arr['directory_mode'] == 'primary') }
if ($arr['directory_mode'] == 'primary') {
$site_directory = DIRECTORY_MODE_PRIMARY; $site_directory = DIRECTORY_MODE_PRIMARY;
if($arr['directory_mode'] == 'secondary') }
if ($arr['directory_mode'] == 'secondary') {
$site_directory = DIRECTORY_MODE_SECONDARY; $site_directory = DIRECTORY_MODE_SECONDARY;
if($arr['directory_mode'] == 'standalone') }
if ($arr['directory_mode'] == 'standalone') {
$site_directory = DIRECTORY_MODE_STANDALONE; $site_directory = DIRECTORY_MODE_STANDALONE;
}
$register_policy = 0; $register_policy = 0;
if($arr['register_policy'] == 'closed') if ($arr['register_policy'] == 'closed') {
$register_policy = REGISTER_CLOSED; $register_policy = REGISTER_CLOSED;
if($arr['register_policy'] == 'open') }
if ($arr['register_policy'] == 'open') {
$register_policy = REGISTER_OPEN; $register_policy = REGISTER_OPEN;
if($arr['register_policy'] == 'approve') }
if ($arr['register_policy'] == 'approve') {
$register_policy = REGISTER_APPROVE; $register_policy = REGISTER_APPROVE;
}
$access_policy = 0; $access_policy = 0;
if (array_key_exists('access_policy',$arr)) { if (array_key_exists('access_policy',$arr)) {
if($arr['access_policy'] === 'private') if ($arr['access_policy'] === 'private') {
$access_policy = ACCESS_PRIVATE; $access_policy = ACCESS_PRIVATE;
if($arr['access_policy'] === 'paid') }
if ($arr['access_policy'] === 'paid') {
$access_policy = ACCESS_PAID; $access_policy = ACCESS_PAID;
if($arr['access_policy'] === 'free') }
if ($arr['access_policy'] === 'free') {
$access_policy = ACCESS_FREE; $access_policy = ACCESS_FREE;
if($arr['access_policy'] === 'tiered') }
if ($arr['access_policy'] === 'tiered') {
$access_policy = ACCESS_TIERED; $access_policy = ACCESS_TIERED;
} }
}
// don't let insecure sites register as public hubs // don't let insecure sites register as public hubs
if(strpos($arr['url'],'https://') === false) if (strpos($arr['url'],'https://') === false) {
$access_policy = ACCESS_PRIVATE; $access_policy = ACCESS_PRIVATE;
}
if ($access_policy != ACCESS_PRIVATE) { if ($access_policy != ACCESS_PRIVATE) {
$x = z_fetch_url($arr['url'] . '/siteinfo.json'); $x = z_fetch_url($arr['url'] . '/siteinfo.json');
@ -2711,8 +2745,9 @@ class Libzot {
* @return string * @return string
*/ */
static function get_rpost_path($observer) { static function get_rpost_path($observer) {
if(! $observer) if (! $observer) {
return ''; return EMPTY_STR;
}
$parsed = parse_url($observer['xchan_url']); $parsed = parse_url($observer['xchan_url']);
@ -2789,8 +2824,9 @@ class Libzot {
} }
$them = [ 'hubloc_id_url' => $desturl ]; $them = [ 'hubloc_id_url' => $desturl ];
if(self::refresh($them)) if (self::refresh($them)) {
return $hash; return $hash;
}
return false; return false;
} }
@ -2815,9 +2851,7 @@ class Libzot {
dbesc($ztarget) dbesc($ztarget)
); );
if ($t) { if ($t) {
$ztarget_hash = $t[0]['hubloc_hash']; $ztarget_hash = $t[0]['hubloc_hash'];
} }
else { else {
@ -2829,7 +2863,6 @@ class Libzot {
} }
} }
$r = null; $r = null;
if (strlen($zhash)) { if (strlen($zhash)) {
@ -2853,7 +2886,6 @@ class Libzot {
dbesc($zaddr) dbesc($zaddr)
); );
} }
else { else {
/** /**
@ -2896,8 +2928,9 @@ class Libzot {
$searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true); $searchable = (($e['channel_pageflags'] & PAGE_HIDDEN) ? false : true);
$deleted = (intval($e['xchan_deleted']) ? true : false); $deleted = (intval($e['xchan_deleted']) ? true : false);
if($deleted || $censored || $sys_channel) if ($deleted || $censored || $sys_channel) {
$searchable = false; $searchable = false;
}
// now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group" // now all forums (public, restricted, and private) set the public_forum flag. So it really means "is a group"
// and has nothing to do with accessibility. // and has nothing to do with accessibility.
@ -2926,11 +2959,13 @@ class Libzot {
$profile['description'] = $p[0]['pdesc']; $profile['description'] = $p[0]['pdesc'];
$profile['birthday'] = $p[0]['dob']; $profile['birthday'] = $p[0]['dob'];
if(($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== '')) if (($profile['birthday'] != '0000-00-00') && (($bd = z_birthday($p[0]['dob'],$e['channel_timezone'])) !== '')) {
$profile['next_birthday'] = $bd; $profile['next_birthday'] = $bd;
}
if($age = age($p[0]['dob'],$e['channel_timezone'],'')) if ($age = age($p[0]['dob'],$e['channel_timezone'],'')) {
$profile['age'] = $age; $profile['age'] = $age;
}
$profile['gender'] = $p[0]['gender']; $profile['gender'] = $p[0]['gender'];
$profile['marital'] = $p[0]['marital']; $profile['marital'] = $p[0]['marital'];
$profile['sexual'] = $p[0]['sexual']; $profile['sexual'] = $p[0]['sexual'];
@ -2952,10 +2987,11 @@ class Libzot {
} }
} }
} }
if($tags) if ($tags) {
$profile['keywords'] = $tags; $profile['keywords'] = $tags;
} }
} }
}
// Communication details // Communication details
@ -2987,11 +3023,13 @@ class Libzot {
$ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments')); $ret['comments'] = map_scope(PermissionLimits::Get($e['channel_id'],'post_comments'));
if($deleted) if ($deleted) {
$ret['deleted'] = $deleted; $ret['deleted'] = $deleted;
}
if(intval($e['channel_removed'])) if (intval($e['channel_removed'])) {
$ret['deleted_locally'] = true; $ret['deleted_locally'] = true;
}
// premium or other channel desiring some contact with potential followers before connecting. // premium or other channel desiring some contact with potential followers before connecting.
// This is a template - %s will be replaced with the follow_url we discover for the return channel. // This is a template - %s will be replaced with the follow_url we discover for the return channel.
@ -3001,8 +3039,9 @@ class Libzot {
} }
// This is a template for our follow url, %s will be replaced with a webbie // This is a template for our follow url, %s will be replaced with a webbie
if(! $ret['follow_url']) if (! $ret['follow_url']) {
$ret['follow_url'] = z_root() . '/follow?f=&url=%s'; $ret['follow_url'] = z_root() . '/follow?f=&url=%s';
}
$permissions = get_all_perms($e['channel_id'],$ztarget_hash,false); $permissions = get_all_perms($e['channel_id'],$ztarget_hash,false);
@ -3012,12 +3051,14 @@ class Libzot {
dbesc($ztarget_hash), dbesc($ztarget_hash),
intval($e['channel_id']) intval($e['channel_id'])
); );
if($b) if ($b) {
$permissions['connected'] = true; $permissions['connected'] = true;
} }
}
if($permissions['view_profile']) if ($permissions['view_profile']) {
$ret['profile'] = $profile; $ret['profile'] = $profile;
}
$concise_perms = []; $concise_perms = [];
@ -3037,9 +3078,9 @@ class Libzot {
// array of (verified) hubs this channel uses // array of (verified) hubs this channel uses
$x = self::encode_locations($e); $x = self::encode_locations($e);
if($x) if ($x) {
$ret['locations'] = $x; $ret['locations'] = $x;
}
$ret['site'] = self::site_info(); $ret['site'] = self::site_info();
call_hooks('zotinfo',$ret); call_hooks('zotinfo',$ret);
@ -3064,17 +3105,22 @@ class Libzot {
$ret['site']['sitekey'] = get_config('system','pubkey'); $ret['site']['sitekey'] = get_config('system','pubkey');
$dirmode = get_config('system','directory_mode'); $dirmode = get_config('system','directory_mode');
if(($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) if (($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) {
$ret['site']['directory_mode'] = 'normal'; $ret['site']['directory_mode'] = 'normal';
}
if($dirmode == DIRECTORY_MODE_PRIMARY) if ($dirmode == DIRECTORY_MODE_PRIMARY) {
$ret['site']['directory_mode'] = 'primary'; $ret['site']['directory_mode'] = 'primary';
elseif($dirmode == DIRECTORY_MODE_SECONDARY) }
elseif ($dirmode == DIRECTORY_MODE_SECONDARY) {
$ret['site']['directory_mode'] = 'secondary'; $ret['site']['directory_mode'] = 'secondary';
elseif($dirmode == DIRECTORY_MODE_STANDALONE) }
elseif ($dirmode == DIRECTORY_MODE_STANDALONE) {
$ret['site']['directory_mode'] = 'standalone'; $ret['site']['directory_mode'] = 'standalone';
if($dirmode != DIRECTORY_MODE_NORMAL) }
if ($dirmode != DIRECTORY_MODE_NORMAL) {
$ret['site']['directory_url'] = z_root() . '/dirsearch'; $ret['site']['directory_url'] = z_root() . '/dirsearch';
}
$ret['site']['encryption'] = Crypto::methods(); $ret['site']['encryption'] = Crypto::methods();
@ -3086,39 +3132,46 @@ class Libzot {
$register_policy = intval(get_config('system','register_policy')); $register_policy = intval(get_config('system','register_policy'));
if($register_policy == REGISTER_CLOSED) if ($register_policy == REGISTER_CLOSED) {
$ret['site']['register_policy'] = 'closed'; $ret['site']['register_policy'] = 'closed';
if($register_policy == REGISTER_APPROVE) }
if ($register_policy == REGISTER_APPROVE) {
$ret['site']['register_policy'] = 'approve'; $ret['site']['register_policy'] = 'approve';
if($register_policy == REGISTER_OPEN) }
if ($register_policy == REGISTER_OPEN) {
$ret['site']['register_policy'] = 'open'; $ret['site']['register_policy'] = 'open';
}
$access_policy = intval(get_config('system','access_policy')); $access_policy = intval(get_config('system','access_policy'));
if($access_policy == ACCESS_PRIVATE) if ($access_policy == ACCESS_PRIVATE) {
$ret['site']['access_policy'] = 'private'; $ret['site']['access_policy'] = 'private';
if($access_policy == ACCESS_PAID) }
if ($access_policy == ACCESS_PAID) {
$ret['site']['access_policy'] = 'paid'; $ret['site']['access_policy'] = 'paid';
if($access_policy == ACCESS_FREE) }
if ($access_policy == ACCESS_FREE) {
$ret['site']['access_policy'] = 'free'; $ret['site']['access_policy'] = 'free';
if($access_policy == ACCESS_TIERED) }
if ($access_policy == ACCESS_TIERED) {
$ret['site']['access_policy'] = 'tiered'; $ret['site']['access_policy'] = 'tiered';
}
$ret['site']['accounts'] = account_total(); $ret['site']['accounts'] = account_total();
require_once('include/channel.php');
$ret['site']['channels'] = channel_total(); $ret['site']['channels'] = channel_total();
$ret['site']['admin'] = get_config('system','admin_email'); $ret['site']['admin'] = get_config('system','admin_email');
$visible_plugins = array(); $visible_plugins = [];
if(is_array(\App::$plugins) && count(\App::$plugins)) { if (is_array(App::$plugins) && count(App::$plugins)) {
$r = q("select * from addon where hidden = 0"); $r = q("select * from addon where hidden = 0");
if($r) if ($r) {
foreach($r as $rr) foreach ($r as $rr) {
$visible_plugins[] = $rr['aname']; $visible_plugins[] = $rr['aname'];
} }
}
}
$ret['site']['plugins'] = $visible_plugins; $ret['site']['plugins'] = $visible_plugins;
$ret['site']['sitehash'] = get_config('system','location_hash'); $ret['site']['sitehash'] = get_config('system','location_hash');
@ -3204,8 +3257,9 @@ class Libzot {
static function sign($data,$key,$alg = 'sha256') { static function sign($data,$key,$alg = 'sha256') {
if(! $key) if (! $key) {
return 'no key'; return 'no key';
}
$sig = ''; $sig = '';
openssl_sign($data,$sig,$key,$alg); openssl_sign($data,$sig,$key,$alg);
return $alg . '.' . base64url_encode($sig); return $alg . '.' . base64url_encode($sig);
@ -3250,16 +3304,10 @@ class Libzot {
foreach ($arr as $v) { foreach ($arr as $v) {
if($v[$check] === 'zot6') { if($v[$check] === 'zot6') {
return $v; return $v;
} }
} }
return $arr[0]; return $arr[0];
} }
} }

View file

@ -3,6 +3,8 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Webfinger;
use Zotlabs\Lib\Zotfinger;
require_once('include/permissions.php'); require_once('include/permissions.php');
@ -238,7 +240,6 @@ class Libzotdir {
// for brand new directory servers, only load the last couple of days. // for brand new directory servers, only load the last couple of days.
// It will take about a month for a new directory to obtain the full current repertoire of channels. // It will take about a month for a new directory to obtain the full current repertoire of channels.
/** @FIXME Go back and pick up earlier ratings if this is a new directory server. These do not get refreshed. */
$token = get_config('system','realm_token'); $token = get_config('system','realm_token');
@ -264,14 +265,17 @@ class Libzotdir {
$r = q("select * from updates where ud_guid = '%s' limit 1", $r = q("select * from updates where ud_guid = '%s' limit 1",
dbesc($t['transaction_id']) dbesc($t['transaction_id'])
); );
if($r) if ($r) {
continue; continue;
}
$ud_flags = 0; $ud_flags = 0;
if (is_array($t['flags']) && in_array('deleted',$t['flags'])) if (is_array($t['flags']) && in_array('deleted',$t['flags']))
$ud_flags |= UPDATE_FLAGS_DELETED; $ud_flags |= UPDATE_FLAGS_DELETED;
if (is_array($t['flags']) && in_array('forced',$t['flags'])) if (is_array($t['flags']) && in_array('forced',$t['flags']))
$ud_flags |= UPDATE_FLAGS_FORCED; $ud_flags |= UPDATE_FLAGS_FORCED;
if (is_array($t['flags']) && in_array('censored',$t['flags']))
$ud_flags |= UPDATE_FLAGS_CENSORED;
$z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr ) $z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )
values ( '%s', '%s', '%s', %d, '%s' ) ", values ( '%s', '%s', '%s', %d, '%s' ) ",
@ -308,9 +312,9 @@ class Libzotdir {
if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) { if ($ud['ud_addr'] && (! ($ud['ud_flags'] & UPDATE_FLAGS_DELETED))) {
$success = false; $success = false;
$href = \Zotlabs\Lib\Webfinger::zot_url(punify($ud['ud_addr'])); $href = Webfinger::zot_url(punify($ud['ud_addr']));
if($href) { if($href) {
$zf = \Zotlabs\Lib\Zotfinger::exec($href); $zf = Zotfinger::exec($href);
} }
if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) { if(is_array($zf) && array_path_exists('signature/signer',$zf) && $zf['signature']['signer'] === $href && intval($zf['signature']['header_valid'])) {
$xc = Libzot::import_xchan($zf['data'], 0, $ud); $xc = Libzot::import_xchan($zf['data'], 0, $ud);
@ -340,7 +344,7 @@ class Libzotdir {
logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG); logger('local_dir_update: uid: ' . $uid, LOGGER_DEBUG);
$p = q("select channel.channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", $p = q("select channel_hash, channel_address, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1",
intval($uid) intval($uid)
); );
@ -380,7 +384,7 @@ class Libzotdir {
$hidden = (1 - intval($p[0]['publish'])); $hidden = (1 - intval($p[0]['publish']));
logger('hidden: ' . $hidden); // logger('hidden: ' . $hidden);
$r = q("select xchan_hidden from xchan where xchan_hash = '%s' limit 1", $r = q("select xchan_hidden from xchan where xchan_hash = '%s' limit 1",
dbesc($p[0]['channel_hash']) dbesc($p[0]['channel_hash'])
@ -554,6 +558,7 @@ class Libzotdir {
'profile' => $profile, 'profile' => $profile,
'update' => $update 'update' => $update
]; ];
/** /**
* @hooks import_directory_profile * @hooks import_directory_profile
* Called when processing delivery of a profile structure from an external source (usually for directory storage). * Called when processing delivery of a profile structure from an external source (usually for directory storage).
@ -561,10 +566,12 @@ class Libzotdir {
* * \e array \b profile * * \e array \b profile
* * \e boolean \b update * * \e boolean \b update
*/ */
call_hooks('import_directory_profile', $d); call_hooks('import_directory_profile', $d);
if (($d['update']) && (! $suppress_update)) if (($d['update']) && (! $suppress_update)) {
self::update_modtime($arr['xprof_hash'],random_string() . '@' . \App::get_hostname(), $addr, $ud_flags); self::update_modtime($arr['xprof_hash'], new_uuid(), $addr, $ud_flags);
}
return $d['update']; return $d['update'];
} }
@ -639,7 +646,7 @@ class Libzotdir {
); );
} }
else { else {
q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and not (ud_flags & %d)>0 ", q("update updates set ud_flags = ( ud_flags | %d ) where ud_addr = '%s' and (ud_flags & %d) = 0 ",
intval(UPDATE_FLAGS_UPDATED), intval(UPDATE_FLAGS_UPDATED),
dbesc($addr), dbesc($addr),
intval(UPDATE_FLAGS_UPDATED) intval(UPDATE_FLAGS_UPDATED)

View file

@ -20,6 +20,7 @@ use Zotlabs\Access\Permissions;
* content ACLs are evaluated (@ref ::Zotlabs::Access::AccessList "AccessList"). * content ACLs are evaluated (@ref ::Zotlabs::Access::AccessList "AccessList").
* These answer the question "Can Joe view *this* album/photo?". * These answer the question "Can Joe view *this* album/photo?".
*/ */
class Permcat { class Permcat {
/** /**

View file

@ -104,6 +104,7 @@ class Share {
} else { } else {
$bb = "[share author='" . urlencode($this->item['author']['xchan_name']). $bb = "[share author='" . urlencode($this->item['author']['xchan_name']).
"' profile='" . $this->item['author']['xchan_url'] . "' profile='" . $this->item['author']['xchan_url'] .
"' portable_id='" . $this->item['author']['xchan_hash'] .
"' avatar='" . $this->item['author']['xchan_photo_s'] . "' avatar='" . $this->item['author']['xchan_photo_s'] .
"' link='" . $this->item['plink'] . "' link='" . $this->item['plink'] .
"' auth='" . (($this->item['author']['network'] === 'zot') ? 'true' : 'false') . "' auth='" . (($this->item['author']['network'] === 'zot') ? 'true' : 'false') .

View file

@ -28,15 +28,11 @@ class System {
} }
static public function get_project_icon() { static public function get_project_icon() {
if(is_array(App::$config) && is_array(App::$config['system']) && array_key_exists('icon',App::$config['system'])) {
return z_root() . '/images/zap4-64.png'; return App::$config['system']['icon'];
if(defined('NOMADIC')) {
return '&#x26A1;';
}
else {
return '&#x2638;';
} }
return z_root() . '/images/z-64.png';
} }

View file

@ -1,21 +0,0 @@
<?php
namespace Zotlabs\Lib;
class Techlevels {
static public function levels() {
$techlevels = [
'0' => t('0. Beginner/Basic'),
'1' => t('1. Novice - not skilled but willing to learn'),
'2' => t('2. Intermediate - somewhat comfortable'),
'3' => t('3. Advanced - very comfortable'),
'4' => t('4. Expert - I can write computer code'),
'5' => t('5. Wizard - I probably know more than you do')
];
return $techlevels;
}
}

View file

@ -31,13 +31,11 @@ class Account_edit {
} }
$service_class = trim($_REQUEST['service_class']); $service_class = trim($_REQUEST['service_class']);
$account_level = intval(trim($_REQUEST['account_level']));
$account_language = trim($_REQUEST['account_language']); $account_language = trim($_REQUEST['account_language']);
$r = q("update account set account_service_class = '%s', account_level = %d, account_language = '%s' $r = q("update account set account_service_class = '%s', account_language = '%s'
where account_id = %d", where account_id = %d",
dbesc($service_class), dbesc($service_class),
intval($account_level),
dbesc($account_language), dbesc($account_language),
intval($account_id) intval($account_id)
); );
@ -68,7 +66,6 @@ class Account_edit {
'$title' => t('Account Edit'), '$title' => t('Account Edit'),
'$pass1' => [ 'pass1', t('New Password'), ' ','' ], '$pass1' => [ 'pass1', t('New Password'), ' ','' ],
'$pass2' => [ 'pass2', t('New Password again'), ' ','' ], '$pass2' => [ 'pass2', t('New Password again'), ' ','' ],
'$account_level' => [ 'account_level', t('Technical skill level'), $x[0]['account_level'], '', \Zotlabs\Lib\Techlevels::levels() ],
'$account_language' => [ 'account_language' , t('Account language (for emails)'), $x[0]['account_language'], '', language_list() ], '$account_language' => [ 'account_language' , t('Account language (for emails)'), $x[0]['account_language'], '', language_list() ],
'$service_class' => [ 'service_class', t('Service class'), $x[0]['account_service_class'], '' ], '$service_class' => [ 'service_class', t('Service class'), $x[0]['account_service_class'], '' ],
'$submit' => t('Submit'), '$submit' => t('Submit'),

View file

@ -123,7 +123,7 @@ class Security {
'$page' => t('Security'), '$page' => t('Security'),
'$form_security_token' => get_form_security_token('admin_security'), '$form_security_token' => get_form_security_token('admin_security'),
'$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")), '$block_public' => array('block_public', t("Block public"), get_config('system','block_public'), t("Check to block public access to all otherwise public personal pages on this site unless you are currently authenticated.")),
'$block_public_search' => array('block_public_search', t("Block public search"), get_config('system','block_public_search'), t("Prevent access to search content unless you are currently authenticated.")), '$block_public_search' => array('block_public_search', t("Block public search"), get_config('system','block_public_search', 1), t("Prevent access to search content unless you are currently authenticated.")),
'$localdir_hide' => [ 'localdir_hide', t('Hide local directory'), intval(get_config('system','localdir_hide')), t('Only use the global directory') ], '$localdir_hide' => [ 'localdir_hide', t('Hide local directory'), intval(get_config('system','localdir_hide')), t('Only use the global directory') ],
'$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ], '$cloud_noroot' => [ 'cloud_noroot', t('Provide a cloud root directory'), 1 - intval(get_config('system','cloud_disable_siteroot')), t('The cloud root directory lists all channel names which provide public files') ],
'$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ], '$cloud_disksize' => [ 'cloud_disksize', t('Show total disk space available to cloud uploads'), intval(get_config('system','cloud_report_disksize')), '' ],

View file

@ -1,59 +1,41 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Web\HTTPSig; use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\ActivityStreams; use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\Activity;
require_once('library/jsonld/jsonld.php');
class Ap_probe extends \Zotlabs\Web\Controller { class Ap_probe extends Controller {
function get() { function get() {
$o .= '<h3>ActivityPub Probe Diagnostic</h3>'; $channel = null;
$o .= '<form action="ap_probe" method="post">'; $o = replace_macros(get_markup_template('ap_probe.tpl'), [
$o .= 'Lookup URI: <input type="text" style="width: 250px;" name="addr" value="' . $_REQUEST['addr'] .'" /><br>'; '$page_title' => t('ActivityPub Probe Diagnostic'),
$o .= 'or paste text: <textarea style="width: 250px;" name="text">' . htmlspecialchars($_REQUEST['text']) . '</textarea><br>'; '$resource' => [ 'resource', t('Object URL') , $_REQUEST['resource'], EMPTY_STR ],
$o .= '<input type="submit" name="submit" value="Submit" /></form>'; '$authf' => [ 'authf', t('Authenticated fetch'), $_REQUEST['authf'], EMPTY_STR, [ t('No'), t('Yes') ] ],
'$submit' => t('Submit')
]);
$o .= '<br /><br />'; if (x($_REQUEST,'resource')) {
$resource = $_REQUEST['resource'];
if(x($_REQUEST,'addr')) { if ($_REQUEST['authf']) {
$addr = $_REQUEST['addr']; $channel = App::get_channel();
if (! $channel) {
$headers = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/ld+json'; $channel = get_sys_channel();
$redirects = 0;
$x = z_fetch_url($addr,true,$redirects, [ 'headers' => [ $headers ]]);
if($x['success'])
$o .= '<pre>' . htmlspecialchars($x['header']) . '</pre>' . EOL;
$o .= '<pre>' . htmlspecialchars($x['body']) . '</pre>' . EOL;
$o .= 'verify returns: ' . str_replace("\n",EOL,print_r(HTTPSig::verify($x),true)) . EOL;
$text = $x['body'];
} }
else {
$text = $_REQUEST['text'];
} }
if($text) { $x = Activity::fetch($resource,$channel);
// if($text && json_decode($text)) { if ($x) {
// $normalized1 = jsonld_normalize(json_decode($text),[ 'algorithm' => 'URDNA2015', 'format' => 'application/nquads' ]); $o .= '<pre>' . str_replace('\\n',"\n",htmlspecialchars(json_encode($x,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT))) . '</pre>';
// $o .= str_replace("\n",EOL,htmlentities(var_export($normalized1,true))); }
// $o .= '<pre>' . json_encode($normalized1, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . '</pre>';
// }
$o .= '<pre>' . str_replace(['\\n','\\'],["\n",''],htmlspecialchars(jindent($text))) . '</pre>';
$AP = new ActivityStreams($text);
$o .= '<pre>' . htmlspecialchars($AP->debug()) . '</pre>';
} }
return $o; return $o;

View file

@ -22,6 +22,7 @@ class Apschema extends \Zotlabs\Web\Controller {
'topicalCollection' => 'zot:topicalCollection', 'topicalCollection' => 'zot:topicalCollection',
'eventRepeat' => 'zot:eventRepeat', 'eventRepeat' => 'zot:eventRepeat',
'emojiReaction' => 'zot:emojiReaction', 'emojiReaction' => 'zot:emojiReaction',
'expires' => 'zot:expires',
] ]
]; ];

489
Zotlabs/Module/Calendar.php Normal file
View file

@ -0,0 +1,489 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libsync;
use Zotlabs\Access\AccessControl;
use Zotlabs\Lib\Apps;
use Zotlabs\Daemon\Master;
require_once('include/conversation.php');
require_once('include/bbcode.php');
require_once('include/datetime.php');
require_once('include/event.php');
require_once('include/items.php');
require_once('include/text.php');
require_once('include/html2plain.php');
require_once('include/security.php');
class Calendar extends Controller {
function post() {
logger('post: ' . print_r($_REQUEST,true), LOGGER_DATA);
if (! local_channel())
return;
$event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0);
$event_hash = ((x($_POST,'event_hash')) ? $_POST['event_hash'] : '');
$xchan = ((x($_POST,'xchan')) ? dbesc($_POST['xchan']) : '');
$summary = escape_tags(trim($_POST['summary']));
$desc = escape_tags(trim($_POST['desc']));
$location = escape_tags(trim($_POST['location']));
$type = escape_tags(trim($_POST['type']));
$start_text = escape_tags($_REQUEST['dtstart']);
$finish_text = escape_tags($_REQUEST['dtend']);
$adjust = intval($_POST['adjust']);
$nofinish = intval($_POST['nofinish']);
$uid = local_channel();
$timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : '');
$tz = (($timezone) ? $timezone : date_default_timezone_get());
$categories = escape_tags(trim($_POST['categories']));
// only allow editing your own events.
if (($xchan) && ($xchan !== get_observer_hash())) {
return;
}
if ($start_text) {
$start = $start_text;
}
else {
$start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute);
}
if ($finish_text) {
$finish = $finish_text;
}
else {
$finish = sprintf('%d-%d-%d %d:%d:0',$finishyear,$finishmonth,$finishday,$finishhour,$finishminute);
}
if ($nofinish) {
$finish = NULL_DATE;
}
if ($adjust) {
$start = datetime_convert($tz,'UTC',$start);
if (! $nofinish) {
$finish = datetime_convert($tz,'UTC',$finish);
}
}
else {
$start = datetime_convert('UTC','UTC',$start);
if (! $nofinish) {
$finish = datetime_convert('UTC','UTC',$finish);
}
}
linkify_tags($location, local_channel());
// Don't allow the event to finish before it begins.
// It won't hurt anything, but somebody will file a bug report
// and we'll waste a bunch of time responding to it. Time that
// could've been spent doing something else.
if (strcmp($finish,$start) < 0 && (! $nofinish)) {
notice( t('Event can not end before it has started.') . EOL);
if (intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
}
killme();
}
if ((! $summary) || (! $start)) {
notice( t('Event title and start time are required.') . EOL);
if (intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
}
killme();
}
$channel = App::get_channel();
$acl = new AccessControl(false);
if ($event_id) {
$x = q("select * from event where id = %d and uid = %d limit 1",
intval($event_id),
intval(local_channel())
);
if (! $x) {
notice( t('Event not found.') . EOL);
if (intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
killme();
}
return;
}
$acl->set($x[0]);
$created = $x[0]['created'];
$edited = datetime_convert();
}
else {
$created = $edited = datetime_convert();
$acl->set_from_array($_POST);
}
$post_tags = [];
$ac = $acl->get();
$str_contact_allow = $ac['allow_cid'];
$str_group_allow = $ac['allow_gid'];
$str_contact_deny = $ac['deny_cid'];
$str_group_deny = $ac['deny_gid'];
$private = $acl->is_private();
$results = linkify_tags($desc, local_channel());
if ($results) {
// Set permissions based on tag replacements
set_linkified_perms($results, $str_contact_allow, $str_group_allow, local_channel(), false, $private);
foreach ($results as $result) {
$success = $result['success'];
if ($success['replaced']) {
$post_tags[] = [
'uid' => local_channel(),
'ttype' => $success['termtype'],
'otype' => TERM_OBJ_POST,
'term' => $success['term'],
'url' => $success['url']
];
}
}
}
if (strlen($categories)) {
$cats = explode(',',$categories);
foreach ($cats as $cat) {
$post_tags[] = array(
'uid' => local_channel(),
'ttype' => TERM_CATEGORY,
'otype' => TERM_OBJ_POST,
'term' => trim($cat),
'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat))
);
}
}
$datarray = [
'dtstart' => $start,
'dtend' => $finish,
'summary' => $summary,
'description' => $desc,
'location' => $location,
'etype' => $type,
'adjust' => $adjust,
'nofinish' => $nofinish,
'uid' => local_channel(),
'account' => get_account_id(),
'event_xchan' => $channel['channel_hash'],
'allow_cid' => $str_contact_allow,
'allow_gid' => $str_group_allow,
'deny_cid' => $str_contact_deny,
'deny_gid' => $str_group_deny,
'private' => intval($private),
'id' => $event_id,
'created' => $created,
'edited' => $edited
];
if (intval($_REQUEST['preview'])) {
$html = format_event_html($datarray);
echo $html;
killme();
}
$event = event_store_event($datarray);
if ($post_tags) {
$datarray['term'] = $post_tags;
}
$item_id = event_store_item($datarray,$event);
if ($item_id) {
$r = q("select * from item where id = %d",
intval($item_id)
);
if ($r) {
xchan_query($r);
$sync_item = fetch_post_tags($r);
$z = q("select * from event where event_hash = '%s' and uid = %d limit 1",
dbesc($r[0]['resource_id']),
intval($channel['channel_id'])
);
if ($z) {
Libsync::build_sync_packet($channel['channel_id'], [ 'event_item' => [ encode_item($sync_item[0],true) ], 'event' => $z]);
}
}
}
Master::Summon( [ 'Notifier', 'event', $item_id ] );
killme();
}
function get() {
if (argc() > 2 && argv(1) == 'ical') {
$event_id = argv(2);
$sql_extra = permissions_sql(local_channel());
$r = q("select * from event where event_hash = '%s' $sql_extra limit 1",
dbesc($event_id)
);
if ($r) {
header('Content-type: text/calendar');
header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' );
echo ical_wrapper($r);
killme();
}
else {
notice( t('Event not found.') . EOL );
return;
}
}
if (! local_channel()) {
notice( t('Permission denied.') . EOL);
return;
}
if ((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) {
$r = q("update event set dismissed = 1 where id = %d and uid = %d",
intval(argv(2)),
intval(local_channel())
);
}
if ((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) {
$r = q("update event set dismissed = 0 where id = %d and uid = %d",
intval(argv(2)),
intval(local_channel())
);
}
$channel = App::get_channel();
$mode = 'view';
$export = false;
$ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '');
if (argc() > 1) {
if (argc() > 2 && argv(1) === 'add') {
$mode = 'add';
$item_id = intval(argv(2));
}
if (argc() > 2 && argv(1) === 'drop') {
$mode = 'drop';
$event_id = argv(2);
}
if (argc() <= 2 && argv(1) === 'export') {
$export = true;
}
if (argc() > 2 && intval(argv(1)) && intval(argv(2))) {
$mode = 'view';
}
if(argc() <= 2) {
$mode = 'view';
$event_id = argv(1);
}
}
if ($mode === 'add') {
event_addtocal($item_id,local_channel());
killme();
}
if ($mode == 'view') {
/* edit/create form */
if ($event_id) {
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
dbesc($event_id),
intval(local_channel())
);
if ($r) {
$orig_event = $r[0];
}
}
$channel = App::get_channel();
if (argv(1) === 'json'){
if (x($_GET,'start')) {
$start = $_GET['start'];
}
if (x($_GET,'end')) {
$finish = $_GET['end'];
}
}
$start = datetime_convert('UTC','UTC',$start);
$finish = datetime_convert('UTC','UTC',$finish);
$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
if (x($_GET,'id')){
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
from event left join item on item.resource_id = event.event_hash
where item.resource_type = 'event' and event.uid = %d and event.id = %d limit 1",
intval(local_channel()),
intval($_GET['id'])
);
}
elseif ($export) {
$r = q("SELECT * from event where uid = %d",
intval(local_channel())
);
}
else {
// fixed an issue with "nofinish" events not showing up in the calendar.
// There's still an issue if the finish date crosses the end of month.
// Noting this for now - it will need to be fixed here and in Friendica.
// Ultimately the finish date shouldn't be involved in the query.
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan, item.id as item_id
from event left join item on event.event_hash = item.resource_id
where item.resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
AND (( event.adjust = 0 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )
OR ( event.adjust = 1 AND ( event.dtend >= '%s' or event.nofinish = 1 ) AND event.dtstart <= '%s' )) ",
intval(local_channel()),
dbesc($start),
dbesc($finish),
dbesc($adjust_start),
dbesc($adjust_finish)
);
}
if($r && ! $export) {
xchan_query($r);
$r = fetch_post_tags($r,true);
$r = sort_by_date($r);
}
$events = [];
if ($r) {
foreach ($r as $rr) {
$start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c'));
if ($rr['nofinish']) {
$end = null;
}
else {
$end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
// give a fake end to birthdays so they get crammed into a
// single day on the calendar
if ($rr['etype'] === 'birthday')
$end = null;
}
$catsenabled = Apps::system_app_installed($x['profile_uid'], 'Categories');
$categories = '';
if ($catsenabled){
if ($rr['term']) {
$categories = array_elm_to_str(get_terms_oftype($rr['term'], TERM_CATEGORY), 'term');
}
}
$allDay = false;
// allDay event rules
if (!strpos($start, 'T') && !strpos($end, 'T'))
$allDay = true;
if (strpos($start, 'T00:00:00') && strpos($end, 'T00:00:00'))
$allDay = true;
$edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false);
$drop = [ z_root() . '/events/drop/' . $rr['event_hash'], t('Delete event'), '', '' ];
$events[] = [
'calendar_id' => 'calendar',
'rw' => true,
'id' => $rr['id'],
'uri' => $rr['event_hash'],
'start' => $start,
'end' => $end,
'drop' => $drop,
'allDay' => $allDay,
'title' => htmlentities($rr['summary'], ENT_COMPAT, 'UTF-8'),
'editable' => $edit ? true : false,
'item' => $rr,
'plink' => [ $rr['plink'], t('Link to source') ],
'description' => htmlentities($rr['description'], ENT_COMPAT, 'UTF-8'),
'location' => htmlentities($rr['location'], ENT_COMPAT, 'UTF-8'),
'allow_cid' => expand_acl($rr['allow_cid']),
'allow_gid' => expand_acl($rr['allow_gid']),
'deny_cid' => expand_acl($rr['deny_cid']),
'deny_gid' => expand_acl($rr['deny_gid']),
'categories' => $categories
];
}
}
if ($export) {
header('Content-type: text/calendar');
header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' );
echo ical_wrapper($r);
killme();
}
if (App::$argv[1] === 'json'){
json_return_and_die($events);
}
}
if ($mode === 'drop' && $event_id) {
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
dbesc($event_id),
intval(local_channel())
);
$sync_event = $r[0];
if ($r) {
$r = q("delete from event where event_hash = '%s' and uid = %d",
dbesc($event_id),
intval(local_channel())
);
if ($r) {
$r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d",
dbesc($event_id),
intval(local_channel())
);
$sync_event['event_deleted'] = 1;
Libsync::build_sync_packet(0, [ 'event' => [ $sync_event ] ]);
killme();
}
notice( t('Failed to remove event' ) . EOL);
killme();
}
}
}
}

View file

@ -2,11 +2,15 @@
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App; use App;
use DBA;
use DateTime;
use Zotlabs\Lib\Apps; use Zotlabs\Lib\Apps;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Storage\BasicAuth;
use Zotlabs\Lib\System;
require_once('include/event.php'); require_once('include/event.php');
require_once('include/auth.php'); require_once('include/auth.php');
require_once('include/security.php'); require_once('include/security.php');
@ -41,7 +45,7 @@ class Cdav extends Controller {
continue; continue;
} }
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]); $sigblock = HTTPSig::parse_sigheader($_SERVER[$head]);
if($sigblock) { if($sigblock) {
$keyId = str_replace('acct:','',$sigblock['keyId']); $keyId = str_replace('acct:','',$sigblock['keyId']);
if($keyId) { if($keyId) {
@ -64,7 +68,7 @@ class Cdav extends Controller {
continue; continue;
if($record) { if($record) {
$verified = \Zotlabs\Web\HTTPSig::verify('',$record['channel']['channel_pubkey']); $verified = HTTPSig::verify('',$record['channel']['channel_pubkey']);
if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) { if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) {
$record = null; $record = null;
} }
@ -114,7 +118,7 @@ class Cdav extends Controller {
* *
*/ */
$pdo = \DBA::$dba->db; $pdo = DBA::$dba->db;
// Autoloader // Autoloader
require_once 'vendor/autoload.php'; require_once 'vendor/autoload.php';
@ -126,17 +130,13 @@ class Cdav extends Controller {
* own backend systems. * own backend systems.
*/ */
$auth = new \Zotlabs\Storage\BasicAuth(); $auth = new BasicAuth();
$auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . 'CalDAV/CardDAV'); $auth->setRealm(ucfirst(System::get_platform_name()) . ' ' . 'CalDAV/CardDAV');
if (local_channel()) { if (local_channel()) {
logger('loggedin'); logger('loggedin');
if((argv(1) == 'calendars') && (!Apps::system_app_installed(local_channel(), 'CalDAV'))) {
killme();
}
if ((argv(1) == 'addressbooks') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { if ((argv(1) == 'addressbooks') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) {
killme(); killme();
} }
@ -146,8 +146,9 @@ class Cdav extends Controller {
$auth->channel_id = $channel['channel_id']; $auth->channel_id = $channel['channel_id'];
$auth->channel_hash = $channel['channel_hash']; $auth->channel_hash = $channel['channel_hash'];
$auth->channel_account_id = $channel['channel_account_id']; $auth->channel_account_id = $channel['channel_account_id'];
if($channel['channel_timezone']) if ($channel['channel_timezone']) {
$auth->setTimezone($channel['channel_timezone']); $auth->setTimezone($channel['channel_timezone']);
}
$auth->observer = $channel['channel_hash']; $auth->observer = $channel['channel_hash'];
$principalUri = 'principals/' . $channel['channel_address']; $principalUri = 'principals/' . $channel['channel_address'];
@ -188,8 +189,9 @@ class Cdav extends Controller {
$server = new \Sabre\DAV\Server($nodes); $server = new \Sabre\DAV\Server($nodes);
if(isset($baseUri)) if (isset($baseUri)) {
$server->setBaseUri($baseUri); $server->setBaseUri($baseUri);
}
// Plugins // Plugins
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth)); $server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth));
@ -218,13 +220,10 @@ class Cdav extends Controller {
} }
function post() { function post() {
if (! local_channel()) if (! local_channel())
return; return;
if((argv(1) === 'calendar') && (! Apps::system_app_installed(local_channel(), 'CalDAV'))) {
return;
}
if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) {
return; return;
} }
@ -235,7 +234,7 @@ class Cdav extends Controller {
if (!cdav_principal($principalUri)) if (!cdav_principal($principalUri))
return; return;
$pdo = \DBA::$dba->db; $pdo = DBA::$dba->db;
require_once 'vendor/autoload.php'; require_once 'vendor/autoload.php';
@ -255,8 +254,9 @@ class Cdav extends Controller {
dbesc($calendarUri) dbesc($calendarUri)
); );
if (count($r)) if ($r) {
$duplicate = true; $duplicate = true;
}
} while ($duplicate == true); } while ($duplicate == true);
$properties = [ $properties = [
@ -280,9 +280,12 @@ class Cdav extends Controller {
return; return;
$title = $_REQUEST['title']; $title = $_REQUEST['title'];
$dtstart = new \DateTime($_REQUEST['dtstart']); $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
if($_REQUEST['dtend']) $dtstart = new DateTime($start);
$dtend = new \DateTime($_REQUEST['dtend']); if ($_REQUEST['dtend']) {
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
$dtend = new DateTime($end);
}
$description = $_REQUEST['description']; $description = $_REQUEST['description'];
$location = $_REQUEST['location']; $location = $_REQUEST['location'];
@ -306,13 +309,17 @@ class Cdav extends Controller {
'DTSTART' => $dtstart 'DTSTART' => $dtstart
] ]
]); ]);
if($dtend) if($dtend) {
$vcalendar->VEVENT->add('DTEND', $dtend); $vcalendar->VEVENT->add('DTEND', $dtend);
$vcalendar->VEVENT->DTEND['TZID'] = App::$timezone;
}
if($description) if($description)
$vcalendar->VEVENT->add('DESCRIPTION', $description); $vcalendar->VEVENT->add('DESCRIPTION', $description);
if($location) if($location)
$vcalendar->VEVENT->add('LOCATION', $location); $vcalendar->VEVENT->add('LOCATION', $location);
$vcalendar->VEVENT->DTSTART['TZID'] = App::$timezone;
$calendarData = $vcalendar->serialize(); $calendarData = $vcalendar->serialize();
$caldavBackend->createCalendarObject($id, $objectUri, $calendarData); $caldavBackend->createCalendarObject($id, $objectUri, $calendarData);
@ -325,8 +332,9 @@ class Cdav extends Controller {
$id = explode(':', $_REQUEST['id']); $id = explode(':', $_REQUEST['id']);
if(! cdav_perms($id[0],$calendars)) if (! cdav_perms($id[0],$calendars)) {
return; return;
}
$mutations = [ $mutations = [
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'], '{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
@ -338,7 +346,6 @@ class Cdav extends Controller {
$caldavBackend->updateCalendar($id, $patch); $caldavBackend->updateCalendar($id, $patch);
$patch->commit(); $patch->commit();
} }
// edit calendar object via ajax request // edit calendar object via ajax request
@ -351,8 +358,12 @@ class Cdav extends Controller {
$uri = $_REQUEST['uri']; $uri = $_REQUEST['uri'];
$title = $_REQUEST['title']; $title = $_REQUEST['title'];
$dtstart = new \DateTime($_REQUEST['dtstart']); $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
$dtend = $_REQUEST['dtend'] ? new \DateTime($_REQUEST['dtend']) : ''; $dtstart = new DateTime($start);
if ($_REQUEST['dtend']) {
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
$dtend = new DateTime($end);
}
$description = $_REQUEST['description']; $description = $_REQUEST['description'];
$location = $_REQUEST['location']; $location = $_REQUEST['location'];
@ -360,23 +371,28 @@ class Cdav extends Controller {
$vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']);
if($title) if ($title) {
$vcalendar->VEVENT->SUMMARY = $title; $vcalendar->VEVENT->SUMMARY = $title;
if($dtstart) }
if ($dtstart) {
$vcalendar->VEVENT->DTSTART = $dtstart; $vcalendar->VEVENT->DTSTART = $dtstart;
if($dtend) }
if ($dtend) {
$vcalendar->VEVENT->DTEND = $dtend; $vcalendar->VEVENT->DTEND = $dtend;
else }
else {
unset($vcalendar->VEVENT->DTEND); unset($vcalendar->VEVENT->DTEND);
if($description) }
if ($description) {
$vcalendar->VEVENT->DESCRIPTION = $description; $vcalendar->VEVENT->DESCRIPTION = $description;
if($location) }
if ($location) {
$vcalendar->VEVENT->LOCATION = $location; $vcalendar->VEVENT->LOCATION = $location;
}
$calendarData = $vcalendar->serialize(); $calendarData = $vcalendar->serialize();
$caldavBackend->updateCalendarObject($id, $uri, $calendarData); $caldavBackend->updateCalendarObject($id, $uri, $calendarData);
killme(); killme();
} }
@ -385,13 +401,13 @@ class Cdav extends Controller {
$id = explode(':', $_REQUEST['target']); $id = explode(':', $_REQUEST['target']);
if(!cdav_perms($id[0],$calendars,true)) if (!cdav_perms($id[0],$calendars,true)) {
return; return;
}
$uri = $_REQUEST['uri']; $uri = $_REQUEST['uri'];
$caldavBackend->deleteCalendarObject($id, $uri); $caldavBackend->deleteCalendarObject($id, $uri);
killme(); killme();
} }
@ -400,12 +416,17 @@ class Cdav extends Controller {
$id = [$_REQUEST['id'][0], $_REQUEST['id'][1]]; $id = [$_REQUEST['id'][0], $_REQUEST['id'][1]];
if(!cdav_perms($id[0],$calendars,true)) if (! cdav_perms($id[0],$calendars,true)) {
return; return;
}
$uri = $_REQUEST['uri']; $uri = $_REQUEST['uri'];
$dtstart = new \DateTime($_REQUEST['dtstart']); $start = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtstart']);
$dtend = $_REQUEST['dtend'] ? new \DateTime($_REQUEST['dtend']) : ''; $dtstart = new DateTime($start);
if($_REQUEST['dtend']) {
$end = datetime_convert(App::$timezone, 'UTC', $_REQUEST['dtend']);
$dtend = new DateTime($end);
}
$object = $caldavBackend->getCalendarObject($id, $uri); $object = $caldavBackend->getCalendarObject($id, $uri);
@ -433,8 +454,9 @@ class Cdav extends Controller {
$id = [intval($_REQUEST['calendarid']), intval($_REQUEST['instanceid'])]; $id = [intval($_REQUEST['calendarid']), intval($_REQUEST['instanceid'])];
if(! cdav_perms($id[0],$calendars)) if (! cdav_perms($id[0],$calendars)) {
return; return;
}
$hash = $_REQUEST['sharee']; $hash = $_REQUEST['sharee'];
@ -467,8 +489,9 @@ class Cdav extends Controller {
dbesc($addressbookUri) dbesc($addressbookUri)
); );
if (count($r)) if ($r) {
$duplicate = true; $duplicate = true;
}
} while ($duplicate == true); } while ($duplicate == true);
$properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']]; $properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']];
@ -481,8 +504,9 @@ class Cdav extends Controller {
$id = $_REQUEST['id']; $id = $_REQUEST['id'];
if(! cdav_perms($id,$addressbooks)) if (! cdav_perms($id,$addressbooks)) {
return; return;
}
$mutations = [ $mutations = [
'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'] '{DAV:}displayname' => $_REQUEST['{DAV:}displayname']
@ -508,11 +532,13 @@ class Cdav extends Controller {
dbesc($uri) dbesc($uri)
); );
if (count($r)) if ($r) {
$duplicate = true; $duplicate = true;
}
} while ($duplicate == true); } while ($duplicate == true);
//TODO: this mostly duplictes the procedure in update addressbook card. should move this part to a function to avoid duplication // TODO: this mostly duplictes the procedure in update addressbook card.
// Should move this part to a function to avoid duplication
$fn = $_REQUEST['fn']; $fn = $_REQUEST['fn'];
$vcard = new \Sabre\VObject\Component\VCard([ $vcard = new \Sabre\VObject\Component\VCard([
@ -607,8 +633,9 @@ class Cdav extends Controller {
$id = $_REQUEST['target']; $id = $_REQUEST['target'];
if(!cdav_perms($id,$addressbooks)) if (!cdav_perms($id,$addressbooks)) {
return; return;
}
$uri = $_REQUEST['uri']; $uri = $_REQUEST['uri'];
@ -735,8 +762,9 @@ class Cdav extends Controller {
$id = $_REQUEST['target']; $id = $_REQUEST['target'];
if(!cdav_perms($id,$addressbooks)) if (!cdav_perms($id,$addressbooks)) {
return; return;
}
$uri = $_REQUEST['uri']; $uri = $_REQUEST['uri'];
@ -747,16 +775,29 @@ class Cdav extends Controller {
// Import calendar or addressbook // Import calendar or addressbook
if (($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) { if (($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) {
$src = @file_get_contents($_FILES['userfile']['tmp_name']); $src = $_FILES['userfile']['tmp_name'];
if ($src) { if ($src) {
if ($_REQUEST['c_upload']) { if ($_REQUEST['c_upload']) {
if ($_REQUEST['target'] == 'calendar') {
$result = parse_ical_file($src,local_channel());
if ($result) {
info( t('Calendar entries imported.') . EOL);
}
else {
notice( t('No calendar entries found.') . EOL);
}
@unlink($src);
return;
}
$id = explode(':', $_REQUEST['target']); $id = explode(':', $_REQUEST['target']);
$ext = 'ics'; $ext = 'ics';
$table = 'calendarobjects'; $table = 'calendarobjects';
$column = 'calendarid'; $column = 'calendarid';
$objects = new \Sabre\VObject\Splitter\ICalendar($src); $objects = new \Sabre\VObject\Splitter\ICalendar(@file_get_contents($src));
$profile = \Sabre\VObject\Node::PROFILE_CALDAV; $profile = \Sabre\VObject\Node::PROFILE_CALDAV;
$backend = new \Sabre\CalDAV\Backend\PDO($pdo); $backend = new \Sabre\CalDAV\Backend\PDO($pdo);
} }
@ -766,7 +807,7 @@ class Cdav extends Controller {
$ext = 'vcf'; $ext = 'vcf';
$table = 'cards'; $table = 'cards';
$column = 'addressbookid'; $column = 'addressbookid';
$objects = new \Sabre\VObject\Splitter\VCard($src); $objects = new \Sabre\VObject\Splitter\VCard(@file_get_contents($src));
$profile = \Sabre\VObject\Node::PROFILE_CARDDAV; $profile = \Sabre\VObject\Node::PROFILE_CARDDAV;
$backend = new \Sabre\CardDAV\Backend\PDO($pdo); $backend = new \Sabre\CardDAV\Backend\PDO($pdo);
} }
@ -793,8 +834,9 @@ class Cdav extends Controller {
dbesc($objectUri) dbesc($objectUri)
); );
if (count($r)) if ($r) {
$duplicate = true; $duplicate = true;
}
} while ($duplicate == true); } while ($duplicate == true);
if ($_REQUEST['c_upload']) { if ($_REQUEST['c_upload']) {
@ -829,31 +871,25 @@ class Cdav extends Controller {
function get() { function get() {
if(!local_channel()) if (! local_channel()) {
return; return;
if((argv(1) === 'calendar') && (! Apps::system_app_installed(local_channel(), 'CalDAV'))) {
//Do not display any associated widgets at this point
App::$pdl = '';
$o = '<b>CalDAV App (Not Installed):</b><br>';
$o .= t('CalDAV capable calendar');
return $o;
} }
if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) { if ((argv(1) === 'addressbook') && (! Apps::system_app_installed(local_channel(), 'CardDAV'))) {
// Do not display any associated widgets at this point // Do not display any associated widgets at this point
App::$pdl = ''; App::$pdl = '';
$o = '<b>CardDAV App (Not Installed):</b><br>'; $o = '<b>' . t('CardDAV App') . ' (' . t('Not Installed') . '):</b><br>';
$o .= t('CalDAV capable addressbook'); $o .= t('CalDAV capable addressbook');
return $o; return $o;
} }
App::$profile_uid = local_channel();
$channel = App::get_channel(); $channel = App::get_channel();
$principalUri = 'principals/' . $channel['channel_address']; $principalUri = 'principals/' . $channel['channel_address'];
$pdo = \DBA::$dba->db; $pdo = DBA::$dba->db;
require_once 'vendor/autoload.php'; require_once 'vendor/autoload.php';
@ -867,28 +903,90 @@ class Cdav extends Controller {
} }
if (argv(1) === 'calendar') { if (argv(1) === 'calendar') {
nav_set_selected('CalDAV'); nav_set_selected('Calendar');
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
$calendars = $caldavBackend->getCalendarsForUser($principalUri); $calendars = $caldavBackend->getCalendarsForUser($principalUri);
} }
// Display calendar(s) here // Display calendar(s) here
if(argc() == 2 && argv(1) === 'calendar') { if(argc() <= 3 && argv(1) === 'calendar') {
head_add_css('/library/fullcalendar/fullcalendar.css'); head_add_css('/library/fullcalendar/packages/core/main.min.css');
head_add_css('/library/fullcalendar/packages/daygrid/main.min.css');
head_add_css('/library/fullcalendar/packages/timegrid/main.min.css');
head_add_css('/library/fullcalendar/packages/list/main.min.css');
head_add_css('cdav_calendar.css'); head_add_css('cdav_calendar.css');
head_add_js('/library/moment/moment.min.js', 1); head_add_js('/library/fullcalendar/packages/core/main.min.js');
head_add_js('/library/fullcalendar/fullcalendar.min.js', 1); head_add_js('/library/fullcalendar/packages/interaction/main.min.js');
head_add_js('/library/fullcalendar/locale-all.js', 1); head_add_js('/library/fullcalendar/packages/daygrid/main.min.js');
head_add_js('/library/fullcalendar/packages/timegrid/main.min.js');
head_add_js('/library/fullcalendar/packages/list/main.min.js');
$sources = '';
$resource_id = '';
$resource = null;
if (argc() == 3) {
$resource_id = argv(2);
}
if ($resource_id) {
$r = q("SELECT event.*, item.author_xchan, item.owner_xchan, item.plink, item.id as item_id FROM event LEFT JOIN item ON event.event_hash = item.resource_id
WHERE event.uid = %d AND event.event_hash = '%s' LIMIT 1",
intval(local_channel()),
dbesc($resource_id)
);
if ($r) {
xchan_query($r);
$r = fetch_post_tags($r,true);
$r[0]['dtstart'] = (($r[0]['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$r[0]['dtstart'], 'c') : datetime_convert('UTC','UTC',$r[0]['dtstart'],'c'));
$r[0]['dtend'] = (($r[0]['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$r[0]['dtend'], 'c') : datetime_convert('UTC','UTC',$r[0]['dtend'],'c'));
$r[0]['plink'] = [$r[0]['plink'], t('Link to source')];
$resource = $r[0];
$catsenabled = Apps::system_app_installed(local_channel(), 'Categories');
$categories = '';
if ($catsenabled){
if($r[0]['term']) {
$categories = array_elm_to_str(get_terms_oftype($r[0]['term'], TERM_CATEGORY), 'term');
}
}
if ($r[0]['dismissed'] == 0) {
q("UPDATE event SET dismissed = 1 WHERE event.uid = %d AND event.event_hash = '%s'",
intval(local_channel()),
dbesc($resource_id)
);
}
}
}
if (get_pconfig(local_channel(), 'cdav_calendar', 'calendar')) {
$sources .= '{
id: \'calendar\',
url: \'/calendar/json/\',
color: \'#3a87ad\'
}, ';
}
$calendars[] = [
'displayname' => $channel['channel_name'],
'id' => 'calendar'
];
foreach ($calendars as $calendar) { foreach ($calendars as $calendar) {
$editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript $editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript
$color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad'); $color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39');
$sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : ''); $sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : '');
$switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]); $switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]);
if ($switch) { if ($switch) {
$sources .= '{ $sources .= '{
id: ' . $calendar['id'][0] . ',
url: \'/cdav/calendar/json/' . $calendar['id'][0] . '/' . $calendar['id'][1] . '\', url: \'/cdav/calendar/json/' . $calendar['id'][0] . '/' . $calendar['id'][1] . '\',
color: \'' . $color . '\' color: \'' . $color . '\'
}, '; }, ';
@ -905,19 +1003,31 @@ class Cdav extends Controller {
$sources = rtrim($sources, ', '); $sources = rtrim($sources, ', ');
$first_day = get_pconfig(local_channel(),'system','cal_first_day'); $first_day = feature_enabled(local_channel(), 'cal_first_day');
$first_day = (($first_day) ? $first_day : 0); $first_day = (($first_day) ? $first_day : 0);
$title = ['title', t('Event title')]; $title = ['title', t('Event title')];
$dtstart = ['dtstart', t('Start date and time'), '', t('Example: YYYY-MM-DD HH:mm')]; $dtstart = ['dtstart', t('Start date and time')];
$dtend = ['dtend', t('End date and time'), '', t('Example: YYYY-MM-DD HH:mm')]; $dtend = ['dtend', t('End date and time')];
$description = ['description', t('Description')]; $description = ['description', t('Description')];
$location = ['location', t('Location')]; $location = ['location', t('Location')];
$catsenabled = Apps::system_app_installed(local_channel(), 'Categories');
require_once('include/acl_selectors.php');
$accesslist = new \Zotlabs\Access\AccessControl($channel);
$perm_defaults = $accesslist->get();
$acl = populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'));
$permissions = $perm_defaults;
$o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [ $o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [
'$sources' => $sources, '$sources' => $sources,
'$color' => $color, '$color' => $color,
'$lang' => App::$language, '$lang' => App::$language,
'$timezone' => App::$timezone,
'$first_day' => $first_day, '$first_day' => $first_day,
'$prev' => t('Previous'), '$prev' => t('Previous'),
'$next' => t('Next'), '$next' => t('Next'),
@ -929,6 +1039,7 @@ class Cdav extends Controller {
'$list_week' => t('List week'), '$list_week' => t('List week'),
'$list_day' => t('List day'), '$list_day' => t('List day'),
'$title' => $title, '$title' => $title,
'$calendars' => $calendars,
'$writable_calendars' => $writable_calendars, '$writable_calendars' => $writable_calendars,
'$dtstart' => $dtstart, '$dtstart' => $dtstart,
'$dtend' => $dtend, '$dtend' => $dtend,
@ -936,11 +1047,25 @@ class Cdav extends Controller {
'$location' => $location, '$location' => $location,
'$more' => t('More'), '$more' => t('More'),
'$less' => t('Less'), '$less' => t('Less'),
'$update' => t('Update'),
'$calendar_select_label' => t('Select calendar'), '$calendar_select_label' => t('Select calendar'),
'$calendar_optiopns_label' => [t('Channel Calendars'), t('CalDAV Calendars')],
'$delete' => t('Delete'), '$delete' => t('Delete'),
'$delete_all' => t('Delete all'), '$delete_all' => t('Delete all'),
'$cancel' => t('Cancel'), '$cancel' => t('Cancel'),
'$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.') '$create' => t('Create'),
'$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'),
'$channel_hash' => $channel['channel_hash'],
'$acl' => $acl,
'$lockstate' => (($accesslist->is_private()) ? 'lock' : 'unlock'),
'$allow_cid' => acl2json($permissions['allow_cid']),
'$allow_gid' => acl2json($permissions['allow_gid']),
'$deny_cid' => acl2json($permissions['deny_cid']),
'$deny_gid' => acl2json($permissions['deny_gid']),
'$catsenabled' => $catsenabled,
'$categories_label' => t('Categories'),
'$resource' => json_encode($resource),
'$categories' => $categories
]); ]);
return $o; return $o;
@ -950,15 +1075,20 @@ class Cdav extends Controller {
// Provide json data for calendar // Provide json data for calendar
if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json' && intval(argv(3)) && intval(argv(4))) { if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json' && intval(argv(3)) && intval(argv(4))) {
$events = [];
$id = [argv(3), argv(4)]; $id = [argv(3), argv(4)];
if(! cdav_perms($id[0],$calendars)) if (! cdav_perms($id[0],$calendars)) {
killme(); json_return_and_die($events);
}
if (x($_GET,'start')) if (x($_GET,'start')) {
$start = new \DateTime($_GET['start']); $start = new \DateTime($_GET['start']);
if (x($_GET,'end')) }
if (x($_GET,'end')) {
$end = new \DateTime($_GET['end']); $end = new \DateTime($_GET['end']);
}
$filters['name'] = 'VCALENDAR'; $filters['name'] = 'VCALENDAR';
$filters['prop-filters'][0]['name'] = 'VEVENT'; $filters['prop-filters'][0]['name'] = 'VEVENT';
@ -967,16 +1097,19 @@ class Cdav extends Controller {
$filters['comp-filters'][0]['time-range']['end'] = $end; $filters['comp-filters'][0]['time-range']['end'] = $end;
$uris = $caldavBackend->calendarQuery($id, $filters); $uris = $caldavBackend->calendarQuery($id, $filters);
if ($uris) { if ($uris) {
$objects = $caldavBackend->getMultipleCalendarObjects($id, $uris); $objects = $caldavBackend->getMultipleCalendarObjects($id, $uris);
foreach ($objects as $object) { foreach ($objects as $object) {
$vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']);
if(isset($vcalendar->VEVENT->RRULE)) if (isset($vcalendar->VEVENT->RRULE)) {
// expanding recurrent events seems to loose timezone info
// save it here so we can add it later
$recurrent_timezone = (string)$vcalendar->VEVENT->DTSTART['TZID'];
$vcalendar = $vcalendar->expand($start, $end); $vcalendar = $vcalendar->expand($start, $end);
}
foreach ($vcalendar->VEVENT as $vevent) { foreach ($vcalendar->VEVENT as $vevent) {
$title = (string)$vevent->SUMMARY; $title = (string)$vevent->SUMMARY;
@ -984,29 +1117,32 @@ class Cdav extends Controller {
$dtend = (string)$vevent->DTEND; $dtend = (string)$vevent->DTEND;
$description = (string)$vevent->DESCRIPTION; $description = (string)$vevent->DESCRIPTION;
$location = (string)$vevent->LOCATION; $location = (string)$vevent->LOCATION;
$timezone = (string)$vevent->DTSTART['TZID'];
$rw = ((cdav_perms($id[0],$calendars,true)) ? true : false); $rw = ((cdav_perms($id[0],$calendars,true)) ? true : false);
$editable = $rw ? true : false;
$recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false); $recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false);
$editable = $rw ? true : false; if ($recurrent) {
if($recurrent)
$editable = false; $editable = false;
$timezone = $recurrent_timezone;
}
$allDay = false; $allDay = false;
// allDay event rules // allDay event rules
if(!strpos($dtstart, 'T') && !strpos($dtend, 'T')) if (!strpos($dtstart, 'T') && !strpos($dtend, 'T')) {
$allDay = true; $allDay = true;
if(strpos($dtstart, 'T000000') && strpos($dtend, 'T000000')) }
if (strpos($dtstart, 'T000000') && strpos($dtend, 'T000000')) {
$allDay = true; $allDay = true;
}
$events[] = [ $events[] = [
'calendar_id' => $id, 'calendar_id' => $id,
'uri' => $object['uri'], 'uri' => $object['uri'],
'title' => $title, 'title' => $title,
'start' => $dtstart, 'start' => datetime_convert($timezone, $timezone, $dtstart, 'c'),
'end' => $dtend, 'end' => (($dtend) ? datetime_convert($timezone, $timezone, $dtend, 'c') : ''),
'description' => $description, 'description' => $description,
'location' => $location, 'location' => $location,
'allDay' => $allDay, 'allDay' => $allDay,
@ -1016,19 +1152,17 @@ class Cdav extends Controller {
]; ];
} }
} }
}
json_return_and_die($events); json_return_and_die($events);
} }
else {
killme();
}
}
// enable/disable calendars // enable/disable calendars
if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && intval(argv(3)) && (argv(4) == 1 || argv(4) == 0)) { if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && argv(3) && (argv(4) == 1 || argv(4) == 0)) {
$id = argv(3); $id = argv(3);
if(! cdav_perms($id,$calendars)) if (! cdav_perms($id,$calendars)) {
killme(); killme();
}
set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4)); set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4));
killme(); killme();
@ -1038,8 +1172,9 @@ class Cdav extends Controller {
if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) { if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) {
$id = [argv(3), argv(4)]; $id = [argv(3), argv(4)];
if(! cdav_perms($id[0],$calendars)) if (! cdav_perms($id[0],$calendars)) {
killme(); killme();
}
$caldavBackend->deleteCalendar($id); $caldavBackend->deleteCalendar($id);
killme(); killme();
@ -1051,8 +1186,9 @@ class Cdav extends Controller {
$id = [argv(3), argv(4)]; $id = [argv(3), argv(4)];
$hash = argv(5); $hash = argv(5);
if(! cdav_perms($id[0],$calendars)) if (! cdav_perms($id[0],$calendars)) {
killme(); killme();
}
$sharee_arr = channelx_by_hash($hash); $sharee_arr = channelx_by_hash($hash);
@ -1080,8 +1216,9 @@ class Cdav extends Controller {
$displayname = cdav_perms($id,$addressbooks); $displayname = cdav_perms($id,$addressbooks);
if(!$displayname) if (! $displayname) {
return; return;
}
head_add_css('cdav_addressbook.css'); head_add_css('cdav_addressbook.css');
@ -1189,7 +1326,6 @@ class Cdav extends Controller {
$cards[] = [ $cards[] = [
'id' => $object['id'], 'id' => $object['id'],
'uri' => $object['uri'], 'uri' => $object['uri'],
'photo' => $photo, 'photo' => $photo,
'fn' => $fn, 'fn' => $fn,
'org' => $org, 'org' => $org,
@ -1245,8 +1381,9 @@ class Cdav extends Controller {
if (argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) { if (argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) {
$id = argv(3); $id = argv(3);
if(! cdav_perms($id,$addressbooks)) if (! cdav_perms($id,$addressbooks)) {
return; return;
}
$carddavBackend->deleteAddressBook($id); $carddavBackend->deleteAddressBook($id);
killme(); killme();
@ -1256,8 +1393,9 @@ class Cdav extends Controller {
function activate($pdo, $channel) { function activate($pdo, $channel) {
if(! $channel) if (! $channel) {
return; return;
}
$uri = 'principals/' . $channel['channel_address']; $uri = 'principals/' . $channel['channel_address'];
@ -1283,12 +1421,13 @@ class Cdav extends Controller {
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
$properties = [ $properties = [
'{DAV:}displayname' => t('Default Calendar'), '{DAV:}displayname' => t('Default Calendar'),
'{http://apple.com/ns/ical/}calendar-color' => '#3a87ad', '{http://apple.com/ns/ical/}calendar-color' => '#6cad39',
'{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name'] '{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name']
]; ];
$id = $caldavBackend->createCalendar($uri, 'default', $properties); $id = $caldavBackend->createCalendar($uri, 'default', $properties);
set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1); set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1);
set_pconfig(local_channel(), 'cdav_calendar' , 'calendar', 1);
// create default addressbook // create default addressbook
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
@ -1298,5 +1437,4 @@ class Cdav extends Controller {
} }
} }
} }

View file

@ -33,8 +33,9 @@ class Channel extends Controller {
goaway('search' . '?f=&search=' . $_GET['search']); goaway('search' . '?f=&search=' . $_GET['search']);
$which = null; $which = null;
if(argc() > 1) if (argc() > 1) {
$which = argv(1); $which = argv(1);
}
if (! $which) { if (! $which) {
if (local_channel()) { if (local_channel()) {
$channel = App::get_channel(); $channel = App::get_channel();
@ -110,7 +111,8 @@ class Channel extends Controller {
$x = array_merge(['@context' => [ $x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV, ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1' 'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], Activity::encode_person($channel,true,((defined('NOMADIC')) ? false : true))); ]], Activity::encode_person($channel,true,((defined('NOMADIC')) ? false : true)));
$headers = []; $headers = [];
@ -248,17 +250,19 @@ class Channel extends Controller {
/** /**
* Get permissions SQL - if $remote_contact is true, our remote user has been pre-verified and we already have fetched his/her groups * Get permissions SQL
*/ */
$item_normal = item_normal(); $item_normal = item_normal();
$item_normal_update = item_normal_update(); $item_normal_update = item_normal_update();
$sql_extra = item_permissions_sql(App::$profile['profile_uid']); $sql_extra = item_permissions_sql(App::$profile['profile_uid']);
if(get_pconfig(App::$profile['profile_uid'],'system','channel_list_mode') && (! $mid)) if (get_pconfig(App::$profile['profile_uid'],'system','channel_list_mode') && (! $mid)) {
$page_mode = 'list'; $page_mode = 'list';
else }
else {
$page_mode = 'client'; $page_mode = 'client';
}
$abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " "; $abook_uids = " and abook.abook_channel = " . intval(App::$profile['profile_uid']) . " ";
@ -285,13 +289,16 @@ class Channel extends Controller {
'title' => 'oembed' 'title' => 'oembed'
]); ]);
if($update && $_SESSION['loadtime']) 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']) . "' ) "; $simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
if($load) }
if ($load) {
$simple_update = ''; $simple_update = '';
}
if($static && $simple_update) if ($static && $simple_update) {
$simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' "; $simple_update .= " and author_xchan = '" . protect_sprintf(get_observer_hash()) . "' ";
}
if (($update) && (! $load)) { if (($update) && (! $load)) {
@ -338,11 +345,12 @@ class Channel extends Controller {
$sql_extra2 .= " and item.item_thread_top != 0 "; $sql_extra2 .= " and item.item_thread_top != 0 ";
} }
if($order === 'post') if ($order === 'post') {
$ordering = "created"; $ordering = "created";
else }
else {
$ordering = "commented"; $ordering = "commented";
}
$itemspage = get_pconfig(local_channel(),'system','itemspage'); $itemspage = get_pconfig(local_channel(),'system','itemspage');
App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
@ -372,7 +380,7 @@ class Channel extends Controller {
} }
} }
else { else {
$r = array(); $r = [];
} }
} }
if ($r) { if ($r) {
@ -398,8 +406,9 @@ class Channel extends Controller {
notice( t('Permission denied.') . EOL); notice( t('Permission denied.') . EOL);
} }
} else { }
$items = array(); else {
$items = [];
} }
if ((! $update) && (! $load)) { if ((! $update) && (! $load)) {

View file

@ -1,11 +1,19 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\ActivityPub; use Zotlabs\Lib\ActivityPub;
use Zotlabs\Lib\Apps; use Zotlabs\Lib\Apps;
use Zotlabs\Lib\AccessList; use Zotlabs\Lib\AccessList;
use Zotlabs\Access\Permissions;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Lib\Permcat;
use Zotlabs\Daemon\Master;
use Zotlabs\Web\HTTPHeaders;
use Sabre\VObject\Reader;
/* @file connedit.php /* @file connedit.php
* @brief In this file the connection-editor form is generated and evaluated. * @brief In this file the connection-editor form is generated and evaluated.
@ -19,7 +27,7 @@ require_once('include/socgraph.php');
require_once('include/photos.php'); require_once('include/photos.php');
class Connedit extends \Zotlabs\Web\Controller { class Connedit extends Controller {
/* @brief Initialize the connection-editor /* @brief Initialize the connection-editor
* *
@ -39,15 +47,15 @@ class Connedit extends \Zotlabs\Web\Controller {
intval(argv(1)) intval(argv(1))
); );
if($r) { if($r) {
\App::$poi = array_shift($r); App::$poi = array_shift($r);
} }
} }
$channel = \App::get_channel(); $channel = App::get_channel();
if($channel) if ($channel) {
head_set_icon($channel['xchan_photo_s']); head_set_icon($channel['xchan_photo_s']);
}
} }
@ -64,7 +72,7 @@ class Connedit extends \Zotlabs\Web\Controller {
if(! $contact_id) if(! $contact_id)
return; return;
$channel = \App::get_channel(); $channel = App::get_channel();
// TODO if configured for hassle-free permissions, we'll post the form with ajax as soon as the // TODO if configured for hassle-free permissions, we'll post the form with ajax as soon as the
// connection enable is toggled to a special autopost url and set permissions immediately, leaving // connection enable is toggled to a special autopost url and set permissions immediately, leaving
@ -89,7 +97,7 @@ class Connedit extends \Zotlabs\Web\Controller {
call_hooks('contact_edit_post', $_POST); call_hooks('contact_edit_post', $_POST);
$vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard'); $vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard');
$vcard = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); $vcard = (($vc) ? Reader::read($vc) : null);
$serialised_vcard = update_vcard($_REQUEST,$vcard); $serialised_vcard = update_vcard($_REQUEST,$vcard);
if($serialised_vcard) if($serialised_vcard)
set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$serialised_vcard); set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$serialised_vcard);
@ -135,7 +143,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$closeness = 80; $closeness = 80;
} }
$all_perms = \Zotlabs\Access\Permissions::Perms(); $all_perms = Permissions::Perms();
$p = EMPTY_STR; $p = EMPTY_STR;
@ -166,8 +174,8 @@ class Connedit extends \Zotlabs\Web\Controller {
// request. The workaround is to approve the connection, then go back and // request. The workaround is to approve the connection, then go back and
// adjust permissions as desired. // adjust permissions as desired.
$p = \Zotlabs\Access\Permissions::connect_perms(local_channel()); $p = Permissions::connect_perms(local_channel());
$my_perms = \Zotlabs\Access\Permissions::serialise($p['perms']); $my_perms = Permissions::serialise($p['perms']);
if ($my_perms) { if ($my_perms) {
set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'system','my_perms',$my_perms); set_abconfig($channel['channel_id'],$orig_record[0]['abook_xchan'],'system','my_perms',$my_perms);
} }
@ -175,8 +183,6 @@ class Connedit extends \Zotlabs\Web\Controller {
$abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']); $abook_pending = (($new_friend) ? 0 : $orig_record[0]['abook_pending']);
$r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d, $r = q("UPDATE abook SET abook_profile = '%s', abook_closeness = %d, abook_pending = %d,
abook_incl = '%s', abook_excl = '%s' abook_incl = '%s', abook_excl = '%s'
where abook_id = %d AND abook_channel = %d", where abook_id = %d AND abook_channel = %d",
@ -194,12 +200,12 @@ class Connedit extends \Zotlabs\Web\Controller {
else else
notice( t('Failed to update connection record.') . EOL); notice( t('Failed to update connection record.') . EOL);
if(! intval(\App::$poi['abook_self'])) { if(! intval(App::$poi['abook_self'])) {
if($new_friend) { if($new_friend) {
\Zotlabs\Daemon\Master::Summon( [ 'Notifier', 'permissions_accept', $contact_id ] ); Master::Summon( [ 'Notifier', 'permissions_accept', $contact_id ] );
} }
\Zotlabs\Daemon\Master::Summon( [ Master::Summon( [
'Notifier', 'Notifier',
(($new_friend) ? 'permissions_create' : 'permissions_update'), (($new_friend) ? 'permissions_create' : 'permissions_update'),
$contact_id $contact_id
@ -211,7 +217,7 @@ class Connedit extends \Zotlabs\Web\Controller {
if($default_group) { if($default_group) {
$g = AccessList::rec_byhash(local_channel(),$default_group); $g = AccessList::rec_byhash(local_channel(),$default_group);
if($g) if($g)
AccessList::member_add(local_channel(),'',\App::$poi['abook_xchan'],$g['id']); AccessList::member_add(local_channel(),'',App::$poi['abook_xchan'],$g['id']);
} }
// Check if settings permit ("post new friend activity" is allowed, and // Check if settings permit ("post new friend activity" is allowed, and
@ -234,9 +240,9 @@ class Connedit extends \Zotlabs\Web\Controller {
$xarr['deny_gid'] = $channel['channel_deny_gid']; $xarr['deny_gid'] = $channel['channel_deny_gid'];
$xarr['item_private'] = (($xarr['allow_cid']||$xarr['allow_gid']||$xarr['deny_cid']||$xarr['deny_gid']) ? 1 : 0); $xarr['item_private'] = (($xarr['allow_cid']||$xarr['allow_gid']||$xarr['deny_cid']||$xarr['deny_gid']) ? 1 : 0);
$xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . \App::$poi['xchan_url'] . ']' . \App::$poi['xchan_name'] . '[/zrl]'; $xarr['body'] = '[zrl=' . $channel['xchan_url'] . ']' . $channel['xchan_name'] . '[/zrl]' . ' ' . t('is now connected to') . ' ' . '[zrl=' . App::$poi['xchan_url'] . ']' . App::$poi['xchan_name'] . '[/zrl]';
$xarr['body'] .= "\n\n\n" . '[zrl=' . \App::$poi['xchan_url'] . '][zmg=80x80]' . \App::$poi['xchan_photo_m'] . '[/zmg][/zrl]'; $xarr['body'] .= "\n\n\n" . '[zrl=' . App::$poi['xchan_url'] . '][zmg=80x80]' . App::$poi['xchan_photo_m'] . '[/zmg][/zrl]';
post_activity_item($xarr); post_activity_item($xarr);
@ -244,7 +250,7 @@ class Connedit extends \Zotlabs\Web\Controller {
// pull in a bit of content if there is any to pull in // pull in a bit of content if there is any to pull in
\Zotlabs\Daemon\Master::Summon(array('Onepoll',$contact_id)); Master::Summon( [ 'Onepoll', $contact_id ]);
} }
@ -257,11 +263,11 @@ class Connedit extends \Zotlabs\Web\Controller {
intval($contact_id) intval($contact_id)
); );
if($r) { if($r) {
\App::$poi = $r[0]; App::$poi = $r[0];
} }
if($new_friend) { if($new_friend) {
$arr = array('channel_id' => local_channel(), 'abook' => \App::$poi); $arr = array('channel_id' => local_channel(), 'abook' => App::$poi);
call_hooks('accept_follow', $arr); call_hooks('accept_follow', $arr);
} }
@ -281,23 +287,23 @@ class Connedit extends \Zotlabs\Web\Controller {
function connedit_clone(&$a) { function connedit_clone(&$a) {
if(! \App::$poi) if(! App::$poi)
return; return;
$channel = \App::get_channel(); $channel = App::get_channel();
$r = q("SELECT abook.*, xchan.* $r = q("SELECT abook.*, xchan.*
FROM abook left join xchan on abook_xchan = xchan_hash FROM abook left join xchan on abook_xchan = xchan_hash
WHERE abook_channel = %d and abook_id = %d LIMIT 1", WHERE abook_channel = %d and abook_id = %d LIMIT 1",
intval(local_channel()), intval(local_channel()),
intval(\App::$poi['abook_id']) intval(App::$poi['abook_id'])
); );
if($r) { if($r) {
\App::$poi = array_shift($r); App::$poi = array_shift($r);
} }
$clone = \App::$poi; $clone = App::$poi;
unset($clone['abook_id']); unset($clone['abook_id']);
unset($clone['abook_account']); unset($clone['abook_account']);
@ -326,11 +332,11 @@ class Connedit extends \Zotlabs\Web\Controller {
} }
$section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : '');
$channel = \App::get_channel(); $channel = App::get_channel();
$yes_no = [ t('No'), t('Yes') ]; $yes_no = [ t('No'), t('Yes') ];
$connect_perms = \Zotlabs\Access\Permissions::connect_perms(local_channel()); $connect_perms = Permissions::connect_perms(local_channel());
$o .= "<script>function connectDefaultShare() { $o .= "<script>function connectDefaultShare() {
\$('.abook-edit-me').each(function() { \$('.abook-edit-me').each(function() {
@ -365,7 +371,7 @@ class Connedit extends \Zotlabs\Web\Controller {
if($cmd === 'update') { if($cmd === 'update') {
// pull feed and consume it, which should subscribe to the hub. // pull feed and consume it, which should subscribe to the hub.
\Zotlabs\Daemon\Master::Summon(array('Poller',$contact_id)); Master::Summon( [ 'Poller', $contact_id ]);
goaway(z_root() . '/connedit/' . $contact_id); goaway(z_root() . '/connedit/' . $contact_id);
} }
@ -375,14 +381,14 @@ class Connedit extends \Zotlabs\Web\Controller {
$recurse = 0; $recurse = 0;
$x = z_fetch_url(zid($url),false,$recurse,['session' => true]); $x = z_fetch_url(zid($url),false,$recurse,['session' => true]);
if($x['success']) { if($x['success']) {
$h = new \Zotlabs\Web\HTTPHeaders($x['header']); $h = new HTTPHeaders($x['header']);
$fields = $h->fetch(); $fields = $h->fetch();
if($fields) { if($fields) {
foreach($fields as $y) { foreach($fields as $y) {
if(array_key_exists('content-type',$y)) { if(array_key_exists('content-type',$y)) {
$type = explode(';',trim($y['content-type'])); $type = explode(';',trim($y['content-type']));
if($type && $type[0] === 'text/vcard' && $x['body']) { if($type && $type[0] === 'text/vcard' && $x['body']) {
$vc = \Sabre\VObject\Reader::read($x['body']); $vc = Reader::read($x['body']);
$vcard = $vc->serialize(); $vcard = $vc->serialize();
if($vcard) { if($vcard) {
set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$vcard); set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$vcard);
@ -406,13 +412,13 @@ class Connedit extends \Zotlabs\Web\Controller {
if($cmd === 'refresh') { if($cmd === 'refresh') {
if($orig_record[0]['xchan_network'] === 'zot6') { if($orig_record[0]['xchan_network'] === 'zot6') {
if(! Libzot::refresh($orig_record[0],\App::get_channel())) if(! Libzot::refresh($orig_record[0], App::get_channel()))
notice( t('Refresh failed - channel is currently unavailable.') ); notice( t('Refresh failed - channel is currently unavailable.') );
} }
else { else {
// if you are on a different network we'll force a refresh of the connection basic info // if you are on a different network we'll force a refresh of the connection basic info
\Zotlabs\Daemon\Master::Summon(array('Notifier','permissions_update',$contact_id)); Master::Summon( [ 'Notifier', 'permissions_update', $contact_id ]);
} }
goaway(z_root() . '/connedit/' . $contact_id); goaway(z_root() . '/connedit/' . $contact_id);
} }
@ -470,18 +476,15 @@ class Connedit extends \Zotlabs\Web\Controller {
if($cmd === 'drop') { if($cmd === 'drop') {
// @FIXME
// We need to send either a purge or a refresh packet to the other side (the channel being unfriended).
// The issue is that the abook DB record _may_ get destroyed when we call contact_remove. As the notifier
// runs in the background there could be a race condition preventing this packet from being sent in all
// cases.
// PLACEHOLDER
if($orig_record[0]['xchan_network'] === 'activitypub') { if($orig_record[0]['xchan_network'] === 'activitypub') {
ActivityPub::contact_remove(local_channel(), $orig_record[0]); ActivityPub::contact_remove(local_channel(), $orig_record[0]);
} }
contact_remove(local_channel(), $orig_record[0]['abook_id']); contact_remove(local_channel(), $orig_record[0]['abook_id']);
// The purge notification is sent to the xchan_hash as the abook record will have just been removed
Master::Summon( [ 'Notifier' , 'purge', $orig_record[0]['xchan_hash'] ] );
Libsync::build_sync_packet(0 /* use the current local_channel */, Libsync::build_sync_packet(0 /* use the current local_channel */,
array('abook' => array(array( array('abook' => array(array(
'abook_xchan' => $orig_record[0]['abook_xchan'], 'abook_xchan' => $orig_record[0]['abook_xchan'],
@ -497,13 +500,13 @@ class Connedit extends \Zotlabs\Web\Controller {
} }
} }
if(\App::$poi) { if(App::$poi) {
$abook_prev = 0; $abook_prev = 0;
$abook_next = 0; $abook_next = 0;
$contact_id = \App::$poi['abook_id']; $contact_id = App::$poi['abook_id'];
$contact = \App::$poi; $contact = App::$poi;
$cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name", $cn = q("SELECT abook_id, xchan_name from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d and abook_self = 0 and xchan_deleted = 0 order by xchan_name",
intval(local_channel()) intval(local_channel())
@ -626,7 +629,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard'); $vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard');
$vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); $vctmp = (($vc) ? Reader::read($vc) : null);
$vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] ); $vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] );
if(! $vcard) if(! $vcard)
$vcard['fn'] = $contact['xchan_name']; $vcard['fn'] = $contact['xchan_name'];
@ -700,9 +703,9 @@ class Connedit extends \Zotlabs\Web\Controller {
$perms = array(); $perms = array();
$channel = \App::get_channel(); $channel = App::get_channel();
$global_perms = \Zotlabs\Access\Permissions::Perms(); $global_perms = Permissions::Perms();
$existing = get_all_perms(local_channel(),$contact['abook_xchan'],false); $existing = get_all_perms(local_channel(),$contact['abook_xchan'],false);
@ -722,7 +725,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$theirs = get_abconfig(local_channel(),$contact['abook_xchan'],'system','their_perms',EMPTY_STR); $theirs = get_abconfig(local_channel(),$contact['abook_xchan'],'system','their_perms',EMPTY_STR);
$their_perms = \Zotlabs\Access\Permissions::FilledPerms(explode(',',$theirs)); $their_perms = Permissions::FilledPerms(explode(',',$theirs));
foreach($global_perms as $k => $v) { foreach($global_perms as $k => $v) {
if(! array_key_exists($k,$their_perms)) if(! array_key_exists($k,$their_perms))
$their_perms[$k] = 1; $their_perms[$k] = 1;
@ -733,7 +736,7 @@ class Connedit extends \Zotlabs\Web\Controller {
foreach($global_perms as $k => $v) { foreach($global_perms as $k => $v) {
$thisperm = ((in_array($k,$my_perms)) ? 1 : 0); $thisperm = ((in_array($k,$my_perms)) ? 1 : 0);
$checkinherited = \Zotlabs\Access\PermissionLimits::Get(local_channel(),$k); $checkinherited = PermissionLimits::Get(local_channel(),$k);
// For auto permissions (when $self is true) we don't want to look at existing // For auto permissions (when $self is true) we don't want to look at existing
// permissions because they are enabled for the channel owner // permissions because they are enabled for the channel owner
@ -743,7 +746,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited); $perms[] = array('perms_' . $k, $v, ((array_key_exists($k,$their_perms)) ? intval($their_perms[$k]) : ''),$thisperm, 1, (($checkinherited & PERMS_SPECIFIC) ? '' : '1'), '', $checkinherited);
} }
$pcat = new \Zotlabs\Lib\Permcat(local_channel()); $pcat = new Permcat(local_channel());
$pcatlist = $pcat->listing(); $pcatlist = $pcat->listing();
$permcats = []; $permcats = [];
if($pcatlist) { if($pcatlist) {

View file

@ -0,0 +1,52 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
class Dircensor extends Controller {
function get() {
if(! is_site_admin()) {
return;
}
$dirmode = intval(get_config('system','directory_mode'));
if (! ($dirmode == DIRECTORY_MODE_PRIMARY || $dirmode == DIRECTORY_MODE_STANDALONE)) {
return;
}
$xchan = argv(1);
if(! $xchan) {
return;
}
$r = q("select * from xchan where xchan_hash = '%s'",
dbesc($xchan)
);
if(! $r) {
return;
}
$val = (($r[0]['xchan_censored']) ? 0 : 1);
q("update xchan set xchan_censored = $val where xchan_hash = '%s'",
dbesc($xchan)
);
if( $val) {
info( t('Entry censored') . EOL);
}
else {
info( t('Entry censored') . EOL);
}
return;
}
}

View file

@ -9,6 +9,7 @@ use Zotlabs\Lib\Libsync;
require_once('include/socgraph.php'); require_once('include/socgraph.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
require_once('include/html2plain.php');
define( 'DIRECTORY_PAGESIZE', 60); define( 'DIRECTORY_PAGESIZE', 60);
@ -154,13 +155,19 @@ class Directory extends Controller {
$dirmode = intval(get_config('system','directory_mode')); $dirmode = intval(get_config('system','directory_mode'));
$directory_admin = false;
if (($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { if (($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
$url = z_root() . '/dirsearch'; $url = z_root() . '/dirsearch';
if (is_site_admin()) {
$directory_admin = true;
}
} }
if (! $url) { if (! $url) {
$directory = Libzotdir::find_upstream_directory($dirmode); $directory = Libzotdir::find_upstream_directory($dirmode);
if((! $directory) || (! array_key_exists('url',$directory)) || (! $directory['url'])) if ((! $directory) || (! array_key_exists('url',$directory)) || (! $directory['url'])) {
logger('CRITICAL: No directory server URL'); logger('CRITICAL: No directory server URL');
}
$url = $directory['url'] . '/dirsearch'; $url = $directory['url'] . '/dirsearch';
} }
@ -219,7 +226,7 @@ class Directory extends Controller {
$query .= '&order=' . urlencode($sort_order); $query .= '&order=' . urlencode($sort_order);
if(App::$pager['page'] != 1) if(App::$pager['page'] != 1)
$query .= '&p=' . \App::$pager['page']; $query .= '&p=' . App::$pager['page'];
logger('mod_directory: query: ' . $query); logger('mod_directory: query: ' . $query);
@ -298,11 +305,14 @@ class Directory extends Controller {
$marital = ((x($profile,'marital') == 1) ? t('Status: ') . $profile['marital']: False); $marital = ((x($profile,'marital') == 1) ? t('Status: ') . $profile['marital']: False);
$homepage = ((x($profile,'homepage') == 1) ? t('Homepage: ') : False); $homepage = ((x($profile,'homepage') == 1) ? t('Homepage: ') : False);
$homepageurl = ((x($profile,'homepage') == 1) ? $profile['homepage'] : ''); $homepageurl = ((x($profile,'homepage') == 1) ? html2plain($profile['homepage']) : '');
$hometown = ((x($profile,'hometown') == 1) ? $profile['hometown'] : False); $hometown = ((x($profile,'hometown') == 1) ? html2plain($profile['hometown']) : False);
$about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'])) : False); $about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'])) : False);
if ($about && $safe_mode) {
$about = html2plain($about);
}
$keywords = ((x($profile,'keywords')) ? $profile['keywords'] : ''); $keywords = ((x($profile,'keywords')) ? $profile['keywords'] : '');
@ -358,9 +368,11 @@ class Directory extends Controller {
'canrate' => (($rating_enabled && local_channel()) ? true : false), 'canrate' => (($rating_enabled && local_channel()) ? true : false),
'pdesc' => $pdesc, 'pdesc' => $pdesc,
'pdesc_label' => t('Description:'), 'pdesc_label' => t('Description:'),
'censor' => (($directory_admin) ? 'dircensor/' . $rr['hash'] : ''),
'censor_label' => (($rr['censored']) ? t('Uncensor') : t('Censor')),
'marital' => $marital, 'marital' => $marital,
'homepage' => $homepage, 'homepage' => $homepage,
'homepageurl' => linkify($homepageurl), 'homepageurl' => (($safe_mode) ? $homepageurl : linkify($homepageurl)),
'hometown' => $hometown, 'hometown' => $hometown,
'hometown_label' => t('Hometown:'), 'hometown_label' => t('Hometown:'),
'about' => $about, 'about' => $about,
@ -403,7 +415,7 @@ class Directory extends Controller {
ksort($entries); // Sort array by key so that foreach-constructs work as expected ksort($entries); // Sort array by key so that foreach-constructs work as expected
if($j['keywords']) { if($j['keywords']) {
\App::$data['directory_keywords'] = $j['keywords']; App::$data['directory_keywords'] = $j['keywords'];
} }
// logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA); // logger('mod_directory: entries: ' . print_r($entries,true), LOGGER_DATA);

View file

@ -1,14 +1,21 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
// This is the primary endpoint for communicating with Zot directory services.
// Additionally the 'sitelist' endpoint may be used to query directory knowledge
// of discovered sites. That endpoint should be merged so that there is one
// definitive endpoint for directory services and since this endpoint already
// performs some site query functions.
class Dirsearch extends Controller {
class Dirsearch extends \Zotlabs\Web\Controller {
function init() { function init() {
\App::set_pager_itemspage(60); App::set_pager_itemspage(60);
} }
function get() { function get() {
@ -25,6 +32,7 @@ class Dirsearch extends \Zotlabs\Web\Controller {
json_return_and_die($ret); json_return_and_die($ret);
} }
$access_token = $_REQUEST['t']; $access_token = $_REQUEST['t'];
$token = get_config('system','realm_token'); $token = get_config('system','realm_token');
@ -41,8 +49,10 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$sql_extra = ''; $sql_extra = '';
$tables = [ 'name', 'address', 'locale', 'region', 'postcode',
'country', 'gender', 'marital', 'sexual', 'keywords' ];
$tables = array('name','address','locale','region','postcode','country','gender','marital','sexual','keywords'); // parse advanced query if present
if ($_REQUEST['query']) { if ($_REQUEST['query']) {
$advanced = $this->dir_parse_query($_REQUEST['query']); $advanced = $this->dir_parse_query($_REQUEST['query']);
@ -61,7 +71,6 @@ class Dirsearch extends \Zotlabs\Web\Controller {
} }
$hash = ((x($_REQUEST['hash'])) ? $_REQUEST['hash'] : ''); $hash = ((x($_REQUEST['hash'])) ? $_REQUEST['hash'] : '');
$name = ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''); $name = ((x($_REQUEST,'name')) ? $_REQUEST['name'] : '');
$hub = ((x($_REQUEST,'hub')) ? $_REQUEST['hub'] : ''); $hub = ((x($_REQUEST,'hub')) ? $_REQUEST['hub'] : '');
$address = ((x($_REQUEST,'address')) ? $_REQUEST['address'] : ''); $address = ((x($_REQUEST,'address')) ? $_REQUEST['address'] : '');
@ -78,14 +87,18 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$kw = ((x($_REQUEST,'kw')) ? intval($_REQUEST['kw']) : 0 ); $kw = ((x($_REQUEST,'kw')) ? intval($_REQUEST['kw']) : 0 );
$type = ((array_key_exists('type',$_REQUEST)) ? intval($_REQUEST['type']) : 0); $type = ((array_key_exists('type',$_REQUEST)) ? intval($_REQUEST['type']) : 0);
// allow a site to disable the directory's keyword list
if (get_config('system','disable_directory_keywords')) if (get_config('system','disable_directory_keywords'))
$kw = 0; $kw = 0;
// by default use a safe search // by default use a safe search
$safe = ((x($_REQUEST,'safe'))); // ? intval($_REQUEST['safe']) : 1 ); $safe = ((x($_REQUEST,'safe')));
if ($safe === false) if ($safe === false) {
$safe = 1; $safe = 1;
}
// Directory mirrors will request sync packets, which are lists
// of records that have changed since the sync datetime.
if (array_key_exists('sync',$_REQUEST)) { if (array_key_exists('sync',$_REQUEST)) {
if ($_REQUEST['sync']) if ($_REQUEST['sync'])
@ -97,41 +110,58 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$sync = false; $sync = false;
if (($dirmode == DIRECTORY_MODE_STANDALONE) && (! $hub)) { if (($dirmode == DIRECTORY_MODE_STANDALONE) && (! $hub)) {
$hub = \App::get_hostname(); $hub = App::get_hostname();
} }
if($hub) if ($hub) {
$hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') "; $hub_query = " and xchan_hash in (select hubloc_hash from hubloc where hubloc_host = '" . protect_sprintf(dbesc($hub)) . "') ";
else }
else {
$hub_query = ''; $hub_query = '';
}
// The order identifier is validated further below
$sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : ''); $sort_order = ((x($_REQUEST,'order')) ? $_REQUEST['order'] : '');
// parse and assemble the query for advanced searches
$joiner = ' OR '; $joiner = ' OR ';
if($_REQUEST['and']) if($_REQUEST['and'])
$joiner = ' AND '; $joiner = ' AND ';
if($name) if ($name) {
$sql_extra .= $this->dir_query_build($joiner,'xchan_name',$name); $sql_extra .= $this->dir_query_build($joiner,'xchan_name',$name);
if($address) }
if ($address) {
$sql_extra .= $this->dir_query_build($joiner,'xchan_addr',$address); $sql_extra .= $this->dir_query_build($joiner,'xchan_addr',$address);
if($locale) }
if ($locale) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_locale',$locale); $sql_extra .= $this->dir_query_build($joiner,'xprof_locale',$locale);
if($region) }
if ($region) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_region',$region); $sql_extra .= $this->dir_query_build($joiner,'xprof_region',$region);
if($postcode) }
if ($postcode) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_postcode',$postcode); $sql_extra .= $this->dir_query_build($joiner,'xprof_postcode',$postcode);
if($country) }
if ($country) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_country',$country); $sql_extra .= $this->dir_query_build($joiner,'xprof_country',$country);
if($gender) }
if ($gender) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_gender',$gender); $sql_extra .= $this->dir_query_build($joiner,'xprof_gender',$gender);
if($marital) }
if ($marital) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_marital',$marital); $sql_extra .= $this->dir_query_build($joiner,'xprof_marital',$marital);
if($sexual) }
if ($sexual) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_sexual',$sexual); $sql_extra .= $this->dir_query_build($joiner,'xprof_sexual',$sexual);
if($keywords) }
if ($keywords) {
$sql_extra .= $this->dir_query_build($joiner,'xprof_keywords',$keywords); $sql_extra .= $this->dir_query_build($joiner,'xprof_keywords',$keywords);
}
// we only support an age range currently. You must set both agege // we only support an age range currently. You must set both agege
// (greater than or equal) and agele (less than or equal) // (greater than or equal) and agele (less than or equal)
@ -157,7 +187,6 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$mtime = ((x($_REQUEST,'mtime')) ? datetime_convert('UTC','UTC',$_REQUEST['mtime']) : ''); $mtime = ((x($_REQUEST,'mtime')) ? datetime_convert('UTC','UTC',$_REQUEST['mtime']) : '');
// ok a separate tag table won't work.
// merge them into xprof // merge them into xprof
$ret['success'] = true; $ret['success'] = true;
@ -168,22 +197,26 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$logic = ((strlen($sql_extra)) ? 'false' : 'true'); $logic = ((strlen($sql_extra)) ? 'false' : 'true');
if($hash) if ($hash) {
$logic = 'true'; $logic = 'true';
}
if ($dirmode == DIRECTORY_MODE_STANDALONE) { if ($dirmode == DIRECTORY_MODE_STANDALONE) {
$sql_extra .= " and xchan_addr like '%%" . \App::get_hostname() . "' "; $sql_extra .= " and xchan_addr like '%%" . App::get_hostname() . "' ";
} }
$safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : ''); $safesql = (($safe > 0) ? " and xchan_censored = 0 and xchan_selfcensored = 0 " : '');
if($safe < 0) if ($safe < 0) {
$safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) "; $safesql = " and ( xchan_censored = 1 OR xchan_selfcensored = 1 ) ";
}
if($type) if ($type) {
$safesql .= " and xchan_type = " . intval($type); $safesql .= " and xchan_type = " . intval($type);
}
if($limit) if ($limit) {
$qlimit = " LIMIT $limit "; $qlimit = " LIMIT $limit ";
}
else { else {
$qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec); $qlimit = " LIMIT " . intval($perpage) . " OFFSET " . intval($startrec);
if ($return_total) { if ($return_total) {
@ -212,34 +245,42 @@ class Dirsearch extends \Zotlabs\Web\Controller {
if ($sync) { if ($sync) {
$spkt = array('transactions' => array());
// generate sync packet for directory mirrors
$spkt = array('transactions' => [] );
$r = q("select * from updates where ud_date >= '%s' and ud_guid != '' order by ud_date desc", $r = q("select * from updates where ud_date >= '%s' and ud_guid != '' order by ud_date desc",
dbesc($sync) dbesc($sync)
); );
if ($r) { if ($r) {
foreach ($r as $rr) { foreach ($r as $rr) {
$flags = array(); $flags = [];
if ($rr['ud_flags'] & UPDATE_FLAGS_DELETED) if ($rr['ud_flags'] & UPDATE_FLAGS_DELETED)
$flags[] = 'deleted'; $flags[] = 'deleted';
if ($rr['ud_flags'] & UPDATE_FLAGS_FORCED) if ($rr['ud_flags'] & UPDATE_FLAGS_FORCED)
$flags[] = 'forced'; $flags[] = 'forced';
if ($rr['ud_flags'] & UPDATE_FLAGS_CENSORED)
$flags[] = 'censored';
$spkt['transactions'][] = array( $spkt['transactions'][] = [
'hash' => $rr['ud_hash'], 'hash' => $rr['ud_hash'],
'address' => $rr['ud_addr'], 'address' => $rr['ud_addr'],
'transaction_id' => $rr['ud_guid'], 'transaction_id' => $rr['ud_guid'],
'timestamp' => $rr['ud_date'], 'timestamp' => $rr['ud_date'],
'flags' => $flags 'flags' => $flags
); ];
} }
} }
// sync ratings - not currently used
$r = q("select * from xlink where xlink_static = 1 and xlink_updated >= '%s' ", $r = q("select * from xlink where xlink_static = 1 and xlink_updated >= '%s' ",
dbesc($sync) dbesc($sync)
); );
if ($r) { if ($r) {
$spkt['ratings'] = array(); $spkt['ratings'] = [];
foreach ($r as $rr) { foreach ($r as $rr) {
$spkt['ratings'][] = array( $spkt['ratings'][] = [
'type' => 'rating', 'type' => 'rating',
'encoding' => 'zot', 'encoding' => 'zot',
'channel' => $rr['xlink_xchan'], 'channel' => $rr['xlink_xchan'],
@ -248,12 +289,14 @@ class Dirsearch extends \Zotlabs\Web\Controller {
'rating_text' => $rr['xlink_rating_text'], 'rating_text' => $rr['xlink_rating_text'],
'signature' => $rr['xlink_sig'], 'signature' => $rr['xlink_sig'],
'edited' => $rr['xlink_updated'] 'edited' => $rr['xlink_updated']
); ];
} }
} }
json_return_and_die($spkt); json_return_and_die($spkt);
} }
else {
// normal directory query
$r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash $r = q("SELECT xchan.*, xprof.* from xchan left join xprof on xchan_hash = xprof_hash
where ( $logic $sql_extra ) $hub_query and xchan_network = 'zot6' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0 where ( $logic $sql_extra ) $hub_query and xchan_network = 'zot6' and xchan_system = 0 and xchan_hidden = 0 and xchan_orphan = 0 and xchan_deleted = 0
@ -262,17 +305,14 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$ret['page'] = $page + 1; $ret['page'] = $page + 1;
$ret['records'] = count($r); $ret['records'] = count($r);
}
if ($r) { if ($r) {
$entries = array(); $entries = [];
foreach ($r as $rr) { foreach ($r as $rr) {
$entry = array(); $entry = [];
$pc = q("select count(xlink_rating) as total_ratings from xlink where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1 group by xlink_rating", $pc = q("select count(xlink_rating) as total_ratings from xlink where xlink_link = '%s' and xlink_rating != 0 and xlink_static = 1 group by xlink_rating",
dbesc($rr['xchan_hash']) dbesc($rr['xchan_hash'])
@ -285,7 +325,8 @@ class Dirsearch extends \Zotlabs\Web\Controller {
$entry['name'] = $rr['xchan_name']; $entry['name'] = $rr['xchan_name'];
$entry['hash'] = $rr['xchan_hash']; $entry['hash'] = $rr['xchan_hash'];
$entry['censored'] = $rr['xchan_censored'];
$entry['selfcensored'] = $rr['xchan_selfcensored'];
$entry['type'] = $rr['xchan_type']; $entry['type'] = $rr['xchan_type'];
$entry['url'] = $rr['xchan_url']; $entry['url'] = $rr['xchan_url'];
$entry['photo_l'] = $rr['xchan_photo_l']; $entry['photo_l'] = $rr['xchan_photo_l'];
@ -316,12 +357,11 @@ class Dirsearch extends \Zotlabs\Web\Controller {
if ($k) { if ($k) {
$ret['keywords'] = array(); $ret['keywords'] = array();
foreach ($k as $kv) { foreach ($k as $kv) {
$ret['keywords'][] = array('term' => $kv[0],'weight' => $kv[1], 'normalise' => $kv[2]); $ret['keywords'][] = [ 'term' => $kv[0], 'weight' => $kv[1], 'normalise' => $kv[2] ];
} }
} }
} }
} }
json_return_and_die($ret); json_return_and_die($ret);
} }
@ -399,11 +439,6 @@ class Dirsearch extends \Zotlabs\Web\Controller {
} }
function list_public_sites() { function list_public_sites() {
$rand = db_getfunc('rand'); $rand = db_getfunc('rand');

View file

@ -8,10 +8,30 @@ use Zotlabs\Web\Controller;
class Expire extends Controller { class Expire extends Controller {
function post() {
logger('expire: ' . print_r($_POST,true));
if(! ( local_channel() && Apps::system_app_installed(local_channel(),'Expire Posts'))) {
return;
}
if($_POST['expire-submit']) {
$expire = intval($_POST['selfexpiredays']);
if($expire < 0)
$expire = 0;
set_pconfig(local_channel(),'system','selfexpiredays',$expire);
info( t('Expiration settings updated.') . EOL);
}
Libsync::build_sync_packet();
}
function get() { function get() {
$desc = t('This app allows you to set an optional expiration date/time for posts, after which they will be deleted. This must be at least fifteen minutes into the future.'); $desc = t('This app allows you to set an optional expiration date/time for posts, after which they will be deleted. This must be at least fifteen minutes into the future. You may also choose to automatically delete all your posts after a set number of days');
$text = '<div class="section-content-info-wrapper">' . $desc . '</div>'; $text = '<div class="section-content-info-wrapper">' . $desc . '</div>';
@ -19,6 +39,27 @@ class Expire extends Controller {
return $text; return $text;
} }
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('selfexpiredays', t('Expire and delete all my posts after this many days'), intval(get_pconfig(local_channel(),'system','selfexpiredays',0)), t('Leave at 0 if you wish to manually control expiration of specific posts.'))
));
$s .= replace_macros(get_markup_template('generic_app_settings.tpl'), array(
'$addon' => array('expire', t('Automatic Expiration Settings'), '', t('Submit')),
'$content' => $setting_fields
));
return $s;
} }

View file

@ -1,6 +1,9 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\System;
require_once('include/help.php'); require_once('include/help.php');
/** /**
@ -12,7 +15,7 @@ require_once('include/help.php');
* *
* The syntax is somewhat strict. * The syntax is somewhat strict.
*/ */
class Help extends \Zotlabs\Web\Controller { class Help extends Controller {
function get() { function get() {
nav_set_selected('Help'); nav_set_selected('Help');
@ -35,7 +38,7 @@ class Help extends \Zotlabs\Web\Controller {
$o .= '<li><a href="help/' . (($path) ? $path . '/' : '') . $fname . '" >' . ucwords(str_replace('_',' ',notags($fname))) . '</a><br>' $o .= '<li><a href="help/' . (($path) ? $path . '/' : '') . $fname . '" >' . ucwords(str_replace('_',' ',notags($fname))) . '</a><br>'
. '<b><i>' . 'help/' . (($path) ? $path . '/' : '') . $fname . '</i></b><br>' . '<b><i>' . 'help/' . (($path) ? $path . '/' : '') . $fname . '</i></b><br>'
. '...' . str_replace('$Projectname', \Zotlabs\Lib\System::get_platform_name(), $rr['text']) . '...<br><br></li>'; . '...' . str_replace('$Projectname', System::get_platform_name(), $rr['text']) . '...<br><br></li>';
} }
$o .= '</ul>'; $o .= '</ul>';
$o .= '</div>'; $o .= '</div>';

View file

@ -2,10 +2,14 @@
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use URLify;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\Libzot; use Zotlabs\Lib\Libzot;
use Zotlabs\Daemon\Master;
require_once('include/channel.php');
require_once('include/import.php'); require_once('include/import.php');
require_once('include/photo_factory.php');
/** /**
@ -14,7 +18,8 @@ require_once('include/import.php');
* Import a channel, either by direct file upload or via * Import a channel, either by direct file upload or via
* connection to another server. * connection to another server.
*/ */
class Import extends \Zotlabs\Web\Controller {
class Import extends Controller {
/** /**
* @brief Import channel into account. * @brief Import channel into account.
@ -64,7 +69,8 @@ class Import extends \Zotlabs\Web\Controller {
logger('Nothing to import.'); logger('Nothing to import.');
notice( t('Nothing to import.') . EOL); notice( t('Nothing to import.') . EOL);
return; return;
} else if(strpos($old_address, '')) { }
elseif (strpos($old_address, '')) {
// if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit. // if you copy the identity address from your profile page, make it work for convenience - WARNING: this is a utf-8 variant and NOT an ASCII ampersand. Please do not edit.
$old_address = str_replace('', '@', $old_address); $old_address = str_replace('', '@', $old_address);
} }
@ -82,12 +88,12 @@ class Import extends \Zotlabs\Web\Controller {
} }
$api_path .= 'channel/export/basic?f=&channel=' . $channelname; $api_path .= 'channel/export/basic?f=&channel=' . $channelname;
if($import_posts) if ($import_posts) {
$api_path .= '&posts=1'; $api_path .= '&posts=1';
}
$binary = false; $binary = false;
$redirects = 0; $redirects = 0;
$opts = array('http_auth' => $email . ':' . $password); $opts = [ 'http_auth' => $email . ':' . $password ];
$ret = z_fetch_url($api_path, $binary, $redirects, $opts); $ret = z_fetch_url($api_path, $binary, $redirects, $opts);
if ($ret['success']) { if ($ret['success']) {
$data = $ret['body']; $data = $ret['body'];
@ -111,9 +117,10 @@ class Import extends \Zotlabs\Web\Controller {
if (! array_key_exists('compatibility',$data)) { if (! array_key_exists('compatibility',$data)) {
call_hooks('import_foreign_channel_data',$data); call_hooks('import_foreign_channel_data',$data);
if($data['handled']) if ($data['handled']) {
return; return;
} }
}
$codebase = ((defined('NOMADIC')) ? 'zap' : 'osada'); $codebase = ((defined('NOMADIC')) ? 'zap' : 'osada');
@ -122,8 +129,9 @@ class Import extends \Zotlabs\Web\Controller {
return; return;
} }
if($moving) if ($moving) {
$seize = 1; $seize = 1;
}
// import channel // import channel
@ -151,7 +159,7 @@ class Import extends \Zotlabs\Web\Controller {
} }
if ((! $x) || strlen($x) > 64) { if ((! $x) || strlen($x) > 64) {
$x = strtolower(\URLify::transliterate($newname)); $x = strtolower(URLify::transliterate($newname));
} }
$newname = $x; $newname = $x;
} }
@ -160,11 +168,11 @@ class Import extends \Zotlabs\Web\Controller {
} }
else { else {
$moving = false; $moving = false;
$channel = \App::get_channel(); $channel = App::get_channel();
} }
if (! $channel) { if (! $channel) {
logger('Channel not found. ', print_r($channel,true)); logger('Channel not found. ' . print_r($channel,true));
notice( t('No channel. Import failed.') . EOL); notice( t('No channel. Import failed.') . EOL);
return; return;
} }
@ -177,58 +185,18 @@ class Import extends \Zotlabs\Web\Controller {
if (array_key_exists('channel',$data)) { if (array_key_exists('channel',$data)) {
if ($data['photo']) { if ($data['photo']) {
require_once('include/photo_factory.php');
import_channel_photo(base64url_decode($data['photo']['data']),$data['photo']['type'],$account_id,$channel['channel_id']); import_channel_photo(base64url_decode($data['photo']['data']),$data['photo']['type'],$account_id,$channel['channel_id']);
} }
if(is_array($data['profile'])) if (is_array($data['profile'])) {
import_profiles($channel,$data['profile']); import_profiles($channel,$data['profile']);
} }
}
logger('import step 3'); logger('import step 3');
if(is_array($data['hubloc'])) {
import_hublocs($channel,$data['hubloc'],$seize,$moving);
}
logger('import step 4');
// create new hubloc for the new channel at this site
if(array_key_exists('channel',$data)) {
$r = hubloc_store_lowlevel(
[
'hubloc_guid' => $channel['channel_guid'],
'hubloc_guid_sig' => $channel['channel_guid_sig'],
'hubloc_id_url' => channel_url($channel),
'hubloc_hash' => $channel['channel_hash'],
'hubloc_addr' => channel_reddress($channel),
'hubloc_network' => 'zot6',
'hubloc_primary' => (($seize) ? 1 : 0),
'hubloc_url' => z_root(),
'hubloc_url_sig' => Libzot::sign(z_root(),$channel['channel_prvkey']),
'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')),
'hubloc_host' => \App::get_hostname(),
'hubloc_callback' => z_root() . '/zot',
'hubloc_sitekey' => get_config('system','pubkey'),
'hubloc_updated' => datetime_convert()
]
);
// reset the original primary hubloc if it is being seized
if($seize) {
$r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
);
}
}
logger('import step 5');
// import xchans and contact photos // import xchans and contact photos
// This *must* be done before importing hublocs
if (array_key_exists('channel',$data) && $seize) { if (array_key_exists('channel',$data) && $seize) {
@ -259,7 +227,7 @@ class Import extends \Zotlabs\Web\Controller {
); );
} }
logger('import step 6'); logger('import step 4');
// import xchans // import xchans
$xchans = $data['xchan']; $xchans = $data['xchan'];
@ -276,12 +244,11 @@ class Import extends \Zotlabs\Web\Controller {
$r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", $r = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1",
dbesc($xchan['xchan_hash']) dbesc($xchan['xchan_hash'])
); );
if($r) if ($r) {
continue; continue;
}
xchan_store_lowlevel($xchan); xchan_store_lowlevel($xchan);
require_once('include/photo_factory.php');
if ($xchan['xchan_hash'] === $channel['channel_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'", $r = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s' where xchan_hash = '%s'",
@ -293,10 +260,12 @@ class Import extends \Zotlabs\Web\Controller {
} }
else { else {
$photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']); $photos = import_xchan_photo($xchan['xchan_photo_l'],$xchan['xchan_hash']);
if($photos[4]) if ($photos[4]) {
$photodate = NULL_DATE; $photodate = NULL_DATE;
else }
else {
$photodate = $xchan['xchan_photo_date']; $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'", $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[0]),
@ -309,9 +278,52 @@ class Import extends \Zotlabs\Web\Controller {
} }
} }
logger('import step 7'); logger('import step 5');
} }
logger('import step 6');
if (is_array($data['hubloc'])) {
import_hublocs($channel,$data['hubloc'],$seize,$moving);
}
logger('import step 7');
// create new hubloc for the new channel at this site
if (array_key_exists('channel',$data)) {
$r = hubloc_store_lowlevel(
[
'hubloc_guid' => $channel['channel_guid'],
'hubloc_guid_sig' => $channel['channel_guid_sig'],
'hubloc_id_url' => channel_url($channel),
'hubloc_hash' => $channel['channel_hash'],
'hubloc_addr' => channel_reddress($channel),
'hubloc_network' => 'zot6',
'hubloc_primary' => (($seize) ? 1 : 0),
'hubloc_url' => z_root(),
'hubloc_url_sig' => Libzot::sign(z_root(),$channel['channel_prvkey']),
'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')),
'hubloc_host' => App::get_hostname(),
'hubloc_callback' => z_root() . '/zot',
'hubloc_sitekey' => get_config('system','pubkey'),
'hubloc_updated' => datetime_convert()
]
);
// reset the original primary hubloc if it is being seized
if ($seize) {
$r = q("update hubloc set hubloc_primary = 0 where hubloc_primary = 1 and hubloc_hash = '%s' and hubloc_url != '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
);
}
}
$friends = 0; $friends = 0;
$feeds = 0; $feeds = 0;
@ -323,8 +335,9 @@ class Import extends \Zotlabs\Web\Controller {
$abook_copy = $abook; $abook_copy = $abook;
$abconfig = null; $abconfig = null;
if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) if (array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) {
$abconfig = $abook['abconfig']; $abconfig = $abook['abconfig'];
}
unset($abook['abook_id']); unset($abook['abook_id']);
unset($abook['abook_rating']); unset($abook['abook_rating']);
@ -336,16 +349,6 @@ class Import extends \Zotlabs\Web\Controller {
$abook['abook_account'] = $account_id; $abook['abook_account'] = $account_id;
$abook['abook_channel'] = $channel['channel_id']; $abook['abook_channel'] = $channel['channel_id'];
if(! array_key_exists('abook_blocked',$abook)) {
$abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001 ) ? 1 : 0);
$abook['abook_ignored'] = (($abook['abook_flags'] & 0x0002 ) ? 1 : 0);
$abook['abook_hidden'] = (($abook['abook_flags'] & 0x0004 ) ? 1 : 0);
$abook['abook_archived'] = (($abook['abook_flags'] & 0x0008 ) ? 1 : 0);
$abook['abook_pending'] = (($abook['abook_flags'] & 0x0010 ) ? 1 : 0);
$abook['abook_unconnected'] = (($abook['abook_flags'] & 0x0020 ) ? 1 : 0);
$abook['abook_self'] = (($abook['abook_flags'] & 0x0080 ) ? 1 : 0);
$abook['abook_feed'] = (($abook['abook_flags'] & 0x0100 ) ? 1 : 0);
}
if(array_key_exists('abook_instance',$abook) && $abook['abook_instance'] && strpos($abook['abook_instance'],z_root()) === false) { if(array_key_exists('abook_instance',$abook) && $abook['abook_instance'] && strpos($abook['abook_instance'],z_root()) === false) {
$abook['abook_not_here'] = 1; $abook['abook_not_here'] = 1;
@ -377,7 +380,12 @@ class Import extends \Zotlabs\Web\Controller {
intval($channel['channel_id']) intval($channel['channel_id'])
); );
if($r) { if($r) {
$columns = db_columns('abook');
foreach ($abook as $k => $v) { foreach ($abook as $k => $v) {
if (! in_array($k,$columns)) {
continue;
}
$r = q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d", $r = q("UPDATE abook SET " . TQUOT . "%s" . TQUOT . " = '%s' WHERE abook_xchan = '%s' AND abook_channel = %d",
dbesc($k), dbesc($k),
dbesc($v), dbesc($v),
@ -390,12 +398,12 @@ class Import extends \Zotlabs\Web\Controller {
abook_store_lowlevel($abook); abook_store_lowlevel($abook);
$friends ++; $friends ++;
if(intval($abook['abook_feed'])) if (intval($abook['abook_feed'])) {
$feeds ++; $feeds ++;
} }
}
if ($abconfig) { if ($abconfig) {
/// @FIXME does not handle sync of del_abconfig
foreach ($abconfig as $abc) { foreach ($abconfig as $abc) {
set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); set_abconfig($channel['channel_id'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']);
} }
@ -410,7 +418,7 @@ class Import extends \Zotlabs\Web\Controller {
if ($groups) { if ($groups) {
$saved = array(); $saved = array();
foreach ($groups as $group) { foreach ($groups as $group) {
$saved[$group['hash']] = array('old' => $group['id']); $saved[$group['hash']] = [ 'old' => $group['id'] ];
if (array_key_exists('name', $group)) { if (array_key_exists('name', $group)) {
$group['gname'] = $group['name']; $group['gname'] = $group['name'];
unset($group['name']); unset($group['name']);
@ -446,67 +454,63 @@ class Import extends \Zotlabs\Web\Controller {
logger('import step 9'); logger('import step 9');
if(is_array($data['xign'])) if (is_array($data['xign'])) {
import_xign($channel,$data['xign']); import_xign($channel,$data['xign']);
}
if(is_array($data['obj'])) if (is_array($data['obj'])) {
import_objs($channel,$data['obj']); import_objs($channel,$data['obj']);
}
if(is_array($data['likes'])) if (is_array($data['likes'])) {
import_likes($channel,$data['likes']); import_likes($channel,$data['likes']);
}
if(is_array($data['app'])) if (is_array($data['app'])) {
import_apps($channel,$data['app']); import_apps($channel,$data['app']);
}
if(is_array($data['sysapp'])) if (is_array($data['sysapp'])) {
import_sysapps($channel,$data['sysapp']); import_sysapps($channel,$data['sysapp']);
}
if(is_array($data['chatroom'])) if (is_array($data['chatroom'])) {
import_chatrooms($channel,$data['chatroom']); import_chatrooms($channel,$data['chatroom']);
}
if(is_array($data['conv'])) if (is_array($data['conv'])) {
import_conv($channel,$data['conv']); import_conv($channel,$data['conv']);
}
if(is_array($data['mail'])) if (is_array($data['mail'])) {
import_mail($channel,$data['mail']); import_mail($channel,$data['mail']);
}
if(is_array($data['event'])) if (is_array($data['event'])) {
import_events($channel,$data['event']); import_events($channel,$data['event']);
}
if(is_array($data['event_item'])) if (is_array($data['event_item'])) {
import_items($channel,$data['event_item'],false,$relocate); import_items($channel,$data['event_item'],false,$relocate);
}
if(is_array($data['menu'])) if (is_array($data['menu'])) {
import_menus($channel,$data['menu']); import_menus($channel,$data['menu']);
}
if(is_array($data['wiki'])) if (is_array($data['wiki'])) {
import_items($channel,$data['wiki'],false,$relocate); import_items($channel,$data['wiki'],false,$relocate);
}
if(is_array($data['webpages'])) if (is_array($data['webpages'])) {
import_items($channel,$data['webpages'],false,$relocate); import_items($channel,$data['webpages'],false,$relocate);
}
$addon = array('channel' => $channel,'data' => $data); $addon = array('channel' => $channel,'data' => $data);
call_hooks('import_channel',$addon); call_hooks('import_channel',$addon);
$saved_notification_flags = notifications_off($channel['channel_id']); $saved_notification_flags = notifications_off($channel['channel_id']);
if ($import_posts && array_key_exists('item',$data) && $data['item']) {
if($import_posts && array_key_exists('item',$data) && $data['item'])
import_items($channel,$data['item'],false,$relocate); import_items($channel,$data['item'],false,$relocate);
}
notifications_on($channel['channel_id'],$saved_notification_flags); notifications_on($channel['channel_id'],$saved_notification_flags);
if(array_key_exists('item_id',$data) && $data['item_id'])
import_item_ids($channel,$data['item_id']);
// send out refresh requests // send out refresh requests
// notify old server that it may no longer be primary. // notify old server that it may no longer be primary.
\Zotlabs\Daemon\Master::Summon(array('Notifier','refresh_all',$channel['channel_id'])); Master::Summon( [ 'Notifier','refresh_all',$channel['channel_id'] ] );
// This will indirectly perform a refresh_all *and* update the directory // This will indirectly perform a refresh_all *and* update the directory
\Zotlabs\Daemon\Master::Summon(array('Directory', $channel['channel_id'])); Master::Summon( [ 'Directory', $channel['channel_id'] ] );
notice( t('Import completed.') . EOL); notice( t('Import completed.') . EOL);
@ -518,13 +522,14 @@ class Import extends \Zotlabs\Web\Controller {
/** /**
* @brief Handle POST action on channel import page. * @brief Handle POST action on channel import page.
*/ */
function post() { function post() {
$account_id = get_account_id(); $account_id = get_account_id();
if(! $account_id) if (! $account_id) {
return; return;
}
check_form_security_token_redirectOnErr('/import', 'channel_import'); check_form_security_token_redirectOnErr('/import', 'channel_import');
$this->import_account($account_id); $this->import_account($account_id);
} }
@ -533,19 +538,19 @@ class Import extends \Zotlabs\Web\Controller {
* *
* @return string with parsed HTML. * @return string with parsed HTML.
*/ */
function get() { function get() {
if (! get_account_id()) { if (! get_account_id()) {
notice( t('You must be logged in to use this feature.') . EOL); notice( t('You must be logged in to use this feature.') . EOL);
return ''; return EMPTY_STR;
} }
$o = replace_macros(get_markup_template('channel_import.tpl'),array( return replace_macros(get_markup_template('channel_import.tpl'), [
'$title' => t('Import Channel'), '$title' => t('Import Channel'),
'$desc' => t('Use this form to import an existing channel from a different server/hub. You may retrieve the channel identity from the old server/hub via the network or provide an export file.'), '$desc' => t('Use this form to import an existing channel from a different server. You may retrieve the channel identity from the old server via the network or provide an export file.'),
'$label_filename' => t('File to Upload'), '$label_filename' => t('File to Upload'),
'$choice' => t('Or provide the old server/hub details'), '$choice' => t('Or provide the old server details'),
'$old_address' => [ 'old_address', t('Your old identity address (xyz@example.com)'), '', ''], '$old_address' => [ 'old_address', t('Your old identity address (xyz@example.com)'), '', ''],
'$email' => [ 'email', t('Your old login email address'), '', '' ], '$email' => [ 'email', t('Your old login email address'), '', '' ],
'$password' => [ 'password', t('Your old login password'), '', '' ], '$password' => [ 'password', t('Your old login password'), '', '' ],
@ -561,9 +566,7 @@ class Import extends \Zotlabs\Web\Controller {
'$form_security_token' => get_form_security_token('channel_import'), '$form_security_token' => get_form_security_token('channel_import'),
'$submit' => t('Submit') '$submit' => t('Submit')
)); ]);
return $o;
} }
} }

View file

@ -101,10 +101,6 @@ class Import_items extends \Zotlabs\Web\Controller {
import_items($channel,$data['item'],false,((array_key_exists('relocate',$data)) ? $data['relocate'] : null)); import_items($channel,$data['item'],false,((array_key_exists('relocate',$data)) ? $data['relocate'] : null));
} }
if(array_key_exists('item_id',$data) && $data['item_id']) {
import_item_ids($channel,$data['item_id']);
}
info( t('Import completed') . EOL); info( t('Import completed') . EOL);
} }

View file

@ -189,7 +189,7 @@ class Layouts extends Controller {
$o .= replace_macros(get_markup_template('layoutlist.tpl'), array( $o .= replace_macros(get_markup_template('layoutlist.tpl'), array(
'$title' => t('Layouts'), '$title' => t('Layouts'),
'$create' => t('Create'), '$create' => t('Create'),
'$help' => array('text' => t('Help'), 'url' => 'help/comanche', 'title' => t('Comanche page description language help')), '$help' => '', // array('text' => t('Help'), 'url' => 'help/comanche', 'title' => t('Comanche page description language help')),
'$editor' => $editor, '$editor' => $editor,
'$baseurl' => $url, '$baseurl' => $url,
'$name' => t('Layout Name'), '$name' => t('Layout Name'),

View file

@ -404,14 +404,13 @@ class Like extends Controller {
} }
$arr['mid'] = $mid;
$arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid); $arr['aid'] = (($extended_like) ? $ch[0]['channel_account_id'] : $owner_aid);
$arr['uid'] = $owner_uid; $arr['uid'] = $owner_uid;
$arr['item_flags'] = $item_flags; $arr['item_flags'] = $item_flags;
$arr['item_wall'] = $item_wall; $arr['item_wall'] = $item_wall;
$arr['parent_mid'] = (($extended_like) ? $mid : $item['mid']); $arr['parent_mid'] = (($extended_like) ? $arr['mid'] : $item['mid']);
$arr['owner_xchan'] = (($extended_like) ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']); $arr['owner_xchan'] = (($extended_like) ? $ch[0]['xchan_hash'] : $thread_owner['xchan_hash']);
$arr['author_xchan'] = $observer['xchan_hash']; $arr['author_xchan'] = $observer['xchan_hash'];

View file

@ -1,8 +1,12 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
class Manage extends \Zotlabs\Web\Controller { require_once('include/security.php');
class Manage extends Controller {
function get() { function get() {
@ -11,9 +15,8 @@ class Manage extends \Zotlabs\Web\Controller {
return; return;
} }
nav_set_selected('Channels'); nav_set_selected('Manage');
require_once('include/security.php');
$change_channel = ((argc() > 1) ? intval(argv(1)) : 0); $change_channel = ((argc() > 1) ? intval(argv(1)) : 0);
@ -37,10 +40,9 @@ class Manage extends \Zotlabs\Web\Controller {
$r = change_channel($change_channel); $r = change_channel($change_channel);
if ((argc() > 2) && !(argv(2) === 'default')) { if ((argc() > 2) && !(argv(2) === 'default')) {
goaway(z_root() . '/' . implode('/',array_slice(\App::$argv,2))); // Go to whatever is after /manage/, but with the new channel goaway(z_root() . '/' . implode('/',array_slice(App::$argv,2))); // Go to whatever is after /manage/, but with the new channel
} }
else { elseif ($r && $r['channel_startpage']) {
if($r && $r['channel_startpage'])
goaway(z_root() . '/' . $r['channel_startpage']); // If nothing extra is specified, go to the default page goaway(z_root() . '/' . $r['channel_startpage']); // If nothing extra is specified, go to the default page
} }
goaway(z_root()); goaway(z_root());
@ -52,7 +54,7 @@ class Manage extends \Zotlabs\Web\Controller {
intval(get_account_id()) intval(get_account_id())
); );
$account = \App::get_account(); $account = App::get_account();
if ($r && count($r)) { if ($r && count($r)) {
$channels = $r; $channels = $r;
@ -70,30 +72,23 @@ class Manage extends \Zotlabs\Web\Controller {
if ($c) { if ($c) {
foreach ($c as $it) { foreach ($c as $it) {
if(intval($it['item_wall'])) if (intval($it['item_wall'])) {
$channels[$x]['home'] ++; $channels[$x]['home'] ++;
else }
else {
$channels[$x]['network'] ++; $channels[$x]['network'] ++;
} }
} }
}
$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 ", $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($channels[$x]['channel_id']) intval($channels[$x]['channel_id'])
); );
if($intr) if ($intr) {
$channels[$x]['intros'] = intval($intr[0]['total']); $channels[$x]['intros'] = intval($intr[0]['total']);
}
$mails = q("SELECT count(id) as total from mail WHERE channel_id = %d AND mail_seen = 0 and from_xchan != '%s' ",
intval($channels[$x]['channel_id']),
dbesc($channels[$x]['channel_hash'])
);
if($mails)
$channels[$x]['mail'] = intval($mails[0]['total']);
$events = q("SELECT etype, dtstart, adjust FROM event $events = q("SELECT etype, dtstart, adjust FROM event
WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0 WHERE event.uid = %d AND dtstart < '%s' AND dtstart > '%s' and dismissed = 0
@ -119,15 +114,17 @@ class Manage extends \Zotlabs\Web\Controller {
} }
if (datetime_convert('UTC', ((intval($e['adjust'])) ? date_default_timezone_get() : 'UTC'), $e['dtstart'], 'Y-m-d') === $str_now) { if (datetime_convert('UTC', ((intval($e['adjust'])) ? date_default_timezone_get() : 'UTC'), $e['dtstart'], 'Y-m-d') === $str_now) {
$channels[$x]['all_events_today'] ++; $channels[$x]['all_events_today'] ++;
if($bd) if ($bd) {
$channels[$x]['birthdays_today'] ++; $channels[$x]['birthdays_today'] ++;
else }
else {
$channels[$x]['events_today'] ++; $channels[$x]['events_today'] ++;
} }
} }
} }
} }
} }
}
} }
@ -143,7 +140,7 @@ class Manage extends \Zotlabs\Web\Controller {
} }
$create = array( 'new_channel', t('Create a new channel'), t('Create New')); $create = [ 'new_channel', t('Create a new channel'), t('Create New') ];
$delegates = null; $delegates = null;
@ -171,7 +168,7 @@ class Manage extends \Zotlabs\Web\Controller {
$delegates = null; $delegates = null;
} }
$o = replace_macros(get_markup_template('channels.tpl'), array( return replace_macros(get_markup_template('channels.tpl'), [
'$header' => t('Channels'), '$header' => t('Channels'),
'$msg_selected' => t('Current Channel'), '$msg_selected' => t('Current Channel'),
'$selected' => local_channel(), '$selected' => local_channel(),
@ -185,10 +182,7 @@ class Manage extends \Zotlabs\Web\Controller {
'$channel_usage_message' => $channel_usage_message, '$channel_usage_message' => $channel_usage_message,
'$delegated_desc' => t('Delegated Channel'), '$delegated_desc' => t('Delegated Channel'),
'$delegates' => $delegates '$delegates' => $delegates
)); ]);
return $o;
} }
} }

View file

@ -251,12 +251,11 @@ class Network extends Controller {
)); ));
} }
$o = $title; $o = $title . $status_editor;
$o .= $status_editor;
} }
elseif ($cid_r) { elseif ($cid_r) {
$item_thread_top = ''; $item_thread_top = EMPTY_STR;
if ($load || $update) { if ($load || $update) {
if (!$pf && $nouveau) { if (!$pf && $nouveau) {

View file

@ -178,13 +178,11 @@ class New_channel extends Controller {
$privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "" ); $privacy_role = ((x($_REQUEST,'permissions_role')) ? $_REQUEST['permissions_role'] : "" );
$perm_roles = \Zotlabs\Access\PermissionRoles::roles(); $perm_roles = \Zotlabs\Access\PermissionRoles::roles();
if((get_account_techlevel() < 4) && $privacy_role !== 'custom')
unset($perm_roles[t('Other')]);
$name = array('name', t('Channel name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), $name_help, "*"); $name = array('name', t('Channel name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), $name_help, "*");
$nickhub = '@' . \App::get_hostname(); $nickhub = '@' . \App::get_hostname();
$nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*"); $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), $nick_help, "*");
$role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.') . '<br>' . '<a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles); $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role compatible with your usage needs and privacy requirements.'),$perm_roles);
$o = replace_macros(get_markup_template('new_channel.tpl'), array( $o = replace_macros(get_markup_template('new_channel.tpl'), array(
'$title' => t('Create a Channel'), '$title' => t('Create a Channel'),

View file

@ -1,15 +0,0 @@
<?php
namespace Zotlabs\Module;
class Nojs extends \Zotlabs\Web\Controller {
function init() {
$n = ((argc() > 1) ? intval(argv(1)) : 1);
setcookie('jsdisabled', $n, 0, '/');
$p = hex2bin($_GET['redir']);
$hasq = strpbrk($p,'?&');
goaway(z_root() . (($p) ? '/' . $p : '') . (($hasq) ? '' : '?f=' ) . '&jsdisabled=' . $n);
}
}

View file

@ -2,9 +2,10 @@
namespace Zotlabs\Module; /** @file */ namespace Zotlabs\Module; /** @file */
use Zotlabs\Lib\Libsync; use Zotlabs\Lib\Libsync;
use Zotlabs\Web\Controller;
class Notes extends \Zotlabs\Web\Controller { class Notes extends Controller {
function init() { function init() {
@ -30,7 +31,6 @@ class Notes extends \Zotlabs\Web\Controller {
// push updates to channel clones // push updates to channel clones
if((argc() > 1) && (argv(1) === 'sync')) { if((argc() > 1) && (argv(1) === 'sync')) {
require_once('include/zot.php');
Libsync::build_sync_packet(); Libsync::build_sync_packet();
} }

View file

@ -123,6 +123,7 @@ class Oep extends \Zotlabs\Web\Controller {
$o = "[share author='".urlencode($p[0]['author']['xchan_name']). $o = "[share author='".urlencode($p[0]['author']['xchan_name']).
"' profile='".$p[0]['author']['xchan_url'] . "' profile='".$p[0]['author']['xchan_url'] .
"' portable_id='".$p[0]['author']['xchan_hash'] .
"' avatar='".$p[0]['author']['xchan_photo_s']. "' avatar='".$p[0]['author']['xchan_photo_s'].
"' link='".$p[0]['plink']. "' link='".$p[0]['plink'].
"' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') . "' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') .
@ -208,6 +209,7 @@ class Oep extends \Zotlabs\Web\Controller {
$o = "[share author='".urlencode($p[0]['author']['xchan_name']). $o = "[share author='".urlencode($p[0]['author']['xchan_name']).
"' profile='".$p[0]['author']['xchan_url'] . "' profile='".$p[0]['author']['xchan_url'] .
"' portable_id='".$p[0]['author']['xchan_hash'] .
"' avatar='".$p[0]['author']['xchan_photo_s']. "' avatar='".$p[0]['author']['xchan_photo_s'].
"' link='".$p[0]['plink']. "' link='".$p[0]['plink'].
"' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') . "' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') .
@ -292,6 +294,7 @@ class Oep extends \Zotlabs\Web\Controller {
$o = "[share author='".urlencode($p[0]['author']['xchan_name']). $o = "[share author='".urlencode($p[0]['author']['xchan_name']).
"' profile='".$p[0]['author']['xchan_url'] . "' profile='".$p[0]['author']['xchan_url'] .
"' portable_id='".$p[0]['author']['xchan_hash'] .
"' avatar='".$p[0]['author']['xchan_photo_s']. "' avatar='".$p[0]['author']['xchan_photo_s'].
"' link='".$p[0]['plink']. "' link='".$p[0]['plink'].
"' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') . "' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') .
@ -367,6 +370,7 @@ class Oep extends \Zotlabs\Web\Controller {
$o = "[share author='".urlencode($p[0]['author']['xchan_name']). $o = "[share author='".urlencode($p[0]['author']['xchan_name']).
"' profile='".$p[0]['author']['xchan_url'] . "' profile='".$p[0]['author']['xchan_url'] .
"' portable_id='".$p[0]['author']['xchan_hash'] .
"' avatar='".$p[0]['author']['xchan_photo_s']. "' avatar='".$p[0]['author']['xchan_photo_s'].
"' link='".$p[0]['plink']. "' link='".$p[0]['plink'].
"' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') . "' auth='".(($p[0]['author']['network'] === 'zot6') ? 'true' : 'false') .

View file

@ -1,17 +1,15 @@
<?php <?php
namespace Zotlabs\Module; /** @file */ namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
class Online extends \Zotlabs\Web\Controller { class Online extends Controller {
function init() { function init() {
$ret = [ 'result' => false ];
$ret = array('result' => false); if (argc() != 2) {
if(argc() != 2)
json_return_and_die($ret);
$ret = get_online_status(argv(1));
json_return_and_die($ret); json_return_and_die($ret);
} }
json_return_and_die(get_online_status(argv(1)));
}
} }

93
Zotlabs/Module/Outbox.php Normal file
View file

@ -0,0 +1,93 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Web\HTTPSig;
use Zotlabs\Lib\Activity;
/**
* Implements an ActivityPub outbox.
* Typically unused for Zot6, but *may* be useful in generating
* a consumeable ActivityStreams feed for the desired channel.
*
*/
class Outbox extends Controller {
function init() {
if(observer_prohibited(true)) {
killme();
}
if(argc() < 2)
killme();
$channel = channelx_by_nick(argv(1));
if(! $channel)
killme();
$observer_hash = get_observer_hash();
$params = [];
$params['begin'] = ((x($_REQUEST,'date_begin')) ? $_REQUEST['date_begin'] : NULL_DATE);
$params['end'] = ((x($_REQUEST,'date_end')) ? $_REQUEST['date_end'] : '');
$params['type'] = 'json';
$params['pages'] = ((x($_REQUEST,'pages')) ? intval($_REQUEST['pages']) : 0);
$params['top'] = ((x($_REQUEST,'top')) ? intval($_REQUEST['top']) : 0);
$params['start'] = ((x($_REQUEST,'start')) ? intval($_REQUEST['start']) : 0);
$params['records'] = ((x($_REQUEST,'records')) ? intval($_REQUEST['records']) : 60);
$params['direction'] = ((x($_REQUEST,'direction')) ? dbesc($_REQUEST['direction']) : 'desc'); // unimplemented
$params['cat'] = ((x($_REQUEST,'cat')) ? escape_tags($_REQUEST['cat']) : '');
$params['compat'] = ((x($_REQUEST,'compat')) ? intval($_REQUEST['compat']) : 1);
$items = items_fetch(
[
'wall' => '1',
'datequery' => $params['end'],
'datequery2' => $params['begin'],
'start' => intval($params['start']),
'records' => intval($params['records']),
'direction' => dbesc($params['direction']),
'pages' => $params['pages'],
'order' => dbesc('post'),
'top' => $params['top'],
'cat' => $params['cat'],
'compat' => $params['compat']
], $channel, $observer_hash, CLIENT_MODE_NORMAL, App::$module
);
if(ActivityStreams::is_as_request()) {
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], Activity::encode_item_collection($items, App::$query_string, 'OrderedCollection',true));
$headers = [];
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
$x['signature'] = LDSignatures::sign($x,$channel);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$h = HTTPSig::create_sig($headers,$channel['channel_prvkey'],channel_url($channel));
HTTPSig::set_headers($h);
echo $ret;
killme();
}
}
}

View file

@ -37,7 +37,8 @@ class Photo extends \Zotlabs\Web\Controller {
$x = array_merge(['@context' => [ $x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV, ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1' 'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], $obj ); ]], $obj );
$headers = []; $headers = [];

View file

@ -12,13 +12,13 @@ class Pubsites extends \Zotlabs\Web\Controller {
if (($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { if (($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) {
$url = z_root() . '/dirsearch'; $url = z_root() . '/dirsearch';
} }
if (! $url) { if (! $url) {
$directory = Libzotdir::find_upstream_directory($dirmode); $directory = Libzotdir::find_upstream_directory($dirmode);
$url = $directory['url'] . '/dirsearch'; $url = $directory['url'] . '/dirsearch';
} }
$url .= '/sites';
$rating_enabled = get_config('system','rating_enabled'); $url .= '/sites';
$o .= '<div class="generic-content-wrapper">'; $o .= '<div class="generic-content-wrapper">';
@ -34,14 +34,16 @@ class Pubsites extends \Zotlabs\Web\Controller {
if($j['sites']) { if($j['sites']) {
$projects = $this->sort_sites($j['sites']); $projects = $this->sort_sites($j['sites']);
foreach($projects as $p => $v) { foreach($projects as $p => $v) {
if (ucfirst($p) === 'Osada') {
// deprecated
continue;
}
$o .= '<strong>' . ucfirst($p) . '</strong>' . EOL; $o .= '<strong>' . ucfirst($p) . '</strong>' . EOL;
$o .= '<table class="table table-striped table-hover"><tr><td>' . t('Hub URL') . '</td><td>' . t('Access Type') . '</td><td>' . t('Registration Policy') . '</td><!--td>' . t('Stats') . '</td--><td>' . t('Software') . '</td>'; $o .= '<table class="table table-striped table-hover"><tr><td>' . t('Hub URL') . '</td><td>' . t('Access Type') . '</td><td>' . t('Registration Policy') . '</td><td>' . t('Software') . '</td>';
if($rating_enabled)
$o .= '<td colspan="2">' . t('Ratings') . '</td>';
$o .= '</tr>'; $o .= '</tr>';
foreach($v as $jj) {
$projectname = explode(' ',$jj['project']);
usort($v, [ $this, 'sort_versions' ]);
foreach ($v as $jj) {
if(strpos($jj['version'],' ')) { if(strpos($jj['version'],' ')) {
$x = explode(' ', $jj['version']); $x = explode(' ', $jj['version']);
if($x[1]) if($x[1])
@ -49,7 +51,6 @@ class Pubsites extends \Zotlabs\Web\Controller {
} }
$m = parse_url($jj['url']); $m = parse_url($jj['url']);
$host = strtolower(substr($jj['url'],strpos($jj['url'],'://')+3)); $host = strtolower(substr($jj['url'],strpos($jj['url'],'://')+3));
$rate_links = ((local_channel()) ? '<td><a href="rate?f=&target=' . $host . '" class="btn-btn-default"><i class="fa fa-check-square-o"></i> ' . t('Rate') . '</a></td>' : '');
$location = ''; $location = '';
if(!empty($jj['location'])) { if(!empty($jj['location'])) {
$location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>'; $location = '<p title="' . t('Location') . '" style="margin: 5px 5px 0 0; text-align: right"><i class="fa fa-globe"></i> ' . $jj['location'] . '</p>';
@ -58,9 +59,7 @@ class Pubsites extends \Zotlabs\Web\Controller {
$location = '<br />&nbsp;'; $location = '<br />&nbsp;';
} }
$urltext = str_replace(array('https://'), '', $jj['url']); $urltext = str_replace(array('https://'), '', $jj['url']);
$o .= '<tr><td><a href="'. (($jj['sellpage']) ? $jj['sellpage'] : $jj['url'] . '/register' ) . '" ><i class="fa fa-link"></i> ' . $urltext . '</a>' . $location . '</td><td>' . $jj['access'] . '</td><td>' . $jj['register'] . '</td><!--td>' . '<a target="stats" href="https://hubchart-tarine.rhcloud.com/hub.jsp?hubFqdn=' . $m['host'] . '"><i class="fa fa-area-chart"></i></a></td--><td>' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . '</td>'; $o .= '<tr><td><a href="'. (($jj['sellpage']) ? $jj['sellpage'] : $jj['url'] . '/register' ) . '" ><i class="fa fa-link"></i> ' . $urltext . '</a>' . $location . '</td><td>' . $jj['access'] . '</td><td>' . $jj['register'] . '</td><td>' . ucwords($jj['project']) . (($jj['version']) ? ' ' . $jj['version'] : '') . '</td>';
if($rating_enabled)
$o .= '<td><a href="ratings/' . $host . '" class="btn-btn-default"><i class="fa fa-eye"></i> ' . t('View') . '</a></td>' . $rate_links ;
$o .= '</tr>'; $o .= '</tr>';
} }
$o .= '</table>'; $o .= '</table>';
@ -83,12 +82,17 @@ class Pubsites extends \Zotlabs\Web\Controller {
} }
} }
$projects = array_keys($ret); $projects = array_keys($ret);
sort($projects);
$newret = []; $newret = [];
foreach($projects as $p) { foreach($projects as $p) {
$newret[$p] = $ret[$p]; $newret[$p] = $ret[$p];
} }
return $newret; return $newret;
} }
function sort_versions($a,$b) {
return version_compare($b['version'],$a['version']);
}
} }

View file

@ -1,10 +1,11 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
require_once('include/channel.php'); use Zotlabs\Web\Controller;
require_once('include/security.php');
class Register extends \Zotlabs\Web\Controller { class Register extends Controller {
function init() { function init() {
@ -40,6 +41,8 @@ class Register extends \Zotlabs\Web\Controller {
function post() { function post() {
check_form_security_token_redirectOnErr('/register', 'register');
$max_dailies = intval(get_config('system','max_daily_registrations')); $max_dailies = intval(get_config('system','max_daily_registrations'));
if($max_dailies) { if($max_dailies) {
$r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s", $r = q("select count(account_id) as total from account where account_created > %s - INTERVAL %s",
@ -227,11 +230,6 @@ class Register extends \Zotlabs\Web\Controller {
$perm_roles = \Zotlabs\Access\PermissionRoles::roles(); $perm_roles = \Zotlabs\Access\PermissionRoles::roles();
// A new account will not have a techlevel, but accounts can also be created by the administrator.
if((get_account_techlevel() < 4) && $privacy_role !== 'custom')
unset($perm_roles[t('Other')]);
// Configurable terms of service link // Configurable terms of service link
$tosurl = get_config('system','tos_url'); $tosurl = get_config('system','tos_url');
@ -263,7 +261,7 @@ class Register extends \Zotlabs\Web\Controller {
$name = array('name', t('Your Name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.')); $name = array('name', t('Your Name'), ((x($_REQUEST,'name')) ? $_REQUEST['name'] : ''), t('Real names are preferred.'));
$nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl')); $nickhub = '@' . str_replace(array('http://','https://','/'), '', get_config('system','baseurl'));
$nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub)); $nickname = array('nickname', t('Choose a short nickname'), ((x($_REQUEST,'nickname')) ? $_REQUEST['nickname'] : ''), sprintf( t('Your nickname will be used to create an easy to remember channel address e.g. nickname%s'), $nickhub));
$role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.') . ' <a href="help/member/member_guide#Channel_Permission_Roles" target="_blank">' . t('Read more about channel permission roles') . '</a>',$perm_roles); $role = array('permissions_role' , t('Channel role and privacy'), ($privacy_role) ? $privacy_role : 'social', t('Select a channel permission role for your usage needs and privacy requirements.'),$perm_roles);
$tos = array('tos', $label_tos, '', '', array(t('no'),t('yes'))); $tos = array('tos', $label_tos, '', '', array(t('no'),t('yes')));
@ -275,6 +273,7 @@ class Register extends \Zotlabs\Web\Controller {
$o = replace_macros(get_markup_template('register.tpl'), array( $o = replace_macros(get_markup_template('register.tpl'), array(
'$form_security_token' => get_form_security_token("register"),
'$title' => t('Registration'), '$title' => t('Registration'),
'$reg_is' => $registration_is, '$reg_is' => $registration_is,
'$registertext' => bbcode(get_config('system','register_text')), '$registertext' => bbcode(get_config('system','register_text')),

View file

@ -6,7 +6,7 @@ use App;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Access\AccessControl; use Zotlabs\Access\AccessControl;
use Zotlabs\Lib\PermissionDescription; use Zotlabs\Lib\PermissionDescription;
use Zotlabs\Lib\Libzot;
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
require_once('include/items.php'); require_once('include/items.php');
@ -46,7 +46,7 @@ class Rpost extends Controller {
// by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin // by the wretched beast called 'suhosin'. All the browsers now allow long GET requests, but suhosin
// blocks them. // blocks them.
$url = get_rpost_path(App::get_observer()); $url = Libzot::get_rpost_path(App::get_observer());
// make sure we're not looping to our own hub // make sure we're not looping to our own hub
if(($url) && (! stristr($url, App::get_hostname()))) { if(($url) && (! stristr($url, App::get_hostname()))) {
foreach($_GET as $key => $arg) { foreach($_GET as $key => $arg) {

View file

@ -1,6 +1,9 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
require_once("include/bbcode.php"); require_once("include/bbcode.php");
require_once('include/security.php'); require_once('include/security.php');
require_once('include/conversation.php'); require_once('include/conversation.php');
@ -9,46 +12,44 @@ require_once('include/conversation.php');
class Search extends \Zotlabs\Web\Controller { class Search extends \Zotlabs\Web\Controller {
function init() { function init() {
if(x($_REQUEST,'search')) if (x($_REQUEST,'search')) {
\App::$data['search'] = escape_tags($_REQUEST['search']); App::$data['search'] = escape_tags($_REQUEST['search']);
}
} }
function get($update = 0, $load = false) { function get($update = 0, $load = false) {
if((get_config('system','block_public')) || (get_config('system','block_public_search'))) { if ((get_config('system','block_public')) || (get_config('system','block_public_search',1))) {
if ((! local_channel()) && (! remote_channel())) { if ((! local_channel()) && (! remote_channel())) {
notice( t('Public access denied.') . EOL); notice( t('Public access denied.') . EOL);
return; return;
} }
} }
if($load) if ($load) {
$_SESSION['loadtime'] = datetime_convert(); $_SESSION['loadtime'] = datetime_convert();
}
nav_set_selected('Search'); nav_set_selected('Search');
$format = (($_REQUEST['format']) ? $_REQUEST['format'] : ''); $format = (($_REQUEST['format']) ? $_REQUEST['format'] : '');
if ($format !== '') { if ($format !== '') {
$update = $load = 1; $update = $load = 1;
} }
$observer = \App::get_observer(); $observer = App::get_observer();
$observer_hash = (($observer) ? $observer['xchan_hash'] : ''); $observer_hash = (($observer) ? $observer['xchan_hash'] : '');
$o = '<div id="live-search"></div>' . "\r\n"; $o = '<div id="live-search"></div>' . "\r\n";
$o .= '<div class="generic-content-wrapper-styled">' . "\r\n";
$o = '<div class="generic-content-wrapper-styled">' . "\r\n";
$o .= '<h3>' . t('Search') . '</h3>'; $o .= '<h3>' . t('Search') . '</h3>';
if(x(\App::$data,'search')) if (x(App::$data,'search')) {
$search = trim(\App::$data['search']); $search = trim(App::$data['search']);
else }
else {
$search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : ''); $search = ((x($_GET,'search')) ? trim(escape_tags(rawurldecode($_GET['search']))) : '');
}
$tag = false; $tag = false;
if (x($_GET,'tag')) { if (x($_GET,'tag')) {
$tag = true; $tag = true;
@ -81,8 +82,9 @@ class Search extends \Zotlabs\Web\Controller {
goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search); goaway(z_root() . '/directory' . '?f=1&navsearch=1&search=' . $search);
} }
if(! $search) if (! $search) {
return $o; return $o;
}
if ($tag) { if ($tag) {
$wildtag = str_replace('*','%',$search); $wildtag = str_replace('*','%',$search);
@ -114,12 +116,12 @@ class Search extends \Zotlabs\Web\Controller {
$o .= '<div id="live-search"></div>' . "\r\n"; $o .= '<div id="live-search"></div>' . "\r\n";
$o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1)) $o .= "<script> var profile_uid = " . ((intval(local_channel())) ? local_channel() : (-1))
. "; var netargs = '?f='; var profile_page = " . \App::$pager['page'] . "; </script>\r\n"; . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
\App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"),array( App::$page['htmlhead'] .= replace_macros(get_markup_template("build_query.tpl"), [
'$baseurl' => z_root(), '$baseurl' => z_root(),
'$pgtype' => 'search', '$pgtype' => 'search',
'$uid' => ((\App::$profile['profile_uid']) ? \App::$profile['profile_uid'] : '0'), '$uid' => ((\App::$profile['profile_uid']) ? App::$profile['profile_uid'] : '0'),
'$gid' => '0', '$gid' => '0',
'$cid' => '0', '$cid' => '0',
'$cmin' => '(-1)', '$cmin' => '(-1)',
@ -133,7 +135,7 @@ class Search extends \Zotlabs\Web\Controller {
'$wall' => '0', '$wall' => '0',
'$static' => $static, '$static' => $static,
'$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0), '$list' => ((x($_REQUEST,'list')) ? intval($_REQUEST['list']) : 0),
'$page' => ((\App::$pager['page'] != 1) ? \App::$pager['page'] : 1), '$page' => ((App::$pager['page'] != 1) ? App::$pager['page'] : 1),
'$search' => (($tag) ? urlencode('#') : '') . $search, '$search' => (($tag) ? urlencode('#') : '') . $search,
'$xchan' => '', '$xchan' => '',
'$order' => '', '$order' => '',
@ -145,7 +147,7 @@ class Search extends \Zotlabs\Web\Controller {
'$net' => '', '$net' => '',
'$dend' => '', '$dend' => '',
'$dbegin' => '' '$dbegin' => ''
)); ]);
} }
@ -153,13 +155,11 @@ class Search extends \Zotlabs\Web\Controller {
$item_normal = item_normal_search(); $item_normal = item_normal_search();
$pub_sql = public_permissions_sql($observer_hash); $pub_sql = public_permissions_sql($observer_hash);
require_once('include/channel.php');
$sys = get_sys_channel(); $sys = get_sys_channel();
if (($update) && ($load)) { if (($update) && ($load)) {
$itemspage = get_pconfig(local_channel(),'system','itemspage'); $itemspage = get_pconfig(local_channel(),'system','itemspage');
\App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); App::set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20));
$pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start'])); $pager_sql = sprintf(" LIMIT %d OFFSET %d ", intval(\App::$pager['itemspage']), intval(\App::$pager['start']));
// in case somebody turned off public access to sys channel content with permissions // in case somebody turned off public access to sys channel content with permissions
@ -199,22 +199,18 @@ class Search extends \Zotlabs\Web\Controller {
} }
} }
else { else {
$r = array(); $r = [];
} }
} }
if ($r) { if ($r) {
xchan_query($r); xchan_query($r);
$items = fetch_post_tags($r,true); $items = fetch_post_tags($r,true);
} else { } else {
$items = array(); $items = [];
} }
if ($format == 'json') { if ($format == 'json') {
$result = array(); $result = array();
require_once('include/conversation.php'); require_once('include/conversation.php');
@ -239,5 +235,4 @@ class Search extends \Zotlabs\Web\Controller {
return $o; return $o;
} }
} }

View file

@ -3,16 +3,22 @@ namespace Zotlabs\Module;
// Autocomplete for saved searches. Should probably be put in the same place as the other autocompletes // Autocomplete for saved searches. Should probably be put in the same place as the other autocompletes
class Search_ac extends \Zotlabs\Web\Controller { use Zotlabs\Web\Controller;
class Search_ac extends Controller {
function init() { function init() {
if(!local_channel())
if (! local_channel()) {
killme(); killme();
}
$start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0); $start = (x($_REQUEST,'start') ? $_REQUEST['start'] : 0);
$count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100); $count = (x($_REQUEST,'count') ? $_REQUEST['count'] : 100);
$search = (x($_REQUEST,'search')?$_REQUEST['search']:""); $search = (x($_REQUEST,'search') ? $_REQUEST['search'] : EMPTY_STR);
if (x($_REQUEST,'query') && strlen($_REQUEST['query'])) { if (x($_REQUEST,'query') && strlen($_REQUEST['query'])) {
$search = $_REQUEST['query']; $search = $_REQUEST['query'];
@ -83,19 +89,12 @@ class Search_ac extends \Zotlabs\Web\Controller {
} }
} }
header("content-type: application/json"); json_return_and_die( [
$o = array(
'start' => $start, 'start' => $start,
'count' => $count, 'count' => $count,
'items' => $results, 'items' => $results,
); ]);
echo json_encode($o);
}
logger('search_ac: ' . print_r($x,true),LOGGER_DATA,LOG_INFO);
killme();
}
} }

View file

@ -508,11 +508,6 @@ class Channel {
$permissions_set = (($permissions_role != 'custom') ? true : false); $permissions_set = (($permissions_role != 'custom') ? true : false);
$perm_roles = \Zotlabs\Access\PermissionRoles::roles(); $perm_roles = \Zotlabs\Access\PermissionRoles::roles();
if((get_account_techlevel() < 4) && $permissions_role !== 'custom')
unset($perm_roles[t('Other')]);
$vnotify = get_pconfig(local_channel(),'system','vnotify'); $vnotify = get_pconfig(local_channel(),'system','vnotify');
$always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices'); $always_show_in_notices = get_pconfig(local_channel(),'system','always_show_in_notices');

View file

@ -29,25 +29,6 @@ class Features {
$arr = []; $arr = [];
$harr = []; $harr = [];
if(intval($_REQUEST['techlevel']))
$level = intval($_REQUEST['techlevel']);
else {
$level = get_account_techlevel();
}
if(! intval($level)) {
notice( t('Permission denied.') . EOL);
return;
}
$techlevels = \Zotlabs\Lib\Techlevels::levels();
// This page isn't accessible at techlevel 0
unset($techlevels[0]);
$def_techlevel = (($level > 0) ? $level : 1);
$techlock = get_config('system','techlevel_lock');
$all_features_raw = get_features(false); $all_features_raw = get_features(false);
@ -57,7 +38,7 @@ class Features {
} }
} }
$features = get_features(true,$level); $features = get_features(true);
foreach($features as $fname => $fdata) { foreach($features as $fname => $fdata) {
$arr[$fname] = array(); $arr[$fname] = array();
@ -72,8 +53,6 @@ class Features {
$o .= replace_macros($tpl, array( $o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("settings_features"), '$form_security_token' => get_form_security_token("settings_features"),
'$title' => t('Additional Features'), '$title' => t('Additional Features'),
'$techlevel' => [ 'techlevel', t('Your technical skill level'), $def_techlevel, t('Used to provide a member experience and additional features consistent with your comfort level'), $techlevels ],
'$techlock' => $techlock,
'$features' => $arr, '$features' => $arr,
'$hiddens' => $harr, '$hiddens' => $harr,
'$baseurl' => z_root(), '$baseurl' => z_root(),

View file

@ -1,8 +1,11 @@
<?php <?php
namespace Zotlabs\Module; /** @file */ namespace Zotlabs\Module;
use Zotlabs\Web\Controller;
class Sitelist extends \Zotlabs\Web\Controller {
class Sitelist extends Controller {
function init() { function init() {
@ -14,30 +17,37 @@ class Sitelist extends \Zotlabs\Web\Controller {
$sql_order = " order by site_url "; $sql_order = " order by site_url ";
$rand = db_getfunc('rand'); $rand = db_getfunc('rand');
if($order == 'random')
if ($order == 'random') {
$sql_order = " order by $rand "; $sql_order = " order by $rand ";
}
$sql_limit = " LIMIT $limit OFFSET $start "; $sql_limit = " LIMIT $limit OFFSET $start ";
$sql_extra = ""; $sql_extra = "";
if($open)
if ($open) {
// only return sites with open registration
$sql_extra = " and site_register = " . intval(REGISTER_OPEN) . " "; $sql_extra = " and site_register = " . intval(REGISTER_OPEN) . " ";
}
$realm = get_directory_realm(); $realm = get_directory_realm();
if ($realm == DIRECTORY_REALM) { if ($realm == DIRECTORY_REALM) {
$sql_extra .= " and ( site_realm = '" . dbesc($realm) . "' or site_realm = '') "; $sql_extra .= " and ( site_realm = '" . dbesc($realm) . "' or site_realm = '') ";
} }
else else
$sql_extra .= " and site_realm = '" . dbesc($realm) . "' "; $sql_extra .= " and site_realm = '" . dbesc($realm) . "' ";
$result = array('success' => false); $result = [ 'success' => false ];
$r = q("select count(site_url) as total from site where site_type = %d and site_dead = 0 $sql_extra ", $r = q("select count(site_url) as total from site where site_type = %d and site_dead = 0 $sql_extra ",
intval(SITE_TYPE_ZOT) intval(SITE_TYPE_ZOT)
); );
if($r) if ($r) {
$result['total'] = intval($r[0]['total']); $result['total'] = intval($r[0]['total']);
}
$result['start'] = $start; $result['start'] = $start;
$result['limit'] = $limit; $result['limit'] = $limit;
@ -53,15 +63,12 @@ class Sitelist extends \Zotlabs\Web\Controller {
$result['success'] = true; $result['success'] = true;
$result['results'] = count($r); $result['results'] = count($r);
foreach($r as $rr) { foreach ($r as $rv) {
$result['entries'][] = array('url' => $rr['site_url']); $result['entries'][] = [ 'url' => $rv['site_url'] ];
} }
} }
echo json_encode($result); json_return_and_die($result);
killme();
} }
} }

View file

@ -1,18 +1,17 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Web\Controller;
require_once('include/event.php'); require_once('include/event.php');
class Tasks extends Controller {
class Tasks extends \Zotlabs\Web\Controller {
function init() { function init() {
// logger('request: ' . print_r($_REQUEST,true)); // logger('request: ' . print_r($_REQUEST,true));
$arr = array(); $arr = [];
if (argc() > 1 && argv(1) === 'fetch') { if (argc() > 1 && argv(1) === 'fetch') {
if (argc() > 2 && argv(2) === 'all') if (argc() > 2 && argv(2) === 'all')
@ -33,15 +32,14 @@ class Tasks extends \Zotlabs\Web\Controller {
function post() { function post() {
// logger('post: ' . print_r($_POST,true)); // logger('post: ' . print_r($_POST,true));
if(! local_channel()) if (! local_channel()) {
return; return;
}
$channel = \App::get_channel(); $channel = App::get_channel();
if ((argc() > 2) && (argv(1) === 'complete') && intval(argv(2))) { if ((argc() > 2) && (argv(1) === 'complete') && intval(argv(2))) {
$ret = array('success' => false); $ret = array('success' => false);
@ -66,18 +64,19 @@ class Tasks extends \Zotlabs\Web\Controller {
$event['edited'] = datetime_convert(); $event['edited'] = datetime_convert();
} }
$x = event_store_event($event); $x = event_store_event($event);
if($x) if ($x) {
$ret['success'] = true; $ret['success'] = true;
} }
}
json_return_and_die($ret); json_return_and_die($ret);
} }
if (argc() == 2 && argv(1) === 'new') { if (argc() == 2 && argv(1) === 'new') {
$text = escape_tags(trim($_REQUEST['summary'])); $text = escape_tags(trim($_REQUEST['summary']));
if(! $text) if (! $text) {
return array('success' => false); return [ 'success' => false ];
$event = array(); }
$event = [];
$event['account'] = $channel['channel_account_id']; $event['account'] = $channel['channel_account_id'];
$event['uid'] = $channel['channel_id']; $event['uid'] = $channel['channel_id'];
$event['event_xchan'] = $channel['channel_hash']; $event['event_xchan'] = $channel['channel_hash'];
@ -88,18 +87,13 @@ class Tasks extends \Zotlabs\Web\Controller {
$event['allow_cid'] = '<' . $channel['channel_hash'] . '>'; $event['allow_cid'] = '<' . $channel['channel_hash'] . '>';
$event['summary'] = escape_tags($_REQUEST['summary']); $event['summary'] = escape_tags($_REQUEST['summary']);
$x = event_store_event($event); $x = event_store_event($event);
if($x) if ($x) {
$x['success'] = true; $x['success'] = true;
else }
$x = array('success' => false); else {
$x = [ 'success' => false ];
}
json_return_and_die($x); json_return_and_die($x);
} }
} }
function get() {
if(! local_channel())
return;
return '';
}
} }

View file

@ -7,8 +7,9 @@ use Zotlabs\Web\Controller;
class Uexport extends Controller { class Uexport extends Controller {
function init() { function init() {
if(! local_channel()) if (! local_channel()) {
return; return;
}
if (argc() > 1) { if (argc() > 1) {

View file

@ -2,11 +2,14 @@
namespace Zotlabs\Photo; namespace Zotlabs\Photo;
use App;
/** /**
* @brief Abstract photo driver class. * @brief Abstract photo driver class.
* *
* Inheritance seems not to be the best design pattern for such photo drivers. * Inheritance seems not to be the best design pattern for such photo drivers.
*/ */
abstract class PhotoDriver { abstract class PhotoDriver {
/** /**
@ -72,6 +75,7 @@ abstract class PhotoDriver {
* false on failure, a PHP image resource for GD driver, an \Imagick object * false on failure, a PHP image resource for GD driver, an \Imagick object
* for ImageMagick driver. * for ImageMagick driver.
*/ */
abstract public function getImage(); abstract public function getImage();
abstract public function doScaleImage($new_width, $new_height); abstract public function doScaleImage($new_width, $new_height);
@ -504,21 +508,24 @@ abstract class PhotoDriver {
* @param scale int * @param scale int
* @return boolean|array * @return boolean|array
*/ */
public function storeThumbnail($arr, $scale = 0) { public function storeThumbnail($arr, $scale = 0) {
$arr['imgscale'] = $scale; $arr['imgscale'] = $scale;
if(boolval(get_config('system','filesystem_storage_thumbnails', 0)) && $scale > 0) { if (boolval(get_config('system','filesystem_storage_thumbnails', 1)) && $scale > 0) {
$channel = \App::get_channel(); $channel = channelx_by_n($arr['uid']);
$arr['os_storage'] = 1; $arr['os_storage'] = 1;
$arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale; $arr['os_syspath'] = 'store/' . $channel['channel_address'] . '/' . $arr['os_path'] . '-' . $scale;
if(! $this->saveImage($arr['os_syspath'])) if (! $this->saveImage($arr['os_syspath'])) {
return false; return false;
} }
}
if (! $this->save($arr)) { if (! $this->save($arr)) {
if(array_key_exists('os_syspath', $arr)) if (array_key_exists('os_syspath', $arr)) {
@unlink($arr['os_syspath']); @unlink($arr['os_syspath']);
}
return false; return false;
} }

View file

@ -2,7 +2,10 @@
namespace Zotlabs\Render; namespace Zotlabs\Render;
class SmartyInterface extends \Smarty { use Smarty;
use App;
class SmartyInterface extends Smarty {
public $filename; public $filename;
@ -16,19 +19,20 @@ class SmartyInterface extends \Smarty {
// The order is thus very important here // The order is thus very important here
$template_dirs = array('theme' => "view/theme/$thname/tpl/"); $template_dirs = array('theme' => "view/theme/$thname/tpl/");
if( x(\App::$theme_info,"extends") ) if ( x(App::$theme_info,"extends") ) {
$template_dirs = $template_dirs + array('extends' => "view/theme/" . \App::$theme_info["extends"] . "/tpl/"); $template_dirs = $template_dirs + array('extends' => "view/theme/" . \App::$theme_info["extends"] . "/tpl/");
}
$template_dirs = $template_dirs + array('base' => 'view/tpl/'); $template_dirs = $template_dirs + array('base' => 'view/tpl/');
$this->setTemplateDir($template_dirs); $this->setTemplateDir($template_dirs);
$basecompiledir = \App::$config['system']['smarty3_folder']; $basecompiledir = App::$config['system']['smarty3_folder'];
$this->setCompileDir($basecompiledir.'/compiled/'); $this->setCompileDir($basecompiledir.'/compiled/');
$this->setConfigDir($basecompiledir.'/config/'); $this->setConfigDir($basecompiledir.'/config/');
$this->setCacheDir($basecompiledir.'/cache/'); $this->setCacheDir($basecompiledir.'/cache/');
$this->left_delimiter = \App::get_template_ldelim('smarty3'); $this->left_delimiter = App::get_template_ldelim('smarty3');
$this->right_delimiter = \App::get_template_rdelim('smarty3'); $this->right_delimiter = App::get_template_rdelim('smarty3');
// Don't report errors so verbosely // Don't report errors so verbosely
$this->error_reporting = E_ALL & (~E_NOTICE); $this->error_reporting = E_ALL & (~E_NOTICE);

View file

@ -2,6 +2,9 @@
namespace Zotlabs\Render; namespace Zotlabs\Render;
use App;
class SmartyTemplate implements TemplateEngine { class SmartyTemplate implements TemplateEngine {
static $name ="smarty3"; static $name ="smarty3";
@ -11,9 +14,11 @@ class SmartyTemplate implements TemplateEngine {
// Cannot use get_config() here because it is called during installation when there is no DB. // Cannot use get_config() here because it is called during installation when there is no DB.
// FIXME: this may leak private information such as system pathnames. // FIXME: this may leak private information such as system pathnames.
$basecompiledir = ((array_key_exists('smarty3_folder',\App::$config['system'])) $basecompiledir = ((array_key_exists('smarty3_folder', App::$config['system']))
? \App::$config['system']['smarty3_folder'] : ''); ? App::$config['system']['smarty3_folder'] : '');
if (!$basecompiledir) $basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH; if (! $basecompiledir) {
$basecompiledir = str_replace('Zotlabs','',dirname(__dir__)) . "/" . TEMPLATE_BUILD_PATH;
}
if (! is_dir($basecompiledir)) { if (! is_dir($basecompiledir)) {
@os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true); @os_mkdir(TEMPLATE_BUILD_PATH, STORAGE_DEFAULT_PERMISSIONS, true);
if (! is_dir($basecompiledir)) { if (! is_dir($basecompiledir)) {
@ -23,7 +28,7 @@ class SmartyTemplate implements TemplateEngine {
if (! is_writable($basecompiledir)) { if (! is_writable($basecompiledir)) {
echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> must be writable by webserver."; killme(); echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> must be writable by webserver."; killme();
} }
\App::$config['system']['smarty3_folder'] = $basecompiledir; App::$config['system']['smarty3_folder'] = $basecompiledir;
} }
// TemplateEngine interface // TemplateEngine interface
@ -31,11 +36,9 @@ class SmartyTemplate implements TemplateEngine {
public function replace_macros($s, $r) { public function replace_macros($s, $r) {
$template = ''; $template = '';
// these are available for use in all templates // macro or macros available for use in all templates
$r['$z_baseurl'] = z_root(); $r['$z_baseurl'] = z_root();
$r['$z_server_role'] = \Zotlabs\Lib\System::get_server_role();
$r['$z_techlevel'] = get_account_techlevel();
if (gettype($s) === 'string') { if (gettype($s) === 'string') {
$template = $s; $template = $s;
@ -58,23 +61,23 @@ class SmartyTemplate implements TemplateEngine {
return $template; return $template;
} }
return ""; return EMPTY_STR;
} }
public function get_intltext_template($file, $root = '') { public function get_intltext_template($file, $root = '') {
$lang = \App::$language; $lang = App::$language;
if ($root != '' && substr($root,-1) != '/' ) { if ($root != '' && substr($root,-1) != '/' ) {
$root .= '/'; $root .= '/';
} }
foreach (Array( foreach ( [ $root . "view/$lang/$file", $root . "view/en/$file", '' ] as $template_file) {
$root."view/$lang/$file", if (is_file($template_file)) {
$root."view/en/$file", break;
'' }
) as $template_file) { }
if (is_file($template_file)) { break; } if ($template_file == '') {
$template_file = theme_include($file,$root);
} }
if ($template_file=='') {$template_file = theme_include($file,$root);}
if ($template_file) { if ($template_file) {
$template = new SmartyInterface(); $template = new SmartyInterface();
$template->filename = $template_file; $template->filename = $template_file;

View file

@ -1,50 +0,0 @@
<?php
namespace Zotlabs\Web;
class CheckJS {
private static $jsdisabled = 0;
function __construct($test = 0) {
if(intval($_REQUEST['jsdisabled']))
$this->jsdisabled = 1;
else
$this->jsdisabled = 0;
if(intval($_COOKIE['jsdisabled']))
$this->jsdisabled = 1;
else
$this->jsdisabled = 0;
$page = bin2hex(\App::$query_string);
if(! $this->jsdisabled) {
if($test) {
$this->jsdisabled = 1;
if(array_key_exists('jsdisabled',$_COOKIE))
$this->jsdisabled = $_COOKIE['jsdisabled'];
if(! array_key_exists('jsdisabled',$_COOKIE)) {
\App::$page['htmlhead'] .= "\r\n" . '<script>document.cookie="jsdisabled=0; path=/"; var jsMatch = /\&jsdisabled=0/; if (!jsMatch.exec(location.href)) { location.href = "' . z_root() . '/nojs/0?f=&redir=' . $page . '" ; }</script>' . "\r\n";
/* emulate JS cookie if cookies are not accepted */
if (array_key_exists('jsdisabled',$_GET)) {
$_COOKIE['jsdisabled'] = $_GET['jsdisabled'];
}
}
}
else {
\App::$page['htmlhead'] .= "\r\n" . '<noscript><meta http-equiv="refresh" content="0; url=' . z_root() . '/nojs?f=&redir=' . $page . '"></noscript>' . "\r\n";
}
}
}
function disabled() {
return $this->jsdisabled;
}
}

View file

@ -1,7 +1,9 @@
<?php /** @file */ <?php
namespace Zotlabs\Web; namespace Zotlabs\Web;
use App;
class WebServer { class WebServer {
public function run() { public function run() {
@ -16,8 +18,8 @@ class WebServer {
sys_boot(); sys_boot();
\App::$language = get_best_language(); App::$language = get_best_language();
load_translation_table(\App::$language,\App::$install); load_translation_table(App::$language,App::$install);
/** /**
@ -31,8 +33,8 @@ class WebServer {
* *
*/ */
if(\App::$session) { if (App::$session) {
\App::$session->start(); App::$session->start();
} }
else { else {
session_start(); session_start();
@ -45,18 +47,20 @@ class WebServer {
*/ */
if (array_key_exists('system_language',$_REQUEST)) { if (array_key_exists('system_language',$_REQUEST)) {
if(strlen($_REQUEST['system_language'])) if (strlen($_REQUEST['system_language'])) {
$_SESSION['language'] = $_REQUEST['system_language']; $_SESSION['language'] = $_REQUEST['system_language'];
else }
else {
unset($_SESSION['language']); unset($_SESSION['language']);
} }
}
if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== $lang)) { if ((x($_SESSION, 'language')) && ($_SESSION['language'] !== $lang)) {
\App::$language = $_SESSION['language']; App::$language = $_SESSION['language'];
load_translation_table(\App::$language); load_translation_table(App::$language);
} }
if ((x($_GET,'zid')) && (! \App::$install)) { if ((x($_GET,'zid')) && (! App::$install)) {
\App::$query_string = strip_zids(\App::$query_string); App::$query_string = strip_zids(App::$query_string);
if (! local_channel()) { if (! local_channel()) {
if ($_SESSION['my_address'] !== $_GET['zid']) { if ($_SESSION['my_address'] !== $_GET['zid']) {
$_SESSION['my_address'] = $_GET['zid']; $_SESSION['my_address'] = $_GET['zid'];
@ -66,34 +70,36 @@ class WebServer {
} }
} }
if ((x($_GET,'zat')) && (! \App::$install)) { if ((x($_GET,'zat')) && (! App::$install)) {
\App::$query_string = strip_zats(\App::$query_string); App::$query_string = strip_zats(App::$query_string);
if (! local_channel()) { if (! local_channel()) {
zat_init(); zat_init();
} }
} }
if ((x($_REQUEST,'owt')) && (! \App::$install)) { if ((x($_REQUEST,'owt')) && (! App::$install)) {
$token = $_REQUEST['owt']; $token = $_REQUEST['owt'];
\App::$query_string = strip_query_param(\App::$query_string,'owt'); App::$query_string = strip_query_param(App::$query_string,'owt');
owt_init($token); owt_init($token);
} }
if((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (\App::$module === 'login')) if ((x($_SESSION, 'authenticated')) || (x($_POST, 'auth-params')) || (App::$module === 'login')) {
require('include/auth.php'); require('include/auth.php');
}
if(! x($_SESSION, 'sysmsg')) if (! x($_SESSION, 'sysmsg')) {
$_SESSION['sysmsg'] = array(); $_SESSION['sysmsg'] = [];
}
if(! x($_SESSION, 'sysmsg_info')) if (! x($_SESSION, 'sysmsg_info')) {
$_SESSION['sysmsg_info'] = array(); $_SESSION['sysmsg_info'] = [];
}
if (App::$install) {
if(\App::$install) {
/* Allow an exception for the view module so that pcss will be interpreted during installation */ /* Allow an exception for the view module so that pcss will be interpreted during installation */
if(\App::$module != 'view') if (App::$module !== 'view')
\App::$module = 'setup'; App::$module = 'setup';
} }
else { else {
@ -123,7 +129,7 @@ class WebServer {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.')); header($_SERVER['SERVER_PROTOCOL'] . ' 403 ' . t('Permission denied.'));
} }
call_hooks('page_end', \App::$page['content']); call_hooks('page_end', App::$page['content']);
construct_page(); construct_page();
@ -135,11 +141,11 @@ class WebServer {
/* initialise content region */ /* initialise content region */
if(! x(\App::$page, 'content')) if (! x(App::$page, 'content')) {
\App::$page['content'] = ''; App::$page['content'] = EMPTY_STR;
}
call_hooks('page_content_top', \App::$page['content']);
call_hooks('page_content_top', App::$page['content']);
} }
private function create_channel_links() { private function create_channel_links() {
@ -150,12 +156,12 @@ class WebServer {
* to all protocol drivers; thus doing it here avoids duplication. * to all protocol drivers; thus doing it here avoids duplication.
*/ */
if (( \App::$module === 'channel' ) && argc() > 1) { if (( App::$module === 'channel' ) && argc() > 1) {
\App::$channel_links = [ App::$channel_links = [
[ [
'rel' => 'jrd', 'rel' => 'jrd',
'type' => 'application/jrd+json', 'type' => 'application/jrd+json',
'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . \App::get_hostname() 'url' => z_root() . '/.well-known/webfinger?f=&resource=acct%3A' . argv(1) . '%40' . App::get_hostname()
], ],
[ [
'rel' => 'zot', 'rel' => 'zot',
@ -165,27 +171,26 @@ class WebServer {
]; ];
if(! defined('NOMADIC')) { if(! defined('NOMADIC')) {
\App::$channel_links[] = App::$channel_links[] =
[ [
'rel' => 'self', 'rel' => 'self',
'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', 'type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
'href' => z_root() . '/channel/' . argv(1) 'href' => z_root() . '/channel/' . argv(1)
]; ];
\App::$channel_links[] = App::$channel_links[] =
[ [
'rel' => 'self', 'rel' => 'self',
'type' => 'application/activity+json', 'type' => 'application/activity+json',
'href' => z_root() . '/channel/' . argv(1) 'href' => z_root() . '/channel/' . argv(1)
]; ];
} }
$x = [ 'channel_address' => argv(1), 'channel_links' => \App::$channel_links ]; $x = [ 'channel_address' => argv(1), 'channel_links' => App::$channel_links ];
call_hooks('channel_links', $x ); call_hooks('channel_links', $x );
\App::$channel_links = $x['channel_links']; App::$channel_links = $x['channel_links'];
header('Link: ' . \App::get_channel_links()); header('Link: ' . App::get_channel_links());
} }
} }
private function set_homebase() { private function set_homebase() {
// If you're just visiting, let javascript take you home // If you're just visiting, let javascript take you home
@ -194,15 +199,11 @@ class WebServer {
$homebase = $_SESSION['visitor_home']; $homebase = $_SESSION['visitor_home'];
} }
elseif (local_channel()) { elseif (local_channel()) {
$homebase = z_root() . '/channel/' . \App::$channel['channel_address']; $homebase = z_root() . '/channel/' . App::$channel['channel_address'];
} }
if (isset($homebase)) { if (isset($homebase)) {
\App::$page['content'] .= '<script>var homebase = "' . $homebase . '";</script>'; App::$page['content'] .= '<script>var homebase = "' . $homebase . '";</script>';
} }
} }
} }

View file

@ -23,7 +23,7 @@ class Catcloud {
case 'articles': case 'articles':
if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_pages')) if(! perm_is_allowed(\App::$profile['profile_uid'], get_observer_hash(), 'view_articles'))
return ''; return '';
return article_catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']); return article_catblock(\App::$profile['profile_uid'], $limit, '', \App::$profile['channel_hash']);

View file

@ -16,12 +16,12 @@ class Categories {
$articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false); $articles = ((array_key_exists('articles',$arr) && $arr['articles']) ? true : false);
if(($articles) && (! feature_enabled(App::$profile['profile_uid'],'articles'))) if(($articles) && (! Apps::addon_app_installed(App::$profile['profile_uid'],'articles')))
return ''; return '';
if((! App::$profile['profile_uid']) if((! App::$profile['profile_uid'])
|| (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_stream')))) { || (! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),(($cards || $articles) ? 'view_pages' : 'view_articles')))) {
return ''; return '';
} }

View file

@ -22,7 +22,7 @@ class Cdav {
$o = ''; $o = '';
if(argc() == 2 && argv(1) === 'calendar') { if(argc() <= 3 && argv(1) === 'calendar') {
$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
@ -57,7 +57,7 @@ class Cdav {
$switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]); $switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]);
$color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad'); $color = (($sabrecal['{http://apple.com/ns/ical/}calendar-color']) ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#6cad39');
$editable = (($sabrecal['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript $editable = (($sabrecal['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript
@ -113,10 +113,22 @@ class Cdav {
} }
} }
$calendars[] = [
'ownernick' => $channel['channel_address'],
'displayname' => $channel['channel_name'],
'calendarid' => 'calendar',
'json_source' => '/calendar/json',
'color' => '#3a87ad',
'editable' => true,
'switch' => get_pconfig(local_channel(), 'cdav_calendar', 'calendar')
];
$o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [ $o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [
'$my_calendars_label' => t('My Calendars'), '$calendars_label' => t('Channel Calendar'),
'$calendars' => $calendars,
'$my_calendars_label' => t('CalDAV Calendars'),
'$my_calendars' => $my_calendars, '$my_calendars' => $my_calendars,
'$shared_calendars_label' => t('Shared Calendars'), '$shared_calendars_label' => t('Shared CalDAV Calendars'),
'$shared_calendars' => $shared_calendars, '$shared_calendars' => $shared_calendars,
'$sharee_options' => $sharee_options, '$sharee_options' => $sharee_options,
'$access_options' => $access_options, '$access_options' => $access_options,
@ -124,10 +136,11 @@ class Cdav {
'$share' => t('Share'), '$share' => t('Share'),
'$edit_label' => t('Calendar name and color'), '$edit_label' => t('Calendar name and color'),
'$edit' => t('Edit'), '$edit' => t('Edit'),
'$create_label' => t('Create new calendar'), '$create_label' => t('Create new CalDAV calendar'),
'$create' => t('Create'), '$create' => t('Create'),
'$create_placeholder' => t('Calendar Name'), '$create_placeholder' => t('Calendar Name'),
'$tools_label' => t('Calendar Tools'), '$tools_label' => t('Calendar Tools'),
'$tools_options_label' => [t('Channel Calendars'), t('CalDAV Calendars')],
'$import_label' => t('Import calendar'), '$import_label' => t('Import calendar'),
'$import_placeholder' => t('Select a calendar to import to'), '$import_placeholder' => t('Select a calendar to import to'),
'$upload' => t('Upload'), '$upload' => t('Upload'),

View file

@ -2,6 +2,7 @@
namespace Zotlabs\Widget; namespace Zotlabs\Widget;
use App;
use Zotlabs\Lib\AccessList; use Zotlabs\Lib\AccessList;
class Collections { class Collections {
@ -41,7 +42,7 @@ class Collections {
$each = 'group'; $each = 'group';
$edit = false; $edit = false;
$current = 0; $current = 0;
$abook_id = \App::$poi['abook_xchan']; $abook_id = App::$poi['abook_xchan'];
$wmode = 1; $wmode = 1;
break; break;
default: default:

View file

@ -5,6 +5,7 @@ namespace Zotlabs\Widget;
class Helpindex { class Helpindex {
function widget($arr) { function widget($arr) {
return EMPTY_STR;
$o .= '<div class="widget">'; $o .= '<div class="widget">';

View file

@ -1,14 +1,15 @@
<?php <?php
namespace Zotlabs\Widget; namespace Zotlabs\Widget;
use App;
class Notifications { class Notifications {
function widget($arr) { function widget($arr) {
$channel = \App::get_channel();
if(local_channel()) { if(local_channel()) {
$channel = App::get_channel();
$notifications[] = [ $notifications[] = [
'type' => 'network', 'type' => 'network',
'icon' => 'th', 'icon' => 'th',
@ -116,8 +117,8 @@ class Notifications {
'type' => 'forums', 'type' => 'forums',
'icon' => 'comments-o', 'icon' => 'comments-o',
'severity' => 'secondary', 'severity' => 'secondary',
'label' => t('Forums'), 'label' => t('Groups'),
'title' => t('Forums'), 'title' => t('Groups'),
'filter' => [ 'filter' => [
'name_label' => t('Filter by name') 'name_label' => t('Filter by name')
] ]
@ -156,7 +157,7 @@ class Notifications {
} }
$o = replace_macros(get_markup_template('notifications_widget.tpl'), array( $o = replace_macros(get_markup_template('notifications_widget.tpl'), array(
'$module' => \App::$module, '$module' => App::$module,
'$notifications' => $notifications, '$notifications' => $notifications,
'$no_notifications' => t('Sorry, you have got no notifications at the moment'), '$no_notifications' => t('Sorry, you have got no notifications at the moment'),
'$loading' => t('Loading'), '$loading' => t('Loading'),

View file

@ -1,6 +1,6 @@
version: 1 version: 1
url: $baseurl/cdav/calendar url: $baseurl/cdav/calendar
requires: local_channel requires: local_channel
name: CalDAV name: Calendar
photo: icon:calendar photo: icon:calendar
categories: Productivity, Personal categories: Productivity, nav_featured_app

View file

@ -342,6 +342,7 @@ define ( 'POLL_OVERWRITE', 0x8000); // If you vote twice remove the prior
define ( 'UPDATE_FLAGS_UPDATED', 0x0001); define ( 'UPDATE_FLAGS_UPDATED', 0x0001);
define ( 'UPDATE_FLAGS_FORCED', 0x0002); define ( 'UPDATE_FLAGS_FORCED', 0x0002);
define ( 'UPDATE_FLAGS_CENSORED', 0x0004);
define ( 'UPDATE_FLAGS_DELETED', 0x1000); define ( 'UPDATE_FLAGS_DELETED', 0x1000);
@ -462,13 +463,14 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' );
define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' ); define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' );
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.7' ); define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.8' );
/** /**
* activity stream defines * activity stream defines
*/ */
define ( 'ACTIVITY_PUBLIC_INBOX', 'https://www.w3.org/ns/activitystreams#Public' ); define ( 'ACTIVITY_PUBLIC_INBOX', 'https://www.w3.org/ns/activitystreams#Public' );
define ( 'ACTIVITY_POST', 'Create' ); define ( 'ACTIVITY_POST', 'Create' );
define ( 'ACTIVITY_CREATE', 'Create' ); define ( 'ACTIVITY_CREATE', 'Create' );
define ( 'ACTIVITY_UPDATE', 'Update' ); define ( 'ACTIVITY_UPDATE', 'Update' );
@ -828,6 +830,7 @@ class App {
/** /**
* App constructor. * App constructor.
*/ */
public static function init() { public static function init() {
// we'll reset this after we read our config file // we'll reset this after we read our config file
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
@ -846,8 +849,8 @@ class App {
set_include_path( set_include_path(
'include' . PATH_SEPARATOR 'include' . PATH_SEPARATOR
. 'library' . PATH_SEPARATOR . 'library' . PATH_SEPARATOR
. 'library/langdet' . PATH_SEPARATOR . '.'
. '.' ); );
self::$scheme = 'http'; self::$scheme = 'http';
if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS']) if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS'])
@ -1115,8 +1118,11 @@ class App {
self::$meta->set('generator', System::get_platform_name()); self::$meta->set('generator', System::get_platform_name());
$i = head_get_icon(); $i = head_get_icon();
if (! $i) {
$i = System::get_project_icon();
}
if($i) { if($i) {
head_add_link(['rel' => 'shortcut icon', 'href' => head_get_icon()]); head_add_link(['rel' => 'shortcut icon', 'href' => $i ]);
} }
$x = [ 'header' => '' ]; $x = [ 'header' => '' ];
@ -1590,6 +1596,12 @@ function login($register = false, $form_id = 'main-login', $hiddens = false, $lo
// your site. // your site.
// If the site supports SSL and this isn't a secure connection, reload the page using https
if (intval($_SERVER['SERVER_PORT']) === 80 && strpos(z_root(), 'https://') !== false) {
goaway(z_root() . '/' . App::$query_string);
}
$register_policy = get_config('system','register_policy'); $register_policy = get_config('system','register_policy');
$reglink = get_config('system', 'register_link', z_root() . '/' . ((intval($register_policy) === REGISTER_CLOSED) ? 'pubsites' : 'register')); $reglink = get_config('system', 'register_link', z_root() . '/' . ((intval($register_policy) === REGISTER_CLOSED) ? 'pubsites' : 'register'));
@ -1605,29 +1617,29 @@ function login($register = false, $form_id = 'main-login', $hiddens = false, $lo
$dest_url = z_root() . '/' . App::$query_string; $dest_url = z_root() . '/' . App::$query_string;
if(local_channel()) { if(local_channel()) {
$tpl = get_markup_template("logout.tpl"); $tpl = get_markup_template('logout.tpl');
} }
else { else {
$tpl = get_markup_template("login.tpl"); $tpl = get_markup_template('login.tpl');
if(strlen(App::$query_string)) if(strlen(App::$query_string))
$_SESSION['login_return_url'] = App::$query_string; $_SESSION['login_return_url'] = App::$query_string;
} }
$o .= replace_macros($tpl,array( $o .= replace_macros($tpl, [
'$dest_url' => $dest_url, '$dest_url' => $dest_url,
'$login_page' => $login_page, '$login_page' => $login_page,
'$logout' => t('Logout'), '$logout' => t('Logout'),
'$login' => t('Login'), '$login' => t('Login'),
'$remote_login' => t('Remote Authentication'), '$remote_login' => t('Remote Authentication'),
'$form_id' => $form_id, '$form_id' => $form_id,
'$lname' => array('username', t('Login/Email') , '', ''), '$lname' => [ 'username', t('Login/Email') , '', '' ],
'$lpassword' => array('password', t('Password'), '', ''), '$lpassword' => [ 'password', t('Password'), '', '' ],
'$remember_me' => array((($login_page) ? 'remember' : 'remember_me'), t('Remember me'), '', '',array(t('No'),t('Yes'))), '$remember_me' => [ (($login_page) ? 'remember' : 'remember_me'), t('Remember me'), '', '', [ t('No'),t('Yes') ] ],
'$hiddens' => $hiddens, '$hiddens' => $hiddens,
'$register' => $reg, '$register' => $reg,
'$lostpass' => t('Forgot your password?'), '$lostpass' => t('Forgot your password?'),
'$lostlink' => t('Password Reset'), '$lostlink' => t('Password Reset'),
)); ]);
/** /**
* @hooks login_hook * @hooks login_hook

153
composer.lock generated
View file

@ -8,16 +8,16 @@
"packages": [ "packages": [
{ {
"name": "blueimp/jquery-file-upload", "name": "blueimp/jquery-file-upload",
"version": "v9.28.0", "version": "v9.30.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/vkhramtsov/jQuery-File-Upload.git", "url": "https://github.com/vkhramtsov/jQuery-File-Upload.git",
"reference": "ff5accfe2e5c4a522777faa980a90cf86a636d1d" "reference": "1fceec556879403e5c1ae32a7c448aa12b8c3558"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/vkhramtsov/jQuery-File-Upload/zipball/ff5accfe2e5c4a522777faa980a90cf86a636d1d", "url": "https://api.github.com/repos/vkhramtsov/jQuery-File-Upload/zipball/1fceec556879403e5c1ae32a7c448aa12b8c3558",
"reference": "ff5accfe2e5c4a522777faa980a90cf86a636d1d", "reference": "1fceec556879403e5c1ae32a7c448aa12b8c3558",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -59,7 +59,7 @@
"upload", "upload",
"widget" "widget"
], ],
"time": "2018-11-13T05:41:39+00:00" "time": "2019-04-22T09:21:57+00:00"
}, },
{ {
"name": "bshaffer/oauth2-server-php", "name": "bshaffer/oauth2-server-php",
@ -420,16 +420,16 @@
}, },
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.0.51", "version": "1.0.52",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/flysystem.git", "url": "https://github.com/thephpleague/flysystem.git",
"reference": "755ba7bf3fb9031e6581d091db84d78275874396" "reference": "c5a5097156387970e6f0ccfcdf03f752856f3391"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/755ba7bf3fb9031e6581d091db84d78275874396", "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/c5a5097156387970e6f0ccfcdf03f752856f3391",
"reference": "755ba7bf3fb9031e6581d091db84d78275874396", "reference": "c5a5097156387970e6f0ccfcdf03f752856f3391",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -500,7 +500,7 @@
"sftp", "sftp",
"storage" "storage"
], ],
"time": "2019-03-30T13:22:34+00:00" "time": "2019-05-20T20:21:14+00:00"
}, },
{ {
"name": "league/html-to-markdown", "name": "league/html-to-markdown",
@ -1576,16 +1576,16 @@
}, },
{ {
"name": "symfony/options-resolver", "name": "symfony/options-resolver",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/options-resolver.git", "url": "https://github.com/symfony/options-resolver.git",
"reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1" "reference": "fd4a5f27b7cd085b489247b9890ebca9f3e10044"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/3896e5a7d06fd15fa4947694c8dcdd371ff147d1", "url": "https://api.github.com/repos/symfony/options-resolver/zipball/fd4a5f27b7cd085b489247b9890ebca9f3e10044",
"reference": "3896e5a7d06fd15fa4947694c8dcdd371ff147d1", "reference": "fd4a5f27b7cd085b489247b9890ebca9f3e10044",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1626,7 +1626,7 @@
"configuration", "configuration",
"options" "options"
], ],
"time": "2019-02-23T15:17:42+00:00" "time": "2019-04-10T16:20:36+00:00"
}, },
{ {
"name": "symfony/polyfill-ctype", "name": "symfony/polyfill-ctype",
@ -1688,16 +1688,16 @@
}, },
{ {
"name": "symfony/process", "name": "symfony/process",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/process.git", "url": "https://github.com/symfony/process.git",
"reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6" "reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", "url": "https://api.github.com/repos/symfony/process/zipball/8cf39fb4ccff793340c258ee7760fd40bfe745fe",
"reference": "1e6cbb41dadcaf29e0db034d6ad0d039a9df06e6", "reference": "8cf39fb4ccff793340c258ee7760fd40bfe745fe",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1733,7 +1733,7 @@
], ],
"description": "Symfony Process Component", "description": "Symfony Process Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-03-10T20:07:02+00:00" "time": "2019-04-10T16:20:36+00:00"
}, },
{ {
"name": "twbs/bootstrap", "name": "twbs/bootstrap",
@ -2899,16 +2899,16 @@
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-docblock",
"version": "4.3.0", "version": "4.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08" "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"reference": "94fd0001232e47129dd3504189fa1c7225010d08", "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2946,7 +2946,7 @@
} }
], ],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2017-11-30T07:14:17+00:00" "time": "2019-04-30T17:48:53+00:00"
}, },
{ {
"name": "phpdocumentor/type-resolver", "name": "phpdocumentor/type-resolver",
@ -3372,16 +3372,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "8.1.2", "version": "8.1.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "e7450b51b6f5d29edcd645ff72b355ab0633ca35" "reference": "01392d4b5878aa617e8d9bc7a529e5febc8fe956"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e7450b51b6f5d29edcd645ff72b355ab0633ca35", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/01392d4b5878aa617e8d9bc7a529e5febc8fe956",
"reference": "e7450b51b6f5d29edcd645ff72b355ab0633ca35", "reference": "01392d4b5878aa617e8d9bc7a529e5febc8fe956",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3450,7 +3450,7 @@
"testing", "testing",
"xunit" "xunit"
], ],
"time": "2019-04-08T16:03:02+00:00" "time": "2019-05-14T04:57:31+00:00"
}, },
{ {
"name": "psr/container", "name": "psr/container",
@ -3758,16 +3758,16 @@
}, },
{ {
"name": "sebastian/environment", "name": "sebastian/environment",
"version": "4.1.0", "version": "4.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/environment.git", "url": "https://github.com/sebastianbergmann/environment.git",
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656" "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6fda8ce1974b62b14935adc02a9ed38252eca656", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
"reference": "6fda8ce1974b62b14935adc02a9ed38252eca656", "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3782,7 +3782,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "4.1-dev" "dev-master": "4.2-dev"
} }
}, },
"autoload": { "autoload": {
@ -3807,7 +3807,7 @@
"environment", "environment",
"hhvm" "hhvm"
], ],
"time": "2019-02-01T05:27:49+00:00" "time": "2019-05-05T09:05:15+00:00"
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
@ -4162,16 +4162,16 @@
}, },
{ {
"name": "symfony/browser-kit", "name": "symfony/browser-kit",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/browser-kit.git", "url": "https://github.com/symfony/browser-kit.git",
"reference": "61d85c5af2fc058014c7c89504c3944e73a086f0" "reference": "c09c18cca96d7067152f78956faf55346c338283"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/61d85c5af2fc058014c7c89504c3944e73a086f0", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c09c18cca96d7067152f78956faf55346c338283",
"reference": "61d85c5af2fc058014c7c89504c3944e73a086f0", "reference": "c09c18cca96d7067152f78956faf55346c338283",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4215,11 +4215,11 @@
], ],
"description": "Symfony BrowserKit Component", "description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-02-23T15:17:42+00:00" "time": "2019-04-07T09:56:43+00:00"
}, },
{ {
"name": "symfony/class-loader", "name": "symfony/class-loader",
"version": "v3.4.24", "version": "v3.4.27",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/class-loader.git", "url": "https://github.com/symfony/class-loader.git",
@ -4275,7 +4275,7 @@
}, },
{ {
"name": "symfony/config", "name": "symfony/config",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/config.git", "url": "https://github.com/symfony/config.git",
@ -4338,16 +4338,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "24206aff3efe6962593297e57ef697ebb220e384" "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/24206aff3efe6962593297e57ef697ebb220e384", "url": "https://api.github.com/repos/symfony/console/zipball/e2840bb38bddad7a0feaf85931e38fdcffdb2f81",
"reference": "24206aff3efe6962593297e57ef697ebb220e384", "reference": "e2840bb38bddad7a0feaf85931e38fdcffdb2f81",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4406,20 +4406,20 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-04-01T07:32:59+00:00" "time": "2019-04-08T14:23:48+00:00"
}, },
{ {
"name": "symfony/contracts", "name": "symfony/contracts",
"version": "v1.0.2", "version": "v1.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/contracts.git", "url": "https://github.com/symfony/contracts.git",
"reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" "reference": "d3636025e8253c6144358ec0a62773cae588395b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", "url": "https://api.github.com/repos/symfony/contracts/zipball/d3636025e8253c6144358ec0a62773cae588395b",
"reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", "reference": "d3636025e8253c6144358ec0a62773cae588395b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4427,19 +4427,22 @@
}, },
"require-dev": { "require-dev": {
"psr/cache": "^1.0", "psr/cache": "^1.0",
"psr/container": "^1.0" "psr/container": "^1.0",
"symfony/polyfill-intl-idn": "^1.10"
}, },
"suggest": { "suggest": {
"psr/cache": "When using the Cache contracts", "psr/cache": "When using the Cache contracts",
"psr/container": "When using the Service contracts", "psr/container": "When using the Service contracts",
"symfony/cache-contracts-implementation": "", "symfony/cache-contracts-implementation": "",
"symfony/event-dispatcher-implementation": "",
"symfony/http-client-contracts-implementation": "",
"symfony/service-contracts-implementation": "", "symfony/service-contracts-implementation": "",
"symfony/translation-contracts-implementation": "" "symfony/translation-contracts-implementation": ""
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.0-dev" "dev-master": "1.1-dev"
} }
}, },
"autoload": { "autoload": {
@ -4474,11 +4477,11 @@
"interoperability", "interoperability",
"standards" "standards"
], ],
"time": "2018-12-05T08:06:11+00:00" "time": "2019-04-27T14:29:50+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
"version": "v3.4.24", "version": "v3.4.27",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/css-selector.git", "url": "https://github.com/symfony/css-selector.git",
@ -4531,16 +4534,16 @@
}, },
{ {
"name": "symfony/dependency-injection", "name": "symfony/dependency-injection",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dependency-injection.git", "url": "https://github.com/symfony/dependency-injection.git",
"reference": "1806e43ff6bff57398d33b326cd753a12d9f434f" "reference": "d161c0c8bc77ad6fdb8f5083b9e34c3015d43eb1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1806e43ff6bff57398d33b326cd753a12d9f434f", "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d161c0c8bc77ad6fdb8f5083b9e34c3015d43eb1",
"reference": "1806e43ff6bff57398d33b326cd753a12d9f434f", "reference": "d161c0c8bc77ad6fdb8f5083b9e34c3015d43eb1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4600,11 +4603,11 @@
], ],
"description": "Symfony DependencyInjection Component", "description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-03-30T15:58:42+00:00" "time": "2019-04-27T11:48:17+00:00"
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dom-crawler.git", "url": "https://github.com/symfony/dom-crawler.git",
@ -4661,16 +4664,16 @@
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544" "reference": "fbce53cd74ac509cbe74b6f227622650ab759b02"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/fbce53cd74ac509cbe74b6f227622650ab759b02",
"reference": "ca5af306fbc37f3cf597e91bc9cfa0c7d3f33544", "reference": "fbce53cd74ac509cbe74b6f227622650ab759b02",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4721,11 +4724,11 @@
], ],
"description": "Symfony EventDispatcher Component", "description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-03-30T15:58:42+00:00" "time": "2019-04-06T13:51:08+00:00"
}, },
{ {
"name": "symfony/filesystem", "name": "symfony/filesystem",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/filesystem.git", "url": "https://github.com/symfony/filesystem.git",
@ -4834,16 +4837,16 @@
}, },
{ {
"name": "symfony/translation", "name": "symfony/translation",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/translation.git", "url": "https://github.com/symfony/translation.git",
"reference": "e46933cc31b68f51f7fc5470fb55550407520f56" "reference": "181a426dd129cb496f12d7e7555f6d0b37a7615b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/e46933cc31b68f51f7fc5470fb55550407520f56", "url": "https://api.github.com/repos/symfony/translation/zipball/181a426dd129cb496f12d7e7555f6d0b37a7615b",
"reference": "e46933cc31b68f51f7fc5470fb55550407520f56", "reference": "181a426dd129cb496f12d7e7555f6d0b37a7615b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -4865,7 +4868,9 @@
"symfony/console": "~3.4|~4.0", "symfony/console": "~3.4|~4.0",
"symfony/dependency-injection": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0", "symfony/finder": "~2.8|~3.0|~4.0",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/intl": "~3.4|~4.0", "symfony/intl": "~3.4|~4.0",
"symfony/var-dumper": "~3.4|~4.0",
"symfony/yaml": "~3.4|~4.0" "symfony/yaml": "~3.4|~4.0"
}, },
"suggest": { "suggest": {
@ -4903,11 +4908,11 @@
], ],
"description": "Symfony Translation Component", "description": "Symfony Translation Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2019-04-01T14:13:08+00:00" "time": "2019-05-01T12:55:36+00:00"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v4.2.5", "version": "v4.2.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/yaml.git", "url": "https://github.com/symfony/yaml.git",

View file

@ -1,53 +0,0 @@
Advanced Directory Search
=========================
Advanced Directory Search is enabled in "Expert Mode" from your Settings => Additional features page.
On the Directory page an option named "Advanced" will apear in the "Find Channels" widget (typically in the sidebar). Clicking "Advanced" will open another search box for entering advanced search requests.
Advanced requests include
* name=xxx
[Channel name contains xxx]
* address=xxx
[Channel address (webbie) contains xxx]
* locale=xxx
[Locale (typically 'city') contains xxx]
* region=xxx
[Region (state/territory) contains xxx]
* postcode=xxx
[Postcode or zip code contains xxx]
* country=xxx
[Country name contains xxx]
* gender=xxx
[Gender contains xxx]
* marital=xxx
[Marital status contains xxx]
* sexual=xxx
[Sexual preference contains xxx]
* keywords=xxx
[Keywords contain xxx]
There are many reasons why a match may not return what you're looking for, as many channels do not provide detailed information in their default (public) profile, and many of these fields allow free-text input in several languages - and this may be difficult to match precisely. For instance you may have better results finding somebody in the USA with 'country=u' (along with some odd channels from Deutschland and Bulgaria and Australia) because this could be represented in a profile as US, U.S.A, USA, United States, etc...
Future revisions of this tool may try to smooth over some of these difficulties.
Requests may be joined together with 'and', 'or', and 'and not'.
Terms containing spaces must be quoted.
Example:
name="charlie brown" and country=canada and not gender=female
#include doc/macros/main_footer.bb;

View file

@ -1,23 +0,0 @@
Comparison of different server-client networks providing activity streams to users
====================================
The goal of this table was to provide an overview of the security and privacy provided by server-client networks providing activity-streams.
| project | license | distributed | supports node isolation | server-to-server encryption | 1-click E2EE* | database encryption sceme | supports cloning[^5] | encryption of private messages | PFS chat | wall-to-wall interaction | supports post editing and unsend private message | other |
|-----------+---------------+-------------+-------------------------+-------------------------------------------+------------------------------------------+-----------------------------------------------------+-------------------------+-------------------------------------------------------------------------------------------------+-----------------------------------------------+--------------------------------------------------------------+--------------------------------------------------+------------------------------------------------------------------------------|
| hubzilla | ISC aka MIT | yes | yes | Zot (PKI) + TLS | yes (via JavaScript, AES-256) | content obfuscation, private keys hidden in the DB. | yes, partly implemented | impossible to message privately in plaintext | no | yes, multiple separated channels possible within one account | yes | privacy built in, run your own @ home, nodes are called hubs |
| diaspora | AGPLv3orlater | yes | no[^1] | PKI + SSL/TLS[^1] | no[^2] | mostly plaintext | no | ? | no | yes, no naming policy | no | nodes are called pods |
| facebook | proprietary | no | no | planned, probably not implemented yet[^3] | implemented but not offered to users[^4] | unknown | no, walled garden | no, 3-d party plugin Cryptocat and pidgin is availiable but the user is not informed about this | no, with Cryptocat: yes, with pidgin+OTR: yes | only one wall allowed | only post editing | "real name"-policy enforced, advertising-driven, for profit company US-based |
| twitter | proprietary | no | no | unknown | no | unknown, probably none | no, walled garden | no | no | yes | only post editing | advertising-driven, for profit company US-based |
| | | | | | | | | | | | | |
This table was edited with emacs using org-mode.
[^1]: https://wiki.diasporafoundation.org/Federation_protocol_overview
[^2]: forks providing this exists
[^3]: http://news.softpedia.com/news/Facebook-Aims-to-Encrypt-Everything-Between-Data-Centers-433091.shtml
[^4]: http://www.computerworld.com/article/2488773/cybercrime-hacking/facebook-holds-back-on-end-to-end-encryption.html
[^5]: see the hubzilla help files for details about this feature.

View file

@ -1,93 +0,0 @@
Creating Page Templates
=======================
A page template for use with Comanche requires two files - a PHP template and a CSS file. Page templates will need to be installed by the system administrator of your site.
First choose a name. Here we'll create a template and call it "demo".
You will need to create the files "view/php/demo.php" and "view/css/demo.css" to hold the PHP template and CSS respectively.
To get a better idea of this process, let's look at an existing template - the "default" template. This is used by default throughout the application.
view/php/default.php
====================
<!DOCTYPE html >
<html>
<head>
<title><?php if(x($page,'title')) echo $page['title'] ?></title>
<script>var baseurl="<?php echo z_root() ?>";</script>
<?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?>
</head>
<body>
<?php if(x($page,'nav')) echo $page['nav']; ?>
<aside id="region_1"><?php if(x($page,'aside')) echo $page['aside']; ?></aside>
<section id="region_2"><?php if(x($page,'content')) echo $page['content']; ?>
<div id="page-footer"></div>
<div id="pause"></div>
</section>
<aside id="region_3"><?php if(x($page,'right_aside')) echo $page['right_aside']; ?></aside>
<footer><?php if(x($page,'footer')) echo $page['footer']; ?></footer>
</body>
</html>
Here's is the corresponding CSS file
view/php/default.css
====================
aside#region_1 {
display: block;
width: 210px;
position: absolute;
top: 65px;
left: 0;
margin-left: 10px;
}
aside input[type='text'] {
width: 174px;
}
section {
position: absolute;
top: 65px;
left: 250px;
display: block;
right: 15px;
padding-bottom: 350px;
}
Some things you may notice when looking at these definitions:
* We have not specified any CSS for the "nav", "right_aside", or "footer" regions. In this template "nav" and "footer" will be the full page width and we will let the size and placement of these elements be controlled by the theme. "right_aside" is not currently used.
* There are elements on the page such as "page-footer" and "pause" for which there is no apparent content. This content will come from Javascript elements.
* Our default template uses absolute positioning. Modern web design often uses "float" div containers so that scrollbars aren't typically needed when viewing on small-screen devices.
To design a new template, it is best to start with an existing template, and modify it as desired. That is what we will do here.
The way that Comanche provides content inside a specific region is by using a region tag.
[region=aside][widget=profile][/widget][/region]
This example will place a "profile" widget in the "aside" region. But what it actually does is place the HTML for the widget into a code variable **$page['aside']**. Our default page template defines a region on the page (the CSS positions this as an absolute sidebar) and then inserts the contents of $page['aside'] (if it exists).
So if you wanted to create a template with a region named "foo", you would provide a place for it on the page, then include the contents of $page['foo'] wherever you wanted to use it, and then using Comanche, you could specify
[region=foo][widget=profile][/widget][/region]
and this would place a profile widget into the "foo" region you created.
Use the CSS file to position the region on the page where desired and optionally control its size.
[To be continued]
#include doc/macros/main_footer.bb;

View file

@ -1,101 +0,0 @@
Creating a Derived Theme
========================
**Lesson 1**
A derived theme takes most of the settings from its "parent" theme and lets you change a few things to your liking without creating an entire theme package.
To create a derived theme, first choose a name. For our example we'll call our theme 'mytheme'. Hopefully you'll be a bit more creative. But throughout this document, wherever you see 'mytheme', replace that with the name you chose.
**Directory Structure**
First you need to create a theme directory structure. We'll keep it simple. We need a php directory and a css directory. Here are the Unix/Linux commands to do this. Assume that 'mywebsite' is your top level $Projectname folder.
cd mywebsite
mkdir view/theme/mytheme
mkdir view/theme/mytheme/css
mkdir view/theme/mytheme/php
Great. Now we need a couple of files. The first one is your theme info file, which describes the theme.
It will be called view/theme/mytheme/php/theme.php (clever name huh?)
Inside it, put the following information - edit as needed
<?php
/**
* * Name: Mytheme
* * Description: Sample Derived theme
* * Version: 1.0
* * Author: Your Name
* * Compat: Red [*]
*
*/
function mytheme_init(&$a) {
App::$theme_info['extends'] = 'redbasic';
}
Remember to rename the mytheme_init function with your theme name. In this case we will be extending the theme 'redbasic'.
Now create another file. We call this a PCSS file, but it's really a PHP file.
The file is called view/theme/mytheme/php/style.php
In it, put the following:
<?php
require_once('view/theme/redbasic/php/style.php');
echo @file_get_contents('view/theme/mytheme/css/style.css');
That's it. This tells the software to read the PCSS information for the redbasic theme first, and then read our CSS file which will just consist of changes we want to make from our parent theme (redbasic).
Now create the actual CSS file for your theme. Put it in view/theme/mytheme/css/style.css (where we just told the software to look for it). For our example, we'll just change the body background color so you can see that it works. You can use any CSS you'd like.
body {
background-color: #DDD;
}
You've just successfully created a derived theme. This needs to be enabled in the admin "themes" panel, and then anybody on the site can use it by selecting it in Settings->Display Settings as their default theme.
**Lesson 2**
If you want to use the redbasic schemas for your derived theme, you have to do a bit more.
Do everything as above, but don't create view/theme/mytheme/php/style.php, but copy instead view/theme/redbasic/php/style.php to view/theme/mytheme/php/style.php. Modify that file and remove (or comment out) these two lines:
if(local_channel() && App::$channel && App::$channel['channel_theme'] != 'redbasic')
set_pconfig(local_channel(), 'redbasic', 'schema', '---');
Also add this line at the bottom:
echo @file_get_contents('view/theme/mytheme/css/style.css');
To show the schema selector you have to copy view/theme/redbasic/tpl/theme_settings.tpl to view/theme/mytheme/tpl/theme_settings.tpl. Modify that file and replace the lines:
{{if $theme == redbasic}}
{{include file="field_select.tpl" field=$schema}}
{{/if}}
with:
{{include file="field_select.tpl" field=$schema}}
#include doc/macros/main_footer.bb;

View file

@ -1,54 +0,0 @@
Developer Guide
===================
**Here is how you can join us.**
First, get yourself a working git package on the system where you will be
doing development.
Create your own github account.
You may fork/clone the $Projectname repository from [https://framagit.org/hubzilla/core.git](https://framagit.org/hubzilla/core.git).
Follow the instructions provided here: [http://help.github.com/fork-a-repo/](http://help.github.com/fork-a-repo/)
to create and use your own tracking fork on framagit
Then go to your framagit page and create a "Pull request" when you are ready
to notify us to merge your work.
**Translations**
Our translations are managed through Transifex. If you wish to help out translating $Projectname to another language, sign up on transifex.com, visit [https://www.transifex.com/projects/p/hubzilla/](https://www.transifex.com/projects/p/hubzilla/) and request to join one of the existing language teams or create a new one. Notify one of the core developers when you have a translation update which requires merging, or ask about merging it yourself if you're comfortable with git and PHP. We have a string file called 'messages.po' which is gettext compliant and a handful of email templates, and from there we automatically generate the application's language files.
[Translations - More Info](help/Translations)
**Important**
Please pull in any changes from the project repository and merge them with your work **before** issuing a pull request. We reserve the right to reject any patch which results in a large number of merge conflicts. This is especially true in the case of language translations - where we may not be able to understand the subtle differences between conflicting versions.
Also - **test your changes**. Don't assume that a simple fix won't break something else. If possible get an experienced Red developer to review the code.
**Licensing**
All code contributed to the project falls under the MIT license, unless otherwise specified. We will accept third-party code which falls under MIT, BSD and LGPL, but copyleft licensing (GPL, and AGPL) is only permitted in addons. It must be possible to completely remove the GPL (copyleft) code from the main project without breaking anything.
**Coding Style**
In the interests of consistency we adopt the following code styling. We may accept patches using other styles, but where possible please try to provide a consistent code style. We aren't going to argue or debate the merits of this style, and it is irrelevant what project 'xyz' uses. This is not project 'xyz'. This is a baseline to try and keep the code readable now and in the future.
* All comments should be in English.
* We use doxygen to generate documentation. This hasn't been consistently applied, but learning it and using it are highly encouraged.
* Indentation is accomplished primarily with tabs using a tab-width of 4.
* String concatenation and operators should be separated by whitespace. e.g. "$foo = $bar . 'abc';" instead of "$foo=$bar.'abc';"
* Generally speaking, we use single quotes for string variables and double quotes for SQL statements. "Here documents" should be avoided. Sometimes using double quoted strings with variable replacement is the most efficient means of creating the string. In most cases, you should be using single quotes.
* Use whitespace liberally to enhance readability. When creating arrays with many elements, we will often set one key/value pair per line, indented from the parent line appropriately. Lining up the assignment operators takes a bit more work, but also increases readability.
* Generally speaking, opening braces go on the same line as the thing which opens the brace. They are the last character on the line. Closing braces are on a line by themselves.
#include doc/macros/main_footer.bb;

View file

@ -1,108 +0,0 @@
Extra Features
==============
The default interface of $Projectname was designed to be uncluttered. There are a huge number of extra features (some of which are extremely useful) which you can turn on and get the most of the application. These are found under the [Extra Features](settings/features) link of your [Settings](settings) page.
**Content Expiration**
Remove posts/comments and/or private messages at a future time. An extra button is added to the post editor which asks you for an expiration. Typically this in "yyyy-mm-dd hh:mm" format, but in the English language you have a bit more freedom and can use most any recognisable date reference such as "next Thursday" or "+1 day". At the specified time (give or take approximately ten minutes based on the remote system's checking frequency) the post is removed.
**Multiple Profiles**
The ability to create multiple profiles which are visible only to specific persons or groups. Your default profile may be visible to anybody, but secondary profiles can all contain different or additional information and can only be seen by those to whom that profile is assigned.
**Web Pages**
Provides the ability to use web page design feaures and create custom webpages from your own content and also to design the pages with page layouts, custom menus, and content blocks.
**Private Notes**
On pages where it is available (your matrix page and personal web pages) provide a "widget" to create and store personal reminders and notes.
**Enhanced Photo Albums**
Provides a photo album viewer that is a bit prettier than the normal interface.
**Extended Identity Sharing**
By default your identity travels with you as you browse the matrix to remote sites - and they know who you are and can show you content that only you can see. With Extended Identity Sharing you can provide this information to any website you visit from within the matrix.
**Expert Mode**
This allows you to see some advanced configuration options that would confuse some people or cause support issues. In particular this can give you full control over theme features and colours - so that you can tweak a large number of settings of the display theme to your liking.
**Premium Channel**
This allows you to set restrictions and terms on those that connect with your channel. This may be used by celebrities or anybody else who wishes to describe their channel to people who wish to connect with it. In certain cases you may be asked for payment in order to connect.
**Richtext Editor**
The status post editor is plaintext, but the matrix allows a wide range of markup using BBcode. The visual editor provides "what you see is what you get" for many of the most frequently used markup tags.
**Post Preview**
Allows previewing posts and comments exactly as they would look on the page before publishing them.
**Channel Sources**
Automatically import and re-publish channel content from other channels or feeds. This allows you to create sub-channels and super-channels from content provided elsewhere. The rules are that the content must be public, and the channel owner must give you permission to source their channel.
**Even More Encryption**
Private messages are encrypted during transport and storage. In this day and age, this encyption may not be enough if your communications are extremely sensitive. This options lets you provide optional encryption of content "end-to-end" with a shared secret key. How the recipient learns the secret key is completely up to you. You can provide a hint such as "the name of aunt Claire's first dog".
**Search by Date**
This provides the ability to select posts by date ranges
**Privacy Group Filter**
Enable widget to display stream posts only from selected groups of connection. This also toggles the outbound permissions while you are viewing a privacy group. This is analogous to Google "circles" or Disapora "aspects".
**Saved Searches**
Provides a search widget on your matrix page which can save selected search terms for re-use.
**Personal Tab**
Enable tab to display only matrix posts that you've interacted with in some way, as an author or a contributor to the conversation.
**New Tab**
Enables a tab to display all new matrix activity as a firehose or timeline.
**Affinity Tool**
Filter matrix stream activity by the depth of your relationships
**Edit Sent Posts**
Edit and correct posts and comments after sending
**Tagging**
Ability to tag existing posts, including those written by others.
**Post Categories**
Add categories to your channel posts
**Saved Folders**
Ability to file posts under folders or tags for later recall
**Dislike Posts**
Ability to dislike posts/comments
**Star Posts**
Ability to mark special posts with a star indicator
**Tag Cloud**
Provide a personal tag cloud on your channel page
#include doc/macros/main_footer.bb;

View file

@ -1,105 +0,0 @@
[b]$Projectname on OpenShift[/b]
You will notice a new .openshift folder when you fetch from upstream, i.e. from [url=https://framagit.org/hubzilla/core.git]https://framagit.org/hubzilla/core.git[/url] , which contains a deploy script to set up Hubzilla on OpenShift with plugins and extra themes.
As of this writing, 2015-10-28, you do not have to pay for OpenShift on the Free plan, which gives you three gears at no cost. The Bronze plan gives you three gears at no cost too, but you can expand to 16 gears by paying, and this requires you to register your payment card. The three gears can give three instances of Hubzilla with one gear each, or you can combine two gears into one high-availability Hubzilla instance and one extra gear. The main difference to be aware of is this: gears on the Free plan will go into hibernation if left idle for too long, this does not happen on the Bronze plan.
Create an account on OpenShift, then use the registration e-mail and password to create your first Hubzilla instance. Install git and RedHat's command line tools - rhc - if you have not already done so. See for example https://developers.openshift.com/en/getting-started-debian-ubuntu.html on how to do this on Debian GNU/Linux, or in the menu on that page for other GNU/Linux distributions or other operating systems.
[code]rhc app-create your_app_name php-5.4 mysql-5.5 cron phpmyadmin --namespace your_domain --from-code https://framagit.org/hubzilla/core.git -l your@email.address -p your_account_password
[/code]
Make a note of the database username and password OpenShift creates for your instance, and use these at [url=https://your_app_name-your_domain.rhcloud.com/]https://your_app_name-your_domain.rhcloud.com/[/url] to complete the setup. You MUST change server address from 127.0.0.1 to localhost.
NOTE: PostgreSQL is NOT supported by the deploy script yet, see [zrl=https://zot-mor.rhcloud.com/display/3c7035f2a6febf87057d84ea0ae511223e9b38dc27913177bc0df053edecac7c@zot-mor.rhcloud.com?zid=haakon%40zot-mor.rhcloud.com]this thread[/zrl].
[b]Update[/b]
To update, consider your own workflow first. I have forked Hubzilla code into my GitHub account to be able to try things out, this remote repo is called origin. Here is how I fetch new code from upstream, merge into my local repo, then push the updated code both into origin and the remote repo called openshift.
[code]git fetch upstream;git checkout master;git merge upstream/master;git push origin;git push openshift HEAD
[/code]
[b]Administration[/b]
Symptoms of need for MySQL database administration are:
[list]
[*] you can visit your domain and see the Hubzilla frontpage, but trying to login throws you back to login. This can mean your session table is marked as crashed.
[*] you can login, but your channel posts are not visible. This can mean your item table is marked as crashed.
[*] you can login and you can see your channel posts, but apparently nobody is getting your posts, comments, likes and so on. This can mean your outq table is marked as crashed.
[/list]
You can check your OpenShift logs by doing
[code]
rhc tail -a your_app_name -n your_domain -l your@email.address -p your_account_password
[/code]
and you might be able to confirm the above suspicions about crashed tables, or other problems you need to fix.
[b]How to fix crashed tables in MySQL[/b]
Using MySQL and the MyISAM database engine can result in table indexes coming out of sync, and you have at least two options for fixing tables marked as crashed.
[list]
[*] Use the database username and password OpenShift creates for your instance at [url=https://your_app_name-your_domain.rhcloud.com/phpmyadmin/]https://your_app_name-your_domain.rhcloud.com/phpmyadmin/[/url] to login via the web into your phpMyAdmin web interface, click your database in the left column, in the right column scroll down to the bottom of the list of tables and click the checkbox for marking all tables, then select Check tables from the drop down menu. This will check the tables for problems, and you can then checkmark only those tables with problems, and select Repair table from the same drop down menu at the bottom.
[*] You can port-forward the MySQL database service to your own machine and use the MySQL client called mysqlcheck to check, repair and optimize your database or individual database tables without stopping the MySQL service on OpenShift. Run the following in two separate console windows.
To port-forward do
[code]rhc port-forward -a your_app_name -n your_domain -l your@email.address -p your_password[/code]
in one console window, then do either -o for optimize, -c for check or -r for repair, like this
[code]mysqlcheck -h 127.0.0.1 -r your_app_name -u your_app_admin_name -p[/code]
and give the app's password at the prompt. If all goes well you should see a number of table names with an OK behind them.
You can now
[code]Press CTRL-C to terminate port forwarding[/code]
[*] You can do
[code]rhc cartridge stop mysql-5.5 -a your_app_name[/code]
to stop the MySQL service running in your app on OpenShift before running myisamchk - which should only be run when MySQL is stopped, and then
login to your instance with SSH - see OpenShift for details - and do
[code]cd mysql/data/your_database
myisamchk -r *.MYI[/code]
or if you get
[code]Can't create new tempfile[/code]
check your OpenShift's gear quota with
[code]quota -gus[/code]
and if you are short on space, then locally (not SSH) do
[code]rhc app-tidy your_app_name -l your_login -p your_password[/code]
to have rhc delete temporary files and OpenShift logs to free space first, then check the size of your local repo dir and execute
[code]git gc[/code]
against it and check the size again, and then to minimize your remote repo connect via SSH to your application gear and execute the same command against it by changing to the remote repo directory - your repo should be in
[code]~/git/your_app_name.git[/code]
(if not, do find -size +1M to find it), then do
[code]
cd
cd mysql/data/yourdatabase
myisamchk -r -v -f*.MYI[/code]
and hopefully your database tables are now okay.
You can now start the MySQL service on OpenShift by locally doing
[code]rhc cartridge start mysql-5.5 -a your_app_name[/code]
[/list]
[b]Notes[/b]
[list]
[*] definitely DO turn off feeds and discovery by default and limit delivery reports from 30 days to 3 days if you are on the Free or Bronze plan on OpenShift with a single 1Gb gear by visiting [observer.baseurl]/admin/site when logged in as administrator of your Hubzilla site.
[*] The above defaults have been added into the deploy script.
[*] DO add git gc to the deploy script
[*] MAYBE DO add myisamchk - only checking? to the end of the deploy script.
[*] mysqlcheck is similar in function to myisamchk, but works differently. The main operational difference is that mysqlcheck must be used when the mysqld server is running, whereas myisamchk should be used when it is not. The benefit of using mysqlcheck is that you do not have to stop the server to perform table maintenance - this means this documenation should be fixed.
[/list]

View file

@ -1,263 +0,0 @@
Creating Plugins/Addons for $Projectname
==========================================
So you want to make $Projectname do something it doesn't already do. There are lots of ways. But let's learn how to write a plugin or addon.
In your $Projectname folder/directory, you will probably see a sub-directory called 'addon'. If you don't have one already, go ahead and create it.
mkdir addon
Then figure out a name for your addon. You probably have at least a vague idea of what you want it to do. For our example I'm going to create a plugin called 'randplace' that provides a somewhat random location for each of your posts. The name of your plugin is used to find the functions we need to access and is part of the function names, so to be safe, use only simple text characters.
Once you've chosen a name, create a directory beneath 'addon' to hold your working file or files.
mkdir addon/randplace
Now create your plugin file. It needs to have the same name, and it's a PHP script, so using your favourite editor, create the file
addon/randplace/randplace.php
The very first line of this file needs to be
<?php
Then we're going to create a comment block to describe the plugin. There's a special format for this. We use /* ... */ comment-style and some tagged lines consisting of
/**
*
* Name: Random Place (here you can use better descriptions than you could in the filename)
* Description: Sample $Projectname plugin, Sets a random place when posting.
* Version: 1.0
* Author: Mike Macgirvin <mike@zothub.com>
*
*/
These tags will be seen by the site administrator when he/she installs or manages plugins from the admin panel. There can be more than one author. Just add another line starting with 'Author:'.
The typical plugin will have at least the following functions:
* pluginname_load()
* pluginname_unload()
In our case, we'll call them randplace_load() and randplace_unload(), as that is the name of our plugin. These functions are called whenever we wish to either initialise the plugin or remove it from the current webpage. Also if your plugin requires things like altering the database schema before it can run for the very first time, you would likely place these instructions in the functions named
* pluginname_install()
* pluginname_uninstall()
Next we'll talk about **hooks**. Hooks are places in $Projectname code where we allow plugins to do stuff. There are a [lot of these](help/Hooks), and they each have a name. What we normally do is use the pluginname_load() function to register a "handler function" for any hooks you are interested in. Then when any of these hooks are triggered, your code will be called.
We register hook handlers with the 'register_hook()' function. It takes 3 arguments. The first is the hook we wish to catch, the second is the filename of the file to find our handler function (relative to the base of your $Projectname installation), and the third is the function name of your handler function. So let's create our randplace_load() function right now.
function randplace_load() {
register_hook('post_local', 'addon/randplace/randplace.php', 'randplace_post_hook');
register_hook('feature_settings', 'addon/randplace/randplace.php', 'randplace_settings');
register_hook('feature_settings_post', 'addon/randplace/randplace.php', 'randplace_settings_post');
}
So we're going to catch three events, 'post_local' which is triggered when a post is made on the local system, 'feature_settings' to set some preferences for our plugin, and 'feature_settings_post' to store those settings.
Next we'll create an unload function. This is easy, as it just unregisters our hooks. It takes exactly the same arguments.
function randplace_unload() {
unregister_hook('post_local', 'addon/randplace/randplace.php', 'randplace_post_hook');
unregister_hook('feature_settings', 'addon/randplace/randplace.php', 'randplace_settings');
unregister_hook('feature_settings_post', 'addon/randplace/randplace.php', 'randplace_settings_post');
}
Hooks are called with two arguments. The first is always $a, which is our global App structure and contains a huge amount of information about the state of the web request we are processing; as well as who the viewer is, and what our login state is, and the current contents of the web page we're probably constructing.
The second argument is specific to the hook you're calling. It contains information relevant to that particular place in the program, and often allows you to look at, and even change it. In order to change it, you need to add '&' to the variable name so it is passed to your function by reference. Otherwise it will create a copy and any changes you make will be lost when the hook process returns. Usually (but not always) the second argument is a named array of data structures. Please see the "hook reference" (not yet written as of this date) for details on any specific hook. Occasionally you may need to view the program source to see precisely how a given hook is called and how the results are processed.
Let's go ahead and add some code to implement our post_local hook handler.
function randplace_post_hook($a, &$item) {
/**
*
* An item was posted on the local system.
* We are going to look for specific items:
* - A status post by a profile owner
* - The profile owner must have allowed our plugin
*
*/
logger('randplace invoked');
if(! local_channel()) /* non-zero if this is a logged in user of this system */
return;
if(local_channel() != $item['uid']) /* Does this person own the post? */
return;
if(($item['parent']) || (! is_item_normal($item))) {
/* If the item has a parent, or isn't "normal", this is a comment or something else, not a status post. */
return;
}
/* Retrieve our personal config setting */
$active = get_pconfig(local_channel(), 'randplace', 'enable');
if(! $active)
return;
/**
*
* OK, we're allowed to do our stuff.
* Here's what we are going to do:
* load the list of timezone names, and use that to generate a list of world cities.
* Then we'll pick one of those at random and put it in the "location" field for the post.
*
*/
$cities = array();
$zones = timezone_identifiers_list();
foreach($zones as $zone) {
if((strpos($zone,'/')) && (! stristr($zone,'US/')) && (! stristr($zone,'Etc/')))
$cities[] = str_replace('_', ' ',substr($zone,strpos($zone,'/') + 1));
}
if(! count($cities))
return;
$city = array_rand($cities,1);
$item['location'] = $cities[$city];
return;
}
Now let's add our functions to create and store preference settings.
/**
*
* Callback from the settings post function.
* $post contains the global $_POST array.
* We will make sure we've got a valid user account
* and that only our own submit button was clicked
* and if so set our configuration setting for this person.
*
*/
function randplace_settings_post($a,$post) {
if(! local_channel())
return;
if($_POST['randplace-submit'])
set_pconfig(local_channel(),'randplace','enable',intval($_POST['randplace']));
}
/**
*
* Called from the Feature Setting form.
* The second argument is a string in this case, the HTML content region of the page.
* Add our own settings info to the string.
*
* For uniformity of settings pages, we use the following convention
* <div class="settings-block">
* <h3>title</h3>
* .... settings html - many elements will be floated...
* <div class="clear"></div> <!-- generic class which clears all floats -->
* <input type="submit" name="pluginnname-submit" class="settings-submit" ..... />
* </div>
*/
function randplace_settings(&$a,&$s) {
if(! local_channel())
return;
/* Add our stylesheet to the page so we can make our settings look nice */
head_add_css('/addon/randplace/randplace.css');
/* Get the current state of our config variable */
$enabled = get_pconfig(local_channel(),'randplace','enable');
$checked = (($enabled) ? ' checked="checked" ' : '');
/* Add some HTML to the existing form */
$s .= '<div class="settings-block">';
$s .= '<h3>' . t('Randplace Settings') . '</h3>';
$s .= '<div id="randplace-enable-wrapper">';
$s .= '<label id="randplace-enable-label" for="randplace-checkbox">' . t('Enable Randplace Plugin') . '</label>';
$s .= '<input id="randplace-checkbox" type="checkbox" name="randplace" value="1" ' . $checked . '/>';
$s .= '</div><div class="clear"></div>';
/* provide a submit button */
$s .= '<div class="settings-submit-wrapper" ><input type="submit" name="randplace-submit" class="settings-submit" value="' . t('Submit') . '" /></div></div>';
}
***Advanced Plugins***
Sometimes your plugins want to provide a range of new functionality which isn't provided at all or is clumsy to provide using hooks. In this case your plugin can also act as a 'module'. A module in our case refers to a structured webpage handler which responds to a given URL. Then anything which accesses that URL will be handled completely by your plugin.
The key to this is to create a simple function named pluginname_module() which does nothing.
function randplace_module() { return; }
Once this function exists, the URL https://yoursite/randplace will access your plugin as a module. Then you can define functions which are called at various points to build a webpage just like the modules in the mod/ directory. The typical functions and the order which they are called is
modulename_init($a) // (e.g. randplace_init($a);) called first - if you wish to emit json or xml,
// you should do it here, followed by killme() which will avoid the default action of building a webpage
modulename_aside($a) // Often used to create sidebar content
modulename_post($a) // Called whenever the page is accessed via the "post" method
modulename_content($a) // called to generate the central page content. This function should return a string
// consisting of the central page content.
Your module functions have access to the URL path as if they were standalone programs in the Unix operating system. For instance if you visit the page
https://yoursite/randplace/something/somewhere/whatever
we will create an argc/argv list for use by your module functions
$x = argc(); $x will be 4, the number of path arguments after the sitename
for($x = 0; $x < argc(); $x ++)
echo $x . ' ' . argv($x);
0 randplace
1 something
2 somewhere
3 whatever
***Porting Friendica Plugins***
$Projectname uses a similar plugin architecture to the Friendica project. The authentication, identity, and permissions systems are completely different. Many Friendica plugins can be ported reasonably easily by renaming a few functions - and then ensuring that the permissions model is adhered to. The functions which need to be renamed are:
* Friendica's pluginname_install() is pluginname_load()
* Friendica's pluginname_uninstall() is pluginname_unload()
$Projectname has _install and _uninstall functions but these are used differently.
* Friendica's "plugin_settings" hook is called "feature_settings"
* Friendica's "plugin_settings_post" hook is called "feature_settings_post"
Changing these will often allow your plugin to function, but please double check all your permission and identity code because the concepts behind it are completely different in $Projectname. Many structured data names (especially DB schema columns) are also quite different.
#include doc/macros/main_footer.bb;

View file

@ -1,47 +0,0 @@
#Primary Directory#
By default, $Projectname will use available Directories on the web, which show you channels available around the world.
There are certain scenarios where you might want your own directory-server that you can connect multiple hubs to. This will limit the channels that appear in all of your hubs to only channels on hubs connected to your directory-server.
##Instuctions on how to set up one hub as the Primary Directory for a series of private hubs.##
***
* On the hub that will be the Directory Server, open the .htconfig.php file and set:
`App::$config['system']['directory_mode'] = DIRECTORY_MODE_PRIMARY;`
By default it should already be set as **DIRECTORY_MODE_NORMAL**, so just edit that line to say **DIRECTORY_MODE_PRIMARY**
* Next, for each hub (including the Directory Server), from a terminal, cd into the folder where it is installed and run this :
`util/config system directory_realm YOURREALMNAME`
(**YOURREALMNAME** can be whatever you want your realm-name to be)
then:
`util/config system realm_token THEPASSWORD`
(**THEPASSWORD** is whatever password you want for your realm)
**NOTE:** Use the same realm-name and password for each hub
* Lastly, for each "client" hub, (from a terminal) run:
`util/config system directory_server https://theaddressofyourdirectoryserver.com`
***
Now when you view the directory of each hub, it should only show the channels that exist on the hubs in your realm. I have tested with two hubs so far, and it seems to be working fine.
Channels created in each hub are reflected in the Primary Directory, and subsequently in the directory of all client hubs
##Issues##
***
When I created the first hub,it was up and running for an hour or so before I changed it to PRIMARY_MODE, and after changing it, there were a few channels from across the matrix still present in the directory. I deleted them from the xchan table and that seems to have fixed the issue.

View file

@ -1,29 +0,0 @@
![Hubzilla](images/hubzilla-banner.png)
Hubzilla - Community Server
===========================
<p align="center" markdown="1">
<em><a href="https://framagit.org/hubzilla/core/blob/master/install/INSTALL.txt">Installing Hubzilla</a></em>
</p>
**What is Hubzilla?**
Hubzilla is a general purpose communication server integrated with a web publishing system and a decentralised permission system. If this sounds like a bunch of technical mumbo-jumbo to you, just think of it as an independent platform for sharing stuff online.
Hubzilla contains some social network bits, some cloud storage bits, some blog and forum bits, and some content management bits. These are all integrated within a common privacy framework - and it is all decentralised.
Everything you publish or share can be restricted to those channels and people you wish to share them with; and these permissions work completely invisibly - even with channels on different servers or other communications services.
Migration and live backups of your connections, settings, and everything you publish are built-in, so you never need worry about server failure.
Hubzilla is completely decentralised and open source, for you modify or adapt to your needs and desires. Plugins, themes, and numerous configuration options extend the overall capabilities to do anything you can imagine.
**Who Are We?**
The Hubzilla community consists of passionate volunteers creating an open source commons of decentralised services which are highly integrated and can rival the feature set of large centralised providers. We do our best to provide ethical software which places you in control of your online communications and privacy expectations.
[![Build Status](https://travis-ci.org/redmatrix/hubzilla.svg)](https://travis-ci.org/redmatrix/hubzilla)

View file

@ -1,26 +0,0 @@
// this page is out of date...
Remove Account
==============
It is presently not possible to remove an account without asking your site administrator for assistance.
We'll have better doco when somebody finishes that bit.
Remove Channel
==============
Visit the URL
https://yoursite/removeme
(replace 'yoursite' with the domain name of your $Projectname site).
You will need to confirm your password and the channel you are currently logged into will be removed.
This is irreversible.
If you have identity clones on other sites this only removes the channel instance which exists
on this site.
#include doc/macros/main_footer.bb;

View file

@ -1,81 +0,0 @@
Red development - a guide to the schema system
==============================================
A schema, in a nutshell, is a collection of settings for a bunch of variables to define
certain elements of a theme. A schema is loaded as though it were part of config.php
and has access to all the same information. Importantly, this means it is identity aware,
and can be used to do some interesting things. One could, for example, restrict options
by service class, or present different options to different members.
By default, we filter only by whether or not expert mode is enabled. If expert mode is
enabled, all options are presented to the member. If it is not, only scheme, background
image, font face, and iconset are available as choices.
A schema is loaded *after* the member's personal settings. Therefore, to allow a member
to overwrite a particular aspect of a schema you would use the following syntax:
if (! $foo)
$foo = 'bar';
However, there are circumstances - particularly with positional elements - where it
may be desirable (or necessary) to override a member's settings. In this case, the syntax
is even simpler:
$foo = 'bar';
Members will not thank you for this, however, so only use it when it is required.
If no personal options are set, and no schema is selected, we will first try to load a schema
with the file name "default.php". This file should never be included with a theme. If it
is, merge conflicts will occur as people update their code. Rather, this should be defined
by administrators on a site by site basis.
default.php and default.css MUST be symlinks to existing scheme files.
You schema does not need to - and should not - contain all of these values. Only the values
that differ from the defaults should be listed. This gives you some very powerful options
with very few lines of code.
Note the options available differ with each theme. The options available with the Redbasic
theme are as follows:
* nav_colour
The colour of the navigation bar. Options are red, black and silver. Alternatively,
one can set $nav_bg_1, $nav_bg_2, $nav_bg_3 and $nav_bg_4 to provide gradient and
hover effects.
* banner_colour
The font colour of the banner element. Accepts an RGB or Hex value.
* bgcolour
Set the body background colour. Accepts an RGB or Hex value.
* background_image
Sets a background image. Accepts a URL or path.
* item_colour
Set the background colour of items. Accepts an RGB or Hex value.
* item_opacity
Set the opacity of items. Accepts a value from 0.01 to 1
* toolicon_colour
Set the colour of tool icons. Accepts an RGB or Hex value.
* toolicon_activecolour
Set the colour of active or hovered icon tools.
* font_size
Set the size of fonts in items and posts. Accepts px or em.
* body_font_size
Sets the size of fonts at the body level. Accepts px or em.
* font_colour
Sets the font colour. Accepts an RGB or Hex value.
* radius
Set the radius of corners. Accepts a numeral, and is always in px.
* shadow
Set the size of shadows shown with inline images. Accepts a numerical
value. Note shadows are not applied to smileys.
* converse_width
Set the maximum width of the content region in px.
* nav_min_opacity
* top_photo
* reply_photo
If a your_schema_name.css file is found, the content of this file will be attached to the end of style.css.
This gives the schem developer the possiblity to override any style component.
#include doc/macros/main_footer.bb;

View file

@ -1,11 +0,0 @@
Privacy Policy
==============
#include doc/gdpr1.md;
Terms of Service
================
#include doc/SiteTOS.md;

View file

@ -1,93 +0,0 @@
Translating $Projectname
==========================
Translation Process
-------------------
The strings used in the UI of $Projectname is translated at [Transifex][1] and then
included in the git repository at github. If you want to help with translation
for any language, be it correcting terms or translating $Projectname to a
currently not supported language, please register an account at transifex.com
and contact the Redmatrix translation team there.
Translating $Projectname is simple. Just use the online tool at transifex. If you
don't want to deal with git & co. that is fine, we check the status of the
translations regularly and import them into the source tree at github so that
others can use them.
We do not include every translation from transifex in the source tree to avoid
a scattered and disturbed overall experience. As an uneducated guess we have a
lower limit of 50% translated strings before we include the language. This
limit is judging only by the amount of translated strings under the assumption
that the most prominent strings for the UI will be translated first by a
translation team. If you feel your translation useable before this limit,
please contact us and we will probably include your teams work in the source
tree.
If you want to get your work into the source tree yourself, feel free to do so
and contact us with and question that arises. The process is simple and
$Projectname ships with all the tools necessary.
The location of the translated files in the source tree is
/view/LNG-CODE/
where LNG-CODE is the language code used, e.g. de for German or fr for French.
For the email templates (the *.tpl files) just place them into the directory
and you are done. The translated strings come as a "hmessages.po" file from
transifex which needs to be translated into the PHP file $Projectname uses. To do
so, place the file in the directory mentioned above and use the "po2php"
utility from the util directory of your $Projectname installation.
Assuming you want to convert the German localization which is placed in
view/de/hmessages.po you would do the following.
1. Navigate at the command prompt to the base directory of your
$Projectname installation
2. Execute the po2php script, which will place the translation
in the hstrings.php file that is used by $Projectname.
$> php util/po2php.php view/de/hmessages.po
The output of the script will be placed at view/de/hstrings.php where
froemdoca os expecting it, so you can test your translation mmediately.
3. Visit your $Projectname page to check if it still works in the language you
just translated. If not try to find the error, most likely PHP will give
you a hint in the log/warnings.about the error.
For debugging you can also try to "run" the file with PHP. This should
not give any output if the file is ok but might give a hint for
searching the bug in the file.
$> php view/de/hstrings.php
4. commit the two files with a meaningful commit message to your git
repository, push it to your fork of the $Projectname repository at github and
issue a pull request for that commit.
Utilities
---------
Additional to the po2php script there are some more utilities for translation
in the "util" directory of the $Projectname source tree. If you only want to
translate $Projectname into another language you wont need any of these tools most
likely but it gives you an idea how the translation process of $Projectname
works.
For further information see the utils/README file.
Known Problems
--------------
* $Projectname uses the language setting of the visitors browser to determain the
language for the UI. Most of the time this works, but there are some known
quirks.
* the early translations are based on the friendica translations, if you
some rough translations please let us know or fix them at Transifex.
Links
------
[1]: http://www.transifex.com/projects/p/hubzilla/
#include doc/macros/main_footer.bb;

View file

@ -1,174 +0,0 @@
Core Widgets
============
Some/many of these widgets have restrictions which may restrict the type of page where they may appear or may require login
* clock - displays the current time
* args: military (1 or 0) - use 24 hour time as opposed to AM/PM
<br />&nbsp;<br />
* profile - displays a profile sidebar on pages which load profiles (pages with nickname in the URL)
* tagcloud - display a tagcloud of webpage items
* args: count - number of items to return (default 24)
<br />&nbsp;<br />
* collections - privacy group selector for the current logged in channel
* args: mode - one of "conversation", "group", "abook" depending on module
<br />&nbsp;<br />
* suggestions - friend suggestions for the current logged on channel
* follow - presents a text box for following another channel
* notes - private notes area for the current logged in channel if private_notes feature is enabled
* savedsearch - network/matrix search with save - must be logged in and savedsearch feature enabled
* filer - select filed items from network/matrix stream - must be logged in
* archive - date range selector for network and channel pages
* args: 'wall' - 1 or 0, limit to wall posts or network/matrix posts (default)
<br />&nbsp;<br />
* fullprofile - same as profile currently
* categories - categories filter (channel page)
* tagcloud_wall - tagcloud for channel page only
* args: 'limit' - number of tags to return (default 50)
<br />&nbsp;<br />
* catcloud_wall - tagcloud for channel page categories
* args: 'limit' - number of categories to return (default 50)
<br />&nbsp;<br />
* affinity - affinity slider for network page - must be logged in
* settings_menu - sidebar menu for settings page, must be logged in
* mailmenu - sidebar menu for private message page - must be logged in
* design_tools - design tools menu for webpage building pages, must be logged in
* findpeople - tools to find other channels
* photo_albums - list photo albums of the current page owner with a selector menu
* vcard - mini profile sidebar for the person of interest (page owner, whatever)
* dirsafemode - directory selection tool - only on directory pages
* dirsort - directory selection tool - only on directory pages
* dirtags - directory tool - only on directory pages
* menu_preview - preview a menu - only on menu edit pages
* chatroom_list - list of chatrooms for the page owner
* bookmarkedchats - list of bookmarked chatrooms collected on this site for the current observer
* suggestedchats - "interesting" chatrooms chosen for the current observer
* item - displays a single webpage item by mid or page title
* args:
* channel_id - channel that owns the content, defualt is the profile_uid
* mid - message_id of webpage to display (must be webpage, not a conversation item)
* title - URL page title of webpage (must provide one of either title or mid)
<br />&nbsp;<br />
* photo - display a single photo
* args:
* url - URL of photo, must be http or https
* zrl - use zid authenticated link
* style - CSS style string
<br />&nbsp;<br />
* cover_photo - display the cover photo for the selected channel
* args:
* channel_id - channel to use, default is the profile_uid
* style - CSS style string (default is dynamically resized to width of region)
<br />&nbsp;<br />
* photo_rand - display a random photo from one of your photo albums. Photo permissions are honoured
* args:
* album - album name (very strongly recommended if you have lots of photos)
* scale - typically 0 (original size), 1 (1024px), 2, (640px), or 3 (320px)
* style - CSS style string
* channel_id - if not your own
<br />&nbsp;<br />
* random_block - display a random block element from your webpage design tools collection. Permissions are honoured.
* args:
* contains - only return blocks which include the contains string in the block name
* channel_id - if not your own
<br />&nbsp;<br />
* tasklist - provide a task or to-do list for the currently logged-in channel.
* args:
* all - display completed tasks if all is non-zero.
<br />&nbsp;<br />
* forums - provide a list of connected public forums with unseen counts for the current logged-in channel.
<br />&nbsp;<br />
* activity - provide a list of authors of unread network content for the current logged-in channel.
* album - provides a widget containing a complete photo album from albums belonging to the page owner; this may be too large to present in a sidebar region as is best implemented as a content region widget.
* args:
* album - album name
* title - optional title, album name is used if not present
<br />&nbsp;<br />
Creating New Widgets
====================
### Class Widgets
To create a class-based widget named 'slugfish' create a file with the following contents:
````
<?php
namespace Zotlabs\Widget;
class Slugfish {
function widget($args) {
... widget code goes here.
... The function returns a string which is the HTML content of the widget.
... $args is a named array which is passed any [var] variables from the layout editor
... For instance [widget=slugfish][var=count]3[/var][/widget] will populate $args with
... [ 'count' => 3 ]
}
````
The resultant file may be placed in widget/Slugfish/Slugfish.php , or Zotlabs/SiteWidgets/Slugfish.php . It also may be linked from a git repository using util/add_widget_repo.
Traditional function based widget:
If you want a widget named 'slugfish', create widget/widget_slugfish.php containing
<?php
function widget_slugfish($args) {
.. widget code goes here. See above information for class-based widgets for details.
}
#include doc/macros/main_footer.bb;

View file

@ -1,109 +0,0 @@
# Zot - A High Level Overview
Here's a high level description of how zot works.
In this example, "Indigo" is going to send a public message from his website at "podunk.edu". "Nickordo" is a recipient on another site ("example.com").
Indigo first posts his message at podunk.edu. podunk.edu looks up who should receive the message and finds Nickordo. Nickordo usually posts from example.com so we add that destination to our list of recipients. We may also add other destinations for nickordo and anybody else that is following Indigo's posts.
In this example we find that we only have one known recipient at one known location.
We send a packet to example.com:
{
"type":"notify",
"sender":{
"guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
"guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
"url":"http:\/\/podunk.edu",
"url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
},
"callback":"\/post",
"version":1,
"secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
}
This packet says the following:
I'm Indigo and here is proof. I'm posting from podunk.edu and here is proof. I've got a package for you. The tracking number is "1eaa6613....".
Example.com accepts this packet and says "whoa, hold on - I don't know you. I want to prove who you are." So Example.com connects to podunk.edu through a "well-known URL" that we use for this purpose and looks up the "guid" mentioned above. It should return a bunch of information, one item of which is a public key. Example.com uses this key to verify the signatures in the message to verify that indeed there is a person named Indigo at podunk.edu. We only need to do this once. (Note that Indigo can post from any location. All we have to do is prove that it's Indigo and that Indigo can prove that he's posting from another site.)
Then example.com disconnects and flags that there's a message waiting at podunk.edu. Either immediately, or whenever the urge hits (depending on how important Indigo is to anybody on this site), example.com "calls" podunk.edu. It says something like this:
{
"type":"pickup",
"url":"http:\/\/example.com",
"callback_sig":"teE1_fLIqfyeCuZY4iS7sNU8jUlUuqYOYBiHLarkC99I9K-uSr8DAwVW8ZPZRK-uYdxRMuKFb6cumF_Gt9XjecCPBM8HkoXHOi_VselzJkxPwor4ZPtWYWWaFtRfcAm794LrWjdz62zdESTQd2JJIZWbrli1sUhK801BF3n0Ye6-X1MWhy9EUTVlNimOeRipcuD_srMhUcAXOEbLlrugZ8ovy2YBe6YOXkS8jj0RSFjsOduXAoVhQmNpcobSYsDvaQS3e3MvE6-oXE602zGQhuNLr7DIMt9PCdAeQo-ZM-DHlZGCkGk4O2oQFCXFzGPqLUMWDACGJfTfIWGoh_EJqT_SD5b_Yi_Wk9S1lj7vb-lmxe5JuIf7ezWzHoBT8vswnZxPYlidH2i9wapdzij9il_qqcCWWHIp7q_XkY_Zj52Z4r4gdmiqM-8y1c_1SDX7hrJFRwqL_PKFbEvyi5nMWTEzqp55Tay5Woiv19STK_H_8ufFfD9AOkYnk6rIOMsk9dn3a5tAFpDRyRndXkBWAXwiJjiND2zjue7BFu7Ty40THXcfYRh1a5XrAXcaGeYuagg-8J9tAufu9_LY3qGazFg8kRBVMOn4M8DRKSIhKj7z4MnbYL0s09gREojy4jqWO3VkaOjP2jUGzoPuUDLasudE1ehWFq0K_MTQNavgmp8",
"callback":"http:\/\/example.com\/post",
"secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467",
"secret_sig":"O7nB4_UJHBXi28Suwl9LBZF9hI_9KGVTgehnUlWF1oYMNRnBbVHB9lzUfAoalvp3STbU3xJbtD_S58tv6MfV7J5j2V_S1W5ex3dulmDGB8Pt_7Fe5mbEPmjQFcfv3Eg5dUjYIuDl0TDScfrHyImj7RZIWHbwd7wWVoMzzDa_o33klpYmKZCBvObCh55bRrlFkXZs_dRuOiPwkfX0C6_XES4OyOIYl45V30rdhmf-STrf4L9dKYy_axQ12RIwRcKychvVLwlUJn3bn9lgNXRRU_HTne-09OPcJbUOdcD3DkFoKOxMULBNKPHzsCau0ICYug7S0EP6LpCom_mW78s08LyVA1vYeFZjevBCiGecj57yIAQDYi6_rpWJfihYaWHRN0oqtScUR4Bdf0bQbEHxMs4zAtrOAxfyJCbi6U1pfnGgzXzB9ulOYGnVGNTF7Ey4K7FOZIBtk0ILY2JfvBUaVvVs8ttagOOHmhWhnbCvrnOFlkNdlce7zoJCSUJENUOCYmTRfwB_Jno5fAzRnrsYU3_Z-l1mzniU_OmUPz8mPEh7PwhkqAiVlyaM-q15gn7l2lAIDk9kp2X_iCme7v4V0ADN_DbpaI_0-6mPw5HLbKrCsA-sxlSMB4DO4lDCHYkauj0l25sbfroRWB_hax1O4Q0oWyOlVJLUqEC5nuUJCCE"
}
What this message says is: This is example.com, I have proof, and I'm here to pick up a package. Here's the tracking number, and here's proof that this is the tracking number you presumably sent to example.com.
Good enough. Podunk.edu checks out the story and indeed, it is example.com, and yes, there's a package waiting with that tracking number. Here's the package...
{
"success":1,
"pickup":{
"notify":{
"type":"notify",
"sender":{
"guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
"guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q",
"url":"http:\/\/z.podunk.edu",
"url_sig":"T8Bp7j5DHHhQDCFcAHXfuhUfGk2P3inPbImwaXXF1xJd3TGgluoXyyKDx6WDm07x0hqbupoAoZB1qBP3_WfvWiJVAK4N1FD77EOYttUEHZ7L43xy5PCpojJQmkppGbPJc2jnTIc_F1vvGvw5fv8gBWZvPqTdb6LWF6FLrzwesZpi7j2rsioZ3wyUkqb5TDZaNNeWQrIEYXrEnWkRI_qTSOzx0dRTsGO6SpU1fPWuOOYMZG8Nh18nay0kLpxReuHCiCdxjXRVvk5k9rkcMbDBJcBovhiSioPKv_yJxcZVBATw3z3TTE95kGi4wxCEenxwhSpvouwa5b0hT7NS4Ay70QaxoKiLb3ZjhZaUUn4igCyZM0h6fllR5I6J_sAQxiMYD0v5ouIlb0u8YVMni93j3zlqMWdDUZ4WgTI7NNbo8ug9NQDHd92TPmSE1TytPTgya3tsFMzwyq0LZ0b-g-zSXWIES__jKQ7vAtIs9EwlPxqJXEDDniZ2AJ6biXRYgE2Kd6W_nmI7w31igwQTms3ecXe5ENI3ckEPUAq__llNnND7mxp5ZrdXzd5HHU9slXwDShYcW3yDeQLEwAVomTGSFpBrCX8W77n9hF3JClkWaeS4QcZ3xUtsSS81yLrp__ifFfQqx9_Be89WVyIOoF4oydr08EkZ8zwlAsbZLG7eLXY"
},
"callback":"\/post",
"version":1,
"secret":"1eaa6613699be6ebb2adcefa5379c61a3678aa0df89025470fac871431b70467"
},
"message":{
"message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
"message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
"message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu",
"created":"2012-11-20 04:04:16",
"edited":"2012-11-20 04:04:16",
"title":"",
"body":"Hi Nickordo",
"app":"",
"verb":"post",
"object_type":"",
"target_type":"",
"permalink":"",
"location":"",
"longlat":"",
"owner":{
"name":"Indigo",
"address":"indigo@podunk.edu",
"url":"http:\/\/podunk.edu",
"photo":{
"mimetype":"image\/jpeg",
"src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
},
"guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
"guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
},
"author":{
"name":"Indigo",
"address":"indigo@podunk.edu",
"url":"http:\/\/podunk.edu",
"photo":{
"mimetype":"image\/jpeg",
"src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
},
"guid":"kgVFf_1_SSbyqH-BNWjWuhAvJ2EhQBTUdw-Q1LwwssAntr8KTBgBSzNVzUm9_RwuDpxI6X8me_QQhZMf7RfjdA",
"guid_sig":"PT9-TApzpm7QtMxC63MjtdK2nUyxNI0tUoWlOYTFGke3kNdtxSzSvDV4uzq_7SSBtlrNnVMAFx2_1FDgyKawmqVtRPmT7QSXrKOL2oPzL8Hu_nnVVTs_0YOLQJJ0GYACOOK-R5874WuXLEept5-KYg0uShifsvhHnxnPIlDM9lWuZ1hSJTrk3NN9Ds6AKpyNRqf3DUdz81-Xvs8I2kj6y5vfFtm-FPKAqu77XP05r74vGaWbqb1r8zpWC7zxXakVVOHHC4plG6rLINjQzvdSFKCQb5R_xtGsPPfvuE24bv4fvN4ZG2ILvb6X4Dly37WW_HXBqBnUs24mngoTxFaPgNmz1nDQNYQu91-ekX4-BNaovjDx4tP379qIG3-NygHTjFoOMDVUvs-pOPi1kfaoMjmYF2mdZAmVYS2nNLWxbeUymkHXF8lT_iVsJSzyaRFJS1Iqn7zbvwH1iUBjD_pB9EmtNmnUraKrCU9eHES27xTwD-yaaH_GHNc1XwXNbhWJaPFAm35U8ki1Le4WbUVRluFx0qwVqlEF3ieGO84PMidrp51FPm83B_oGt80xpvf6P8Ht5WvVpytjMU8UG7-js8hAzWQeYiK05YTXk-78xg0AO6NoNe_RSRk05zYpF6KlA2yQ_My79rZBv9GFt4kUfIxNjd9OiV1wXdidO7Iaq_Q"
}
}
}
}
And that's the package (the original message). Example.com converts this into a form suitable for viewing by Nickordo and notifies Nickordo that there's a new message. Podunk.edu **might** discover that there are other packages waiting for example.com. If this happens it may also send any and all other waiting packages at this time. Each has the original tracking number attached.
#include doc/macros/main_footer.bb;

View file

@ -1,204 +0,0 @@
[h3]What is $Projectname?[/h3]
$Projectname is a [b]free and open source[/b] set of web applications and services running on a special kind of web server, called a "hub", that can connect to other hubs in a decentralised network we like to call "the grid", providing sophisticated communications, identity, and access control services which work together seamlessly across domains and independent websites. It allows anybody to publicly or [b]privately[/b] publish content via "channels", which are the fundamental, cryptographically secured identities that provide authentication independently of the hubs which host them. This revolutionary liberation of online identity from individual servers and domains is called "nomadic identity", and it is powered by the Zot protocol, a new framework for decentralised access control with fine-grained, extensible permissions.
[h3]Right... so what is $Projectname?[/h3]
From the practical perspective of hub members who use the software, $Projectname offers a variety of familiar, integrated web apps and services, including:
[ul]
[li]social networking discussion threads[/li]
[li]cloud file storage[/li]
[li]calendar and contacts (with CalDAV and CardDAV support)[/li]
[li]webpage hosting with a content management system[/li]
[li]wiki[/li]
[li]and more...[/li][/ul]
While all of these apps and services can be found in other software packages, only $Projectname allows you to set permissions for groups and individuals who may not even have accounts on your hub! In typical web apps, if you want to share things privately on the internet, the people you share with must have accounts on the server hosting your data; otherwise, there is no robust way for your server to [i]authenticate[/i] visitors to the site to know whether to grant them access. $Projectname solves this problem with an advanced system of [i]remote authentication[/i] that validates the identity of visitors by employing techniques that include public key cryptography.
[h3]Software Stack[/h3]
The $Projectname software stack is a relatively standard webserver application written primarily in PHP/MySQL and [url=https://framagit.org/hubzilla/core/blob/master/install/INSTALL.txt]requiring little more than a web server, a MySQL-compatible database, and the PHP scripting language[/url]. It is designed to be easily installable by those with basic website administration skills on typical shared hosting platforms with a broad range of computing hardware. It is also easily extended via plugins and themes and other third-party tools.
[h3]Glossary[/h3]
[dl terms="b"]
[*= hub] An instance of this software running on a standard web server
[*= grid] The global network of hubs that exchange information with each other using the Zot protocol.
[*= channel] The fundamental identity on the grid. A channel can represent a person, a blog, or a forum to name a few. Channels can make connections with other channels to share information with highly detailed permissions.
[*= clone] Channels can have clones associated with separate and otherwise unrelated accounts on independent hubs. Communications shared with a channel are synchronized among the channel clones, allowing a channel to send and receive messages and access shared content from multiple hubs. This provides resilience against network and hardware failures, which can be a significant problem for self-hosted or limited-resource web servers. Cloning allows you to completely move a channel from one hub to another, taking your data and connections with you. See nomadic identity.
[*= nomadic identity] The ability to authenticate and easily migrate an identity across independent hubs and web domains. Nomadic identity provides true ownership of an online identity, because the identities of the channels controlled by an account on a hub are not tied to the hub itself. A hub is more like a "host" for channels. With Hubzilla, you don't have an "account" on a server like you do on typical websites; you own an identity that you can take with you across the grid by using clones.
[*= [url=[baseurl]/help/developer/zot_protocol]Zot[/url]] The novel JSON-based protocol for implementing secure decentralised communications and services. It differs from many other communication protocols by building communications on top of a decentralised identity and authentication framework. The authentication component is similar to OpenID conceptually but is insulated from DNS-based identities. Where possible remote authentication is silent and invisible. This provides a mechanism for internet-scale distributed access control which is unobtrusive.
[/dl]
[h3]Features[/h3]
This page lists some of the core features of $Projectname that are bundled with the official release. $Projectname is a highly extensible platform, so more features and capabilities can be added via additional themes and plugins.
[h4]Affinity Slider[/h4]
When adding connnections in $Projectname, members have the option of assigning "affinity" levels (how close your friendship is) to the new connection. For example, when adding someone who happens to be a person whose blog you follow, you could assign their channel an affinity level of &quot;Acquaintances&quot;.
On the other hand, when adding a friend's channel, they could be placed under the affinity level of &quot;Friends&quot;.
At this point, $Projectname [i]Affinity Slider[/i] tool, which usually appears at the top of your &quot;Matrix&quot; page, adjusts the content on the page to include those within the desired affinity range. Channels outside that range will not be displayed, unless you adjust the slider to include them.
The Affinity Slider allows instantaneous filtering of large amounts of content, grouped by levels of closeness.
[h4]Connection Filtering[/h4]
You have the ability to control precisely what appears in your stream using the optional "Connection Filter". When enabled, the Connection Editor provides inputs for selecting criteria which needs to be matched in order to include or exclude a specific post from a specific channel. Once a post has been allowed, all comments to that post are allowed regardless of whether they match the selection criteria. You may select words that if present block the post or ensure it is included in your stream. Regular expressions may be used for even finer control, as well as hashtags or even the detected language of the post.
[h4]Access Control Lists[/h4]
When sharing content, members have the option of restricting who sees the content. By clicking on the padlock underneath the sharing box, one may choose desired recipients of the post, by clicking on their names.
Once sent, the message will be viewable only by the sender and the selected recipients. In other words, the message will not appear on any public walls.
Access Control Lists may be applied to content and posts, photos, events, webpages, chatrooms and files.
[h4]Single Sign-on[/h4]
Access Control Lists work for all channels in the grid due to our unique single sign-on technology. Most internal links provide an identity token which can be verified on other $Projectname sites and used to control access to private resources. You login once to your home hub. After that, authentication to all $Projectname resources is "magic".
[h4]WebDAV enabled File Storage[/h4]
Files may be uploaded to your personal storage area using your operating system utilities (drag and drop in most cases). You may protect these files with Access Control Lists to any combination of $Projectname members (including some third party network members) or make them public.
[h4]Photo Albums[/h4]
Store photos in albums. All your photos may be protected by Access Control Lists.
[h4]Events Calendar[/h4]
Create and manage events and tasks, which may also be protected with Access Control Lists. Events can be imported/exported to other software using the industry standard vcalendar/iCal format and shared in posts with others. Birthday events are automatically added from your friends and converted to your correct timezone so that you will know precisely when the birthday occurs - no matter where you are located in the world in relation to the birthday person. Events are normally created with attendance counters so your friends and connections can RSVP instantly.
[h4]Chatrooms[/h4]
You may create any number of personal chatrooms and allow access via Access Control Lists. These are typically more secure than XMPP, IRC, and other Instant Messaging transports, though we also allow using these other services via plugins.
[h4]Webpage Building[/h4]
$Projectname has many "Content Management" creation tools for building webpages, including layout editing, menus, blocks, widgets, and page/content regions. All of these may be access controlled so that the resulting pages are private to their intended audience.
[h4]Apps[/h4]
Apps may be built and distributed by members. These are different from traditional "vendor lockin" apps because they are controlled completely by the author - who can provide access control on the destination app pages and charge accordingly for this access. Most apps in $Projectname are free and can be created easily by those with no programming skills.
[h4]Layout[/h4]
Page layout is based on a description language called Comanche. $Projectname is itself written in Comanche layouts which you can change. This allows a level of customisation you won't typically find in so-called "multi-user environments".
[h4]Bookmarks[/h4]
Share and save/manage bookmarks from links provided in conversations.
[h4]Private Message Encryption and Privacy Concerns[/h4]
Private mail is stored in an obscured format. While this is not bullet-proof it typically prevents casual snooping by the site administrator or ISP.
Each $Projectname channel has it's own unique set of private and associated public RSA 4096-bit keys, generated when the channels is first created. This is used to protect private messages and posts in transit.
Additionally, messages may be created utilising "end-to-end encryption" which cannot be read by $Projectname operators or ISPs or anybody who does not know the passcode.
Public messages are generally not encrypted in transit or in storage.
Private messages may be retracted (unsent) although there is no guarantee the recipient hasn't read it yet.
Posts and messages may be created with an expiration date, at which time they will be deleted/removed on the recipient's site.
[h4]Service Federation[/h4]
In addition to addon "cross-post connectors" to a variety of alternate networks, there is native support for importation of content from RSS/Atom feeds and using this to create special channels. Plugins are also available to communicate with others using the Diaspora and GNU-Social (OStatus) protocols. These networks do not support nomadic identity or cross-domain access control; however basic communications are supported to/from Diaspora, Friendica, GNU-Social, Mastodon and other providers which use these protocols.
There is also experimental support for OpenID authentication which may be used in Access Control Lists. This is a work in progress. Your $Projectname hub may be used as an OpenID provider to authenticate you to external services which use this technology.
Channels may have permissions to become "derivative channels" where two or more existing channels combine to create a new topical channel.
[h4]Privacy Groups[/h4]
Our implementation of privacy groups is similar to Google "Circles" and Diaspora "Aspects". This allows you to filter your incoming stream by selected groups, and automatically set the outbound Access Control List to only those in that privacy group when you post. You may over-ride this at any time (prior to sending the post).
[h4]Directory Services[/h4]
We provide easy access to a directory of members and provide decentralised tools capable of providing friend "suggestions". The directories are normal $Projectname sites which have chosen to accept the directory server role. This requires more resources than most typical sites so is not the default. Directories are synchronised and mirrored so that they all contain up-to-date information on the entire network (subject to normal propagation delays).
[h4]TLS/SSL[/h4]
For $Projectname hubs that use TLS/SSL, client to server communications are encrypted via TLS/SSL. Given recent disclosures in the media regarding widespread, global surveillance and encryption circumvention by the NSA and GCHQ, it is reasonable to assume that HTTPS-protected communications may be compromised in various ways. Private communications are consequently encrypted at a higher level before sending offsite.
[h4]Channel Settings[/h4]
When a channel is created, a role is chosen which applies a number of pre-configured security and privacy settings. These are chosen for best practives to maintain privacy at the requested levels.
If you choose a "custom" privacy role, each channel allows fine-grained permissions to be set for various aspects of communication. For example, under the &quot;Security and Privacy Settings&quot; heading, each aspect on the left side of the page, has six (6) possible viewing/access options, that can be selected by clicking on the dropdown menu. There are also a number of other privacy settings you may edit.
The options are:
- Nobody except yourself.
- Only those you specifically allow.
- Anybody in your address book.
- Anybody on this website.
- Anybody in this network.
- Anybody authenticated.
- Anybody on the Internet.
[h4]Public and Private Forums[/h4]
Forums are typically channels which may be open to participation from multiple authors. There are currently two mechanisms to post to forums: 1) "wall-to-wall" posts and 2) via forum @mention tags. Forums can be created by anybody and used for any purpose. The directory contains an option to search for public forums. Private forums can only be posted to and often only seen by members.
[h4]Account Cloning[/h4]
Accounts in $Projectname are referred to as [i]nomadic identities[/i], because a member's identity is not bound to the hub where the identity was originally created. For example, when you create a Facebook or Gmail account, it is tied to those services. They cannot function without Facebook.com or Gmail.com.
By contrast, say you've created a $Projectname identity called [b]tina@$Projectnamehub.com[/b]. You can clone it to another $Projectname hub by choosing the same, or a different name: [b]liveForever@Some$ProjectnameHub.info[/b]
Both channels are now synchronized, which means all your contacts and preferences will be duplicated on your clone. It doesn't matter whether you send a post from your original hub, or the new hub. Posts will be mirrored on both accounts.
This is a rather revolutionary feature, if we consider some scenarios:
- What happens if the hub where an identity is based suddenly goes offline? Without cloning, a member will not be able to communicate until that hub comes back online (no doubt many of you have seen and cursed the Twitter "Fail Whale"). With cloning, you just log into your cloned account, and life goes on happily ever after.
- The administrator of your hub can no longer afford to pay for his free and public $Projectname hub. He announces that the hub will be shutting down in two weeks. This gives you ample time to clone your identity(ies) and preserve your$Projectname relationships, friends and content.
- What if your identity is subject to government censorship? Your hub provider may be compelled to delete your account, along with any identities and associated data. With cloning, $Projectname offers [b]censorship resistance[/b]. You can have hundreds of clones, if you wanted to, all named different, and existing on many different hubs, strewn around the internet.
$Projectname offers interesting new possibilities for privacy. You can read more at the &lt;&lt;Private Communications Best Practices&gt;&gt; page.
Some caveats apply. For a full explanation of identity cloning, read the &lt;HOW TO CLONE MY IDENTITY&gt;.
[h4]Multiple Profiles[/h4]
Any number of profiles may be created containing different information and these may be made visible to certain of your connections/friends. A "default" profile can be seen by anybody and may contain limited information, with more information available to select groups or people. This means that the profile (and site content) your beer-drinking buddies see may be different than what your co-workers see, and also completely different from what is visible to the general public.
[h4]Account Backup[/h4]
$Projectname offers a simple, one-click account backup, where you can download a complete backup of your profile(s). Backups can then be used to clone or restore a profile.
[h4]Account Deletion[/h4]
Accounts can be immediately deleted by clicking on a link. That's it. All associated content is then deleted from the grid (this includes posts and any other content produced by the deleted profile). Depending on the number of connections you have, the process of deleting remote content could take some time but it is scheduled to happen as quickly as is practical.
[h4]Deletion of content[/h4]
Any content created in $Projectname remains under the control of the member (or channel) that originally created it. At any time, a member can delete a message, or a range of messages. The deletion process ensures that the content is deleted, regardless of whether it was posted on a channel's primary (home) hub, or on another hub, where the channel was remotely authenticated via Zot ($Projectname communication and authentication protocol).
[h4]Media[/h4]
Similar to any other modern blogging system, social network, or a micro-blogging service, $Projectname supports the uploading of files, embedding of videos, linking web pages.
[h4]Previewing/Editing[/h4]
Posts and comments can be previewed prior to sending and edited after sending.
[h4]Voting/Consensus[/h4]
Posts can be turned into "consensus" items which allows readers to offer feedback, which is collated into "agree", "disagree", and "abstain" counters. This lets you gauge interest for ideas and create informal surveys.
[h4]Extending $Projectname[/h4]
$Projectname can be extended in a number of ways, through site customisation, personal customisation, option setting, themes, and addons/plugins.
[h4]API[/h4]
An API is available for use by third-party services. A plugin also provides a basic implementation of the Twitter API (for which hundreds of third-party tools exist). Access may be provided by login/password or OAuth, and client registration of OAuth applications is provided.

View file

@ -1,7 +0,0 @@
[h3]Site Info[/h3]
[list][*][url=[baseurl]/siteinfo]Site Info[/url]
[*][url=[baseurl]/siteinfo/json]Site Info (JSON format)[/url][/list]
[h3]Terms of Service[/h3]
[list][*][url=[baseurl]/help/TermsOfService]Terms of Service for this hub[/url][/list]
#include doc/SiteTOS.md;

View file

@ -1,186 +0,0 @@
[h3]$Projectname Governance[/h3]
Governance relates to the management of a project and particularly how this relates to conflict resolution.
[h4]Community Governance[/h4]
The project is maintained and decisions made by the 'community'. The governance structure is still evolving. Until the structure is finalised, decisions are made in the following order:
[ol]
[*] Lazy Consensus
If a project proposal is made to one of the community governance forums and there are no serious objections in a "reasonable" amount of time from date of proposal (we usually provide 2-3 days for all interested parties to weigh in), no vote needs to be taken and the proposal will be considered approved. Some concerns may be raised at this time, but if these are addressed during discussion and work-arounds provided, it will still be considered approved.
[*] Veto
Senior developers with a significant history of project commits may veto any decision. The decision may not proceed until the veto is removed or an alternative proposal is presented.
[*] Community Vote
A decision which does not have a clear mandate or clear consensus, but is not vetoed, can be taken to a community vote. At present this is a simple popular vote in one of the applicable community forums. At this time, popular vote decides the outcome. This may change in the future if the community adopts a 'council' governance model. This document will be updated at that time with the updated governance rules.
[/ol]
Community Voting does not always provide a pleasant outcome and can generate polarised factions in the community (hence the reason why other models are under consideration). If the proposal is 'down voted' there are still several things which can be done and the proposal re-submitted with slightly different parameters (convert to an addon, convert to an optional feature which is disabled by default, etc.). If interest in the feature is high and the vote is "close", it can generate lots of bad feelings amongst the losing voters. On such close votes, it is [b]strongly recommended[/b] that the proposer take steps to address any concerns that were raised and re-submit.
[h4]Privacy Policy[/h4]
Q: Who can see my content?
A: By default ANYBODY on the internet, UNLESS you restrict it. $Projectname allows you to choose the privacy level you desire. Restricted content will NOT be visible to "spy networks" and advertisers. It will be protected against eavesdropping by outsiders - to the best of our ability. Hub administrators with sufficient skills and patience MAY be able to eavesdrop on some private communications but they must expend effort to do so. Privacy modes exist within $Projectname which are even resistant to eavesdropping by skilled and determined hub administrators.
Q: Can my content be censored?
A: $Projectname (the network) CANNOT censor your content. Server and hub administrators are subject to local laws and MAY remove objectionable content from their site/hub. Anybody MAY become a hub administrator, including you; and therefore publish content which might otherwise be censored. You still MAY be subject to local laws.
[h5]Definitions[/h5]
**$Projectname**
Otherwise referred to as "the network", $Projectname is a collection of individual computers/servers (aka **hubs**) which connect together to form a larger cooperative network.
**hub**
An individual computer or server connected to $Projectname. These are provided by a **hub administrator** and may be public or private, paid or free.
**hub administrator**
The system operator of an individual hub.
[h5]Policies[/h5]
**Public Information**
Any information or anything posted by you within $Projectname MAY be public or visible to anybody on the internet. To the extent possible, $Projectname allows you to protect content and restrict who can view it.
Your profile photo, your channel name, and the location (URL or network address) of your channel are visible to anybody on the internet and privacy controls will not affect the display of these items.
You MAY additionally provide other profile information. Any information which you provide in your "default" or **public profile** MAY be transmitted to other hubs in $Projectname and additionally MAY be displayed in the channel directory. You can restrict the viewing of this profile information. It may be restricted only to members of your hub, or only connections (friends), or other limited sets of viewers as you desire. If you wish for your profile to be restricted, you must set the appropriate privacy setting, or simply DO NOT provide additional information.
**Content**
Content you provide (status posts, photos, files, etc.) belongs to you. $Projectname default is to publish content openly and visible to anybody on the internet (PUBLIC). You MAY control this in your channel settings and restrict the default permissions or you MAY restrict the visibility of any single published item separately (PRIVATE). $Projectname developers will ensure that restricted content is ONLY visible to those in the restriction list - to the best of their ability.
Content (especially status posts) that you share with other networks or that you have made visible to anybody on the internet (PUBLIC) cannot easily be taken back once it has been published. It MAY be shared with other networks and made available through RSS/Atom feeds. It may also be syndicated on other $Projectname sites. It MAY appear on other networks and websites and be visible in internet searches. If you do not wish this default behaviour please adjust your channel settings and restrict who can see your content.
**Comments and Forum posts**
Comments to posts that were created by others and posts which are designated as forum posts belong to you as the creator/author, but the distribution of these posts is not under your direct control, and you relinquish SOME rights to these items. These posts/comments MAY be re-distributed to others, and MAY be visible to anybody on the internet. In the case of comments, the creator of the "first message" in the thread (conversation) to which you are replying controls the distribution of all comments and replies to that message. They "own" and therefore have certain rights with regard to the entire conversation (including all comments contained within it). You can still edit or delete the comment, but the conversation owner also has rights to edit, delete, re-distribute, and backup/restore any or all the content from the conversation.
**Private Information**
$Projectname developers will ensure that any content you provide which is designated as PRIVATE will be protected against eavesdropping - to the best of their ability. Private channel content CAN be seen in the database of every involved hub administrator, but private messages are obscured in the database. The latter means that it is very difficult, but NOT impossible for this content to be seen by a hub administrator. Private channel content and private messages are also stripped from email notifications. End to end encryption is provided as an optional feature and this CANNOT be seen, even by a determined administrator.
[h5]Identity Privacy[/h5]
Privacy for your identity is another aspect. Because you have a decentralized identity in $Projectname, your privacy extends beyond your home hub. If you want to have complete control of your privacy and security you should run your own hub on a dedicated server. For many people, this is complicated and may stretch their technical abilities. So let's list a few precautions you can make to assure your privacy as much as possible.
A decentralized identity has a lot of advantages and gives you al lot of interesting features, but you should be aware of the fact that your identity is known by other hubs in $Projectname network. One of those advantages is that other channels can serve you customized content and allow you to see private things (such as private photos which others wish to share with you). Because of this those channels need to know who you are. But we understand that sometimes those other channels know more from you than you might desire. For instance the plug-in Visage that can tell a channel owner the last time you visit their profile. You can easily OPT-OUT of this low level and we think, harmless tracking.
* You can enable [Do Not Track (DNT)](http://donottrack.us/) in your web browser. We respect this new privacy policy proposal. All modern browsers support DNT. You will find it in the privacy settings of your browsers or else you can consult the web browser's manual. This will not affect the functionality of $Projectname. This setting is probably enough for most people.
*You can [disable publication](settings) of your channel in our channel directory. If you want people to find your channel, you should give your channel address directly to them. We think this is a good indication that you prefer extra privacy and automatically enable "Do Not Track" if this is the case.
* You can have a blocked hub. That means that all channels and content on that hub is not public, and not visible to the outside world. This is something only your hub administrator can do. We also respect this and automatically enable "Do Not Track" if it is set.
[h5]Censorship[/h5]
$Projectname is a global network which is inclusive of all religions and cultures. This does not imply that every member of the network feels the same way you do on contentious issues, and some people may be STRONGLY opposed to the content you post. In general, if you wish to post something that you know may nor be universally acceptable, the best approach is to restrict the audience using privacy controls to a small circle of friends.
$Projectname as a network provider is unable to censor content. However, hub administrators MAY censor any content which appears on their hub to comply with local laws or even personal judgement. Their decision is final. If you have issues with any hub administrator, you may move your account and postings to another site which is more in line with your expectations. Please check (periodically) the [Terms of Service](help/TermsOfService) of your hub to learn about any rules or guidelines. If your content consists of material which is illegal or may cause issues, you are STRONGLY encouraged to host your own (become a hub administrator). You may still find that your content is blocked on some hubs, but $Projectname as a network cannot block it from being posted.
$Projectname RECOMMENDS that hub administrators provide a grace period of 1-2 days between warning an account holder of content that needs to be removed and physically removing or disabling the account. This will give the content owner an opportunity to export their channel meta-data and import it to another site. In rare cases the content may be of such a nature to justify the immediate termination of the account. This is a hub decision, not a $Projectname decision.
If you typically and regularly post content of an adult or offensive nature, you are STRONGLY encouraged to mark your account "NSFW" (Not Safe For Work). This will prevent the display of your profile photo in the directory except to viewers that have chosen to disable "safe mode". If your profile photo is found by directory administrators to be adult or offensive, the directory administrator MAY flag your profile photo as NSFW. There is currently no official mechanism to contest or reverse this decision, which is why you SHOULD mark your own account NSFW if it is likely to be inappropriate for general audiences.
[h3]Credits[/h3]
Thanks to all who have helped and contributed to the project and its predecessors over the years.
It is possible we missed in your name but this is unintentional. We also thank the community and
its members for providing valuable input and without whom this entire effort would be meaningless.
It is also worth acknowledging the contributions and solutions to problems which arose from
discussions amongst members and developers of other somewhat related and competing projects;
even if we have had our occasional disagreements.
[list]
[li]Mike Macgirvin[/li]
[li]Fabio Comuni[/li]
[li]Simon L'nu[/li]
[li]marijus[/li]
[li]Tobias Diekershoff[/li]
[li]fabrixxm[/li]
[li]tommy tomson[/li]
[li]Simon[/li]
[li]zottel[/li]
[li]Christian Vogeley[/li]
[li]jeroenpraat[/li]
[li]Michael Vogel[/li]
[li]erik[/li]
[li]Zach Prezkuta[/li]
[li]Paolo T[/li]
[li]Michael Meer[/li]
[li]Michael[/li]
[li]Abinoam P. Marques Jr[/li]
[li]Tobias Hößl[/li]
[li]Alexander Kampmann[/li]
[li]Olaf Conradi[/li]
[li]Paolo Tacconi[/li]
[li]tobiasd[/li]
[li]Devlon Duthie[/li]
[li]Zvi ben Yaakov (a.k.a rdc)[/li]
[li]Alexandre Hannud Abdo[/li]
[li]Olivier Migeot[/li]
[li]Chris Case[/li]
[li]Klaus Weidenbach[/li]
[li]Michael Johnston[/li]
[li]olivierm[/li]
[li]Vasudev Kamath[/li]
[li]pixelroot[/li]
[li]Max Weller[/li]
[li]duthied[/li]
[li]Martin Schmitt[/li]
[li]Sebastian Egbers[/li]
[li]Erkan Yilmaz[/li]
[li]sasiflo[/li]
[li]Stefan Parviainen[/li]
[li]Haakon Meland Eriksen[/li]
[li]Oliver Hartmann (23n)[/li]
[li]Erik Lundin[/li]
[li]habeascodice[/li]
[li]sirius[/li]
[li]Charles[/li]
[li]Tony Baldwin[/li]
[li]Hauke Zuehl[/li]
[li]Keith Fernie[/li]
[li]Anne Walk[/li]
[li]toclimb[/li]
[li]Daniel Frank[/li]
[li]Matthew Exon[/li]
[li]Michal Supler[/li]
[li]Tobias Luther[/li]
[li]U-SOUND\mike[/li]
[li]mrjive[/li]
[li]nostupidzone[/li]
[li]tonnerkiller[/li]
[li]Antoine G[/li]
[li]Christian Drechsler[/li]
[li]Ludovic Grossard[/li]
[li]RedmatrixCanada[/li]
[li]Stanislav Lechev [0xAF][/li]
[li]aweiher[/li]
[li]bufalo1973[/li]
[li]dsp1986[/li]
[li]felixgilles[/li]
[li]ike[/li]
[li]maase2[/li]
[li]mycocham[/li]
[li]ndurchx[/li]
[li]pafcu[/li]
[li]Simó Albert i Beltran[/li]
[li]Manuel Reva[/li]
[li]Manuel Jiménez Friaza[/li]
[li]Gustav Wall aka "neue medienordnung plus"[/li]
[/list]

View file

@ -1,19 +0,0 @@
[size=large][b]Accounts, Profiles and Channels[/b][/size]
Once you have registered an [i]account[/i] at the matrix you have also created a [i]profile[/i] and a [i]channel[/i].
[b]Account[/b]
You have [i]one[/i] account. This consists of your email account and your password. With your account you access your profile and your channel.
[i]Think of your account as the way you authenticate at one $Projectname site. It lets you do things, such as creating profiles and channels with which you can connect to other people.[/i]
[b]Profile[/b]
You have surely registered with some other internet services, such as forums or online communities. For all of them you provided some information about yourself, such as date of birth, country, age and the likes. [observer=1]If you like you can see your profile here: [baseurl]/profile/[observer.webname] and edit it by clicking on the pencil icon next to your avatar image. [/observer]
Unlike other services $Projectname offers you the advantage of creating [i]many more profiles[/i]. That way you are able to distinguish between profiles targeted specially at everyone (your public profile), your work mates, your family and your partner.
[i]Think of your profile as the basic information about yourself you tell other people.[/i]
[b]Channel[/b]
During the registration you created your first [i]channel[/i]. Yes, besides several profiles you are able to have several channels. This might be a bit confusing in the beginning, but let's clear things up. You already have created one channel. You can use this one for the public, to communicate with people about every day life. But perhaps you are an avid book reader and many people are bored by that. So you open a [i]second channel[/i] just for the book lovers, where you all can talk about books as much as you like. Obviously this is a new stream of posts, with a new profile (... or new profile[i]s[/i] ...) and completely different contacts. Some connections might exist in both channels, but there will be some that are exclusive to only one of both. You yourself just switch between both of them just like you would in real life switch when talking to people you meet on the street or people you meet specially to talk about books. You can even connect to yourself, or better: to your other channel. :)
[i]Think of a channel as different spaces dedicated to different topics where you meet with different people.[/i]
#include doc/macros/main_footer.bb;

View file

@ -1,16 +0,0 @@
<!-- Network and channel posts cannot change their permissions after being sent, this help
file is for items of that nature. Files and photos etc should use a different help file. -->
<h2>Post Permissions</h2>
<p>The permissions dialog lets you select which channels and/or privacy groups can see the post. You can also select who is explicitly denied access. For example, say you are planning a surprise party for a friend. You can send an invitation post to everyone in your <b>Friends</b> group <i>except</i> the friend you are surprising. In this case you "Show" the <b>Friends</b> group but "Don't show" that one person.</p>
<dl class="text-info dl-terms-large dl-horizontal">
<dt style="width: 3em;">Tip!</td>
<dd style="margin-left: 4em;">The border color of each channel indicates whether that channel &mdash; or one of the groups it's a member of &mdash; will have access to the post. The border color will also indicate when a channel, or group it belongs to, has been explicitly set to "Don't show".</dd>
</dl>
<h3>Why can't I edit a post's permissions after I saved it?</h3>
<p>You are able to change permissons to your files, photos and the likes, but not to posts after you have saved them. The main reason is: Once you have saved a post it is being distributed either to the public channel and from there to other Hubzilla servers or to those you intended it to go. Just like you cannot reclaim something you gave to another person, you cannot change permissions to Hubzilla posts. We would need to track everywhere your posting goes, keep track of everyone you allowed to see it and then keep track of from whom to delete it.</p>
<p>If a posting is public this is even harder, as the Hubzilla is a global network and there is no way to follow a post, let alone reclaim it reliably. Other networks that may receive your post have no reliable way to delete or reclaim the post.</p>

View file

@ -1,117 +0,0 @@
[h3]Plugins/Addons[/h3]
[list=1]
[*] abcjsplugin - Create musical scores in your posts
[*] adultphotoflag - prevents nsfw photos from being displayed in public albums
[*] authchoose - only send identity assertions to sites of friends
[*] b2tbtn - provide button to go directly to top of page if you are scrolled a long way down
[*] bbmath - use complex math expressions in your posts
[*] bookmarker - replace #^ with bookmark link in posts
[*] buglink - provide a bug reporting icon in the lower-left corner of every page
[*] calc - a scientific calculator
[*] chess - cross domain identity aware interactive chess games
[*] chords - generate fingering charts and alternatives for every known guitar chord
[*] custom_home - set a custom page as the hub start page
[*] diaspora - Diaspora protocol emulator
[*] dirstats - show some interesting statistics generated by the directory server
[*] docs - alternate documentation pages
[*] donate - provides a project donation page
[*] dreamhost - provide a more reliable service on Dreamhost shared hosting
[*] dwpost - crosspost to Dreamwidth
[*] emojione - allow emojis as emoticons
[*] extcron - use an external cron service to run your hub's scheduled tasks
[*] firefox - provide a link to install the Firefox Sharing API
[*] flattrwidget - provides a "Flattr Us" button
[*] flip - create upside down text
[*] fortunate - displays random quote (fortune cookie). Requires setting up a fortune server.
[*] friendica - Friendica (DFRN) protocol. Under development.
[*] frphotos - import photo albums from Friendica
[*] gnusoc - GNU-Social (OStatus) protocol. Under development.
[*] hexit - hexadecimal conversion tool
[*] hilite - allow language-specific highlighted code blocks in posts
[*] hubwall - send an admin email to all hub accounts
[*] ijpost - crosspost to Insanejournal
[*] irc - connect to IRC chatrooms
[*] jappixmini - XMPP chat
[*] js_upload - upload multiple photos to photo albums at once.
[*] keepout - prevents nearly all use of site when not logged in, more restrictive than 'block public' setting
[*] ldapauth - login via account on LDAP or Windows Active Directory domain
[*] libertree - crosspost to Libertree
[*] likebanner - create a "like us on red#matrix" banner image
[*] ljpost - crosspost to LiveJournal
[*] logrot - logfile rotation utility
[*] mahjongg - Chinese puzzle game
[*] mailhost - when using multiple channel clones, select one to receive email notifications
[*] mailtest - interface for testing mail delivery system
[*] metatag - provide SEO friendly pages
[*] mayan_places - set location field to a random city in the Mayan world
[*] morechoice - additional gender/sexual-preference choices for profiles (not safe for work)
[*] moremoods - Additional mood options
[*] morepokes - additional poke options (not safe for work)
[*] msgfooter - provide legal or other text on each outgoing post
[*] noembed - use noembed.com as an addition to $Projectname native oembed functionality (currently broken)
[*] nofed - prevent "federation" of channel posts, maintains all interaction on your site
[*] nsabait - add random terrorism related hashtags to your posts
[*] nsfw - Highly recommended plugin to collpase posts with inappropriate content
[*] openclipatar - choose a profile photo from hundreds of royalty free images
[*] openid - OpenID authentication and OpenID server. Your OpenID URL is [observer.baseurl]/id/[observer.webname]
[*] opensearch - allow your site to become a browser search provider
[*] openstreetmap - render locations and maps using OpenStreetMap
[*] pageheader - display text at the top of every page on the site
[*] phpmailer - alternate mail delivery system with more configurability
[*] piwik - open source website analytics
[*] planets - set location field to a random planet from Star Wars
[*] pong - classic pong game
[*] pubcrawl - ActivityPub protocol emulator
[*] pubsubhubbub - PuSH protocol for optimised delivery to feed subscribers (required by GNU-Social protocol)
[*] pumpio - crosspost to Pump.io
[*] qrator - generate QR code images
[*] rainbowtag - display your tag and category clouds in colours
[*] randpost - post/reply bot based on and requires fortunate
[*] redfiles - import file storage from redmatrix
[*] redphotos - import photo albums from redmatrix
[*] redred - Crosspost to another Red Matrix or Hubzilla channel
[*] rendezvous - group location tracking
[*] rtof - Crosspost to Friendica
[*] sendzid - add 'zid' auth parmaters to all outbound links, not just in-network links
[*] skeleton - sample addon/plugin to demonstrate plugin development
[*] smiley_pack - extend the built-in smilie (emoticon) support
[*] smileybutton - provides a smiley selector on the post window
[*] startpage - set a personal preferred page to redirect after logging in.
[*] statistics - Diaspora statistics generator
[*] statusnet - GNU-social and StatusNet crosspost [zrl=[baseurl]/help/addons_gnusocial]Posting To Gnu Social[/zrl]
[*] std_embeds - allow unfiltered embeds for popular providers like youtube, vimeo and soundcloud
[*] superblock - Highly recommended - completely block an offensive channel from your stream
[*] testdrive - Turns your hub into a test drive site with accounts that expire after a trail period.
[*] tictac - 3D tic-tac-toe
[*] torch - flashlight app
[*] tour - feature tour for new members
[*] tripleaes - demo plugin for providing custom encryption algorithms
[*] twitter - crosspost to Twitter
[*] twitter_api - Twitter/Statusnet compatible API
[*] upload_limits - discover what server setting (there are a few) may be causing large photo uploads to fail
[*] visage - show visitors to your channel
[*] webmention - process webmentions
[*] wholikesme - provides a page to display what connections have 'liked' your posts the most
[*] webRTC - use an external server (mayfirst.org) to negotiate webRTC hookups
[*] wppost - crosspost to WordPress (or other wordpress XMLRPC service)
[*] xmpp - XMPP chat based on converse.js
[/list]
[h3]Addon Repositories[/h3]
We [b]strongly recommend[/b] that authors of addons publish/submit them to the project addon repository. This has several advantages. Project developers can easily fix security flaws and make changes to comply with recent changes in core code. Addons provided in third-party repositories are considered untrusted. If the project core code changes in an incompatible way, there may be no alternative but to physically remove or rename the addon files in order to get your site working again. Often only the plugin/addon author can help you regain control of your website, and project developers are unable to assist you; because by definition your site configuration has been modified in ways that we cannot easily test or verify.
For these reasons we [b]strongly recommend[/b] that you do NOT install addons from third-party repositories.
We also recognise that some developers prefer working on their own and do not wish their code to be mingled with the project repository for a variety of reasons. These developers can ease troubleshooting and debugging by providing a README file in their respective code repository outlining the process for submitting patches and bug fixes. It is also recommended that these projects provide both a 'dev' (development) and 'master' (production) branch which tracks the current project branches of those names. This is because dev and master are often not compatible from the viewpoint of library interfaces. It is also highly recommended that your repository versions are tagged and moved forward within 24 hours of project releases. This is a major inconvenience for everybdy involved, and can present downtime for production sites while this process is being carried out; which is one more reason why we [b]strongly recommend[/b] that addons be submitted to the project addon repository and that you do NOT install such third-party addons.
[url=https://framagit.org/hubzilla/addons]https://framagit.org/hubzilla/addons[/url] Main project addon repository
[url=https://github.com/23n/red-addons]https://github.com/23n/red-addons[/url] Oliver's repository (mayan_places and flip)
#include doc/macros/main_footer.bb;

View file

@ -1,65 +0,0 @@
[b]How to cross-post to a GNUsocial instance[/b]
Start on the GNUSocial instance where you have your account.
In the GNUSocial instance, go to Settings > Connections. In the right column under "Developers," click the link to "Register an OAuth client application to use with this instance of StatusNet." This link may be found at your instance here:
https://yourgnusocialinstance.org/settings/oauthapps
Next, click the link to Register a new application. That brings up the new application form. Here's what to do on each field.
Icon. I uploaded $Projectname icon located at this link, after saving it to my computer:
https://framagit.org/hubzilla/core/blob/master/images/rm-32.png
Name. Give the application an appropriate name. I called mine hubzilla. You might prefer r2g.
Description. Use this field to describe the purpose of the application. I put something to the effect of use for crossposting from $Projectname to GNUsocial.
Source URL. Put the main domain name of the Red site you're using. Don't forget to put the "s" in https://yourhubzillasite.com. If your Red installation is a subdomain, that would probably be called for.
Organization. Since $Projectname is unorganized, I put that. If you use your installation for a group or business, that might be a good option.
Homepage. If your group is using a subdomain, you probably want to put your main domain URI here. Since I'm on a hosted site, I put redmatrix.me.
Callback URL. Leave blank.
Type of application: select "desktop."
Default access: select "Read-write."
All fields except the callback URL must be filled in.
Click on the save button.
Then click on the icon or the name of the application for the information you'll need to insert over on $Projectname.
*****
Now open up a new tab or window and go to your $Projectname account, to Settings > Feature settings. Find the StatusNet Posting Settings.
Insert the strings of numbers given on the GNUsocial site into $Projectname fields for Consumer Key and Consumer Secret.
The Base API Path (remember the trailing /) will be your instance domain, plus the /api/ following. It will probably look like this:
https://yourgnusocialinstance.org/api/
In case of doubt check on your GNUsocial instance site in order to find the domain URLs of the Request token, Access token, and Authorization. It will be the first part of the domains, minus the /oauth/....
StatusNet application name: Insert the name you gave to the application over on the GNUsocial site.
Click Submit.
A button will appear for you to "Sign in to StatusNet." Click it and that will open a tab or window on the GNUsocial site for you to click "Allow." Once clicked and successfully authorized, a security code number will appear. Copy it and go back to $Projectname app you just left and insert it in the field: "Copy the security code from StatusNet here." Click Submit.
If successful, your information from the GNUsocial instance should appear in $Projectname app.
You now have several options to choose, if you desire, and those will need to be confirmed by clicking "Submit" also. The most interesting is "Send public postings to StatusNet by default." This option automatically sends any post of yours made in your $Projectname account to your GNUsocial instance.
If you don't choose this option, you will have an option to send a post to your GNUsocial instance by first opening the post (by clicking in the post text area) and clicking on the lock icon next to the Share button. Select the GNUsocial icon made up of three colored dialog baloons. Close that window, then make your post.
If all goes well, you have just cross-posted your $Projectname post to your account on a GNUsocial instance.
#include doc/macros/addons_footer.bb;

View file

@ -1,402 +0,0 @@
### Overview
$Projectname is more than a simple web application. It is a
complex communications system which more closely resembles an email server
than a web server. For reliability and performance, messages are delivered in
the background and are queued for later delivery when sites are down. This
kind of functionality requires a bit more of the host system than the typical
blog. Not every PHP/MySQL hosting provider will be able to support
$Projectname. Many will but please review the requirements and confirm these
with your hosting provider prior to installation.
We've tried very hard to ensure that $Projectname will run on commodity
hosting platforms such as those used to host Wordpress blogs and Drupal
websites. It will run on most any Linux VPS system. Windows LAMP platforms
such as XAMPP and WAMP are not officially supported at this time however
we welcome patches if you manage to get it working.
### Where to find more help
If you encounter problems or have issues not addressed in this documentation,
please let us know via the [Github issue
tracker](https://framagit.org/hubzilla/core/issues). Please be as clear as you
can about your operating environment and provide as much detail as possible
about any error messages you may see, so that we can prevent it from happening
in the future. Due to the large variety of operating systems and PHP platforms
in existence we may have only limited ability to debug your PHP installation or
acquire any missing modules * but we will do our best to solve any general code
issues.
### Before you begin
#### Choose a domain name or subdomain name for your server
$Projectname can only be installed into the root of a domain or sub-domain, and can
not be installed using alternate TCP ports.
#### Decide if you will use SSL and obtain an SSL certificate before software installation
You SHOULD use SSL. If you use SSL, you MUST use a "browser-valid" certificate.
*You MUST NOT use self-signed certificates!*
Please test your certificate prior to installation. A web tool for testing your
certificate is available at "http://www.digicert.com/help/". When visiting your
site for the first time, please use the SSL ("https://") URL if SSL is available.
This will avoid problems later. The installation routine will not allow you to
use a non browser-valid certificate.
This restriction is incorporated because public posts from you may contain
references to images on your own hub. Other members viewing their stream on
other hubs will get warnings if your certificate is not trusted by their web
browser. This will confuse many people because this is a decentralised network
and they will get the warning about your hub while viewing their own hub and may
think their own hub has an issue. These warnings are very technical and scary to
some folks, many of whom will not know how to proceed except to follow the browser
advice. This is disruptive to the community. That said, we recognise the issues
surrounding the current certificate infrastructure and agree there are many
problems, but that doesn't change the requirement.
Free "browser-valid" certificates are available from providers such as StartSSL
and LetsEncrypt.
If you do NOT use SSL, there may be a delay of up to a minute for the initial
install script - while we check the SSL port to see if anything responds there.
When communicating with new sites, $Projectname always attempts connection on the
SSL port first, before falling back to a less secure connection. If you do not
use SSL, your webserver MUST NOT listen on port 443 at all.
If you use LetsEncrypt to provide certificates and create a file under
.well-known/acme-challenge so that LetsEncrypt can verify your domain ownership,
please remove or rename the .well-known directory as soon as the certificate is
generated. $Projectname will provide its own handler for ".well-known" services when
it is installed, and an existing directory in this location may prevent some of
these services from working correctly. This should not be a problem with Apache,
but may be an issue with nginx or other web server platforms.
### Deployment
There are several ways to deploy a new hub.
* Manual installation on an existing server
* Automated installation on an existing server using a shell script
* Automated deployment using an OpenShift virtual private server (VPS)
### Requirements
* Apache with mod-rewrite enabled and "AllowOverride All" so you can use a
local .htaccess file. Some folks have successfully used nginx and lighttpd.
Example config scripts are available for these platforms in doc/install.
Apache and nginx have the most support.
* PHP 5.5 or later.
* Note that on some shared hosting environments, the _command line_ version of
PHP might differ from the _webserver_ version
* PHP *command line* access with register_argc_argv set to true in the
php.ini file * and with no hosting provider restrictions on the use of
exec() and proc_open().
* curl, gd (with at least jpeg and png support), mysqli, mbstring, mcrypt,
and openssl extensions. The imagick extension is not required but desirable.
* xml extension is required if you want webdav to work.
* some form of email server or email gateway such that PHP mail() works.
* Mysql 5.x or MariaDB or postgres database server.
* ability to schedule jobs with cron.
* Installation into a top-level domain or sub-domain (without a
directory/path component in the URL) is REQUIRED.
### Manual Installation
#### Unpack the $Projectname files into the root of your web server document area
If you copy the directory tree to your webserver, make sure that you include the
hidden files like .htaccess.
If you are able to do so, we recommend using git to clone the source
repository rather than to use a packaged tar or zip file. This makes the
software much easier to update. The Linux command to clone the repository
into a directory "mywebsite" would be:
git clone https://framagit.org/hubzilla/core.git mywebsite
and then you can pick up the latest changes at any time with:
git pull
make sure folders ``store/[data]/smarty3`` and ``store`` exist and are
writable by the webserver:
mkdir -p "store/[data]/smarty3"
chmod -R 777 store
This permission (777) is very dangerous and if you have sufficient
privilege and knowledge you should make these directories writeable
only by the webserver and, if different, the user that will run the
cron job (see below). In many shared hosting environments this may be
difficult without opening a trouble ticket with your provider. The
above permissions will allow the software to work, but are not
optimal.
The following directories also need to be writable by the webserver in order for certain
web-based administrative tools to function:
* `addon`
* `extend`
* `view/theme`
* `widget`
#### Official addons
##### Installation
Navigate to your website. Then you should clone the addon repository (separately). We'll give this repository a nickname of 'hzaddons'. You can pull in other hubzilla addon repositories by giving them different nicknames::
cd mywebsite
util/add_addon_repo https://framagit.org/hubzilla/addons.git hzaddons
##### Updating
For keeping the addon tree updated, you should be on your top level website directory and issue an update command for that repository::
cd mywebsite
util/update_addon_repo hzaddons
Create searchable representations of the online documentation. You may do this
any time that the documentation is updated :
cd mywebsite
util/importdoc
### Automated installation via the .homeinstall shell script
There is a shell script in (``.homeinstall/hubzilla-setup.sh``) that will install $Projectname and its dependencies on a fresh installation of Debian 9 stable (Stetch). It should work on similar Linux systems but your results may vary.
#### Requirements
The installation script was originally designed for a small hardware server behind your home router. However, it has been tested on several systems running Debian 9:
* Home-PC (Debian-9.2-amd64) and Rapberry-Pi 3 (Rasbian = Debian 9.3)
* Internet connection and router at home
* Mini-PC / Raspi connected to your router
* USB drive for backups
* Fresh installation of Debian on your mini-pc
* Router with open ports 80 and 443 for your Debian
#### Overview of installation steps
1. `apt-get install git`
1. `mkdir -p /var/www/html`
1. `cd /var/www/html`
1. `git clone https://framagit.org/hubzilla/core.git .`
1. `nano .homeinstall/hubzilla-config.txt`
1. `cd .homeinstall/`
1. `./hubzilla-setup.sh`
1. `service apache2 reload`
1. Open your domain with a browser and step throught the initial configuration of $Projectname.
### Recommended Addons
We recommend the following addons be installed on all public sites:
nsfw - hide inappropriate posts/comments
superblock - block content from offensive channels
### Federation Addons
Several web communities have begun to converge using common protocols. The protocols involved are somewhat limited in their abilities. The GNU-Social protocol for instance offers no privacy modes, and the Diaspora protocol is somewhat restrictive in what kinds of communications are allowed. All comments must be signed in a very unique manner by the original author. The ActivityPub protocol is also being considered and may be supported at a future date. No other existing protocol supports nomadic location as used by this project. This presents some support challenges as some features work with some networks and don't work with others. Nevertheless the federation protocols allow connections to be made to a much larger community of people worldwide. They are provided as addons.
> diaspora - The Diaspora Protocol used by Diaspora and Friendica. You should enable 'Diaspora Statistics' (statistics) first to enable all the available features.
> gnusoc - The GNU-Social Protocol, used by GNU-Social, Mastodon and several other communities. This addon requires you first install the 'pubsubhubbub' service (also an addon).
Each member of your site must choose whether or not to allow these protocols individually as they may conflict with several desirable core features and abilities of this software (such as channel migration and cloning). They do this from their 'Settings -> Feature/Addon Settings' page. The administrator may also set the following:
util/config system.diaspora_allowed 1
util/config system.gnusoc_allowed 1
and enable these protocols automatically for all newly created channels.
### Techlevels
We've implemented several different mechanisms in order to reduce the apparent complexity and learning curve presented to new members. At the same time, we do not wish to limit any functionality for people who are able to grasp some slightly advanced technical technical features. The first mechanism was to move several features to an optional 'Features' page where they could be enabled at will; with the default interface kept somewhat lean.
The problem we had now is that the number of features began to grow dramatically, and the Feature page is daunting in possibilities. There are also features present which probably should not be available to all members, but may be extremely useful to those with technical backgrounds.
The techlevels seeeks to remedy this by grouping features within different levels of technical ability; starting at 0 (uncomfortable with technology), and up to 5 (Unix wizard or equivalent).
When a new member registers, their account is provided a techlevel setting of 0. On the account settings page they may change this to any available level. A higher level opens more advanced features and possible interactions.
The account administrator may also lock a particular level, lock a maximum level, or change/re-arrange the features available to any level. Those with the minimum level are typically not very exploratory and are unlikely to discover the advanced modes. This is by design. Those that look around and desire more interactions will find them. In the absence of administrator defaults they may choose any level. As they look at the features available to the level in question, it is generally expected that they will discover some features are beyond their comprehension and it is hoped they will back off to a level where the interface and features are comfortable to their skill level.
### Service Classes
Service classes allow you to set limits on system resources by limiting what individual
accounts can do, including file storage and top-level post limits. Define custom service
classes according to your needs in the `.htconfig.php` file. For example, create
a _standard_ and _premium_ class using the following lines:
// Service classes
App::$config['system']['default_service_class']='standard'; // this is the default service class that is attached to every new account
// configuration for standard service class
App::$config['service_class']['standard'] =
array('photo_upload_limit'=>2097152, // total photo storage limit per channel (here 2MB)
'total_identities' =>1, // number of channels an account can create
'total_items' =>0, // number of top level posts a channel can create. Applies only to top level posts of the channel user, other posts and comments are unaffected
'total_pages' =>100, // number of pages a channel can create
'total_channels' =>100, // number of channels the user can add, other users can still add this channel, even if the limit is reached
'attach_upload_limit' =>2097152, // total attachment storage limit per channel (here 2MB)
'chatters_inroom' =>20);
// configuration for premium service class
App::$config['service_class']['premium'] =
array('photo_upload_limit'=>20000000000, // total photo storage limit per channel (here 20GB)
'total_identities' =>20, // number of channels an account can create
'total_items' =>20000, // number of top level posts a channel can create. Applies only to top level posts of the channel user, other posts and comments are unaffected
'total_pages' =>400, // number of pages a channel can create
'total_channels' =>2000, // number of channels the user can add, other users can still add this channel, even if the limit is reached
'attach_upload_limit' =>20000000000, // total attachment storage limit per channel (here 20GB)
'chatters_inroom' =>100);
To apply a service class to an existing account, use the command line utility from the
web root:
`util/service_class`
list service classes
`util/config system default_service_class firstclass`
set the default service class to 'firstclass'
`util/service_class firstclass`
list the services that are part of 'firstclass' service class
`util/service_class firstclass photo_upload_limit 10000000`
set firstclass total photo disk usage to 10 million bytes
`util/service_class --account=5 firstclass`
set account id 5 to service class 'firstclass' (with confirmation)
`util/service_class --channel=blogchan firstclass`
set the account that owns channel 'blogchan' to service class 'firstclass' (with confirmation)
**Service class limit options**
* photo_upload_limit - maximum total bytes for photos
* total_items - maximum total toplevel posts
* total_pages - maximum comanche pages
* total_identities - maximum number of channels owned by account
* total_channels - maximum number of connections
* total_feeds - maximum number of rss feed connections
* attach_upload_limit - maximum file upload storage (bytes)
* minimum_feedcheck_minutes - lowest setting allowed for polling rss feeds
* chatrooms - maximum chatrooms
* chatters_inroom - maximum chatters per room
* access_tokens - maximum number of Guest Access Tokens per channel
### Theme management
#### Repo management example
1. Navigate to your hub web root
```
root@hub:/root# cd /var/www
```
2. Add the theme repo and give it a name
```
root@hub:/var/www# util/add_theme_repo https://github.com/DeadSuperHero/redmatrix-themes.git DeadSuperHero
```
3. Update the repo by using
```
root@hub:/var/www# util/update_theme_repo DeadSuperHero
```
### Channel Directory
#### Keywords
There is a "tag cloud" of keywords that can appear on the channel directory page.
If you wish to hide these keywords, which are drawn from the directory server, you
can use the *config* tool:
util/config system disable_directory_keywords 1
If your hub is in the standalone mode because you do not wish to connect to the
global grid, you may instead ensure the the _directory_server_ system option is
empty:
util/config system directory_server ""
### Administration
#### Site Administration
Administration of the website is commonly done through the admin webpage located at /admin on your website. In order to access this page you must have administration rights to the server. Administration rights are granted to the first account to register on your site, **provided** the email address of that account exactly matches the email address you provided as the administrator's email address during setup.
There are several ways that this can fail and leave the system without an administrator account, for instance if the first account that was created provided a different email address than the administrator email address that was supplied during setup.
For security reasons there is no web page or interface on the system which will give you administrator access. If you need to correct a situation where a system has no administrator account it **must** be done by editing the account table in the database. There is no other way. To do this, you will need to locate the entry in the account table which belongs to the desired administrator, and set 'account_roles' for that entry to 4096. You will then be able to access the admin page from your system's profile menu or directly via /admin .
A hub can have multiple admins and there is no limit to how administrators you can have. Repeat the above process for every account you wish to provide with administration rights.
### Troubleshooting
#### Log files
The system logfile is an extremely useful resource for tracking down things that go wrong. This can be enabled in the admin/log configuration page. A loglevel setting of LOGGER_DEBUG is preferred for stable production sites. Most things that go wrong with communications or storage are listed here. A setting of LOGGER_DATA provides [b]much[/b] more detail, but may fill your disk. In either case we recommend the use of logrotate on your operating system to cycle logs and discard older entries.
At the bottom of your .htconfig.php file are several lines (commented out) which enable PHP error logging. This reports issues with code syntax and executing the code and is the first place you should look for issues which result in a "white screen" or blank page. This is typically the result of code/syntax problems.
Database errors are reported to the system logfile, but we've found it useful to have a file in your top-level directory called dbfail.out which [b]only[/b] collects database related issues. If the file exists and is writable, database errors will be logged to it as well as to the system logfile.
In the case of "500" errors, the issues may often be logged in your webserver logs, often /var/log/apache2/error.log or something similar. Consult your operating system documentation.
There are three different log facilities.
**The first is the database failure log**. This is only used if you create a file called specifically 'dbfail.out' in the root folder of your website and make it write-able by the web server. If we have any database failed queries, they are all reported here. They generally indicate typos in our queries, but also occur if the database server disconnects or tables get corrupted. On rare occasions we'll see race conditions in here where two processes tried to create an xchan or cache entry with the same ID. Any other errors (especially persistent errors) should be investigated.
**The second is the PHP error log**. This is created by the language processor and only reports issues in the language environment. Again these can be syntax errors or programming errors, but these generally are fatal and result in a "white screen of death"; e.g. PHP terminates. You should probably look at this file if something goes wrong that doesn't result in a white screen of death, but it isn't uncommon for this file to be empty for days on end.
There are some lines at the bottom of the supplied .htconfig.php file; which if uncommented will enable a PHP error log (*extremely* useful for finding the source of white screen failures). This isn't done by default due to potential issues with logfile ownership and write permissions and the fact that there is no logfile rotation by default.
**The third is the "application log"**. This is used by $Projectname to report what is going on in the program and usually reports any difficulties or unexpected data we received. It also occasionally reports "heartbeat" status messages to indicate that we reached a certain point in a script. **This** is the most important log file to us, as we create it ourself for the sole purpose of reporting the status of background tasks and anything that seems weird or out of place. It may not be fatal, but maybe just unexpected. If you're performing a task and there's a problem, let us know what is in this file when the problem occurred. (Please don't send me 100M dumps you'll only piss me off). Just a few relevant lines so I can rule out a few hundred thousand lines of code and concentrate on where the problem starts showing up.
These are your site logs, not mine. We report serious issues at any log level. I highly recommend 'DEBUG' log level for most sites - which provides a bit of additional info and doesn't create huge logfiles. When there's a problem which defies all attempts to track, you might wish to use DATA log level for a short period of time to capture all the detail of what structures we were dealing with at the time. This log level will use a lot of space so is recommended only for brief periods or for developer test sites.
I recommend configuring logrotate for both the php log and the application log. I usually have a look at dbfail.out every week or two, fix any issues reported and then starting over with a fresh file. Likewise with the PHP logfile. I refer to it once in a while to see if there's something that needs fixing.
If something goes wrong, and it's not a fatal error, I look at the application logfile. Often I will
```
tail -f logfile.out
```
While repeating an operation that has problems. Often I'll insert extra logging statements in the code if there isn't any hint what's going wrong. Even something as simple as "got here" or printing out the value of a variable that might be suspect. You can do this too - in fact I encourage you to do so. Once you've found what you need to find, you can
```
git checkout file.php
```
To immediately clear out all the extra logging stuff you added. Use the information from this log and any detail you can provide from your investigation of the problem to file your bug report - unless your analysis points to the source of the problem. In that case, just fix it.
##### Rotating log files
1. Enable the **logrot** addon in the official [hubzilla-addons](https://framagit.org/hubzilla/addons) repo
1. Create a directory in your web root called `log` with webserver write permissions
1. Go to the **logrot** admin settings and enter this folder name as well as the max size and number of retained log files.
#### Reporting issues
When reporting issues, please try to provide as much detail as may be necessary for developers to reproduce the issue and provide the complete text of all error messages.
We encourage you to try to the best of your abilities to use these logs combined with the source code in your possession to troubleshoot issues and find their cause. The community is often able to help, but only you have access to your site logfiles and it is considered a security risk to share them.
If a code issue has been uncovered, please report it on the project bugtracker (https://framagit.org/hubzilla/core/issues). Again provide as much detail as possible to avoid us going back and forth asking questions about your configuration or how to duplicate the problem, so that we can get right to the problem and figure out what to do about it. You are also welcome to offer your own solutions and submit patches. In fact we encourage this as we are all volunteers and have little spare time available. The more people that help, the easier the workload for everybody. It's OK if your solution isn't perfect. Every little bit helps and perhaps we can improve on it.

View file

@ -1,127 +0,0 @@
### Hub Snapshot Tools
Hubzilla developers frequently need to switch between branches that might have
incompatible database schemas or content. The following two scripts create and
restore complete snapshots of a Hubzilla instance, including both the hub web
root and the entire database state. Each script requires a config file called
`hub-snapshot.conf` residing in the same folder and containing the specific
directories and database details of your hub.
### Config
The format of the config file is very strict. There must be no spaces between the
variable name and the value. Replace only the content inside the quotes with your
configuration. Save this file as `hub-snapshot.conf` alongside the scripts.
# Location of hub root. Typically this is the location of the Hubzilla repo clone.
HUBROOT="/var/www/"
# MySQL database name
DBNAME="hubzilla"
# MySQL database user
DBUSER="hubzilla"
# MySQL database password
DBPWD="akeufajeuwfb"
# The target snapshot folder where the git repo will be initialized
SNAPSHOTROOT="/root/snapshots/hubzilla/"
### Snapshot
Example usage:
sh hub-snapshot.sh my-hub.conf "Commit message for the snapshot"
**hub-snapshot.sh**:
#!/bin/bash
if ! [ -f "$1" ]; then
echo "$1 is not a valid file. Aborting..."
exit 1
fi
source "$1"
#echo "$DBNAME"
#echo "$DBUSER"
#echo "$DBPWD"
#echo "$HUBROOT"
#echo "$SNAPSHOTROOT"
MESSAGE="snapshot: $2"
if [ "$DBPWD" == "" -o "$SNAPSHOTROOT" == "" -o "$DBNAME" == "" -o "$DBUSER" == "" -o "$HUBROOT" == "" ]; then
echo "Required variable is not set. Aborting..."
exit 1
fi
if [ ! -d "$SNAPSHOTROOT"/db/ ]; then
mkdir -p "$SNAPSHOTROOT"/db/
fi
if [ ! -d "$SNAPSHOTROOT"/www/ ]; then
mkdir -p "$SNAPSHOTROOT"/www/
fi
if [ ! -d "$SNAPSHOTROOT"/www/ ] || [ ! -d "$SNAPSHOTROOT"/db/ ]; then
echo "Error creating snapshot directories. Aborting..."
exit 1
fi
echo "Export database..."
mysqldump -u "$DBUSER" -p"$DBPWD" "$DBNAME" > "$SNAPSHOTROOT"/db/"$DBNAME".sql
echo "Copy hub root files..."
rsync -va --delete --exclude=.git* "$HUBROOT"/ "$SNAPSHOTROOT"/www/
cd "$SNAPSHOTROOT"
if [ ! -d ".git" ]; then
git init
fi
if [ ! -d ".git" ]; then
echo "Cannot initialize git repo. Aborting..."
exit 1
fi
git add -A
echo "Commit hub snapshot..."
git commit -a -m "$MESSAGE"
exit 0
### Restore
#!/bin/bash
# Restore hub to a previous state. Input hub config and commit hash
if ! [ -f "$1" ]; then
echo "$1 is not a valid file. Aborting..."
exit 1
fi
source "$1"
COMMIT=$2
if [ "$DBPWD" == "" -o "$SNAPSHOTROOT" == "" -o "$DBNAME" == "" -o "$DBUSER" == "" -o "$HUBROOT" == "" ]; then
echo "Required variable is not set. Aborting..."
exit 1
fi
RESTOREDIR="$(mktemp -d)/"
if [ ! -d "$RESTOREDIR" ]; then
echo "Cannot create restore directory. Aborting..."
exit 1
fi
echo "Cloning the snapshot repo..."
git clone "$SNAPSHOTROOT" "$RESTOREDIR"
cd "$RESTOREDIR"
echo "Checkout requested snapshot..."
git checkout "$COMMIT"
echo "Restore hub root files..."
rsync -a --delete --exclude=.git* "$RESTOREDIR"/www/ "$HUBROOT"/
echo "Restore hub database..."
mysql -u "$DBUSER" -p"$DBPWD" "$DBNAME" < "$RESTOREDIR"/db/"$DBNAME".sql
chown -R www-data:www-data "$HUBROOT"/{store,extend,addon,.htlog,.htconfig.php}
echo "Restored hub to snapshot $COMMIT"
echo "Removing temporary files..."
rm -rf "$RESTOREDIR"
exit 0

View file

@ -1,15 +0,0 @@
[h2]Documentation for Hub Administrators[/h2]
[h3]Deploying your hub[/h3]
[zrl=[baseurl]/help/install]Install[/zrl]
[zrl=[baseurl]/help/red2pi]Installing $Projectname on the Raspberry Pi[/zrl]
[zrl=[baseurl]/help/Hubzilla_on_OpenShift]$Projectname on OpenShift[/zrl]
[h3]Taking care of your hub[/h3]
[zrl=[baseurl]/help/troubleshooting]Troubleshooting Tips[/zrl]
[zrl=[baseurl]/help/theme_management]Theme Management[/zrl]
[zrl=[baseurl]/help/hidden_configs]Tweaking $Projectname's Hidden Configurations[/zrl]
[zrl=[baseurl]/help/service_classes]Service Classes[/zrl]
[zrl=[baseurl]/help/directories]Working with and configuring Directories[/zrl]
[h3]Frequently asked questions[/h3]
[zrl=[baseurl]/help/faq_admins]FAQ For Admins[/zrl]
#include doc/macros/main_footer.bb;

View file

@ -1,66 +0,0 @@
API albums
==========
Description: list photo albums
GET /api/z/1.0/albums
Output:
text - textual name
total - number of photos in this album
url - web URL
urlencode - textual name, urlencoded
bin2hex - textual name using bin2hex (which is used in the web URL link)
Example:
{
"success": true,
"albums": [
{
"text": "/",
"total": "2",
"url": "https://xyz.macgirvin.com/photos/hubzilla/album/",
"urlencode": "",
"bin2hex": ""
},
{
"text": "2016-01",
"total": "6",
"url": "https://xyz.macgirvin.com/photos/hubzilla/album/323031362d3031",
"urlencode": "2016-01",
"bin2hex": "323031362d3031"
},
{
"text": "2016-02",
"total": "7",
"url": "https://xyz.macgirvin.com/photos/hubzilla/album/323031362d3032",
"urlencode": "2016-02",
"bin2hex": "323031362d3032"
},
{
"text": "Cover Photos",
"total": "5",
"url": "https://xyz.macgirvin.com/photos/hubzilla/album/436f7665722050686f746f73",
"urlencode": "Cover+Photos",
"bin2hex": "436f7665722050686f746f73"
},
{
"text": "Profile Photos",
"total": "26",
"url": "https://xyz.macgirvin.com/photos/hubzilla/album/50726f66696c652050686f746f73",
"urlencode": "Profile+Photos",
"bin2hex": "50726f66696c652050686f746f73"
}
]
}

View file

@ -1,66 +0,0 @@
API filedata
=============
Provides the ability to download a file from cloud storage in chunks
GET /api/z/1.0/filedata
Required:
- file_id
attach.hash of desired file ('begins with' match)
Optional:
- start
starting byte of returned data in file (counting from 0)
- length
length (prior to base64 encoding) of chunk to download
Returns:
attach (DB) structure with base64 encoded 'content' comprised of the desired chunk
Example:
https://xyz.macgirvin.com/api/z/1.0/filedata?f=&file_id=9f5217770fd&start=0&length=48
Returns:
{
"attach": {
"id": "107",
"aid": "1",
"uid": "2",
"hash": "9f5217770fd55d563bd77f84d534d8e119a187514bbd391714626cd9c0e60207",
"creator": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"filename": "pcxtopbm.c",
"filetype": "application/octet-stream",
"filesize": "3934",
"revision": "0",
"folder": "",
"flags": "0",
"is_dir": "0",
"is_photo": "0",
"os_storage": "1",
"os_path": "",
"display_path": "",
"content": "LyogcGN4dG9wYm0uYyAtIGNvbnZlcnQgUEMgcGFpbnRicnVzaCAoLnBjeCkgZmls",
"created": "2016-07-24 23:13:01",
"edited": "2016-07-24 23:13:01",
"allow_cid": "",
"allow_gid": "",
"deny_cid": "",
"deny_gid": "",
"start": 0,
"length": 48
}
}

View file

@ -1,103 +0,0 @@
API files
=========
List file storage (attach DB)
GET /api/z/1.0/files
Options:
- hash
return only entries matching hash (exactly)
- filename
return only entries matching filename (substring)
- filetype
return only entries matching filetype/mimetype (substring)
- start
start at record (default 0)
- records
number of records to return or 0 for unlimited
Example:
curl -u mychannel:mypassword https://xyz.macgirvin.com/api/z/1.0/files -d filetype=multipart/mixed
Returns:
{
"success": true,
"results": [
{
"id": "1",
"aid": "1",
"uid": "2",
"hash": "44ee8b2a1a7f36dea07b93b7747a2383a1bc0fdd08339e8928bfcbe45f65d939",
"filename": "Profile Photos",
"filetype": "multipart/mixed",
"filesize": "0",
"revision": "0",
"folder": "",
"os_storage": "1",
"is_dir": "1",
"is_photo": "0",
"flags": "0",
"created": "2016-01-02 21:51:17",
"edited": "2016-01-02 21:51:17",
"allow_cid": "",
"allow_gid": "",
"deny_cid": "",
"deny_gid": ""
},
{
"id": "12",
"aid": "1",
"uid": "2",
"hash": "71883f1fc64af33889229cbc79c5a056deeec5fc277d765f182f19073e1b2998",
"filename": "Cover Photos",
"filetype": "multipart/mixed",
"filesize": "0",
"revision": "0",
"folder": "",
"os_storage": "1",
"is_dir": "1",
"is_photo": "0",
"flags": "0",
"created": "2016-01-15 00:24:33",
"edited": "2016-01-15 00:24:33",
"allow_cid": "",
"allow_gid": "",
"deny_cid": "",
"deny_gid": ""
},
{
"id": "16",
"aid": "1",
"uid": "2",
"hash": "f48f7ec3278499d1dd86b72c3207beaaf4717b07df5cc9b373f14d7aad2e1bcd",
"filename": "2016-01",
"filetype": "multipart/mixed",
"filesize": "0",
"revision": "0",
"folder": "",
"os_storage": "1",
"is_dir": "1",
"is_photo": "0",
"flags": "0",
"created": "2016-01-22 03:24:55",
"edited": "2016-01-22 03:26:57",
"allow_cid": "",
"allow_gid": "",
"deny_cid": "",
"deny_gid": ""
}
]
}

View file

@ -1,133 +0,0 @@
[b]Red Twitter API[/b]
The &quot;basic&quot; Red web API is based on the Twitter API, as this provides instant compatibility with a huge number of third-party clients and applications without requiring any code changes on their part. It is also a super-set of the StatusNet version of the Twitter API, as this also has existing wide support.
Red has a lot more capability that isn't exposed in the Twitter interfaces or where we are forced to &quot;dumb-down&quot; the API functions to work with the primitive Twitter/StatusNet communications and privacy model. So we plan to extend the Twitter API in ways that will allow Red-specific clients to make full use of Red features without being crippled.
A dedicated Red API is also being developed to work with native data structures and permissions and which do not require translating to different privacy and permission models and storage formats. This will be described in other documents. The prefix for all of the native endpoints is 'api/red'.
Red provides multiple channels accesible via the same login account. With Red, any API function which requires authentication will accept a parameter &amp;channel={channel_nickname} - and will select that channel and make it current before executing the API command. By default, the default channel associated with an account is selected.
Red also provides an extended permission model. In the absence of any Red specific API calls to set permissions, they will be set to the default permissions settings which are associated with the current channel.
Red will probably never be able to support the Twitter 'api/friendships' functions fully because Red is not a social network and has no concept of &quot;friendships&quot; - it only recognises permissions to do stuff (or not do stuff as the case may be).
Legend: T= Twitter, S= StatusNet, F= Friendica, R= Red, ()=Not yet working, J= JSON only (XML formats deprecated)
Twitter API compatible functions:
api/account/verify_credentials T,S,F,R
api/statuses/update T,S,F,R
api/users/show T,S,F,R
api/statuses/home_timeline T,S,F,R
api/statuses/friends_timeline T,S,F,R
api/statuses/public_timeline T,S,F,R
api/statuses/show T,S,F,R
api/statuses/retweet T,S,F,R
api/statuses/destroy T,S,F,(R)
api/statuses/mentions T,S,F,(R)
api/statuses/replies T,S,F,(R)
api/statuses/user_timeline T,S,F,(R)
api/favorites T,S,F,R
api/account/rate_limit_status T,S,F,R
api/help/test T,S,F,R
api/statuses/friends T,S,F,R
api/statuses/followers T,S,F,R
api/friends/ids T,S,F,R
api/followers/ids T,S,F,R
api/direct_messages/new T,S,F,R
api/direct_messages/conversation T,S,F,R
api/direct_messages/all T,S,F,R
api/direct_messages/sent T,S,F,R
api/direct_messages T,S,F,R
api/oauth/request_token T,S,F,R
api/oauth/access_token T,S,F,R
api/favorites T,S,R
api/favorites/create T,S,R
api/favorites/destroy T,S,R
Twitter API functions supported by StatusNet but not currently by Friendica or Red
api/statuses/retweets_of_me T,S
api/friendships/create T,S
api/friendships/destroy T,S
api/friendships/exists T,S
api/friendships/show T,S
api/account/update_location T,S
api/account/update_profile_background_image T,S
api/account/update_profile_image T,S
api/blocks/create T,S
api/blocks/destroy T,S
Twitter API functions not currently supported by StatusNet
api/statuses/retweeted_to_me T
api/statuses/retweeted_by_me T
api/direct_messages/destroy T
api/account/end_session T,(R)
api/account/update_delivery_device T
api/notifications/follow T
api/notifications/leave T
api/blocks/exists T
api/blocks/blocking T
api/lists T
Statusnet compatible extensions to the Twitter API supported in both Friendica and Red
api/statusnet/version S,F,R
api/statusnet/config S,F,R
Friendica API extensions to the Twitter API supported in both Friendica and Red
api/statuses/mediap F,R
Red specific API extensions to the Twitter API not supported in Friendica
api/account/logout R
api/export/basic R,J
api/friendica/config R
api/red/config R
api/friendica/version R
api/red/version R
api/red/channel/export/basic R,J
api/red/channel/stream R,J (currently post only)
api/red/albums R,J
api/red/photos R,J (option album=xxxx)
Red proposed API extensions to the Twitter API
api/statuses/edit (R),J
api/statuses/permissions (R),J
api/statuses/permissions/update (R),J
api/statuses/ids (R),J # search for existing message_id before importing a foreign post
api/files/show (R),J
api/files/destroy (R),J
api/files/update (R),J
api/files/permissions (R),J
api/files/permissions/update (R),J
api/pages/show (R),J
api/pages/destroy (R),J
api/pages/update (R),J
api/pages/permissions (R),J
api/pages/permissions/update (R),J
api/events/show (R),J
api/events/update (R),J
api/events/permissions (R),J
api/events/permissions/update (R),J
api/events/destroy (R),J
api/photos/show (R),J
api/photos/update (R),J
api/photos/permissions (R),J
api/photos/permissions/update (R),J
api/albums/destroy (R),J
api/albums/show (R),J
api/albums/update (R),J
api/albums/permissions (R),J
api/albums/permissions/update (R),J
api/albums/destroy (R),J
api/friends/permissions (R),J
#include doc/macros/main_footer.bb;

View file

@ -1,133 +0,0 @@
API group_members
=================
GET /api/z/1.0/group_members
Required:
group_id or group_name
Returns:
group_member+abook+xchan (DB join) for each member of the privacy group
[
{
"id": "1",
"uid": "2",
"gid": "1",
"xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"abook_id": "2",
"abook_account": "1",
"abook_channel": "2",
"abook_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"abook_my_perms": "218555",
"abook_their_perms": "0",
"abook_closeness": "0",
"abook_created": "2016-01-02 21:16:26",
"abook_updated": "2016-01-02 21:16:26",
"abook_connected": "0000-00-00 00:00:00",
"abook_dob": "0000-00-00 00:00:00",
"abook_flags": "0",
"abook_blocked": "0",
"abook_ignored": "0",
"abook_hidden": "0",
"abook_archived": "0",
"abook_pending": "0",
"abook_unconnected": "0",
"abook_self": "1",
"abook_feed": "0",
"abook_profile": "",
"abook_incl": "",
"abook_excl": "",
"abook_instance": "",
"xchan_hash": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"xchan_guid": "lql-1VnxtiO4-WF0h72wLX1Fu8szzHDOXgQaTbELwXW77k8AKFfh-hYr70vqMrc3SSvWN-Flrc5HFhRTWB7ICw",
"xchan_guid_sig": "PafvEL0VpKfxATxlCqDjfOeSIMdmpr3iU7X-Sysa1h5LzDpjSXsjO37tYZL-accb1M5itLlfnW5epkTa5I4flsW21zSY1A2jCuBQUTLLGV7rNyyBy7lgqJUFvAMRx0TfXzP9lcaPqlM9T1tA6jfWOsOmkdzwofGeXBnsjGfjsO2xdGYe6vwjOU0DSavukvzDMnOayB9DekpvDnaNBTxeGLM45Skzr7ZEMcNF7TeXMbnvpfLaALYEKeQs9bGH-UgAG8fBWgzVAzeBfx_XSR1rdixjyiZGP0kq0h35SlmMPcEjliodOBFwMXqpXFB7Ibp4F6o6te2p2ErViJccQVG8VNKB6SbKNXY6bhP5zVcVsJ-vR-p4xXoYJJvzTN7yTDsGAXHOLF4ZrXbo5yi5gFAlIrTLAF2EdWQwxSGyLRWKxG8PrDkzEzX6cJJ0VRcLh5z6OI5QqQNdeghPZbshMFMJSc_ApCPi9_hI4ZfctCIOi3T6bdgTNKryLm5fhy_eqjwLAZTGP-aUBgLZpb1mf2UojBn6Ey9cCyq-0T2RWyk-FcIcbV4qJ-p_8oODqw13Qs5FYkjLr1bGBq82SuolkYrXEwQClxnrfKa4KYc2_eHAXPL01iS9zVnI1ySOCNJshB97Odpooc4wk7Nb2Fo-Q6THU9zuu0uK_-JbK7IIl6go2qA",
"xchan_pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA18JB76lyP4zzL/y7BCej\neJnfZIWZNtM3MZvI1zEVMWmmwOS+u/yH8oPwyaDk4Y/tnj8GzMPj1lCGVRcd8EJa\nNrCMd50HODA5EsJtxpsOzRcILYjOcTtIAG1K4LtKqELi9ICAaFp0fNfa+Jf0eCek\nvPusx2/ORhy+o23hFoSMhL86o2gmaiRnmnA3Vz4ZMG92ieJEDMXt9IA1EkIqS4y5\nBPZfVPLD1pv8iivj+dtN1XjwplgjUbtxmU0/Ej808nHppscRIqx/XJ0XZU90oNGw\n/wYoK2EzJlPbRsAkwNqoFrAYlr5HPpn4BJ2ebFYQgWBUraD7HwS5atsQEaxGfO21\nlUP0+lDg9t3CXvudDj0UG1jiEKbVIGA+4aG0GN2DSC5AyRq/GRxqyay5W2vQbAZH\nyvxPGrZFO24I65g3pjhpjEsLqZ4ilTLQoLMs0drCIcRm5RxMUo4s/LMg16lT4cEk\n1qRtk2X0Sb1AMQQ2uRXiVtWz77QHMONEYkf6OW4SHbwcv5umvlv69NYEGfCcbgq0\nAV7U4/BWztUz/SWj4r194CG43I9I8dmaEx9CFA/XMePIAXQUuABfe1QMOR6IxLpq\nTHG1peZgHQKeGz4aSGrhQkZNNoOVNaZoIfcvopxcHDTZLigseEIaPPha4WFYoKPi\nUPbZ5o8gTLc750uzrnb2jwcCAwEAAQ==\n-----END PUBLIC KEY-----\n",
"xchan_photo_mimetype": "image/png",
"xchan_photo_l": "https://xyz.macgirvin.com/photo/profile/l/2",
"xchan_photo_m": "https://xyz.macgirvin.com/photo/profile/m/2",
"xchan_photo_s": "https://xyz.macgirvin.com/photo/profile/s/2",
"xchan_addr": "teller@xyz.macgirvin.com",
"xchan_url": "https://xyz.macgirvin.com/channel/teller",
"xchan_connurl": "https://xyz.macgirvin.com/poco/teller",
"xchan_follow": "https://xyz.macgirvin.com/follow?f=&url=%s",
"xchan_connpage": "",
"xchan_name": "Teller",
"xchan_network": "zot",
"xchan_instance_url": "",
"xchan_flags": "0",
"xchan_photo_date": "2016-10-19 01:26:50",
"xchan_name_date": "2016-01-02 21:16:26",
"xchan_hidden": "0",
"xchan_orphan": "0",
"xchan_censored": "0",
"xchan_selfcensored": "0",
"xchan_system": "0",
"xchan_pubforum": "0",
"xchan_deleted": "0"
},
{
"id": "12",
"uid": "2",
"gid": "1",
"xchan": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
"abook_id": "24",
"abook_account": "1",
"abook_channel": "2",
"abook_xchan": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
"abook_my_perms": "218555",
"abook_their_perms": "218555",
"abook_closeness": "80",
"abook_created": "2016-01-27 00:48:43",
"abook_updated": "2016-12-04 17:16:58",
"abook_connected": "2016-12-04 17:16:58",
"abook_dob": "0001-01-01 00:00:00",
"abook_flags": "0",
"abook_blocked": "0",
"abook_ignored": "0",
"abook_hidden": "0",
"abook_archived": "0",
"abook_pending": "0",
"abook_unconnected": "0",
"abook_self": "0",
"abook_feed": "0",
"abook_profile": "debb5236efb1626cfbad33ccb49892801e5f844aa04bf81f580cfa7d13204819",
"abook_incl": "",
"abook_excl": "",
"abook_instance": "",
"xchan_hash": "xuSMUYxw1djBB97qXsbrBN1nzJH_gFwQL6pS4zIy8fuusOfBxNlMiVb4h_q5tOEvpE7tYf1EsryjNciMuPIj5w",
"xchan_guid": "d5EMLlt1tHHZ0dANoA7B5Wq9UgXoWcFS9-gXOkL_AAejcPApoQRyxfHTuu8DoTbUaO-bYmX5HPuWuK9PHyqNmA",
"xchan_guid_sig": "CVWEMRPtzI1YcHfnnWHTuv3H964OAmSElgUfxMoX6RdQdxNpqb_POirpVuyP8s3W17mVCfO5V9IAjkg5iKcqCk6YcvOD_egmMy-AnM9TC1kKndQHw55CunD82Q8K_xBNSXkSROizcNkKh9DVLjJPFjW1AqtI4njkZ3EMgrWqnbFRM1qPToUoCY9zM3tEMHoAD9YX1zP90wl40LzfN-dtcNWpSBbiz9owou62uzLbN7mrCwKOMlXLjwwGswRnxIsEnb3O-FXOs8hs0mArKe9snq1-BKeD16LyzxgwlpVLElzIJZGEZGtMdIJgeRzKuBvPjsOIpQ1yAkuOpFJ3nGCM-IPOIIjAmyVl5zD3xPVcxxpZlJRn5fG1Y-gnqTgsrEQCA7M6XPWQdrdHU4akZfyUyFJDhv3uM-jon9VzrYTBw68R0WA-1Z8WafEHA4qh5OWAj85lUarwhr7iTiEckH51ypPCPs6VbT6Pw7yMaxfjFOcipashQagx0tfOlDhE5dQANOXKASFtH1J9-CZY2MQdLPQ6u54d5whuHKMGaJ0V68pnmZ2rOn7g344Ah2WCJrm17jj60QsRMorqRFj7GMdPIA1XB8Wrk88MuYOe3Dhyuu6ZWKI7YTWJS690ZVkKUqAiNHqj0W86DtaiPUc_mmGR0fHl4Gksnko3WmCFv9q2X2E",
"xchan_pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoj2xCJktBA8Ww7Hp+ZNL\nrNuQpo8UB/bfvRkIy+yua3xpF1TuXcnAH61kyRz8vXgOu/l2CyxQbIoaGslCV5Sy\n8JKeNXe+IilUdSSEjMIwCPfSPsYnMHsSnHWmPmclvJwEtQUKOZmW5mMuVBvXy7D2\njomFwc69AYphdyys6eQ7Dcn6+FRBiQbyMprZ5lxyVW+O4DuXVNa3ej2ebx0gCJZ4\ntTIlBoKwEey91dY+FyKVFjdwfNczpmL7LgmZXqcVx+MG3mYgibwdVMiXVj5X06cs\nV9hJ5Xi+Aklsv/UWJtjw9FVt7y9TLptnhh4Ra6T/MDmnBBIAkOR7P/X8cRv078MT\nl0IMsP0RJcDEtTLtwHFVtDs6p52KDFqclKWbqmxmxqV3OTPVYtArRGIzgnJi/5ur\nHRr5G6Cif7QY3UowsIOf78Qvy28LwSbdymgBAWwPPKIviXWxGO+9kMWdmPSUQrWy\nK0+7YA9P9fBUFfn9Hc+p8SJQmQ6OAqLwrDGiPSOlGaNrbEqwqLGgIpXwK+lEFcFJ\n3SPOjJRWdR2whlMxvpwX+39+H7dWN3vSa3Al4/Sq7qW8yW2rYwf+eGyp4Z0lRR+8\nJxFMCwZkSw5g14YdlikAPojv5V1c6KuA5ieg8G1hwyONV7A4JHPyEdPt0W0TZi6C\nCOVkPaC3xGrguETZpJfVpwUCAwEAAQ==\n-----END PUBLIC KEY-----\n",
"xchan_photo_mimetype": "image/png",
"xchan_photo_l": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-4",
"xchan_photo_m": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-5",
"xchan_photo_s": "https://xyz.macgirvin.com/photo/9da63aa910ea14e1501ee1a749d181a6-6",
"xchan_addr": "cloner@xyz.macgirvin.com",
"xchan_url": "http://abc.macgirvin.com/channel/cloner",
"xchan_connurl": "http://abc.macgirvin.com/poco/cloner",
"xchan_follow": "https://xyz.macgirvin.com/follow?f=&url=%s",
"xchan_connpage": "",
"xchan_name": "Karen",
"xchan_network": "zot",
"xchan_instance_url": "",
"xchan_flags": "0",
"xchan_photo_date": "2016-03-31 19:59:20",
"xchan_name_date": "2016-01-26 23:23:42",
"xchan_hidden": "0",
"xchan_orphan": "0",
"xchan_censored": "0",
"xchan_selfcensored": "0",
"xchan_system": "0",
"xchan_pubforum": "0",
"xchan_deleted": "0"
}
]

View file

@ -1,225 +0,0 @@
API item/update
===============
Usage: POST /api/z/1.0/item/update
Description: item/update posts an item (typically a conversation item or post, but can be any item) using form input.
Required:
- body
text/bbcode contents by default.
Optional:
- $_FILES['media']
uploaded media file to include with post
- title
title of post/item
- contact_allow
array of xchan.xchan_hash allowed to view this item
- group_allow
array of group.hash allowed to view this item
- contact_deny
array of xchan.xchan_hash not allowed to view this item
- group_deny
array of group.hash not allowed to view this item
- coord
geographic coordinates
- location
freefrom location
- expire
datetime this post will expire or be removed
- mimetype
mimetype if not text/bbcode
- parent
item.id of parent to this post (makes it a comment)
- parent_mid
alternate form of parent using message_id
- remote_xchan
xchan.xchan_hash of this message author if not the channel owner
- consensus
boolean set to true if this is a consensus or voting item (default false)
- nocomment
boolean set to true if comments are to be disabled (default false)
- origin
do not use this without reading the code
- namespace
persistent identity for a remote network or service
- remote_id
message_id of this resource on a remote network or service
- message_id
message_id of this item (leave unset to generate one)
- created
datetime of message creation
- post_id
existing item.id if this is an edit operation
- app
application or network name to display with item
- categories
comma separated categories for this item
- webpage
item.page_type if not 0
- pagetitle
for webpage and design elements, the 'page name'
- layout_mid
item.mid of layout for this design element
- plink
permalink for this item if different than the default
- verb
activitystream verb for this item/activity
- obj_type
activitystream object type for this item/activity
Example:
curl -u mychannel:mypassword https://xyz.macgirvin.com/api/z/1.0/item/update -d body="hello world"
Returns:
{
"success": true,
"item_id": "2245",
"item": {
"id": "2245",
"mid": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
"aid": "1",
"uid": "2",
"parent": "2245",
"parent_mid": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
"thr_parent": "14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
"created": "2016-12-03 20:00:12",
"edited": "2016-12-03 20:00:12",
"expires": "0001-01-01 00:00:00",
"commented": "2016-12-03 20:00:12",
"received": "2016-12-03 20:00:12",
"changed": "2016-12-03 20:00:12",
"comments_closed": "0001-01-01 00:00:00",
"owner_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"author_xchan": "pgcJx1IQjuPkx8aI9qheJlBMZzJz-oTPjHy3h5pWlOVOriBO_cSiUhhqwhuZ74TYJ8_ECO3pPiRMWC0q8YPCQg",
"source_xchan": "",
"mimetype": "text/bbcode",
"title": "",
"body": "hello world",
"html": "",
"app": "",
"lang": "",
"revision": "0",
"verb": "http://activitystrea.ms/schema/1.0/post",
"obj_type": "http://activitystrea.ms/schema/1.0/note",
"obj": "",
"tgt_type": "",
"target": "",
"layout_mid": "",
"postopts": "",
"route": "",
"llink": "https://xyz.macgirvin.com/display/14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
"plink": "https://xyz.macgirvin.com/channel/mychannel/?f=&mid=14135cdecf6b8e3891224e4391748722114da6668eebbcb56fe4667b60b88249@xyz.macgirvin.com",
"resource_id": "",
"resource_type": "",
"attach": "",
"sig": "sa4TOQNfHtV13HDZ1tuQGWNBpZp-nWhT2GMrZEmelXxa_IvEepD2SEsCTWOBqM8OKPJLfNy8_i-ORXjrOIIgAa_aT8cw5vka7Q0C8L9eEb_LegwQ_BtH0CXO5uT30e_8uowkwzh6kmlVg1ntD8QqrGgD5jTET_fMQOIw4gQUBh40GDG9RB4QnPp_MKsgemGrADnRk2vHO7-bR32yQ0JI-8G-eyeqGaaJmIwkHoi0vXsfjZtU7ijSLuKEBWboNjKEDU89-vQ1c5Kh1r0pmjiDk-a5JzZTYShpuhVA-vQgEcADA7wkf4lJZCYNwu3FRwHTvhSMdF0nmyv3aPFglQDky38-SAXZyQSvd7qlABHGCVVDmYrYaiq7Dh4rRENbAUf-UJFHPCVB7NRg34R8HIqmOKq1Su99bIWaoI2zuAQEVma9wLqMoFsluFhxX58KeVtlCZlro7tZ6z619-dthS_fwt0cL_2dZ3QwjG1P36Q4Y4KrCTpntn9ot5osh-HjVQ01h1I9yNCj6XPgYJ8Im3KT_G4hmMDFM7H9RUrYLl2o9XYyiS2nRrf4aJHa0UweBlAY4zcQG34bw2AMGCY53mwsSArf4Hs3rKu5GrGphuwYX0lHa7XEKMglwBWPWHI49q7-oNWr7aWwn1FnfaMfl4cQppCMtKESMNRKm_nb9Dsh5e0",
"diaspora_meta": "",
"location": "",
"coord": "",
"public_policy": "",
"comment_policy": "contacts",
"allow_cid": "",
"allow_gid": "",
"deny_cid": "",
"deny_gid": "",
"item_restrict": "0",
"item_flags": "0",
"item_private": "0",
"item_origin": "1",
"item_unseen": "0",
"item_starred": "0",
"item_uplink": "0",
"item_consensus": "0",
"item_wall": "1",
"item_thread_top": "1",
"item_notshown": "0",
"item_nsfw": "0",
"item_relay": "0",
"item_mentionsme": "0",
"item_nocomment": "0",
"item_obscured": "0",
"item_verified": "1",
"item_retained": "0",
"item_rss": "0",
"item_deleted": "0",
"item_type": "0",
"item_hidden": "0",
"item_unpublished": "0",
"item_delayed": "0",
"item_pending_remove": "0",
"item_blocked": "0"
}
}

View file

@ -1,24 +0,0 @@
[b][size=xx-large]Posting to the Matrix via the API[/size][/b]
The API allows you to post to the red# by HTTP POST request. Below you see an example using the command line tool cURL:
[code]curl -ssl -u [color=blue]$E-Mail[/color]:[color=blue]$Password[/color] -d "[color=blue]$Parameters[/color]" [url][observer=1][observer.baseurl][/observer][observer=0]example.com[/observer]/api/statuses/update
[/url][/code]
[table][tr][td]$E-Mail:[/td][td]The E-Mail Address you use to login, or the channel nickname (without the hostname)[/td][/tr]
[tr][td]$Password:[/td][td]The Password you use to login[/td][/tr]
[tr][td]$Parameters:[/td][td]That's the interesting part, here you insert the content you want to send using the following parameters:[/td][/tr][/table]
[ul]
[*]title: the title of the posting
[*]channel: the channel you want to post to (do not use this parameter with HTTP Basic auth)
[*]category: a comma-seperated list of categories for the posting
[*]status: the content of the posting, formatted with BBCode
OR
[*]htmlstatus:the content of the posting, formatted in HTML.
[/ul]
To post to a specific channel, replace the email address with the channel nickname. If you supply the channel parameter, it has to match the "email", but is superfluous anyway.
Instead of calling [observer=1][observer.baseurl][/observer][observer=0]example.com[/observer]/api/statuses/update which returns a json (you could also add .json on the end to clarify) output, you can use [observer.baseurl]/api/statuses/update.xml to get an xml formatted return.
Instead of Basic HTTP Authentification you could also use oAuth.

View file

@ -1,44 +0,0 @@
API xchan
=========
An xchan is a global location independent channel and is the primary record for a network
identity. It may refer to channels on other websites, networks, or services.
GET /api/z/1.0/xchan
Required: one of [ address, hash, guid ] as GET parameters
Returns a portable xchan structure
Example: https://xyz.macgirvin.com/api/z/1.0/xchan?f=&address=mike@macgirvin.com
Returns:
{
"hash": "jr54M_y2l5NgHX5wBvP0KqWcAHuW23p1ld-6Vn63_pGTZklrI36LF8vUHMSKJMD8xzzkz7s2xxCx4-BOLNPaVA",
"guid": "sebQ-IC4rmFn9d9iu17m4BXO-kHuNutWo2ySjeV2SIW1LzksUkss12xVo3m3fykYxN5HMcc7gUZVYv26asx-Pg",
"guid_sig": "Llenlbl4zHo6-g4sa63MlQmTP5dRCrsPmXHHFmoCHG63BLq5CUZJRLS1vRrrr_MNxr7zob_Ykt_m5xPKe5H0_i4pDj-UdP8dPZqH2fqhhx00kuYL4YUMJ8gRr5eO17vsZQ3XxTcyKewtgeW0j7ytwMp6-hFVUx_Cq08MrXas429ZrjzaEwgTfxGnbgeQYQ0R5EXpHpEmoERnZx77VaEahftmdjAUx9R4YKAp13pGYadJOX5xnLfqofHQD8DyRHWeMJ4G1OfWPSOlXfRayrV_jhnFlZjMU7vOdQwHoCMoR5TFsRsHuzd-qepbvo3pzvQZRWnTNu6oPucgbf94p13QbalYRpBXKOxdTXJrGdESNhGvhtaZnpT9c1QVqC46jdfP0LOX2xrVdbvvG2JMWFv7XJUVjLSk_yjzY6or2VD4V6ztYcjpCi9d_WoNHruoxro_br1YO3KatySxJs-LQ7SOkQI60FpysfbphNyvYMkotwUFI59G08IGKTMu3-GPnV1wp7NOQD1yzJbGGEGSEEysmEP0SO9vnN45kp3MiqbffBGc1r4_YM4e7DPmqOGM94qksOcLOJk1HNESw2dQYWxWQTBXPfOJT6jW9_crGLMEOsZ3Jcss0XS9KzBUA2p_9osvvhUKuKXbNztqH0oZIWlg37FEVsDs_hUwUJpv2Ar09k4",
"pubkey": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7QCwvuEIwCHjhjbpz3Oc\ntyei/Pz9nDksNbsc44Cm8jxYGMXsTPFXDZYCcCB5rcAhPPdZSlzaPkv4vPVcMIrw\n5cdX0tvbwa3rNTng6uFE7qkt15D3YCTkwF0Y9FVZiZ2Ko+G23QeBt9wqb9dlDN1d\nuPmu9BLYXIT/JXoBwf0vjIPFM9WBi5W/EHGaiuqw7lt0qI7zDGw77yO5yehKE4cu\n7dt3SakrXphL70LGiZh2XGoLg9Gmpz98t+gvPAUEotAJxIUqnoiTA8jlxoiQjeRK\nHlJkwMOGmRNPS33awPos0kcSxAywuBbh2X3aSqUMjcbE4cGJ++/13zoa6RUZRObC\nZnaLYJxqYBh13/N8SfH7d005hecDxWnoYXeYuuMeT3a2hV0J84ztkJX5OoxIwk7S\nWmvBq4+m66usn6LNL+p5IAcs93KbvOxxrjtQrzohBXc6+elfLVSQ1Rr9g5xbgpub\npSc+hvzbB6p0tleDRzwAy9X16NI4DYiTj4nkmVjigNo9v2VPnAle5zSam86eiYLO\nt2u9YRqysMLPKevNdj3CIvst+BaGGQONlQalRdIcq8Lin+BhuX+1TBgqyav4XD9K\nd+JHMb1aBk/rFLI9/f2S3BJ1XqpbjXz7AbYlaCwKiJ836+HS8PmLKxwVOnpLMbfH\nPYM8k83Lip4bEKIyAuf02qkCAwEAAQ==\n-----END PUBLIC KEY-----\n",
"photo_mimetype": "image/jpeg",
"photo_l": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-4",
"photo_m": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-5",
"photo_s": "https://xyz.macgirvin.com/photo/350b74555c04429148f2e12775f6c403-6",
"address": "mike@macgirvin.com",
"url": "https://macgirvin.com/channel/mike",
"connurl": "https://macgirvin.com/poco/mike",
"follow": "https://macgirvin.com/follow?f=&url=%s",
"connpage": "https://macgirvin.com/connect/mike",
"name": "Mike Macgirvin",
"network": "zot",
"instance_url": "",
"flags": "0",
"photo_date": "2012-12-06 05:06:11",
"name_date": "2012-12-06 04:59:13",
"hidden": "1",
"orphan": "0",
"censored": "0",
"selfcensored": "0",
"system": "0",
"pubforum": "0",
"deleted": "0"
}

View file

@ -1,41 +0,0 @@
API group
=========
GET /api/z/1.0/group
Description: list privacy groups
Returns: DB tables of all privacy groups.
To use with API group_members, provide either 'group_id' from the id element returned in this call, or 'group_name' from the gname returned in this call.
[
{
"id": "1",
"hash": "966c946394f3e2627bbb8a55026b5725e582407098415c02f85232de3f3fde76Friends",
"uid": "2",
"visible": "0",
"deleted": "0",
"gname": "Friends"
},
{
"id": "2",
"hash": "852ebc17f8c3ed4866f2162e384ded0f9b9d1048f93822c0c84196745f6eec66Family",
"uid": "2",
"visible": "1",
"deleted": "0",
"gname": "Family"
},
{
"id": "3",
"hash": "cc3cb5a7f9818effd7c7c80a58b09a189b62efa698a74319117babe33ee30ab9Co-workers",
"uid": "2",
"visible": "0",
"deleted": "0",
"gname": "Co-workers"
}
]

View file

@ -1,23 +0,0 @@
[h2]statuses/update[/h2]
Parameters
title: Title of the status
status: Status in text [or bbcode] format
htmlstatus: Status in HTML format
in_reply_to_status_id
lat: latitude
long: longitude
media: image data
source: Application name
group_allow
contact_allow
group_deny
contact_deny
Example
[code]
curl -u theUsername:thePassword http://mywebsite/api/statuses/update.xml -d status='Hello world'
[/code]

Some files were not shown because too many files have changed in this diff Show more