diff options
Diffstat (limited to 'Zotlabs')
56 files changed, 1954 insertions, 486 deletions
diff --git a/Zotlabs/Access/PermissionRoles.php b/Zotlabs/Access/PermissionRoles.php index 8b116adc5..94c49c44f 100644 --- a/Zotlabs/Access/PermissionRoles.php +++ b/Zotlabs/Access/PermissionRoles.php @@ -7,6 +7,9 @@ use Zotlabs\Lib as Zlib; class PermissionRoles { + static public function version() { + return 1; + } static function role_perms($role) { @@ -160,6 +163,7 @@ class PermissionRoles { $ret['limits'] = PermissionLimits::Std_Limits(); break; + case 'custom': default: break; } @@ -174,7 +178,48 @@ class PermissionRoles { return $ret; } + static public function new_custom_perms($uid,$perm,$abooks) { + + // set permissionlimits for this permission here, for example: + + // if($perm === 'mynewperm') + // \Zotlabs\Access\PermissionLimits::Set($uid,$perm,1); + + + // set autoperms here if applicable + // choices are to set to 0, 1, or the value of an existing perm + + if(get_pconfig($uid,'system','autoperms')) { + + $c = channelx_by_n($uid); + $value = 0; + // if($perm === 'mynewperm') + // $value = get_abconfig($uid,$c['channel_hash'],'autoperms','someexistingperm')); + + if($c) { + set_abconfig($uid,$c['channel_hash'],'autoperms',$perm,$value); + } + + + } + + // now set something for all existing connections. + + if($abooks) { + foreach($abooks as $ab) { + switch($perm) { + // case 'mynewperm': + // choices are to set to 1, set to 0, or clone an existing perm + // set_abconfig($uid,$ab['abook_xchan'],'my_perms',$perm, + // get_abconfig($uid,$ab['abook_xchan'],'my_perms','someexistingperm')); + + default: + break; + } + } + } + } static public function roles() { diff --git a/Zotlabs/Access/Permissions.php b/Zotlabs/Access/Permissions.php index 61ea51a48..5fded5f03 100644 --- a/Zotlabs/Access/Permissions.php +++ b/Zotlabs/Access/Permissions.php @@ -10,9 +10,20 @@ class Permissions { /** * Extensible permissions. * To add new permissions, add to the list of $perms below, with a simple description. + * * Also visit PermissionRoles.php and add to the $ret['perms_connect'] property for any role * if this permission should be granted to new connections. * + * Next look at PermissionRoles::new_custom_perms() and provide a handler for updating custom + * permission roles. You will want to set a default PermissionLimit for each channel and also + * provide a sane default for any existing connections. You may or may not wish to provide a + * default auto permission. If in doubt, leave this alone as custom permissions by definition + * are the responsibility of the channel owner to manage. You just don't want to create any + * suprises or break things so you have an opportunity to provide sane settings. + * + * Update the version here and in PermissionRoles + * + * * Permissions with 'view' in the name are considered read permissions. Anything * else requires authentication. Read permission limits are PERMS_PUBLIC and anything else * is given PERMS_SPECIFIC. @@ -23,6 +34,11 @@ class Permissions { * */ + static public function version() { + // This must match the version in PermissionRoles.php before permission updates can run. + return 1; + } + static public function Perms($filter = '') { diff --git a/Zotlabs/Daemon/Addon.php b/Zotlabs/Daemon/Addon.php new file mode 100644 index 000000000..c2889e596 --- /dev/null +++ b/Zotlabs/Daemon/Addon.php @@ -0,0 +1,14 @@ +<?php + +namespace Zotlabs\Daemon; + +require_once('include/zot.php'); + +class Addon { + + static public function run($argc,$argv) { + + call_hooks('daemon_addon',$argv); + + } +} diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php index 36edbc057..63ced4f56 100644 --- a/Zotlabs/Daemon/Notifier.php +++ b/Zotlabs/Daemon/Notifier.php @@ -491,7 +491,7 @@ class Notifier { // Now we have collected recipients (except for external mentions, FIXME) // Let's reduce this to a set of hubs. - $r = q("select * from hubloc where hubloc_hash in (" . implode(',',$recipients) . ") + $r = q("select hubloc.*, site.site_crypto from hubloc left join site on site_url = hubloc_url where hubloc_hash in (" . implode(',',$recipients) . ") and hubloc_error = 0 and hubloc_deleted = 0" ); @@ -603,8 +603,8 @@ class Notifier { $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } elseif($packet_type === 'request') { - $packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hash, - array('message_id' => $request_message_id) + $packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hub['site_crypto'], + $hash, array('message_id' => $request_message_id) ); } @@ -618,7 +618,7 @@ class Notifier { )); } else { - $packet = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash); + $packet = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null), $hub['site_crypto'],$hash); queue_insert(array( 'hash' => $hash, 'account_id' => $target_item['aid'], diff --git a/Zotlabs/Daemon/Ratenotif.php b/Zotlabs/Daemon/Ratenotif.php index 1cba5e26d..a94b89004 100644 --- a/Zotlabs/Daemon/Ratenotif.php +++ b/Zotlabs/Daemon/Ratenotif.php @@ -77,7 +77,7 @@ class Ratenotif { continue; $hash = random_string(); - $n = zot_build_packet($channel,'notify',null,null,$hash); + $n = zot_build_packet($channel,'notify',null,null,'',$hash); queue_insert(array( 'hash' => $hash, diff --git a/Zotlabs/Lib/AConfig.php b/Zotlabs/Lib/AConfig.php index ab8648a18..4e7c5483f 100644 --- a/Zotlabs/Lib/AConfig.php +++ b/Zotlabs/Lib/AConfig.php @@ -10,8 +10,8 @@ class AConfig { return XConfig::Load('a_' . $account_id); } - static public function Get($account_id,$family,$key) { - return XConfig::Get('a_' . $account_id,$family,$key); + static public function Get($account_id,$family,$key,$default = false) { + return XConfig::Get('a_' . $account_id,$family,$key, $default); } static public function Set($account_id,$family,$key,$value) { diff --git a/Zotlabs/Lib/AbConfig.php b/Zotlabs/Lib/AbConfig.php index cb5d96951..dfc9efc6c 100644 --- a/Zotlabs/Lib/AbConfig.php +++ b/Zotlabs/Lib/AbConfig.php @@ -16,7 +16,7 @@ class AbConfig { } - static public function Get($chan,$xhash,$family,$key) { + static public function Get($chan,$xhash,$family,$key, $default = false) { $r = q("select * from abconfig where chan = %d and xchan = '%s' and cat = '%s' and k = '%s' limit 1", intval($chan), dbesc($xhash), @@ -26,7 +26,7 @@ class AbConfig { if($r) { return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); } - return false; + return $default; } diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index a2f253299..65f5b3556 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -38,6 +38,7 @@ class Apps { if(plugin_is_installed($plugin)) { $x = self::parse_app_description($f,$translate); if($x) { + $x['plugin'] = $plugin; $ret[] = $x; } } @@ -54,7 +55,6 @@ class Apps { return; $apps = self::get_system_apps(false); - self::$installed_system_apps = q("select * from app where app_system = 1 and app_channel = %d", intval(local_channel()) ); @@ -68,7 +68,7 @@ class Apps { if($id !== true) { // if we already installed this app, but it changed, preserve any categories we created $s = ''; - $r = q("select * from term where otype = %d and oid = d", + $r = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($id) ); @@ -102,11 +102,13 @@ class Apps { foreach(self::$installed_system_apps as $iapp) { if($iapp['app_id'] == hash('whirlpool',$app['name'])) { $notfound = false; - if($iapp['app_version'] != $app['version']) { + if(($iapp['app_version'] != $app['version']) + || ($app['plugin'] && (! $iapp['app_plugin']))) { return intval($iapp['app_id']); } } } + return $notfound; } @@ -238,9 +240,9 @@ class Apps { 'Profile Photo' => t('Profile Photo') ); - if(array_key_exists($arr['name'],$apps)) + if(array_key_exists($arr['name'],$apps)) { $arr['name'] = $apps[$arr['name']]; - + } } @@ -267,6 +269,9 @@ class Apps { self::translate_system_apps($papp); + if(($papp['plugin']) && (! plugin_is_installed($papp['plugin']))) + return ''; + $papp['papp'] = self::papp_encode($papp); if(! strstr($papp['url'],'://')) @@ -332,14 +337,19 @@ class Apps { } $install_action = (($installed) ? t('Update') : t('Install')); + $icon = ((strpos($papp['photo'],'icon:') === 0) ? substr($papp['photo'],5) : ''); + return replace_macros(get_markup_template('app.tpl'),array( '$app' => $papp, + '$icon' => $icon, '$hosturl' => $hosturl, '$purchase' => (($papp['page'] && (! $installed)) ? t('Purchase') : ''), '$install' => (($hosturl && $mode == 'view') ? $install_action : ''), '$edit' => ((local_channel() && $installed && $mode == 'edit') ? t('Edit') : ''), - '$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : '') + '$delete' => ((local_channel() && $installed && $mode == 'edit') ? t('Delete') : ''), + '$undelete' => ((local_channel() && $installed && $mode == 'edit') ? t('Undelete') : ''), + '$deleted' => $papp['deleted'] )); } @@ -359,7 +369,7 @@ class Apps { if($r) { if(! $r[0]['app_system']) { if($app['categories'] && (! $app['term'])) { - $r[0]['term'] = q("select * from term where otype = %d and oid = d", + $r[0]['term'] = q("select * from term where otype = %d and oid = %d", intval(TERM_OBJ_APP), intval($r[0]['id']) ); @@ -382,31 +392,60 @@ class Apps { intval($uid) ); if($x) { - $x[0]['app_deleted'] = 1; - q("delete from term where otype = %d and oid = %d", - intval(TERM_OBJ_APP), - intval($x[0]['id']) - ); - if($x[0]['app_system']) { - $r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", - dbesc($app['guid']), - intval($uid) + if(! intval($x[0]['app_deleted'])) { + $x[0]['app_deleted'] = 1; + q("delete from term where otype = %d and oid = %d", + intval(TERM_OBJ_APP), + intval($x[0]['id']) ); + if($x[0]['app_system']) { + $r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); + } + else { + $r = q("delete from app where app_id = '%s' and app_channel = %d", + dbesc($app['guid']), + intval($uid) + ); + + // we don't sync system apps - they may be completely different on the other system + build_sync_packet($uid,array('app' => $x)); + } } else { - $r = q("delete from app where app_id = '%s' and app_channel = %d", + self::app_undestroy($uid,$app); + } + } + } + } + + + static public function app_undestroy($uid,$app) { + + // undelete a system app + + if($uid && $app['guid']) { + + $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", + dbesc($app['guid']), + intval($uid) + ); + if($x) { + if($x[0]['app_system']) { + $r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", dbesc($app['guid']), intval($uid) ); - - // we don't sync system apps - they may be completely different on the other system - build_sync_packet($uid,array('app' => $x)); } } } } + + static public function app_installed($uid,$app) { $r = q("select id from app where app_id = '%s' and app_version = '%s' and app_channel = %d limit 1", @@ -421,7 +460,7 @@ class Apps { static public function app_list($uid, $deleted = false, $cat = '') { if($deleted) - $sql_extra = " and app_deleted = 1 "; + $sql_extra = ""; else $sql_extra = " and app_deleted = 0 "; @@ -467,7 +506,7 @@ class Apps { static public function app_store($arr) { - // logger('app_store: ' . print_r($arr,true)); + //logger('app_store: ' . print_r($arr,true)); $darray = array(); $ret = array('success' => false); @@ -478,7 +517,7 @@ class Apps { if((! $darray['app_url']) || (! $darray['app_channel'])) return $ret; - if($arr['photo'] && ! strstr($arr['photo'],z_root())) { + if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) { $x = import_xchan_photo($arr['photo'],get_observer_hash(),true); $arr['photo'] = $x[1]; } @@ -494,13 +533,14 @@ class Apps { $darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : ''); $darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : ''); $darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : ''); + $darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags($arr['plugin']) : ''); $darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : ''); $darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0); $darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0); $created = datetime_convert(); - $r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited, app_system, app_deleted ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d )", + $r = q("insert into app ( app_id, app_sig, app_author, app_name, app_desc, app_url, app_photo, app_version, app_channel, app_addr, app_price, app_page, app_requires, app_created, app_edited, app_system, app_plugin, app_deleted ) values ( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', %d )", dbesc($darray['app_id']), dbesc($darray['app_sig']), dbesc($darray['app_author']), @@ -517,6 +557,7 @@ class Apps { dbesc($created), dbesc($created), intval($darray['app_system']), + dbesc($darray['app_plugin']), intval($darray['app_deleted']) ); if($r) { @@ -545,6 +586,7 @@ class Apps { static public function app_update($arr) { + //logger('app_update: ' . print_r($arr,true)); $darray = array(); $ret = array('success' => false); @@ -555,7 +597,7 @@ class Apps { if((! $darray['app_url']) || (! $darray['app_channel']) || (! $darray['app_id'])) return $ret; - if($arr['photo'] && ! strstr($arr['photo'],z_root())) { + if($arr['photo'] && (strpos($arr['photo'],'icon:') !== 0) && (! strstr($arr['photo'],z_root()))) { $x = import_xchan_photo($arr['photo'],get_observer_hash(),true); $arr['photo'] = $x[1]; } @@ -569,13 +611,14 @@ class Apps { $darray['app_addr'] = ((x($arr,'addr')) ? escape_tags($arr['addr']) : ''); $darray['app_price'] = ((x($arr,'price')) ? escape_tags($arr['price']) : ''); $darray['app_page'] = ((x($arr,'page')) ? escape_tags($arr['page']) : ''); + $darray['app_plugin'] = ((x($arr,'plugin')) ? escape_tags($arr['plugin']) : ''); $darray['app_requires'] = ((x($arr,'requires')) ? escape_tags($arr['requires']) : ''); $darray['app_system'] = ((x($arr,'system')) ? intval($arr['system']) : 0); $darray['app_deleted'] = ((x($arr,'deleted')) ? intval($arr['deleted']) : 0); $edited = datetime_convert(); - $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_deleted = %d where app_id = '%s' and app_channel = %d", + $r = q("update app set app_sig = '%s', app_author = '%s', app_name = '%s', app_desc = '%s', app_url = '%s', app_photo = '%s', app_version = '%s', app_addr = '%s', app_price = '%s', app_page = '%s', app_requires = '%s', app_edited = '%s', app_system = %d, app_plugin = '%s', app_deleted = %d where app_id = '%s' and app_channel = %d", dbesc($darray['app_sig']), dbesc($darray['app_author']), dbesc($darray['app_name']), @@ -589,6 +632,7 @@ class Apps { dbesc($darray['app_requires']), dbesc($edited), intval($darray['app_system']), + dbesc($darray['app_plugin']), intval($darray['app_deleted']), dbesc($darray['app_id']), intval($darray['app_channel']) @@ -673,6 +717,9 @@ class Apps { if($app['app_system']) $ret['system'] = $app['app_system']; + if($app['app_plugin']) + $ret['plugin'] = $app['app_plugin']; + if($app['app_deleted']) $ret['deleted'] = $app['app_deleted']; diff --git a/Zotlabs/Lib/Config.php b/Zotlabs/Lib/Config.php index d4ee1aeda..5625a3f79 100644 --- a/Zotlabs/Lib/Config.php +++ b/Zotlabs/Lib/Config.php @@ -98,13 +98,13 @@ class Config { * @return mixed Return value or false on error or if not set */ - static public function Get($family,$key) { + static public function Get($family,$key,$default = false) { if((! array_key_exists($family, \App::$config)) || (! array_key_exists('config_loaded', \App::$config[$family]))) self::Load($family); if(array_key_exists('config_loaded', \App::$config[$family])) { if(! array_key_exists($key, \App::$config[$family])) { - return false; + return $default; } return ((! is_array(\App::$config[$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$family][$key])) ? unserialize(\App::$config[$family][$key]) @@ -112,7 +112,7 @@ class Config { ); } - return false; + return $default; } /** diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index b6f4d3351..257687567 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -102,7 +102,7 @@ class Enotify { $title = $params['item']['title']; $body = $params['item']['body']; } - if($item['created'] < datetime_convert('UTC','UTC','now - 1 month')) { + if($params['item']['created'] < datetime_convert('UTC','UTC','now - 1 month')) { logger('notification invoked for an old item which may have been refetched.',LOGGER_DEBUG,LOG_INFO); return; } @@ -217,6 +217,85 @@ class Enotify { $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); } + if ($params['type'] == NOTIFY_LIKE) { +// logger("notification: params = " . print_r($params, true), LOGGER_DEBUG); + + $itemlink = $params['link']; + + // ignore like/unlike activity on posts - they probably require a separate notification preference + + if (array_key_exists('item',$params) && (! activity_match($params['item']['verb'],ACTIVITY_LIKE))) { + logger('notification: not a like activity. Ignoring.'); + pop_lang(); + return; + } + + $parent_mid = $params['parent_mid']; + + // Check to see if there was already a notify for this post. + // If so don't create a second notification + + $p = null; + $p = q("select id from notify where link = '%s' and uid = %d limit 1", + dbesc($params['link']), + intval($recip['channel_id']) + ); + if ($p) { + logger('notification: like already notified'); + pop_lang(); + return; + } + + + // if it's a post figure out who's post it is. + + $p = null; + + if($params['otype'] === 'item' && $parent_mid) { + $p = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($parent_mid), + intval($recip['channel_id']) + ); + } + + xchan_query($p); + + + $item_post_type = item_post_type($p[0]); +// $private = $p[0]['item_private']; + $parent_id = $p[0]['id']; + + $parent_item = $p[0]; + + + // "your post" + if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && intval($p[0]['item_wall'])) + $dest_str = sprintf(t('%1$s, %2$s liked [zrl=%3$s]your %4$s[/zrl]'), + $recip['channel_name'], + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', + $itemlink, + $item_post_type); + else { + pop_lang(); + return; + } + + // Some mail softwares relies on subject field for threading. + // So, we cannot have different subjects for notifications of the same thread. + // Before this we have the name of the replier on the subject rendering + // differents subjects for messages on the same thread. + + $subject = sprintf( t('[$Projectname:Notify] Like received to conversation #%1$d by %2$s'), $parent_id, $sender['xchan_name']); + $preamble = sprintf( t('%1$s, %2$s liked an item/conversation you created.'), $recip['channel_name'], $sender['xchan_name']); + $epreamble = $dest_str; + + $sitelink = t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf( $sitelink, $siteurl ); + $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); + } + + + if($params['type'] == NOTIFY_WALL) { $subject = sprintf( t('[$Projectname:Notify] %s posted to your profile wall') , $sender['xchan_name']); diff --git a/Zotlabs/Lib/IConfig.php b/Zotlabs/Lib/IConfig.php index 28c9ab58e..33d94bd49 100644 --- a/Zotlabs/Lib/IConfig.php +++ b/Zotlabs/Lib/IConfig.php @@ -10,7 +10,7 @@ class IConfig { return; } - static public function Get(&$item, $family, $key) { + static public function Get(&$item, $family, $key, $default = false) { $is_item = false; @@ -28,7 +28,7 @@ class IConfig { $iid = $item; if(! $iid) - return false; + return $default; if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { foreach($item['iconfig'] as $c) { @@ -48,7 +48,7 @@ class IConfig { $item['iconfig'][] = $r[0]; return $r[0]['v']; } - return false; + return $default; } diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php new file mode 100644 index 000000000..c3380939a --- /dev/null +++ b/Zotlabs/Lib/NativeWiki.php @@ -0,0 +1,197 @@ +<?php + +namespace Zotlabs\Lib; + +define ( 'NWIKI_ITEM_RESOURCE_TYPE', 'nwiki' ); + +class NativeWiki { + + + static public function listwikis($channel, $observer_hash) { + + $sql_extra = item_permissions_sql($channel['channel_id'], $observer_hash); + $wikis = q("SELECT * FROM item WHERE resource_type = '%s' AND mid = parent_mid AND uid = %d AND item_deleted = 0 $sql_extra", + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + intval($channel['channel_id']) + ); + + if($wikis) { + foreach($wikis as &$w) { + $w['rawName'] = get_iconfig($w, 'wiki', 'rawName'); + $w['htmlName'] = get_iconfig($w, 'wiki', 'htmlName'); + $w['urlName'] = get_iconfig($w, 'wiki', 'urlName'); + $w['mimeType'] = get_iconfig($w, 'wiki', 'mimeType'); + $w['lock'] = (($w['item_private'] || $w['allow_cid'] || $w['allow_gid'] || $w['deny_cid'] || $w['deny_gid']) ? true : false); + } + } + // TODO: query db for wikis the observer can access. Return with two lists, for read and write access + return array('wikis' => $wikis); + } + + + function create_wiki($channel, $observer_hash, $wiki, $acl) { + + // Generate unique resource_id using the same method as item_message_id() + do { + $dups = false; + $resource_id = random_string(); + $r = q("SELECT mid FROM item WHERE resource_id = '%s' AND resource_type = '%s' AND uid = %d LIMIT 1", + dbesc($resource_id), + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + intval($channel['channel_id']) + ); + if($r) + $dups = true; + } while($dups == true); + + $ac = $acl->get(); + $mid = item_message_id(); + + $arr = array(); // Initialize the array of parameters for the post + $item_hidden = ((intval($wiki['postVisible']) === 0) ? 1 : 0); + $wiki_url = z_root() . '/wiki/' . $channel['channel_address'] . '/' . $wiki['urlName']; + $arr['aid'] = $channel['channel_account_id']; + $arr['uid'] = $channel['channel_id']; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; + $arr['item_hidden'] = $item_hidden; + $arr['resource_type'] = NWIKI_ITEM_RESOURCE_TYPE; + $arr['resource_id'] = $resource_id; + $arr['owner_xchan'] = $channel['channel_hash']; + $arr['author_xchan'] = $observer_hash; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); + $arr['llink'] = $arr['plink']; + $arr['title'] = $wiki['htmlName']; // name of new wiki; + $arr['allow_cid'] = $ac['allow_cid']; + $arr['allow_gid'] = $ac['allow_gid']; + $arr['deny_cid'] = $ac['deny_cid']; + $arr['deny_gid'] = $ac['deny_gid']; + $arr['item_wall'] = 1; + $arr['item_origin'] = 1; + $arr['item_thread_top'] = 1; + $arr['item_private'] = intval($acl->is_private()); + $arr['verb'] = ACTIVITY_CREATE; + $arr['obj_type'] = ACTIVITY_OBJ_WIKI; + $arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]'; + + // Save the wiki name information using iconfig. This is shareable. + if(! set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) { + return array('item' => null, 'success' => false); + } + if(! set_iconfig($arr, 'wiki', 'htmlName', $wiki['htmlName'], true)) { + return array('item' => null, 'success' => false); + } + if(! set_iconfig($arr, 'wiki', 'urlName', $wiki['urlName'], true)) { + return array('item' => null, 'success' => false); + } + if(! set_iconfig($arr, 'wiki', 'mimeType', $wiki['mimeType'], true)) { + return array('item' => null, 'success' => false); + } + + $post = item_store($arr); + $item_id = $post['item_id']; + + if($item_id) { + \Zotlabs\Daemon\Master::Summon(array('Notifier', 'activity', $item_id)); + return array('item' => $post['item'], 'success' => true); + } + else { + return array('item' => null, 'success' => false); + } + } + + function delete_wiki($channel_id,$observer_hash,$resource_id) { + + $w = self::get_wiki($channel_id,$observer_hash,$resource_id); + $item = $w['wiki']; + if(! $item) { + return array('item' => null, 'success' => false); + } + else { + $drop = drop_item($item['id'], false, DROPITEM_NORMAL, true); + } + + info( t('Wiki files deleted successfully')); + + return array('item' => $item, 'success' => (($drop === 1) ? true : false)); + } + + + static public function get_wiki($channel_id, $observer_hash, $resource_id) { + + $sql_extra = item_permissions_sql($channel_id,$observer_hash); + + $item = q("SELECT * FROM item WHERE uid = %d AND resource_type = '%s' AND resource_id = '%s' AND item_deleted = 0 + $sql_extra limit 1", + intval($channel_id), + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) + ); + if(! $item) { + return array('wiki' => null); + } + else { + + $w = $item[0]; // wiki item table record + // Get wiki metadata + $rawName = get_iconfig($w, 'wiki', 'rawName'); + $htmlName = get_iconfig($w, 'wiki', 'htmlName'); + $urlName = get_iconfig($w, 'wiki', 'urlName'); + $mimeType = get_iconfig($w, 'wiki', 'mimeType'); + + return array( + 'wiki' => $w, + 'rawName' => $rawName, + 'htmlName' => $htmlName, + 'urlName' => $urlName, + 'mimeType' => $mimeType + ); + } + } + + + static public function exists_by_name($uid, $urlName) { + + $sql_extra = item_permissions_sql($uid); + + $item = q("SELECT id, resource_id FROM item WHERE resource_type = '%s' AND title = '%s' AND uid = %d + AND item_deleted = 0 $sql_extra limit 1", + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + dbesc(escape_tags(urldecode($urlName))), + intval($uid) + ); + + if($item) { + return array('id' => $item[0]['id'], 'resource_id' => $item[0]['resource_id']); + } + else { + return array('id' => null, 'resource_id' => null); + } + } + + + static public function get_permissions($resource_id, $owner_id, $observer_hash) { + // TODO: For now, only the owner can edit + $sql_extra = item_permissions_sql($owner_id, $observer_hash); + + if(local_channel() && local_channel() == $owner_id) { + return [ 'read' => true, 'write' => true, 'success' => true ]; + } + + $r = q("SELECT * FROM item WHERE uid = %d and resource_type = '%s' AND resource_id = '%s' $sql_extra LIMIT 1", + intval($owner_id), + dbesc(NWIKI_ITEM_RESOURCE_TYPE), + dbesc($resource_id) + ); + + if(! $r) { + return array('read' => false, 'write' => false, 'success' => true); + } + else { + // TODO: Create a new permission setting for wiki analogous to webpages. Until + // then, use webpage permissions + $write = perm_is_allowed($owner_id, $observer_hash,'write_pages'); + return array('read' => true, 'write' => $write, 'success' => true); + } + } +} diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php new file mode 100644 index 000000000..0c3698519 --- /dev/null +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -0,0 +1,646 @@ +<?php + +namespace Zotlabs\Lib; + +use \Zotlabs\Lib as Zlib; + +class NativeWikiPage { + + static public function page_list($channel_id,$observer_hash, $resource_id) { + + // TODO: Create item table records for pages so that metadata like title can be applied + $w = Zlib\NativeWiki::get_wiki($channel_id,$observer_hash,$resource_id); + + $pages[] = [ + 'resource_id' => '', + 'title' => 'Home', + 'url' => 'Home', + 'link_id' => 'id_wiki_home_0' + ]; + + $sql_extra = item_permissions_sql($channel_id,$observer_hash); + + $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d + $sql_extra group by mid", + dbesc($resource_id), + intval($channel_id) + ); + if($r) { + $items = fetch_post_tags($r,true); + foreach($items as $page_item) { + $title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)')); + if(urldecode($title) !== 'Home') { + $pages[] = [ + 'resource_id' => $resource_id, + 'title' => urldecode($title), + 'url' => $title, + 'link_id' => 'id_' . substr($resource_id, 0, 10) . '_' . $page_item['id'] + ]; + } + } + } + + return array('pages' => $pages, 'wiki' => $w); + } + + + static public function create_page($channel_id, $observer_hash, $name, $resource_id) { + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + + // create an empty activity + + $arr = []; + $arr['uid'] = $channel_id; + $arr['author_xchan'] = $observer_hash; + $arr['resource_type'] = 'nwikipage'; + $arr['resource_id'] = $resource_id; + + // We may wish to change this some day. + $arr['item_unpublished'] = 1; + + set_iconfig($arr,'nwikipage','pagetitle',urlencode(($name) ? $name : t('(No Title)')),true); + + post_activity_item($arr, false, false); + + $page = [ + 'rawName' => $name, + 'htmlName' => escape_tags($name), + 'urlName' => urlencode(escape_tags($name)), + 'fileName' => urlencode(escape_tags($name)) . Zlib\NativeWikiPage::get_file_ext($w) + ]; + + return array('page' => $page, 'wiki' => $w, 'message' => '', 'success' => true); + } + + static public function rename_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $pageNewName = ((array_key_exists('pageNewName',$arr)) ? $arr['pageNewName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if(! $w['wiki']) { + return array('message' => t('Wiki not found.'), 'success' => false); + } + + + $ic = q("select * from iconfig left join item on iconfig.iid = item.id + where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", + intval($channel_id), + dbesc($pageNewName) + ); + + if($ic) { + return [ 'success' => false, 'message' => t('Destination name already exists') ]; + } + + + $ids = []; + + $ic = q("select *, item.id as item_id from iconfig left join item on iconfig.iid = item.id + where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", + intval($channel_id), + dbesc($pageUrlName) + ); + + if($ic) { + foreach($ic as $c) { + set_iconfig($c['item_id'],'nwikipage','pagetitle',$pageNewName); + } + + $page = [ + 'rawName' => $pageNewName, + 'htmlName' => escape_tags($pageNewName), + 'urlName' => urlencode(escape_tags($pageNewName)) + ]; + + return [ 'success' => true, 'page' => $page ]; + } + + return [ 'success' => false, 'message' => t('Page not found') ]; + + } + + static public function get_page_content($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? intval($arr['channel_id']) : 0); + $revision = ((array_key_exists('revision',$arr)) ? intval($arr['revision']) : (-1)); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (! $w['wiki']) { + return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + } + + $item = self::load_page($arr); + + if($item) { + $content = $item['body']; + + return [ + 'content' => json_encode($content), + 'mimeType' => $w['mimeType'], + 'message' => '', + 'success' => true + ]; + } + + return array('content' => null, 'message' => t('Error reading page content'), 'success' => false); + + } + + static public function page_history($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return array('history' => null, 'message' => 'Error reading wiki', 'success' => false); + } + + $items = self::load_page_history($arr); + + $history = []; + + if($items) { + $processed = 0; + foreach($items as $item) { + if($processed > 1000) + break; + $processed ++; + $history[] = [ + 'revision' => $item['revision'], + 'date' => datetime_convert('UTC',date_default_timezone_get(),$item['created']), + 'name' => $item['author']['xchan_name'], + 'title' => get_iconfig($item,'nwikipage','commit_msg') + ]; + + } + + return [ 'success' => true, 'history' => $history ]; + } + + return [ 'success' => false ]; + + } + + + static public function load_page($arr) { + + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1)); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (! $w['wiki']) { + return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + } + + $ids = ''; + + $ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", + intval($channel_id), + dbesc($pageUrlName) + ); + + if($ic) { + foreach($ic as $c) { + if($ids) + $ids .= ','; + $ids .= intval($c['iid']); + } + } + + $sql_extra = item_permissions_sql($channel_id,$observer_hash); + if($revision == (-1)) + $sql_extra .= " order by revision desc "; + elseif($revision) + $sql_extra .= " and revision = " . intval($revision) . " "; + + $r = null; + if($ids) { + $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra limit 1", + dbesc($resource_id), + intval($channel_id) + ); + + if($r) { + $items = fetch_post_tags($r,true); + return $items[0]; + } + } + + return null; + } + + static public function load_page_history($arr) { + + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : (-1)); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (! $w['wiki']) { + return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + } + + $ids = ''; + + $ic = q("select * from iconfig left join item on iconfig.iid = item.id where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", + intval($channel_id), + dbesc($pageUrlName) + ); + + if($ic) { + foreach($ic as $c) { + if($ids) + $ids .= ','; + $ids .= intval($c['iid']); + } + } + + $sql_extra = item_permissions_sql($channel_id,$observer_hash); + $sql_extra .= " order by revision desc "; + + $r = null; + if($ids) { + $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and id in ( $ids ) $sql_extra", + dbesc($resource_id), + intval($channel_id) + ); + if($r) { + xchan_query($r); + $items = fetch_post_tags($r,true); + return $items; + } + } + + return null; + } + + + + static public function prepare_content($s) { + + $text = preg_replace_callback('{ + (?:\n\n|\A\n?) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?> + [ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + }xm', + 'self::nwiki_prepare_content_callback', $s); + + return $text; + } + + static public function nwiki_prepare_content_callback($matches) { + $codeblock = $matches[1]; + + $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES, UTF8, false); + return "\n\n" . $codeblock ; + } + + + + static public function save_page($arr) { + + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $content = ((array_key_exists('content',$arr)) ? purify_html(Zlib\NativeWikiPage::prepare_content($arr['content'])) : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + + if (!$w['wiki']) { + return array('message' => t('Error reading wiki'), 'success' => false); + } + + // fetch the most recently saved revision. + + $item = self::load_page($arr); + if(! $item) { + return array('message' => t('Page not found'), 'success' => false); + } + + // change just the fields we need to change to create a revision; + + unset($item['id']); + unset($item['author']); + + $item['parent'] = 0; + $item['body'] = $content; + $item['author_xchan'] = $observer_hash; + $item['revision'] = (($arr['revision']) ? intval($arr['revision']) + 1 : intval($item['revision']) + 1); + $item['edited'] = datetime_convert(); + + if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) { + for($x = 0; $x < count($item['iconfig']); $x ++) { + unset($item['iconfig'][$x]['id']); + unset($item['iconfig'][$x]['iid']); + } + } + + $ret = item_store($item, false, false); + + if($ret['item_id']) + return array('message' => '', 'filename' => $filename, 'success' => true); + else + return array('message' => t('Page update failed.'), 'success' => false); + } + + static public function delete_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + + if(! $w['wiki']) { + return [ 'success' => false, 'message' => t('Error reading wiki') ]; + } + + $ids = []; + + $ic = q("select * from iconfig left join item on iconfig.iid = item.id + where uid = %d and cat = 'nwikipage' and k = 'pagetitle' and v = '%s'", + intval($channel_id), + dbesc($pageUrlName) + ); + + if($ic) { + foreach($ic as $c) { + $ids[] = intval($c['iid']); + } + } + + if($ids) { + foreach($ids as $id) { + drop_item($id,false); + } + return [ 'success' => true ]; + } + + return [ 'success' => false, 'message' => t('Nothing deleted') ]; + } + + static public function revert_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $commitHash = ((array_key_exists('commitHash',$arr)) ? $arr['commitHash'] : null); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + + if (! $commitHash) { + return array('content' => $content, 'message' => 'No commit was provided', 'success' => false); + } + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (!$w['wiki']) { + return array('content' => $content, 'message' => 'Error reading wiki', 'success' => false); + } + + $x = $arr; + + if(intval($commitHash) > 0) { + unset($x['commitHash']); + $x['revision'] = intval($commitHash) - 1; + $loaded = self::load_page($x); + + if($loaded) { + $content = $loaded['body']; + return [ 'content' => $content, 'success' => true ]; + } + return [ 'content' => $content, 'success' => false ]; + } + } + + static public function compare_page($arr) { + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $currentCommit = ((array_key_exists('currentCommit',$arr)) ? $arr['currentCommit'] : (-1)); + $compareCommit = ((array_key_exists('compareCommit',$arr)) ? $arr['compareCommit'] : 0); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + + if (!$w['wiki']) { + return array('message' => t('Error reading wiki'), 'success' => false); + } + + $x = $arr; + $x['revision'] = (-1); + + $currpage = self::load_page($x); + if($currpage) + $currentContent = $currpage['body']; + + $x['revision'] = $compareCommit; + $comppage = self::load_page($x); + if($comppage) + $compareContent = $comppage['body']; + + if($currpage && $comppage) { + require_once('library/class.Diff.php'); + $diff = \Diff::toTable(\Diff::compare($currentContent, $compareContent)); + + return [ 'success' => true, 'diff' => $diff ]; + } + return [ 'success' => false, 'message' => t('Compare: object not found.') ]; + + } + + static public function commit($arr) { + + $commit_msg = ((array_key_exists('commit_msg', $arr)) ? $arr['commit_msg'] : t('Page updated')); + $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); + $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : t('Untitled')); + + if(array_key_exists('resource_id', $arr)) { + $resource_id = $arr['resource_id']; + } + else { + return array('message' => t('Wiki resource_id required for git commit'), 'success' => false); + } + + $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + + if (! $w['wiki']) { + return array('message' => t('Error reading wiki'), 'success' => false); + } + + $page = self::load_page($arr); + + if($page) { + set_iconfig($page['id'],'nwikipage','commit_msg',escape_tags($commit_msg),true); + return [ 'success' => true, 'page' => $page ]; + } + + return [ 'success' => false, 'message' => t('Page not found.') ]; + + } + + static public function convert_links($s, $wikiURL) { + + if (strpos($s,'[[') !== false) { + preg_match_all("/\[\[(.*?)\]\]/", $s, $match); + $pages = $pageURLs = array(); + foreach ($match[1] as $m) { + // TODO: Why do we need to double urlencode for this to work? + $pageURLs[] = urlencode(urlencode(escape_tags($m))); + $pages[] = $m; + } + $idx = 0; + while(strpos($s,'[[') !== false) { + $replace = '<a href="'.$wikiURL.'/'.$pageURLs[$idx].'">'.$pages[$idx].'</a>'; + $s = preg_replace("/\[\[(.*?)\]\]/", $replace, $s, 1); + $idx++; + } + } + return $s; + } + + /** + * Replace the instances of the string [toc] with a list element that will be populated by + * a table of contents by the JavaScript library + * @param string $s + * @return string + */ + static public function generate_toc($s) { + if (strpos($s,'[toc]') !== false) { + //$toc_md = wiki_toc($s); // Generate Markdown-formatted list prior to HTML render + $toc_md = '<ul id="wiki-toc"></ul>'; // use the available jQuery plugin http://ndabas.github.io/toc/ + $s = preg_replace("/\[toc\]/", $toc_md, $s, -1); + } + return $s; + } + + /** + * Converts a select set of bbcode tags. Much of the code is copied from include/bbcode.php + * @param string $s + * @return string + */ + static public function bbcode($s) { + + $s = str_replace(array('[baseurl]', '[sitename]'), array(z_root(), get_config('system', 'sitename')), $s); + + $s = preg_replace_callback("/\[observer\.language\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_callback', $s); + + $s = preg_replace_callback("/\[observer\.language\!\=(.*?)\](.*?)\[\/observer\]/ism",'oblanguage_necallback', $s); + + + $observer = \App::get_observer(); + if ($observer) { + $s1 = '<span class="bb_observer" title="' . t('Different viewers will see this text differently') . '">'; + $s2 = '</span>'; + $obsBaseURL = $observer['xchan_connurl']; + $obsBaseURL = preg_replace("/\/poco\/.*$/", '', $obsBaseURL); + $s = str_replace('[observer.baseurl]', $obsBaseURL, $s); + $s = str_replace('[observer.url]', $observer['xchan_url'], $s); + $s = str_replace('[observer.name]', $s1 . $observer['xchan_name'] . $s2, $s); + $s = str_replace('[observer.address]', $s1 . $observer['xchan_addr'] . $s2, $s); + $s = str_replace('[observer.webname]', substr($observer['xchan_addr'], 0, strpos($observer['xchan_addr'], '@')), $s); + $s = str_replace('[observer.photo]', '', $s); + } + else { + $s = str_replace('[observer.baseurl]', '', $s); + $s = str_replace('[observer.url]', '', $s); + $s = str_replace('[observer.name]', '', $s); + $s = str_replace('[observer.address]', '', $s); + $s = str_replace('[observer.webname]', '', $s); + $s = str_replace('[observer.photo]', '', $s); + } + + return $s; + } + + static public function get_file_ext($arr) { + if($arr['mimeType'] == 'text/bbcode') + return '.bb'; + else + return '.md'; + } + + // This function is derived from + // http://stackoverflow.com/questions/32068537/generate-table-of-contents-from-markdown-in-php + static public function toc($content) { + // ensure using only "\n" as line-break + $source = str_replace(["\r\n", "\r"], "\n", $content); + + // look for markdown TOC items + preg_match_all( + '/^(?:=|-|#).*$/m', + $source, + $matches, + PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE + ); + + // preprocess: iterate matched lines to create an array of items + // where each item is an array(level, text) + $file_size = strlen($source); + foreach ($matches[0] as $item) { + $found_mark = substr($item[0], 0, 1); + if ($found_mark == '#') { + // text is the found item + $item_text = $item[0]; + $item_level = strrpos($item_text, '#') + 1; + $item_text = substr($item_text, $item_level); + } else { + // text is the previous line (empty if <hr>) + $item_offset = $item[1]; + $prev_line_offset = strrpos($source, "\n", -($file_size - $item_offset + 2)); + $item_text = + substr($source, $prev_line_offset, $item_offset - $prev_line_offset - 1); + $item_text = trim($item_text); + $item_level = $found_mark == '=' ? 1 : 2; + } + if (!trim($item_text) OR strpos($item_text, '|') !== FALSE) { + // item is an horizontal separator or a table header, don't mind + continue; + } + $raw_toc[] = ['level' => $item_level, 'text' => trim($item_text)]; + } + $o = ''; + foreach($raw_toc as $t) { + $level = intval($t['level']); + $text = $t['text']; + switch ($level) { + case 1: + $li = '* '; + break; + case 2: + $li = ' * '; + break; + case 3: + $li = ' * '; + break; + case 4: + $li = ' * '; + break; + default: + $li = '* '; + break; + } + $o .= $li . $text . "\n"; + } + return $o; + } + +}
\ No newline at end of file diff --git a/Zotlabs/Lib/PConfig.php b/Zotlabs/Lib/PConfig.php index 3b47a250a..d70697fbc 100644 --- a/Zotlabs/Lib/PConfig.php +++ b/Zotlabs/Lib/PConfig.php @@ -67,16 +67,16 @@ class PConfig { * @return mixed Stored value or false if it does not exist */ - static public function Get($uid,$family,$key,$instore = false) { + static public function Get($uid,$family,$key,$default = false) { if(is_null($uid) || $uid === false) - return false; + return $default; if(! array_key_exists($uid, \App::$config)) self::Load($uid); if((! array_key_exists($family, \App::$config[$uid])) || (! array_key_exists($key, \App::$config[$uid][$family]))) - return false; + return $default; return ((! is_array(\App::$config[$uid][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$uid][$family][$key])) ? unserialize(\App::$config[$uid][$family][$key]) diff --git a/Zotlabs/Lib/System.php b/Zotlabs/Lib/System.php index 32aaa82a9..306c90f4a 100644 --- a/Zotlabs/Lib/System.php +++ b/Zotlabs/Lib/System.php @@ -42,6 +42,20 @@ class System { } + static public function get_project_link() { + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_link']) + return \App::$config['system']['project_link']; + return 'https://hubzilla.org'; + } + + static public function get_project_srclink() { + if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['project_srclink']) + return \App::$config['system']['project_srclink']; + return 'https://github.com/redmatrix/hubzilla'; + } + + + static public function get_server_role() { if(is_array(\App::$config) && is_array(\App::$config['system']) && \App::$config['system']['server_role']) return \App::$config['system']['server_role']; @@ -54,5 +68,15 @@ class System { return '0.0.0'; } + static public function compatible_project($p) { + if(get_directory_realm() != DIRECTORY_REALM) + return true; + + foreach(['hubzilla','zap'] as $t) { + if(stristr($p,$t)) + return true; + } + return false; + } } diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 48b50b295..0ee8e6680 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -44,7 +44,7 @@ class ThreadItem { * Only add those that will be displayed */ - if((! visible_activity($item)) || array_key_exists('author_blocked',$item)) { + if((! visible_activity($item)) || array_key_exists('blocked',$item)) { continue; } @@ -295,7 +295,7 @@ class ThreadItem { $owner_address = substr($item['owner']['xchan_addr'],0,strpos($item['owner']['xchan_addr'],'@')); $viewthread = $item['llink']; if($conv->get_mode() === 'channel') - $viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . $item['mid']; + $viewthread = z_root() . '/channel/' . $owner_address . '?f=&mid=' . urlencode($item['mid']); $comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children ); $list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : ''); @@ -335,6 +335,8 @@ class ThreadItem { 'wall' => t('Wall-to-Wall'), 'vwall' => t('via Wall-To-Wall:'), 'profile_url' => $profile_link, + 'thread_action_menu' => thread_action_menu($item,$conv->get_mode()), + 'thread_author_menu' => thread_author_menu($item,$conv->get_mode()), 'item_photo_menu' => item_photo_menu($item), 'dreport' => $dreport, 'name' => $profile_name, @@ -407,7 +409,7 @@ class ThreadItem { 'comment' => $this->get_comment_box($indent), 'previewing' => ($conv->is_preview() ? ' preview ' : ''), 'wait' => t('Please wait'), - 'submid' => substr($item['mid'],0,32), + 'submid' => str_replace(['+','='], ['',''], base64_encode(substr($item['mid'],0,32))), 'thread_level' => $thread_level ); diff --git a/Zotlabs/Lib/XConfig.php b/Zotlabs/Lib/XConfig.php index 7f3d0f2cd..bf78c360f 100644 --- a/Zotlabs/Lib/XConfig.php +++ b/Zotlabs/Lib/XConfig.php @@ -59,16 +59,16 @@ class XConfig { * @return mixed Stored $value or false if it does not exist */ - static public function Get($xchan, $family, $key) { + static public function Get($xchan, $family, $key, $default = false) { if(! $xchan) - return false; + return $default; if(! array_key_exists($xchan, \App::$config)) load_xconfig($xchan); if((! array_key_exists($family, \App::$config[$xchan])) || (! array_key_exists($key, \App::$config[$xchan][$family]))) - return false; + return $default; return ((! is_array(\App::$config[$xchan][$family][$key])) && (preg_match('|^a:[0-9]+:{.*}$|s', \App::$config[$xchan][$family][$key])) ? unserialize(\App::$config[$xchan][$family][$key]) diff --git a/Zotlabs/Module/Acl.php b/Zotlabs/Module/Acl.php index 29c1e5280..2b364d9ac 100644 --- a/Zotlabs/Module/Acl.php +++ b/Zotlabs/Module/Acl.php @@ -87,8 +87,8 @@ class Acl extends \Zotlabs\Web\Controller { $order_extra2 = "CASE WHEN xchan_name LIKE " . protect_sprintf( "'%" . dbesc($search) . "%'" ) - . " then POSITION('" . dbesc($search) - . "' IN xchan_name) else position('" . dbesc($search) . "' IN xchan_addr) end, "; + . " then POSITION('" . protect_sprintf(dbesc($search)) + . "' IN xchan_name) else position('" . protect_sprintf(dbesc($search)) . "' IN xchan_addr) end, "; $col = ((strpos($search,'@') !== false) ? 'xchan_addr' : 'xchan_name' ); $sql_extra3 = "AND $col like " . protect_sprintf( "'%" . dbesc($search) . "%'" ) . " "; diff --git a/Zotlabs/Module/Admin/Site.php b/Zotlabs/Module/Admin/Site.php index 251533e68..829ca71e4 100644 --- a/Zotlabs/Module/Admin/Site.php +++ b/Zotlabs/Module/Admin/Site.php @@ -22,6 +22,7 @@ class Site { $banner = ((x($_POST,'banner')) ? trim($_POST['banner']) : false); $admininfo = ((x($_POST,'admininfo')) ? trim($_POST['admininfo']) : false); + $siteinfo = ((x($_POST,'siteinfo')) ? trim($_POST['siteinfo']) : ''); $language = ((x($_POST,'language')) ? notags(trim($_POST['language'])) : ''); $theme = ((x($_POST,'theme')) ? notags(trim($_POST['theme'])) : ''); $theme_mobile = ((x($_POST,'theme_mobile')) ? notags(trim($_POST['theme_mobile'])) : ''); @@ -97,6 +98,7 @@ class Site { linkify_tags($a, $admininfo, local_channel()); set_config('system', 'admininfo', $admininfo); } + set_config('system','siteinfo',$siteinfo); set_config('system', 'language', $language); set_config('system', 'theme', $theme); if ( $theme_mobile === '---' ) { @@ -273,6 +275,7 @@ class Site { '$banner' => array('banner', t("Banner/Logo"), $banner, ""), '$admininfo' => array('admininfo', t("Administrator Information"), $admininfo, t("Contact information for site administrators. Displayed on siteinfo page. BBCode can be used here")), + '$siteinfo' => array('siteinfo', t('Site Information'), get_config('system','siteinfo'), t("Publicly visible description of this site. Displayed on siteinfo page. BBCode can be used here")), '$language' => array('language', t("System language"), get_config('system','language'), "", $lang_choices), '$theme' => array('theme', t("System theme"), get_config('system','theme'), t("Default system theme - may be over-ridden by user profiles - <a href='#' id='cnftheme'>change theme settings</a>"), $theme_choices), '$theme_mobile' => array('theme_mobile', t("Mobile system theme"), get_config('system','mobile_theme'), t("Theme for mobile devices"), $theme_choices_mobile), diff --git a/Zotlabs/Module/Appman.php b/Zotlabs/Module/Appman.php index a200e986a..a7631758b 100644 --- a/Zotlabs/Module/Appman.php +++ b/Zotlabs/Module/Appman.php @@ -27,6 +27,7 @@ class Appman extends \Zotlabs\Web\Controller { 'price' => escape_tags($_REQUEST['price']), 'requires' => escape_tags($_REQUEST['requires']), 'system' => intval($_REQUEST['system']), + 'plugin' => escape_tags($_REQUEST['plugin']), 'sig' => escape_tags($_REQUEST['sig']), 'categories' => escape_tags($_REQUEST['categories']) ); @@ -56,13 +57,14 @@ class Appman extends \Zotlabs\Web\Controller { if($_POST['delete']) { Zlib\Apps::app_destroy(local_channel(),$papp); } - + if($_POST['edit']) { return; } - + if($_SESSION['return_url']) goaway(z_root() . '/' . $_SESSION['return_url']); + goaway(z_root() . '/apps'); @@ -75,7 +77,7 @@ class Appman extends \Zotlabs\Web\Controller { notice( t('Permission denied.') . EOL); return; } - + $channel = \App::get_channel(); $app = null; $embed = null; @@ -121,6 +123,7 @@ class Appman extends \Zotlabs\Web\Controller { '$price' => array('price', t('Price of app'),(($app) ? $app['app_price'] : ''), ''), '$page' => array('page', t('Location (URL) to purchase app'),(($app) ? $app['app_page'] : ''), ''), '$system' => (($app) ? intval($app['app_system']) : 0), + '$plugin' => (($app) ? $app['app_plugin'] : ''), '$requires' => (($app) ? $app['app_requires'] : ''), '$embed' => $embed, '$submit' => t('Submit') diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 4dab621b2..9186848df 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -12,8 +12,8 @@ class Apps extends \Zotlabs\Web\Controller { $mode = 'edit'; else $mode = 'list'; - - $_SESSION['return_url'] = \App::$cmd; + + $_SESSION['return_url'] = \App::$query_string; $apps = array(); @@ -21,7 +21,7 @@ class Apps extends \Zotlabs\Web\Controller { if(local_channel()) { Zlib\Apps::import_system_apps(); $syslist = array(); - $list = Zlib\Apps::app_list(local_channel(), false, $_GET['cat']); + $list = Zlib\Apps::app_list(local_channel(), (($mode == 'edit') ? true : false), $_GET['cat']); if($list) { foreach($list as $x) { $syslist[] = Zlib\Apps::app_encode($x); diff --git a/Zotlabs/Module/Cal.php b/Zotlabs/Module/Cal.php index 1279a51b1..b982d19a8 100644 --- a/Zotlabs/Module/Cal.php +++ b/Zotlabs/Module/Cal.php @@ -292,8 +292,8 @@ class Cal extends \Zotlabs\Web\Controller { $title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8')); } $html = format_event_html($rr); - $rr['desc'] = bbcode($rr['desc']); - $rr['location'] = bbcode($rr['location']); + $rr['desc'] = zidify_links(smilies(bbcode($rr['desc']))); + $rr['location'] = zidify_links(smilies(bbcode($rr['location']))); $events[] = array( 'id'=>$rr['id'], 'hash' => $rr['event_hash'], diff --git a/Zotlabs/Module/Chatsvc.php b/Zotlabs/Module/Chatsvc.php index 6a28a7c4d..0f79e3b4c 100644 --- a/Zotlabs/Module/Chatsvc.php +++ b/Zotlabs/Module/Chatsvc.php @@ -111,8 +111,22 @@ class Chatsvc extends \Zotlabs\Web\Controller { intval(\App::$data['chat']['room_id']) ); if($r) { - foreach($r as $rr) { - switch($rr['cp_status']) { + foreach($r as $rv) { + if(! $rv['xchan_name']) { + $rv['xchan_hash'] = $rv['cp_xchan']; + $rv['xchan_name'] = substr($rv['cp_xchan'],strrpos($rv['cp_xchan'],'.')+1); + $rv['xchan_addr'] = ''; + $rv['xchan_network'] = 'unknown'; + $rv['xchan_url'] = z_root(); + $rv['xchan_hidden'] = 1; + $rv['xchan_photo_mimetype'] = 'image/jpeg'; + $rv['xchan_photo_l'] = get_default_profile_photo(300); + $rv['xchan_photo_m'] = get_default_profile_photo(80); + $rv['xchan_photo_s'] = get_default_profile_photo(48); + + } + + switch($rv['cp_status']) { case 'away': $status = t('Away'); $status_class = 'away'; @@ -124,7 +138,7 @@ class Chatsvc extends \Zotlabs\Web\Controller { break; } - $inroom[] = array('img' => zid($rr['xchan_photo_m']), 'img_type' => $rr['xchan_photo_mimetype'],'name' => $rr['xchan_name'], 'status' => $status, 'status_class' => $status_class); + $inroom[] = array('img' => zid($rv['xchan_photo_m']), 'img_type' => $rv['xchan_photo_mimetype'],'name' => $rv['xchan_name'], 'status' => $status, 'status_class' => $status_class); } } @@ -143,7 +157,7 @@ class Chatsvc extends \Zotlabs\Web\Controller { 'name' => $rr['xchan_name'], 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'c'), 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $rr['created'], 'r'), - 'text' => smilies(bbcode($rr['chat_text'])), + 'text' => zidify_links(smilies(bbcode($rr['chat_text']))), 'self' => ((get_observer_hash() == $rr['chat_xchan']) ? 'self' : '') ); } diff --git a/Zotlabs/Module/Connections.php b/Zotlabs/Module/Connections.php index a412d16ae..950be660d 100644 --- a/Zotlabs/Module/Connections.php +++ b/Zotlabs/Module/Connections.php @@ -228,10 +228,18 @@ class Connections extends \Zotlabs\Web\Controller { $contacts = array(); - if(count($r)) { - + if($r) { + + vcard_query($r); + + foreach($r as $rr) { if($rr['xchan_url']) { + + if(($rr['vcard']) && is_array($rr['vcard']['tels']) && $rr['vcard']['tels'][0]['nr']) + $phone = ((\App::$is_mobile || \App::$is_tablet) ? $rr['vcard']['tels'][0]['nr'] : ''); + else + $phone = ''; $status_str = ''; $status = array( @@ -267,6 +275,8 @@ class Connections extends \Zotlabs\Web\Controller { 'network_label' => t('Network'), 'network' => network_to_name($rr['xchan_network']), 'public_forum' => ((intval($rr['xchan_pubforum'])) ? true : false), + 'call' => t('Call'), + 'phone' => $phone, 'status_label' => t('Status'), 'status' => $status_str, 'connected_label' => t('Connected'), diff --git a/Zotlabs/Module/Connedit.php b/Zotlabs/Module/Connedit.php index 5ab6f814b..92f4fc345 100644 --- a/Zotlabs/Module/Connedit.php +++ b/Zotlabs/Module/Connedit.php @@ -37,15 +37,17 @@ class Connedit extends \Zotlabs\Web\Controller { intval(argv(1)) ); if($r) { - \App::$poi = $r[0]; + \App::$poi = array_shift($r); } } + $channel = \App::get_channel(); if($channel) head_set_icon($channel['xchan_photo_s']); } + /* @brief Evaluate posted values and set changes * @@ -84,6 +86,12 @@ class Connedit extends \Zotlabs\Web\Controller { call_hooks('contact_edit_post', $_POST); + $vc = get_abconfig(local_channel(),$orig_record['abook_xchan'],'system','vcard'); + $vcard = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); + $serialised_vcard = update_vcard($_REQUEST,$vcard); + if($serialised_vcard) + set_abconfig(local_channel(),$orig_record[0]['abook_xchan'],'system','vcard',$serialised_vcard); + if(intval($orig_record[0]['abook_self'])) { $autoperms = intval($_POST['autoperms']); $is_self = true; @@ -365,7 +373,7 @@ class Connedit extends \Zotlabs\Web\Controller { intval(\App::$poi['abook_id']) ); if($r) { - \App::$poi = $r[0]; + \App::$poi = array_shift($r); } $clone = \App::$poi; @@ -396,6 +404,7 @@ class Connedit extends \Zotlabs\Web\Controller { return login(); } + $section = ((array_key_exists('section',$_REQUEST)) ? $_REQUEST['section'] : ''); $channel = \App::get_channel(); $my_perms = get_channel_default_perms(local_channel()); $role = get_pconfig(local_channel(),'system','permissions_role'); @@ -546,9 +555,33 @@ class Connedit extends \Zotlabs\Web\Controller { if(\App::$poi) { + $abook_prev = 0; + $abook_next = 0; + $contact_id = \App::$poi['abook_id']; $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 order by xchan_name", + intval(local_channel()) + ); + + if($cn) { + $pntotal = count($cn); + + for($x = 0; $x < $pntotal; $x ++) { + if($cn[$x]['abook_id'] == $contact_id) { + if($x === 0) + $abook_prev = 0; + else + $abook_prev = $cn[$x - 1]['abook_id']; + if($x === $pntotal) + $abook_next = 0; + else + $abook_next = $cn[$x +1]['abook_id']; + } + } + } + $tools = array( 'view' => array( @@ -612,23 +645,49 @@ class Connedit extends \Zotlabs\Web\Controller { ), ); + + $sections = []; + + $sections['perms'] = [ + 'label' => t('Permissions'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=perms', + 'sel' => '', + 'title' => t('Open Individual Permissions section by default'), + ]; $self = false; - if(intval($contact['abook_self'])) + if(intval($contact['abook_self'])) { $self = true; + $abook_prev = $abook_next = 0; + } + $vc = get_abconfig(local_channel(),$contact['abook_xchan'],'system','vcard'); + + $vctmp = (($vc) ? \Sabre\VObject\Reader::read($vc) : null); + $vcard = (($vctmp) ? get_vcard_array($vctmp,$contact['abook_id']) : [] ); + if(! $vcard) + $vcard['fn'] = $contact['xchan_name']; + + $tpl = get_markup_template("abook_edit.tpl"); if(feature_enabled(local_channel(),'affinity')) { + + $sections['affinity'] = [ + 'label' => t('Affinity'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=affinity', + 'sel' => '', + 'title' => t('Open Set Affinity section by default'), + ]; - $labels = array( + $labels = [ t('Me'), t('Family'), t('Friends'), t('Acquaintances'), t('All') - ); + ]; call_hooks('affinity_labels',$labels); $label_str = ''; @@ -650,6 +709,15 @@ class Connedit extends \Zotlabs\Web\Controller { '$labels' => $label_str, )); } + + if(feature_enabled(local_channel(),'connfilter')) { + $sections['filter'] = [ + 'label' => t('Filter'), + 'url' => z_root() . '/connedit/' . $contact['abook_id'] . '/?f=§ion=filter', + 'sel' => '', + 'title' => t('Open Custom Filter section by default'), + ]; + } $rating_val = 0; $rating_text = ''; @@ -746,11 +814,13 @@ class Connedit extends \Zotlabs\Web\Controller { else $locstr = t('none'); - $o .= replace_macros($tpl,array( - + $o .= replace_macros($tpl, [ '$header' => (($self) ? t('Connection Default Permissions') : sprintf( t('Connection: %s'),$contact['xchan_name'])), '$autoperms' => array('autoperms',t('Apply these permissions automatically'), ((get_pconfig(local_channel(),'system','autoperms')) ? 1 : 0), t('Connection requests will be approved without your interaction'), $yes_no), '$addr' => $contact['xchan_addr'], + '$section' => $section, + '$sections' => $sections, + '$vcard' => $vcard, '$addr_text' => t('This connection\'s primary address is'), '$loc_text' => t('Available locations:'), '$locstr' => $locstr, @@ -788,12 +858,42 @@ class Connedit extends \Zotlabs\Web\Controller { '$permnote_self' => t('Some permissions may be inherited from your channel\'s <a href="settings"><strong>privacy settings</strong></a>, which have higher priority than individual settings. You can change those settings here but they wont have any impact unless the inherited setting changes.'), '$lastupdtext' => t('Last update:'), '$last_update' => relative_date($contact['abook_connected']), + '$is_mobile' => ((\App::$is_mobile || \App::$is_tablet) ? true : false), '$profile_select' => contact_profile_assign($contact['abook_profile']), '$multiprofs' => $multiprofs, '$contact_id' => $contact['abook_id'], '$name' => $contact['xchan_name'], - - )); + '$abook_prev' => $abook_prev, + '$abook_next' => $abook_next, + '$vcard_label' => t('Details'), + '$displayname' => $displayname, + '$name_label' => t('Name'), + '$org_label' => t('Organisation'), + '$title_label' => t('Title'), + '$tel_label' => t('Phone'), + '$email_label' => t('Email'), + '$impp_label' => t('Instant messenger'), + '$url_label' => t('Website'), + '$adr_label' => t('Address'), + '$note_label' => t('Note'), + '$mobile' => t('Mobile'), + '$home' => t('Home'), + '$work' => t('Work'), + '$other' => t('Other'), + '$add_card' => t('Add Contact'), + '$add_field' => t('Add Field'), + '$create' => t('Create'), + '$update' => t('Update'), + '$delete' => t('Delete'), + '$cancel' => t('Cancel'), + '$po_box' => t('P.O. Box'), + '$extra' => t('Additional'), + '$street' => t('Street'), + '$locality' => t('Locality'), + '$region' => t('Region'), + '$zip_code' => t('ZIP Code'), + '$country' => t('Country') + ]); $arr = array('contact' => $contact,'output' => $o); diff --git a/Zotlabs/Module/Directory.php b/Zotlabs/Module/Directory.php index da9bb146f..59ae88857 100644 --- a/Zotlabs/Module/Directory.php +++ b/Zotlabs/Module/Directory.php @@ -68,6 +68,7 @@ class Directory extends \Zotlabs\Web\Controller { $observer = get_observer_hash(); $globaldir = get_directory_setting($observer, 'globaldir'); + // override your personal global search pref if we're doing a navbar search of the directory if(intval($_REQUEST['navsearch'])) $globaldir = 1; @@ -262,7 +263,7 @@ class Directory extends \Zotlabs\Web\Controller { $hometown = ((x($profile,'hometown') == 1) ? $profile['hometown'] : False); - $about = ((x($profile,'about') == 1) ? bbcode($profile['about']) : False); + $about = ((x($profile,'about') == 1) ? zidify_links(bbcode($profile['about'])) : False); $keywords = ((x($profile,'keywords')) ? $profile['keywords'] : ''); diff --git a/Zotlabs/Module/Dirsearch.php b/Zotlabs/Module/Dirsearch.php index 927800bbb..e6cf5449a 100644 --- a/Zotlabs/Module/Dirsearch.php +++ b/Zotlabs/Module/Dirsearch.php @@ -12,7 +12,7 @@ class Dirsearch extends \Zotlabs\Web\Controller { } - function get() { + function get() { $ret = array('success' => false); @@ -410,13 +410,13 @@ class Dirsearch extends \Zotlabs\Web\Controller { $rand = db_getfunc('rand'); $realm = get_directory_realm(); if($realm == DIRECTORY_REALM) { - $r = q("select * from site where site_access != 0 and site_register !=0 and ( site_realm = '%s' or site_realm = '') and site_type = %d order by $rand", + $r = q("select * from site where site_access != 0 and site_register !=0 and ( site_realm = '%s' or site_realm = '') and site_type = %d and site_dead = 0 order by $rand", dbesc($realm), intval(SITE_TYPE_ZOT) ); } else { - $r = q("select * from site where site_access != 0 and site_register !=0 and site_realm = '%s' and site_type = %d order by $rand", + $r = q("select * from site where site_access != 0 and site_register !=0 and site_realm = '%s' and site_type = %d and site_dead = 0 order by $rand", dbesc($realm), intval(SITE_TYPE_ZOT) ); @@ -457,6 +457,6 @@ class Dirsearch extends \Zotlabs\Web\Controller { } } return $ret; - } - + } + } diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php index 42f6dd4ac..638aa881a 100644 --- a/Zotlabs/Module/Display.php +++ b/Zotlabs/Module/Display.php @@ -30,12 +30,10 @@ class Display extends \Zotlabs\Web\Controller { if(argc() > 1 && argv(1) !== 'load') $item_hash = argv(1); - if($_REQUEST['mid']) $item_hash = $_REQUEST['mid']; - - - if(! $item_hash) { + + if(! $item_hash) { \App::$error = 404; notice( t('Item not found.') . EOL); return; @@ -93,9 +91,15 @@ class Display extends \Zotlabs\Web\Controller { // find a copy of the item somewhere $target_item = null; - + + if(strpos($item_hash,'b64.') === 0) + $decoded = @base64url_decode(substr($item_hash,4)); + if($decoded) + $item_hash = $decoded; + $r = q("select id, uid, mid, parent_mid, item_type, item_deleted from item where mid like '%s' limit 1", - dbesc($item_hash . '%') + dbesc($item_hash . '%'), + dbesc($decoded . '%') ); if($r) { diff --git a/Zotlabs/Module/Editpost.php b/Zotlabs/Module/Editpost.php index 5c04653b8..d7612b165 100644 --- a/Zotlabs/Module/Editpost.php +++ b/Zotlabs/Module/Editpost.php @@ -78,6 +78,7 @@ class Editpost extends \Zotlabs\Web\Controller { $x = array( 'nickname' => $channel['channel_address'], + 'item' => $itm[0], 'editor_autocomplete'=> true, 'bbco_autocomplete'=> 'bbcode', 'return_path' => $_SESSION['return_url'], diff --git a/Zotlabs/Module/Events.php b/Zotlabs/Module/Events.php index b8910b644..edc6dd3f0 100644 --- a/Zotlabs/Module/Events.php +++ b/Zotlabs/Module/Events.php @@ -43,6 +43,10 @@ class Events extends \Zotlabs\Web\Controller { $adjust = intval($_POST['adjust']); $nofinish = intval($_POST['nofinish']); + $timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : ''); + + $tz = (($timezone) ? $timezone : date_default_timezone_get()); + $categories = escape_tags(trim($_POST['category'])); // only allow editing your own events. @@ -71,9 +75,9 @@ class Events extends \Zotlabs\Web\Controller { if($adjust) { - $start = datetime_convert(date_default_timezone_get(),'UTC',$start); + $start = datetime_convert($tz,'UTC',$start); if(! $nofinish) - $finish = datetime_convert(date_default_timezone_get(),'UTC',$finish); + $finish = datetime_convert($tz,'UTC',$finish); } else { $start = datetime_convert('UTC','UTC',$start); @@ -374,11 +378,14 @@ class Events extends \Zotlabs\Web\Controller { $event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']); $mid = ((x($orig_event)) ? $orig_event['mid'] : ''); - if(! x($orig_event)) + if(! x($orig_event)) { $sh_checked = ''; - else + $a_checked = ' checked="checked" '; + } + else { $sh_checked = ((($orig_event['allow_cid'] === '<' . $channel['channel_hash'] . '>' || (! $orig_event['allow_cid'])) && (! $orig_event['allow_gid']) && (! $orig_event['deny_cid']) && (! $orig_event['deny_gid'])) ? '' : ' checked="checked" ' ); - + } + if($orig_event['event_xchan']) $sh_checked .= ' disabled="disabled" '; @@ -478,6 +485,8 @@ class Events extends \Zotlabs\Web\Controller { '$allow_gid' => acl2json($permissions['allow_gid']), '$deny_cid' => acl2json($permissions['deny_cid']), '$deny_gid' => acl2json($permissions['deny_gid']), + '$tz_choose' => feature_enabled(local_channel(),'event_tz_select'), + '$timezone' => array('timezone_select' , t('Timezone:'), date_default_timezone_get(), '', get_timezones()), '$lockstate' => (($acl->is_private()) ? 'lock' : 'unlock'), @@ -625,14 +634,14 @@ class Events extends \Zotlabs\Web\Controller { $drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'',''); - $title = strip_tags(html_entity_decode(bbcode($rr['summary']),ENT_QUOTES,'UTF-8')); + $title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])),ENT_QUOTES,'UTF-8')); if(! $title) { list($title, $_trash) = explode("<br",bbcode($rr['desc']),2); $title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8')); } $html = format_event_html($rr); - $rr['desc'] = bbcode($rr['desc']); - $rr['location'] = bbcode($rr['location']); + $rr['desc'] = zidify_links(smilies(bbcode($rr['desc']))); + $rr['location'] = zidify_links(smilies(bbcode($rr['location']))); $events[] = array( 'id'=>$rr['id'], 'hash' => $rr['event_hash'], diff --git a/Zotlabs/Module/Help.php b/Zotlabs/Module/Help.php index 570e8e0cf..e247416d9 100644 --- a/Zotlabs/Module/Help.php +++ b/Zotlabs/Module/Help.php @@ -44,12 +44,56 @@ class Help extends \Zotlabs\Web\Controller { return $o; } + + + if(argc() > 2 && argv(argc()-2) === 'assets') { + $path = ''; + for($x = 1; $x < argc(); $x ++) { + if(strlen($path)) + $path .= '/'; + $path .= argv($x); + } + $realpath = 'doc/' . $path; + //Set the content-type header as appropriate + $imageInfo = getimagesize($realpath); + switch ($imageInfo[2]) { + case IMAGETYPE_JPEG: + header("Content-Type: image/jpeg"); + break; + case IMAGETYPE_GIF: + header("Content-Type: image/gif"); + break; + case IMAGETYPE_PNG: + header("Content-Type: image/png"); + break; + default: + break; + } + header("Content-Length: " . filesize($realpath)); - $content = get_help_content(); + // dump the picture and stop the script + readfile($realpath); + killme(); + } + + $headings = [ + 'about' => t('About'), + 'member' => t('Members'), + 'admin' => t('Administrators'), + 'developer' => t('Developers'), + 'tutorials' => t('Tutorials') + ]; + + if(array_key_exists(argv(1), $headings)) + $heading = $headings[argv(1)]; + + $content = get_help_content(); return replace_macros(get_markup_template('help.tpl'), array( '$title' => t('$Projectname Documentation'), - '$content' => $content + '$tocHeading' => t('Contents'), + '$content' => $content, + '$heading' => $heading )); } diff --git a/Zotlabs/Module/Import.php b/Zotlabs/Module/Import.php index 451effaca..aa79fa8bf 100644 --- a/Zotlabs/Module/Import.php +++ b/Zotlabs/Module/Import.php @@ -118,7 +118,7 @@ class Import extends \Zotlabs\Web\Controller { return; } - $moving = false; + $moving = intval($_REQUEST['moving']); if(array_key_exists('compatibility',$data) && array_key_exists('database',$data['compatibility'])) { $v1 = substr($data['compatibility']['database'],-4); @@ -196,8 +196,8 @@ class Import extends \Zotlabs\Web\Controller { if($completed < 4) { - if(is_array($data['hubloc']) && (! $moving)) { - import_hublocs($channel,$data['hubloc'],$seize); + if(is_array($data['hubloc'])) { + import_hublocs($channel,$data['hubloc'],$seize,$moving); } logger('import step 4'); @@ -536,6 +536,9 @@ class Import extends \Zotlabs\Web\Controller { '$label_old_pass' => t('Your old login password'), '$common' => t('For either option, please choose whether to make this hub your new primary address, or whether your old location should continue this role. You will be able to post from either location, but only one can be marked as the primary location for files, photos, and media.'), '$label_import_primary' => t('Make this hub my primary location'), + '$allow_move' => 0, + '$label_import_moving' => t('Move this channel (disable all previous locations)'), + '$label_import_posts' => t('Import existing posts if possible (experimental - limited by available memory'), '$pleasewait' => t('This process may take several minutes to complete. Please submit the form only once and leave this page open until finished.'), '$email' => '', diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php index 344e839f4..48992a676 100644 --- a/Zotlabs/Module/Item.php +++ b/Zotlabs/Module/Item.php @@ -21,6 +21,7 @@ require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/attach.php'); require_once('include/bbcode.php'); +require_once('include/security.php'); use \Zotlabs\Lib as Zlib; @@ -34,9 +35,7 @@ class Item extends \Zotlabs\Web\Controller { if((! local_channel()) && (! remote_channel()) && (! x($_REQUEST,'commenter'))) return; - - require_once('include/security.php'); - + $uid = local_channel(); $channel = null; $observer = null; @@ -126,6 +125,8 @@ class Item extends \Zotlabs\Web\Controller { $ret = $this->item_check_service_class($uid,(($_REQUEST['webpage'] == ITEM_TYPE_WEBPAGE) ? true : false)); if (!$ret['success']) { notice( t($ret['message']) . EOL) ; + if($api_source) + return ( [ 'success' => false, 'message' => 'service class exception' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -139,6 +140,7 @@ class Item extends \Zotlabs\Web\Controller { $item_flags = $item_restrict = 0; + $expires = NULL_DATE; $route = ''; $parent_item = null; @@ -180,6 +182,8 @@ class Item extends \Zotlabs\Web\Controller { if(($r === false) || (! count($r))) { notice( t('Unable to locate original post.') . EOL); + if($api_source) + return ( [ 'success' => false, 'message' => 'invalid post id' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -214,6 +218,8 @@ class Item extends \Zotlabs\Web\Controller { if(! $can_comment) { notice( t('Permission denied.') . EOL) ; + if($api_source) + return ( [ 'success' => false, 'message' => 'permission denied' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -222,6 +228,8 @@ class Item extends \Zotlabs\Web\Controller { else { if(! perm_is_allowed($profile_uid,$observer['xchan_hash'],($webpage) ? 'write_pages' : 'post_wall')) { notice( t('Permission denied.') . EOL) ; + if($api_source) + return ( [ 'success' => false, 'message' => 'permission denied' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -276,6 +284,8 @@ class Item extends \Zotlabs\Web\Controller { if(! $channel) { logger("mod_item: no channel."); + if($api_source) + return ( [ 'success' => false, 'message' => 'no channel' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -291,6 +301,8 @@ class Item extends \Zotlabs\Web\Controller { } else { logger("mod_item: no owner."); + if($api_source) + return ( [ 'success' => false, 'message' => 'no owner' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -383,6 +395,7 @@ class Item extends \Zotlabs\Web\Controller { $postopts = $orig_post['postopts']; $created = $orig_post['created']; + $expires = $orig_post['expires']; $mid = $orig_post['mid']; $parent_mid = $orig_post['parent_mid']; $plink = $orig_post['plink']; @@ -416,7 +429,9 @@ class Item extends \Zotlabs\Web\Controller { $body = trim($_REQUEST['body']); $body .= trim($_REQUEST['attachment']); $postopts = ''; - + + $allow_empty = ((array_key_exists('allow_empty',$_REQUEST)) ? intval($_REQUEST['allow_empty']) : 0); + $private = intval($acl->is_private() || ($public_policy)); // If this is a comment, set the permissions from the parent. @@ -429,10 +444,12 @@ class Item extends \Zotlabs\Web\Controller { $owner_hash = $parent_item['owner_xchan']; } - if(! strlen($body)) { + if((! $allow_empty) && (! strlen($body))) { if($preview) killme(); info( t('Empty post discarded.') . EOL ); + if($api_source) + return ( [ 'success' => false, 'message' => 'no content' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -440,7 +457,6 @@ class Item extends \Zotlabs\Web\Controller { } - $expires = NULL_DATE; if(feature_enabled($profile_uid,'content_expire')) { if(x($_REQUEST,'expire')) { @@ -449,7 +465,8 @@ class Item extends \Zotlabs\Web\Controller { $expires = NULL_DATE; } } - + + $mimetype = notags(trim($_REQUEST['mimetype'])); if(! $mimetype) $mimetype = 'text/bbcode'; @@ -473,6 +490,8 @@ class Item extends \Zotlabs\Web\Controller { } else { notice( t('Executable content type not permitted to this channel.') . EOL); + if($api_source) + return ( [ 'success' => false, 'message' => 'forbidden content type' ] ); if(x($_REQUEST,'return')) goaway(z_root() . "/" . $return_path ); killme(); @@ -540,42 +559,8 @@ class Item extends \Zotlabs\Web\Controller { if($x) $body .= "\n\n@group+" . $x[0]['abook_id'] . "\n"; } - - /** - * fix naked links by passing through a callback to see if this is a hubzilla site - * (already known to us) which will get a zrl, otherwise link with url, add bookmark tag to both. - * First protect any url inside certain bbcode tags so we don't double link it. - */ - - - $body = preg_replace_callback('/\[code(.*?)\[\/(code)\]/ism','\red_escape_codeblock',$body); - $body = preg_replace_callback('/\[url(.*?)\[\/(url)\]/ism','\red_escape_codeblock',$body); - $body = preg_replace_callback('/\[zrl(.*?)\[\/(zrl)\]/ism','\red_escape_codeblock',$body); - - $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ism", 'nakedoembed', $body); - $body = preg_replace_callback("/([^\]\='".'"'."\/]|^|\#\^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]+)/ism", '\red_zrl_callback', $body); - - $body = preg_replace_callback('/\[\$b64zrl(.*?)\[\/(zrl)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64url(.*?)\[\/(url)\]/ism','\red_unescape_codeblock',$body); - $body = preg_replace_callback('/\[\$b64code(.*?)\[\/(code)\]/ism','\red_unescape_codeblock',$body); - - - // fix any img tags that should be zmg - - $body = preg_replace_callback('/\[img(.*?)\](.*?)\[\/img\]/ism','\red_zrlify_img_callback',$body); - - - $body = bb_translate_video($body); - - /** - * Fold multi-line [code] sequences - */ - - $body = preg_replace('/\[\/code\]\s*\[code\]/ism',"\n",$body); - - $body = scale_external_images($body,false); - + $body = cleanup_bbcode($body); // Look for tags and linkify them $results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid); @@ -739,6 +724,8 @@ class Item extends \Zotlabs\Web\Controller { if(! $mid) { $mid = (($message_id) ? $message_id : item_message_id()); } + + if(! $parent_mid) { $parent_mid = $mid; } @@ -752,7 +739,7 @@ class Item extends \Zotlabs\Web\Controller { $thr_parent = $mid; $datarray = array(); - + $item_thread_top = ((! $parent) ? 1 : 0); if ((! $plink) && ($item_thread_top)) { @@ -863,7 +850,8 @@ class Item extends \Zotlabs\Web\Controller { logger('mod_item: post cancelled by plugin or duplicate suppressed.'); if($return_path) goaway(z_root() . "/" . $return_path); - + if($api_source) + return ( [ 'success' => false, 'message' => 'operation cancelled' ] ); $json = array('cancel' => 1); $json['reload'] = z_root() . '/' . $_REQUEST['jsreload']; echo json_encode($json); @@ -916,6 +904,10 @@ class Item extends \Zotlabs\Web\Controller { if(! $nopush) \Zotlabs\Daemon\Master::Summon(array('Notifier', 'edit_post', $post_id)); + + if($api_source) + return($x); + if((x($_REQUEST,'return')) && strlen($return_path)) { logger('return: ' . $return_path); goaway(z_root() . "/" . $return_path ); @@ -945,7 +937,7 @@ class Item extends \Zotlabs\Web\Controller { 'from_xchan' => $datarray['author_xchan'], 'to_xchan' => $datarray['owner_xchan'], 'item' => $datarray, - 'link' => z_root() . '/display/' . $datarray['mid'], + 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), 'verb' => ACTIVITY_POST, 'otype' => 'item', 'parent' => $parent, @@ -963,7 +955,7 @@ class Item extends \Zotlabs\Web\Controller { 'from_xchan' => $datarray['author_xchan'], 'to_xchan' => $datarray['owner_xchan'], 'item' => $datarray, - 'link' => z_root() . '/display/' . $datarray['mid'], + 'link' => z_root() . '/display/' . gen_link_id($datarray['mid']), 'verb' => ACTIVITY_POST, 'otype' => 'item' )); @@ -990,8 +982,11 @@ class Item extends \Zotlabs\Web\Controller { else { logger('mod_item: unable to retrieve post that was just stored.'); notice( t('System error. Post not saved.') . EOL); - goaway(z_root() . "/" . $return_path ); - // NOTREACHED + if($return_path) + goaway(z_root() . "/" . $return_path ); + if($api_source) + return ( [ 'success' => false, 'message' => 'system error' ] ); + killme(); } if(($parent) && ($parent != $post_id)) { @@ -1012,7 +1007,7 @@ class Item extends \Zotlabs\Web\Controller { } $datarray['id'] = $post_id; - $datarray['llink'] = z_root() . '/display/' . $channel['channel_address'] . '/' . $post_id; + $datarray['llink'] = z_root() . '/display/' . gen_link_id($datarray['mid']); call_hooks('post_local_end', $datarray); @@ -1046,9 +1041,7 @@ class Item extends \Zotlabs\Web\Controller { if((! local_channel()) && (! remote_channel())) return; - - require_once('include/security.php'); - + if((argc() == 3) && (argv(1) === 'drop') && intval(argv(2))) { require_once('include/items.php'); @@ -1084,6 +1077,14 @@ class Item extends \Zotlabs\Web\Controller { else { // complex deletion that needs to propagate and be performed in phases drop_item($i[0]['id'],true,DROPITEM_PHASE1); + $r = q("select * from item where id = %d", + intval($i[0]['id']) + ); + if($r) { + xchan_query($r); + $sync_item = fetch_post_tags($r); + build_sync_packet($i[0]['uid'],array('item' => array(encode_item($sync_item[0],true)))); + } tag_deliver($i[0]['uid'],$i[0]['id']); } } diff --git a/Zotlabs/Module/Like.php b/Zotlabs/Module/Like.php index 43d318623..5ce8ec7f0 100644 --- a/Zotlabs/Module/Like.php +++ b/Zotlabs/Module/Like.php @@ -447,7 +447,7 @@ class Like extends \Zotlabs\Web\Controller { $arr['thr_parent'] = $item['mid']; $ulink = '[zrl=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/zrl]'; $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; - $plink = '[zrl=' . z_root() . '/display/' . $item['mid'] . ']' . $post_type . '[/zrl]'; + $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; $allow_cid = $item['allow_cid']; $allow_gid = $item['allow_gid']; $deny_cid = $item['deny_cid']; diff --git a/Zotlabs/Module/Mail.php b/Zotlabs/Module/Mail.php index c767c8e56..459ce5acf 100644 --- a/Zotlabs/Module/Mail.php +++ b/Zotlabs/Module/Mail.php @@ -23,7 +23,34 @@ class Mail extends \Zotlabs\Web\Controller { $rstr = ((x($_REQUEST,'messagerecip')) ? notags(trim($_REQUEST['messagerecip'])) : ''); $preview = ((x($_REQUEST,'preview')) ? intval($_REQUEST['preview']) : 0); $expires = ((x($_REQUEST,'expires')) ? datetime_convert(date_default_timezone_get(),'UTC', $_REQUEST['expires']) : NULL_DATE); - + + if($preview) { + + $body = cleanup_bbcode($body); + $results = linkify_tags($a, $body, local_channel()); + + if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { + $attachments = array(); + foreach($match[2] as $mtch) { + $hash = substr($mtch,0,strpos($mtch,',')); + $rev = intval(substr($mtch,strpos($mtch,','))); + $r = attach_by_hash_nodata($hash,get_observer_hash(),$rev); + if($r['success']) { + $attachments[] = array( + 'href' => z_root() . '/attach/' . $r['data']['hash'], + 'length' => $r['data']['filesize'], + 'type' => $r['data']['filetype'], + 'title' => urlencode($r['data']['filename']), + 'revision' => $r['data']['revision'] + ); + } + $body = trim(str_replace($match[1],'',$body)); + } + } + echo json_encode(['preview' => zidify_links(smilies(bbcode($body)))]); + killme(); + } + // If we have a raw string for a recipient which hasn't been auto-filled, // it means they probably aren't in our address book, hence we don't know // if we have permission to send them private messages. @@ -82,6 +109,8 @@ class Mail extends \Zotlabs\Web\Controller { require_once('include/text.php'); linkify_tags($a, $body, local_channel()); + // I don't think this is used any more. + if($preview) { $mail = [ 'mailbox' => 'outbox', @@ -90,8 +119,8 @@ class Mail extends \Zotlabs\Web\Controller { 'from_name' => $channel['xchan_name'], 'from_url' => $channel['xchan_url'], 'from_photo' => $channel['xchan_photo_s'], - 'subject' => smilies(bbcode($subject)), - 'body' => smilies(bbcode($body)), + 'subject' => zidify_links(smilies(bbcode($subject))), + 'body' => zidify_links(smilies(bbcode($body))), 'attachments' => '', 'can_recall' => false, 'is_recalled' => '', @@ -341,7 +370,7 @@ class Mail extends \Zotlabs\Web\Controller { 'to_url' => chanlink_hash($message['to_xchan']), 'to_photo' => $message['to']['xchan_photo_s'], 'subject' => $message['title'], - 'body' => smilies(bbcode($message['body'])), + 'body' => zidify_links(smilies(bbcode($message['body']))), 'attachments' => $s, 'delete' => t('Delete message'), 'dreport' => t('Delivery report'), diff --git a/Zotlabs/Module/Message.php b/Zotlabs/Module/Message.php index ea2127a1d..7494f4bf4 100644 --- a/Zotlabs/Module/Message.php +++ b/Zotlabs/Module/Message.php @@ -79,7 +79,7 @@ class Message extends \Zotlabs\Web\Controller { 'to_photo' => $rr['to']['xchan_photo_s'], 'subject' => (($rr['seen']) ? $rr['title'] : '<strong>' . $rr['title'] . '</strong>'), 'delete' => t('Delete conversation'), - 'body' => smilies(bbcode($rr['body'])), + 'body' => zidify_links(smilies(bbcode($rr['body']))), 'date' => datetime_convert('UTC',date_default_timezone_get(),$rr['created'], t('D, d M Y - g:i A')), 'seen' => $rr['seen'] ); diff --git a/Zotlabs/Module/Mood.php b/Zotlabs/Module/Mood.php index d1bd44526..eeb050040 100644 --- a/Zotlabs/Module/Mood.php +++ b/Zotlabs/Module/Mood.php @@ -89,7 +89,7 @@ class Mood extends \Zotlabs\Web\Controller { $item['item_thread_top'] = 1; if ((! $arr['plink']) && intval($arr['item_thread_top'])) { - $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; + $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); } diff --git a/Zotlabs/Module/Notifications.php b/Zotlabs/Module/Notifications.php index 2db02c758..e0313dd8b 100644 --- a/Zotlabs/Module/Notifications.php +++ b/Zotlabs/Module/Notifications.php @@ -23,10 +23,13 @@ class Notifications extends \Zotlabs\Web\Controller { if($r) { $notifications_available = 1; foreach ($r as $it) { + $x = strip_tags(bbcode($it['msg'])); + if(strpos($x,',')) + $x = substr($x,strpos($x,',')+1); $notif_content .= replace_macros(get_markup_template('notify.tpl'),array( '$item_link' => z_root().'/notify/view/'. $it['id'], '$item_image' => $it['photo'], - '$item_text' => strip_tags(bbcode($it['msg'])), + '$item_text' => $x, '$item_when' => relative_date($it['created']) )); } diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php index e459469ef..582174d0e 100644 --- a/Zotlabs/Module/Photos.php +++ b/Zotlabs/Module/Photos.php @@ -188,13 +188,12 @@ class Photos extends \Zotlabs\Web\Controller { } if((argc() > 2) && (x($_REQUEST,'delete')) && ($_REQUEST['delete'] === t('Delete Photo'))) { - // same as above but remove single photo $ob_hash = get_observer_hash(); if(! $ob_hash) goaway(z_root() . '/' . $_SESSION['photo_return']); - + $r = q("SELECT id, resource_id FROM photo WHERE ( xchan = '%s' or uid = %d ) AND resource_id = '%s' LIMIT 1", dbesc($ob_hash), intval(local_channel()), @@ -686,36 +685,32 @@ class Photos extends \Zotlabs\Web\Controller { notice( t('Album name could not be decoded') . EOL); logger('mod_photos: illegal album encoding: ' . $datum); $datum = ''; + goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']); } } $album = (($datum) ? hex2bin($datum) : ''); - - + \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n"; - - - $r = q("SELECT resource_id, max(imgscale) AS imgscale FROM photo WHERE uid = %d AND album = '%s' - AND imgscale <= 4 and photo_usage IN ( %d, %d ) and is_nsfw = %d $sql_extra GROUP BY resource_id", + + //check if the album exists and if we have perms + $r = q("SELECT album FROM photo WHERE uid = %d AND album = '%s' and is_nsfw = %d $sql_extra LIMIT 1", intval($owner_uid), dbesc($album), - intval(PHOTO_NORMAL), - intval(PHOTO_PROFILE), intval($unsafe) ); - if(count($r)) { - \App::set_pager_total(count($r)); + + if($r) { \App::set_pager_itemspage(60); } else { goaway(z_root() . '/photos/' . \App::$data['channel']['channel_address']); } - + if($_GET['order'] === 'posted') $order = 'ASC'; else $order = 'DESC'; - - + $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.imgscale, p.description, p.created FROM photo p INNER JOIN (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) and is_nsfw = %d $sql_extra GROUP BY resource_id) ph ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) @@ -1280,9 +1275,9 @@ class Photos extends \Zotlabs\Web\Controller { // Default - show recent photos with upload link (if applicable) //$o = ''; - \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n"; - + \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n"; + /* $r = q("SELECT resource_id, max(imgscale) AS imgscale FROM photo WHERE uid = %d and photo_usage in ( %d, %d ) and is_nsfw = %d $sql_extra GROUP BY resource_id", intval(\App::$data['channel']['channel_id']), @@ -1294,6 +1289,9 @@ class Photos extends \Zotlabs\Web\Controller { \App::set_pager_total(count($r)); \App::set_pager_itemspage(60); } + */ + + \App::set_pager_itemspage(60); $r = q("SELECT p.resource_id, p.id, p.filename, p.mimetype, p.album, p.imgscale, p.created FROM photo p INNER JOIN ( SELECT resource_id, max(imgscale) imgscale FROM photo diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php index 9e868db92..0bc23952b 100644 --- a/Zotlabs/Module/Profile.php +++ b/Zotlabs/Module/Profile.php @@ -60,7 +60,9 @@ class Profile extends \Zotlabs\Web\Controller { } $groups = array(); - + + + $tab = 'profile'; $o = ''; @@ -69,6 +71,15 @@ class Profile extends \Zotlabs\Web\Controller { return; } + + + if(argc() > 2 && argv(2) === 'vcard') { + header('Content-type: text/vcard'); + header('content-disposition: attachment; filename="' . t('vcard') . '-' . $profile['channel_address'] . '.vcf"' ); + echo \App::$profile['profile_vcard']; + killme(); + } + $is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false); diff --git a/Zotlabs/Module/Profiles.php b/Zotlabs/Module/Profiles.php index 19a642a83..e166c3c19 100644 --- a/Zotlabs/Module/Profiles.php +++ b/Zotlabs/Module/Profiles.php @@ -191,7 +191,7 @@ class Profiles extends \Zotlabs\Web\Controller { } } - function post() { + function post() { if(! local_channel()) { notice( t('Permission denied.') . EOL); @@ -243,6 +243,7 @@ class Profiles extends \Zotlabs\Web\Controller { check_form_security_token_redirectOnErr('/profiles', 'profile_edit'); + $is_default = (($orig[0]['is_default']) ? 1 : 0); $profile_name = notags(trim($_POST['profile_name'])); @@ -315,6 +316,43 @@ class Profiles extends \Zotlabs\Web\Controller { $hide_friends = ((intval($_POST['hide_friends'])) ? 1: 0); + + $orig_vcard = (($orig[0]['profile_vcard']) ? \Sabre\VObject\Reader::read($orig[0]['profile_vcard']) : null); + + $channel = \App::get_channel(); + + $default_vcard_cat = ((defined('DEFAULT_VCARD_CAT')) ? DEFAULT_VCARD_CAT : 'HOME'); + + $defcard = [ + 'fn' => $name, + 'title' => $pdesc, + 'photo' => $channel['xchan_photo_l'], + 'adr' => [], + 'adr_type' => [ $default_vcard_cat ], + 'tel' => [], + 'tel_type' => [ $default_vcard_cat ], + 'email' => [], + 'email_type' => [ $default_vcard_cat ], + 'impp' => [], + 'impp_type' => [ $default_vcard_cat ], + 'url' => [], + 'url_type' => [ $default_vcard_cat ] + ]; + + $defcard['adr'][] = [ + 0 => '', + 1 => '', + 2 => $address, + 3 => $locality, + 4 => $region, + 5 => $postal_code, + 6 => $country_name + ]; + + + $profile_vcard = update_vcard($defcard,$orig_vcard); + + require_once('include/text.php'); linkify_tags($a, $likes, local_channel()); linkify_tags($a, $dislikes, local_channel()); @@ -511,7 +549,8 @@ class Profiles extends \Zotlabs\Web\Controller { romance = '%s', employment = '%s', education = '%s', - hide_friends = %d + hide_friends = %d, + profile_vcard = '%s' WHERE id = %d AND uid = %d", dbesc($profile_name), dbesc($name), @@ -546,6 +585,7 @@ class Profiles extends \Zotlabs\Web\Controller { dbesc($work), dbesc($education), intval($hide_friends), + dbesc($profile_vcard), intval(argv(1)), intval(local_channel()) ); diff --git a/Zotlabs/Module/Pubsites.php b/Zotlabs/Module/Pubsites.php index 1c9cd5121..d87967189 100644 --- a/Zotlabs/Module/Pubsites.php +++ b/Zotlabs/Module/Pubsites.php @@ -36,7 +36,7 @@ class Pubsites extends \Zotlabs\Web\Controller { $o .= '</tr>'; if($j['sites']) { foreach($j['sites'] as $jj) { - if(! $jj['project']) + if(! \Zotlabs\Lib\System::compatible_project($jj['project'])) continue; if(strpos($jj['version'],' ')) { $x = explode(' ', $jj['version']); diff --git a/Zotlabs/Module/Search.php b/Zotlabs/Module/Search.php index b319b19d6..89eaa4ffa 100644 --- a/Zotlabs/Module/Search.php +++ b/Zotlabs/Module/Search.php @@ -211,7 +211,7 @@ class Search extends \Zotlabs\Web\Controller { $result = array(); require_once('include/conversation.php'); foreach($items as $item) { - $item['html'] = bbcode($item['body']); + $item['html'] = zidify_links(bbcode($item['body'])); $x = encode_item($item); $x['html'] = prepare_text($item['body'],$item['mimetype']); $result[] = $x; diff --git a/Zotlabs/Module/Settings/Channel.php b/Zotlabs/Module/Settings/Channel.php index e18416393..a73aa2e60 100644 --- a/Zotlabs/Module/Settings/Channel.php +++ b/Zotlabs/Module/Settings/Channel.php @@ -404,8 +404,11 @@ class Channel { '$desc' => t('Your channel address is'), '$nickname' => $nickname, '$subdir' => $subdir, + '$davdesc' => t('Your files/photos are accessible via WebDAV at'), + '$davpath' => ((get_account_techlevel() > 3) ? z_root() . '/dav/' . $nickname : ''), '$basepath' => \App::get_hostname() )); + $stpl = get_markup_template('settings.tpl'); @@ -515,6 +518,8 @@ class Channel { '$notify7' => array('notify7', t('You are tagged in a post'), ($notify & NOTIFY_TAGSELF), NOTIFY_TAGSELF, '', $yes_no), '$notify8' => array('notify8', t('You are poked/prodded/etc. in a post'), ($notify & NOTIFY_POKE), NOTIFY_POKE, '', $yes_no), + '$notify9' => array('notify9', t('Someone likes your post/comment'), ($notify & NOTIFY_LIKE), NOTIFY_LIKE, '', $yes_no), + '$lbl_vnot' => t('Show visual notifications including:'), diff --git a/Zotlabs/Module/Settings/Tokens.php b/Zotlabs/Module/Settings/Tokens.php index e63fed128..d32a00c95 100644 --- a/Zotlabs/Module/Settings/Tokens.php +++ b/Zotlabs/Module/Settings/Tokens.php @@ -115,6 +115,7 @@ class Tokens { $desc2 = t('You may also provide <em>dropbox</em> style access links to friends and associates by adding the Login Password to any specific site URL as shown. Examples:'); $global_perms = \Zotlabs\Access\Permissions::Perms(); + $their_perms = []; $existing = get_all_perms(local_channel(),(($atoken_xchan) ? $atoken_xchan : '')); @@ -123,7 +124,6 @@ class Tokens { intval(local_channel()), dbesc($atoken_xchan) ); - $their_perms = array(); if($theirs) { foreach($theirs as $t) { $their_perms[$t['k']] = $t['v']; diff --git a/Zotlabs/Module/Setup.php b/Zotlabs/Module/Setup.php index fde9fe823..9c688af01 100644 --- a/Zotlabs/Module/Setup.php +++ b/Zotlabs/Module/Setup.php @@ -161,13 +161,6 @@ class Setup extends \Zotlabs\Web\Controller { } } - function get_db_errno() { - if(class_exists('mysqli')) - return mysqli_connect_errno(); - else - return mysql_errno(); - } - /** * @brief Get output for the setup page. * @@ -175,6 +168,7 @@ class Setup extends \Zotlabs\Web\Controller { * * @return string parsed HTML output */ + function get() { $o = ''; @@ -401,7 +395,8 @@ class Setup extends \Zotlabs\Web\Controller { if (strlen($phpath)) { $passed = file_exists($phpath); - } else { + } + elseif(function_exists('shell_exec')) { if(is_windows()) $phpath = trim(shell_exec('where php')); else @@ -426,9 +421,13 @@ class Setup extends \Zotlabs\Web\Controller { if($passed) { $str = autoname(8); $cmd = "$phpath install/testargs.php $str"; - $result = trim(shell_exec($cmd)); - $passed2 = $result == $str; $help = ''; + + if(function_exists('shell_exec')) + $result = trim(shell_exec($cmd)); + else + $help .= t('Unable to check command line PHP, as shell_exec() is disabled. This is required.') . EOL; + $passed2 = (($result == $str) ? true : false); if(!$passed2) { $help .= t('The command line version of PHP on your system does not have "register_argc_argv" enabled.'). EOL; $help .= t('This is required for message delivery to work.'); @@ -457,7 +456,7 @@ class Setup extends \Zotlabs\Web\Controller { userReadableSize($result['max_upload_filesize']), $result['max_file_uploads'] ); - $help .= '<br>' . t('You can adjust these settings in the servers php.ini.'); + $help .= '<br>' . t('You can adjust these settings in the server php.ini file.'); $this->check_add($checks, t('PHP upload limits'), true, false, $help); } @@ -497,6 +496,11 @@ class Setup extends \Zotlabs\Web\Controller { function check_funcs(&$checks) { $ck_funcs = array(); + $disabled = explode(',',ini_get('disable_functions')); + if($disabled) + array_walk($disabled,'array_trim'); + + // add check metadata, the real check is done bit later and return values set $this->check_add($ck_funcs, t('libCurl PHP module'), true, true); $this->check_add($ck_funcs, t('GD graphics PHP module'), true, true); @@ -512,11 +516,17 @@ class Setup extends \Zotlabs\Web\Controller { $this->check_add($ck_funcs, t('Apache mod_rewrite module'), true, true); } } - if((! function_exists('proc_open')) || strstr(ini_get('disable_functions'),'proc_open')) { - $this->check_add($ck_funcs, t('proc_open'), false, true, t('Error: proc_open is required but is either not installed or has been disabled in php.ini')); + if((! function_exists('exec')) || in_array('exec',$disabled)) { + $this->check_add($ck_funcs, t('exec'), false, true, t('Error: exec is required but is either not installed or has been disabled in php.ini')); + } + else { + $this->check_add($ck_funcs, t('exec'), true, true); + } + if((! function_exists('shell_exec')) || in_array('shell_exec',$disabled)) { + $this->check_add($ck_funcs, t('shell_exec'), false, true, t('Error: shell_exec is required but is either not installed or has been disabled in php.ini')); } else { - $this->check_add($ck_funcs, t('proc_open'), true, true); + $this->check_add($ck_funcs, t('shell_exec'), true, true); } if(! function_exists('curl_init')) { @@ -579,7 +589,7 @@ class Setup extends \Zotlabs\Web\Controller { if(! is_writable(TEMPLATE_BUILD_PATH) ) { $status = false; - $help = t('Red uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.') .EOL; + $help = t('This software uses the Smarty3 template engine to render its web views. Smarty3 compiles templates to PHP to speed up rendering.') .EOL; $help .= sprintf( t('In order to store these compiled templates, the web server needs to have write access to the directory %s under the top level web folder.'), TEMPLATE_BUILD_PATH) . EOL; $help .= t('Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder.').EOL; $help .= sprintf( t('Note: as a security measure, you should give the web server write access to %s only--not the template files (.tpl) that it contains.'), TEMPLATE_BUILD_PATH) . EOL; @@ -601,7 +611,7 @@ class Setup extends \Zotlabs\Web\Controller { if(! is_writable('store')) { $status = false; - $help = t('This software uses the store directory to save uploaded files. The web server needs to have write access to the store directory under the Red top level folder') . EOL; + $help = t('This software uses the store directory to save uploaded files. The web server needs to have write access to the store directory under the top level web folder') . EOL; $help .= t('Please ensure that the user that your web server runs as (e.g. www-data) has write access to this folder.').EOL; } @@ -716,7 +726,7 @@ class Setup extends \Zotlabs\Web\Controller { // (e.g. NSS used in RedHat) require different syntax, so hopefully // the default curl cipher list will work for most sites. If not, // this can set via config. Many distros are now disabling RC4, - // but many Red sites still use it and are unable to change it. + // but many existing sites still use it and are unable to change it. // We do not use SSL for encryption, only to protect session cookies. // z_fetch_url() is also used to import shared links and other content // so in theory most any cipher could show up and we should do our best diff --git a/Zotlabs/Module/Siteinfo.php b/Zotlabs/Module/Siteinfo.php index a15e2896d..7c3918425 100644 --- a/Zotlabs/Module/Siteinfo.php +++ b/Zotlabs/Module/Siteinfo.php @@ -15,63 +15,33 @@ class Siteinfo extends \Zotlabs\Web\Controller { function get() { - if(! get_config('system','hidden_version_siteinfo')) { - $version = sprintf( t('Version %s'), \Zotlabs\Lib\System::get_project_version()); - if(@is_dir('.git') && function_exists('shell_exec')) { - $commit = @shell_exec('git log -1 --format="%h"'); - $tag = \Zotlabs\Lib\System::get_std_version(); // @shell_exec('git describe --tags --abbrev=0'); - } - if(! isset($commit) || strlen($commit) > 16) - $commit = ''; - } - else { - $version = $commit = ''; - } + $siteinfo = replace_macros(get_markup_template('siteinfo.tpl'), + [ + '$title' => t('About this site'), + '$sitenametxt' => t('Site Name'), + '$sitename' => \Zotlabs\Lib\System::get_site_name(), + '$headline' => t('Site Information'), + '$site_about' => bbcode(get_config('system','siteinfo')), + '$admin_headline' => t('Administrator'), + '$admin_about' => bbcode(get_config('system','admininfo')), + '$terms' => t('Terms of Service'), + '$prj_header' => t('Software and Project information'), + '$prj_name' => t('This site is powered by $Projectname'), + '$prj_transport' => t('Federated and decentralised networking and identity services provided by Zot'), + '$transport_link' => '<a href="https://zotlabs.com">https://zotlabs.com</a>', + '$prj_version' => ((get_config('system','hidden_version_siteinfo')) ? '' : sprintf( t('Version %s'), \Zotlabs\Lib\System::get_project_version())), + '$prj_linktxt' => t('Project homepage'), + '$prj_srctxt' => t('Developer homepage'), + '$prj_link' => \Zotlabs\Lib\System::get_project_link(), + '$prj_src' => \Zotlabs\Lib\System::get_project_srclink(), + ] + ); - $plugins_list = implode(', ',visible_plugin_list()); + call_hooks('about_hook', $siteinfo); + + return $siteinfo; - if($plugins_list) - $plugins_text = t('Installed plugins/addons/apps:'); - else - $plugins_text = t('No installed plugins/addons/apps'); - - $txt = get_config('system','admininfo'); - $admininfo = bbcode($txt); - - if(file_exists('doc/site_donate.html')) - $donate .= file_get_contents('doc/site_donate.html'); - - if(function_exists('sys_getloadavg')) - $loadavg = sys_getloadavg(); - - $o = replace_macros(get_markup_template('siteinfo.tpl'), array( - '$title' => t('$Projectname'), - '$description' => t('This is a hub of $Projectname - a global cooperative network of decentralized privacy enhanced websites.'), - '$version' => $version, - '$tag_txt' => t('Tag: '), - '$tag' => $tag, - '$polled' => t('Last background fetch: '), - '$lastpoll' => get_poller_runtime(), - '$load_average' => t('Current load average: '), - '$loadavg_all' => $loadavg[0] . ', ' . $loadavg[1] . ', ' . $loadavg[2], - '$commit' => $commit, - '$web_location' => t('Running at web location') . ' ' . z_root(), - '$visit' => t('Please visit <a href="http://hubzilla.org">hubzilla.org</a> to learn more about $Projectname.'), - '$bug_text' => t('Bug reports and issues: please visit'), - '$bug_link_url' => 'https://github.com/redmatrix/hubzilla/issues', - '$bug_link_text' => t('$projectname issues'), - '$contact' => t('Suggestions, praise, etc. - please email "redmatrix" at librelist - dot com'), - '$donate' => $donate, - '$adminlabel' => t('Site Administrators'), - '$admininfo' => $admininfo, - '$plugins_text' => $plugins_text, - '$plugins_list' => $plugins_list - )); - - call_hooks('about_hook', $o); - - return $o; - } + } diff --git a/Zotlabs/Module/Sslify.php b/Zotlabs/Module/Sslify.php index db73f85e0..2891f3691 100644 --- a/Zotlabs/Module/Sslify.php +++ b/Zotlabs/Module/Sslify.php @@ -10,21 +10,16 @@ class Sslify extends \Zotlabs\Web\Controller { $h = explode("\n",$x['header']); foreach ($h as $l) { list($k,$v) = array_map("trim", explode(":", trim($l), 2)); - $hdrs[$k] = $v; + $hdrs[strtolower($k)] = $v; } - if (array_key_exists('Content-Type', $hdrs)) - $type = $hdrs['Content-Type']; - - header('Content-Type: ' . $type); + if (array_key_exists('content-type', $hdrs)) { + $type = $hdrs['content-type']; + header('Content-Type: ' . $type); + } + echo $x['body']; killme(); } killme(); - // for some reason when this fallback is in place - it gets triggered - // often, (creating mixed content exceptions) even though there is - // nothing obvious missing on the page when we bypass it. - goaway($_REQUEST['url']); - } - - + } } diff --git a/Zotlabs/Module/Subthread.php b/Zotlabs/Module/Subthread.php index 0226664d7..dae8bf020 100644 --- a/Zotlabs/Module/Subthread.php +++ b/Zotlabs/Module/Subthread.php @@ -138,7 +138,7 @@ class Subthread extends \Zotlabs\Web\Controller { $ulink = '[zrl=' . $item_author['xchan_url'] . ']' . $item_author['xchan_name'] . '[/zrl]'; $alink = '[zrl=' . $observer['xchan_url'] . ']' . $observer['xchan_name'] . '[/zrl]'; - $plink = '[zrl=' . z_root() . '/display/' . $item['mid'] . ']' . $post_type . '[/zrl]'; + $plink = '[zrl=' . z_root() . '/display/' . gen_link_id($item['mid']) . ']' . $post_type . '[/zrl]'; $arr['body'] = sprintf( $bodyverb, $alink, $ulink, $plink ); diff --git a/Zotlabs/Module/Tagger.php b/Zotlabs/Module/Tagger.php index 25f518d53..98e901965 100644 --- a/Zotlabs/Module/Tagger.php +++ b/Zotlabs/Module/Tagger.php @@ -60,7 +60,7 @@ class Tagger extends \Zotlabs\Web\Controller { $links = array(array('rel' => 'alternate','type' => 'text/html', - 'href' => z_root() . '/display/' . $item['mid'])); + 'href' => z_root() . '/display/' . gen_link_id($item['mid']))); $target = json_encode(array( 'type' => $targettype, @@ -81,11 +81,6 @@ class Tagger extends \Zotlabs\Web\Controller { ), )); - - - $link = xmlify('<link rel="alternate" type="text/html" href="' - . z_root() . '/display/' . $owner['nickname'] . '/' . $item['id'] . '" />' . "\n") ; - $tagid = z_root() . '/search?tag=' . $term; $objtype = ACTIVITY_OBJ_TAGTERM; diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php index 9268fbb0a..c6fe7518e 100644 --- a/Zotlabs/Module/Wall_attach.php +++ b/Zotlabs/Module/Wall_attach.php @@ -12,7 +12,7 @@ class Wall_attach extends \Zotlabs\Web\Controller { $using_api = false; - if(\App::$data['api_info'] && array_key_exists('media',$_FILES)) { + if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) { $using_api = true; } diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 0da699c73..46b94f091 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -173,7 +173,6 @@ class Webpages extends \Zotlabs\Web\Controller { if($_REQUEST['pagetitle']) $x['pagetitle'] = $_REQUEST['pagetitle']; - $editor = status_editor($a,$x); // Get a list of webpages. We can't display all them because endless scroll makes that unusable, // so just list titles and an edit link. @@ -197,6 +196,11 @@ class Webpages extends \Zotlabs\Web\Controller { // intval(ITEM_TYPE_WEBPAGE) // ); + if(! $r) + $x['pagetitle'] = 'home'; + + $editor = status_editor($a,$x); + $pages = null; if($r) { diff --git a/Zotlabs/Module/Wiki.php b/Zotlabs/Module/Wiki.php index edcd6ec58..746ad9b4f 100644 --- a/Zotlabs/Module/Wiki.php +++ b/Zotlabs/Module/Wiki.php @@ -2,26 +2,35 @@ namespace Zotlabs\Module; +use \Zotlabs\Lib as Zlib; + +require_once('include/acl_selectors.php'); +require_once('include/conversation.php'); +require_once('include/bbcode.php'); + + class Wiki extends \Zotlabs\Web\Controller { + private $wiki = null; + function init() { // Determine which channel's wikis to display to the observer $nick = null; if (argc() > 1) $nick = argv(1); // if the channel name is in the URL, use that - if (!$nick && local_channel()) { // if no channel name was provided, assume the current logged in channel + if (! $nick && local_channel()) { // if no channel name was provided, assume the current logged in channel $channel = \App::get_channel(); if ($channel && $channel['channel_address']) { $nick = $channel['channel_address']; goaway(z_root() . '/wiki/' . $nick); } } - if (!$nick) { - notice(t('You must be logged in to see this page.') . EOL); - goaway('/login'); + if (! $nick) { + notice( t('Profile Unavailable.') . EOL); + goaway(z_root()); } - profile_load($nick); + profile_load($nick); } function get() { @@ -34,31 +43,28 @@ class Wiki extends \Zotlabs\Web\Controller { notice( t('Not found') . EOL); return; } - - $tab = 'wiki'; - - - require_once('include/wiki.php'); - require_once('include/acl_selectors.php'); - require_once('include/conversation.php'); + // TODO: Combine the interface configuration into a unified object // Something like $interface = array('new_page_button' => false, 'new_wiki_button' => false, ...) + $wiki_owner = false; $showNewWikiButton = false; - $showCommitMsg = false; - $hidePageHistory = false; $pageHistory = array(); $local_observer = null; $resource_id = ''; // init() should have forced the URL to redirect to /wiki/channel so assume argc() > 1 + $nick = argv(1); $owner = channelx_by_nick($nick); // The channel who owns the wikis being viewed if(! $owner) { notice( t('Invalid channel') . EOL); goaway('/' . argv(0)); } + + $observer_hash = get_observer_hash(); + // Determine if the observer is the channel owner so the ACL dialog can be populated if (local_channel() === intval($owner['channel_id'])) { @@ -66,12 +72,14 @@ class Wiki extends \Zotlabs\Web\Controller { // Obtain the default permission settings of the channel $owner_acl = array( - 'allow_cid' => $owner['channel_allow_cid'], - 'allow_gid' => $owner['channel_allow_gid'], - 'deny_cid' => $owner['channel_deny_cid'], - 'deny_gid' => $owner['channel_deny_gid'] + 'allow_cid' => $owner['channel_allow_cid'], + 'allow_gid' => $owner['channel_allow_gid'], + 'deny_cid' => $owner['channel_deny_cid'], + 'deny_gid' => $owner['channel_deny_gid'] ); + // Initialize the ACL to the channel default permissions + $x = array( 'lockstate' => (( $owner['channel_allow_cid'] || $owner['channel_allow_gid'] || @@ -82,157 +90,191 @@ class Wiki extends \Zotlabs\Web\Controller { 'acl' => populate_acl($owner_acl), 'allow_cid' => acl2json($owner_acl['allow_cid']), 'allow_gid' => acl2json($owner_acl['allow_gid']), - 'deny_cid' => acl2json($owner_acl['deny_cid']), - 'deny_gid' => acl2json($owner_acl['deny_gid']), + 'deny_cid' => acl2json($owner_acl['deny_cid']), + 'deny_gid' => acl2json($owner_acl['deny_gid']), 'bang' => '' ); - } else { + } + else { // Not the channel owner $owner_acl = $x = array(); } - + + $is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false); + $o = profile_tabs($a, $is_owner, \App::$profile['channel_address']); + // Download a wiki - if ((argc() > 3) && (argv(2) === 'download') && (argv(3) === 'wiki')) { - $resource_id = argv(4); - $w = wiki_get_wiki($resource_id); - if (!$w['path']) { - notice(t('Error retrieving wiki') . EOL); - } - $zip_folder_name = random_string(10); - $zip_folderpath = '/tmp/' . $zip_folder_name; - if (!mkdir($zip_folderpath, 0770, false)) { - logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); - notice(t('Error creating zip file export folder') . EOL); - } - $zip_filename = $w['urlName']; - $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; - // Generate the zip file - \Zotlabs\Lib\ExtendedZip::zipTree($w['path'], $zip_filepath, \ZipArchive::CREATE); - // Output the file for download - header('Content-disposition: attachment; filename="' . $zip_filename . '.zip"'); - header("Content-Type: application/zip"); - $success = readfile($zip_filepath); - if ($success) { - rrmdir($zip_folderpath); // delete temporary files - } else { - rrmdir($zip_folderpath); // delete temporary files - logger('Error downloading wiki: ' . $resource_id); - } + + if((argc() > 3) && (argv(2) === 'download') && (argv(3) === 'wiki')) { + + $resource_id = argv(4); + + $w = Zlib\NativeWiki::get_wiki($owner,$observer_hash,$resource_id); + if(! $w['htmlName']) { + notice(t('Error retrieving wiki') . EOL); + } + + $zip_folder_name = random_string(10); + $zip_folderpath = '/tmp/' . $zip_folder_name; + if(!mkdir($zip_folderpath, 0770, false)) { + logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); + notice(t('Error creating zip file export folder') . EOL); + } + + $zip_filename = $w['urlName']; + $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + + // Generate the zip file + ZLib\ExtendedZip::zipTree($w['path'], $zip_filepath, \ZipArchive::CREATE); + + // Output the file for download + + header('Content-disposition: attachment; filename="' . $zip_filename . '.zip"'); + header('Content-Type: application/zip'); + + $success = readfile($zip_filepath); + + if(!$success) { + logger('Error downloading wiki: ' . $resource_id); + notice(t('Error downloading wiki: ' . $resource_id) . EOL); + } + + // delete temporary files + rrmdir($zip_folderpath); + killme(); + } - switch (argc()) { + switch(argc()) { case 2: - // Configure page template - $wikiheaderName = t('Wiki'); - $wikiheaderPage = t('Sandbox'); - require_once('library/markdown.php'); - $content = t('"# Wiki Sandbox\n\nContent you **edit** and **preview** here *will not be saved*."'); - $renderedContent = Markdown(json_decode($content)); - $hide_editor = false; - $showPageControls = false; - $showNewWikiButton = $wiki_owner; - $showNewPageButton = false; - $hidePageHistory = true; - $showCommitMsg = false; + $wikis = Zlib\NativeWiki::listwikis($owner, get_observer_hash()); + if($wikis) { + $o .= replace_macros(get_markup_template('wikilist.tpl'), array( + '$header' => t('Wikis'), + '$channel' => $owner['channel_address'], + '$wikis' => $wikis['wikis'], + // If the observer is the local channel owner, show the wiki controls + '$owner' => ((local_channel() && local_channel() === intval(\App::$profile['uid'])) ? true : false), + '$edit' => t('Edit'), + '$download' => t('Download'), + '$view' => t('View'), + '$create' => t('Create New'), + '$submit' => t('Submit'), + '$wikiName' => array('wikiName', t('Wiki name')), + '$mimeType' => array('mimeType', t('Content type'), '', '', ['text/markdown' => 'Markdown', 'text/bbcode' => 'BB Code']), + '$name' => t('Name'), + '$type' => t('Type'), + '$lockstate' => $x['lockstate'], + '$acl' => $x['acl'], + '$allow_cid' => $x['allow_cid'], + '$allow_gid' => $x['allow_gid'], + '$deny_cid' => $x['deny_cid'], + '$deny_gid' => $x['deny_gid'], + '$notify' => array('postVisible', t('Create a status post for this wiki'), '', '', array(t('No'), t('Yes'))) + )); + + return $o; + } break; + case 3: + // /wiki/channel/wiki -> No page was specified, so redirect to Home.md + $wikiUrlName = urlencode(argv(2)); - goaway('/'.argv(0).'/'.argv(1).'/'.$wikiUrlName.'/Home'); + goaway(z_root() . '/' . argv(0) . '/' . argv(1) . '/' . $wikiUrlName . '/Home'); + case 4: + // GET /wiki/channel/wiki/page // Fetch the wiki info and determine observer permissions + $wikiUrlName = urlencode(argv(2)); $pageUrlName = urlencode(argv(3)); - $w = wiki_exists_by_name($owner['channel_id'], $wikiUrlName); - if(!$w['resource_id']) { + + $w = Zlib\NativeWiki::exists_by_name($owner['channel_id'], $wikiUrlName); + + if(! $w['resource_id']) { notice(t('Wiki not found') . EOL); - goaway('/'.argv(0).'/'.argv(1)); + goaway(z_root() . '/' . argv(0) . '/' . argv(1)); } + $resource_id = $w['resource_id']; - if (!$wiki_owner) { + if(! $wiki_owner) { // Check for observer permissions $observer_hash = get_observer_hash(); - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['read']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['read']) { notice(t('Permission denied.') . EOL); - goaway('/'.argv(0).'/'.argv(1)); - } - if($perms['write']) { - $wiki_editor = true; - } else { - $wiki_editor = false; + goaway(z_root() . '/' . argv(0) . '/' . argv(1)); + return; //not reached } - } else { + $wiki_editor = (($perms['write']) ? true : false); + } + else { $wiki_editor = true; } + $wikiheaderName = urldecode($wikiUrlName); $wikiheaderPage = urldecode($pageUrlName); - $p = wiki_get_page_content(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); - if(!$p['success']) { - notice(t('Error retrieving page content') . EOL); - goaway('/'.argv(0).'/'.argv(1).'/'.$wikiUrlName); + + $renamePage = (($wikiheaderPage === 'Home') ? '' : t('Rename page')); + + $p = Zlib\NativeWikiPage::get_page_content(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + if(! $p['success']) { + notice( t('Error retrieving page content') . EOL); + goaway(z_root() . '/' . argv(0) . '/' . argv(1) ); } - $content = ($p['content'] !== '' ? htmlspecialchars_decode($p['content'],ENT_COMPAT) : '"# New page\n"'); + + $mimeType = $p['mimeType']; + + $rawContent = (($p['mimeType'] == 'text/bbcode') + ? htmlspecialchars_decode(json_decode($p['content']),ENT_COMPAT) + : htmlspecialchars_decode($p['content'],ENT_COMPAT) + ); + $content = ($p['content'] !== '' ? $rawContent : '"# New page\n"'); // Render the Markdown-formatted page content in HTML - require_once('library/markdown.php'); - $html = wiki_generate_toc(zidify_text(purify_html(Markdown(wiki_bbcode(json_decode($content)))))); - $renderedContent = wiki_convert_links($html,argv(0).'/'.argv(1).'/'.$wikiUrlName); - $hide_editor = false; + if($mimeType == 'text/bbcode') { + $renderedContent = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))), argv(0) . '/' . argv(1) . '/' . $wikiUrlName); + } + else { + require_once('library/markdown.php'); + $html = Zlib\NativeWikiPage::generate_toc(zidify_text(purify_html(Markdown(Zlib\NativeWikiPage::bbcode(json_decode($content)))))); + $renderedContent = Zlib\NativeWikiPage::convert_links($html, argv(0) . '/' . argv(1) . '/' . $wikiUrlName); + } $showPageControls = $wiki_editor; - $showNewWikiButton = $wiki_owner; - $showNewPageButton = $wiki_editor; - $hidePageHistory = false; - $showCommitMsg = true; - $pageHistory = wiki_page_history(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); break; default: // Strip the extraneous URL components - goaway('/'.argv(0).'/'.argv(1).'/'.$wikiUrlName.'/'.$pageUrlName); + goaway('/' . argv(0) . '/' . argv(1) . '/' . $wikiUrlName . '/' . $pageUrlName); } $wikiModalID = random_string(3); - $wikiModal = replace_macros( - get_markup_template('generic_modal.tpl'), array( - '$id' => $wikiModalID, - '$title' => t('Revision Comparison'), - '$ok' => t('Revert'), - '$cancel' => t('Cancel') - ) - ); - - $is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false); - - $o .= profile_tabs($a, $is_owner, \App::$profile['channel_address']); + $wikiModal = replace_macros(get_markup_template('generic_modal.tpl'), array( + '$id' => $wikiModalID, + '$title' => t('Revision Comparison'), + '$ok' => (($showPageControls) ? t('Revert') : ''), + '$cancel' => t('Cancel') + )); + $placeholder = t('Short description of your changes (optional)'); + $o .= replace_macros(get_markup_template('wiki.tpl'),array( '$wikiheaderName' => $wikiheaderName, '$wikiheaderPage' => $wikiheaderPage, - '$hideEditor' => $hide_editor, + '$renamePage' => $renamePage, '$showPageControls' => $showPageControls, - '$tools_label' => 'Wiki Tools', - '$showNewWikiButton'=> $showNewWikiButton, - '$showNewPageButton'=> $showNewPageButton, - '$hidePageHistory' => $hidePageHistory, - '$showCommitMsg' => $showCommitMsg, + '$editOrSourceLabel' => (($showPageControls) ? t('Edit') : t('Source')), + '$tools_label' => 'Page Tools', '$channel' => $owner['channel_address'], '$resource_id' => $resource_id, '$page' => $pageUrlName, - '$lockstate' => $x['lockstate'], - '$acl' => $x['acl'], - '$allow_cid' => $x['allow_cid'], - '$allow_gid' => $x['allow_gid'], - '$deny_cid' => $x['deny_cid'], - '$deny_gid' => $x['deny_gid'], - '$bang' => $x['bang'], + '$mimeType' => $mimeType, '$content' => $content, '$renderedContent' => $renderedContent, - '$wikiName' => array('wikiName', t('Enter the name of your new wiki:'), '', ''), - '$pageName' => array('pageName', t('Enter the name of the new page:'), '', ''), - '$pageRename' => array('pageRename', t('Enter the new name:'), '', ''), - '$commitMsg' => array('commitMsg', '', '', '', '', 'placeholder="(optional) Enter a custom message when saving the page..."'), - '$pageHistory' => $pageHistory['history'], + '$pageRename' => array('pageRename', t('New page name'), '', ''), + '$commitMsg' => array('commitMsg', '', '', '', '', 'placeholder="' . $placeholder . '"'), '$wikiModal' => $wikiModal, '$wikiModalID' => $wikiModalID, '$commit' => 'HEAD', @@ -242,17 +284,21 @@ class Wiki extends \Zotlabs\Web\Controller { '$embedPhotosModalOK' => t('OK'), '$modalchooseimages' => t('Choose images to embed'), '$modalchoosealbum' => t('Choose an album'), - '$modaldiffalbum' => t('Choose a different album...'), + '$modaldiffalbum' => t('Choose a different album'), '$modalerrorlist' => t('Error getting album list'), '$modalerrorlink' => t('Error getting photo link'), '$modalerroralbum' => t('Error getting album'), )); - head_add_js('library/ace/ace.js'); // Ace Code Editor + + if($p['mimeType'] != 'text/bbcode') + head_add_js('library/ace/ace.js'); // Ace Code Editor + return $o; } function post() { - require_once('include/wiki.php'); + + require_once('include/bbcode.php'); $nick = argv(1); $owner = channelx_by_nick($nick); @@ -268,13 +314,22 @@ class Wiki extends \Zotlabs\Web\Controller { // Render mardown-formatted text in HTML for preview if((argc() > 2) && (argv(2) === 'preview')) { $content = $_POST['content']; - $resource_id = $_POST['resource_id']; - require_once('library/markdown.php'); - $content = wiki_bbcode($content); - $html = wiki_generate_toc(zidify_text(purify_html(Markdown($content)))); - $w = wiki_get_wiki($resource_id); - $wikiURL = argv(0).'/'.argv(1).'/'.$w['urlName']; - $html = wiki_convert_links($html,$wikiURL); + $resource_id = $_POST['resource_id']; + $w = Zlib\NativeWiki::get_wiki($owner['channel_id'],$observer_hash,$resource_id); + + $wikiURL = argv(0) . '/' . argv(1) . '/' . $w['urlName']; + + $mimeType = $w['mimeType']; + + if($mimeType == 'text/bbcode') { + $html = Zlib\NativeWikiPage::convert_links(zidify_links(smilies(bbcode($content))),$wikiURL); + } + else { + require_once('library/markdown.php'); + $content = Zlib\NativeWikiPage::bbcode($content); + $html = Zlib\NativeWikiPage::generate_toc(zidify_text(purify_html(Markdown($content)))); + $html = Zlib\NativeWikiPage::convert_links($html,$wikiURL); + } json_return_and_die(array('html' => $html, 'success' => true)); } @@ -288,31 +343,34 @@ class Wiki extends \Zotlabs\Web\Controller { if (local_channel() !== intval($owner['channel_id'])) { goaway('/' . argv(0) . '/' . $nick . '/'); } - $wiki = array(); // Generate new wiki info from input name $wiki['postVisible'] = ((intval($_POST['postVisible']) === 0) ? 0 : 1); $wiki['rawName'] = $_POST['wikiName']; $wiki['htmlName'] = escape_tags($_POST['wikiName']); $wiki['urlName'] = urlencode($_POST['wikiName']); + $wiki['mimeType'] = $_POST['mimeType']; + if($wiki['urlName'] === '') { notice( t('Error creating wiki. Invalid name.') . EOL); goaway('/wiki'); } + // Get ACL for permissions $acl = new \Zotlabs\Access\AccessList($owner); $acl->set_from_array($_POST); - $r = wiki_create_wiki($owner, $observer_hash, $wiki, $acl); - if ($r['success']) { - $homePage = wiki_create_page('Home', $r['item']['resource_id']); - if(!$homePage['success']) { + $r = Zlib\NativeWiki::create_wiki($owner, $observer_hash, $wiki, $acl); + if($r['success']) { + $homePage = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash,'Home', $r['item']['resource_id']); + if(! $homePage['success']) { notice( t('Wiki created, but error creating Home page.')); - goaway('/wiki/'.$nick.'/'.$wiki['urlName']); + goaway(z_root() . '/wiki/' . $nick . '/' . $wiki['urlName']); } - goaway('/wiki/'.$nick.'/'.$wiki['urlName'].'/'.$homePage['page']['urlName']); - } else { - notice(t('Error creating wiki')); - goaway('/wiki'); + goaway(z_root() . '/wiki/' . $nick . '/' . $wiki['urlName'] . '/' . $homePage['page']['urlName']); + } + else { + notice( t('Error creating wiki')); + goaway(z_root() . '/wiki'); } } @@ -323,15 +381,16 @@ class Wiki extends \Zotlabs\Web\Controller { // more detail permissions framework if (local_channel() !== intval($owner['channel_id'])) { logger('Wiki delete permission denied.'); - json_return_and_die(array('message' => 'Wiki delete permission denied.', 'success' => false)); + json_return_and_die(array('message' => t('Wiki delete permission denied.'), 'success' => false)); } $resource_id = $_POST['resource_id']; - $deleted = wiki_delete_wiki($resource_id); + $deleted = Zlib\NativeWiki::delete_wiki($owner['channel_id'],$observer_hash,$resource_id); if ($deleted['success']) { json_return_and_die(array('message' => '', 'success' => true)); - } else { - logger('Error deleting wiki: ' . $resource_id); - json_return_and_die(array('message' => 'Error deleting wiki', 'success' => false)); + } + else { + logger('Error deleting wiki: ' . $resource_id . ' ' . $deleted['message']); + json_return_and_die(array('message' => t('Error deleting wiki'), 'success' => false)); } } @@ -343,30 +402,46 @@ class Wiki extends \Zotlabs\Web\Controller { // Determine if observer has permission to create a page - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['write']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); } - $name = $_POST['name']; //Get new page name - if(urlencode(escape_tags($_POST['name'])) === '') { + $name = $_POST['pageName']; //Get new page name + if(urlencode(escape_tags($_POST['pageName'])) === '') { json_return_and_die(array('message' => 'Error creating page. Invalid name.', 'success' => false)); } - $page = wiki_create_page($name, $resource_id); - if ($page['success']) { - json_return_and_die(array('url' => '/'.argv(0).'/'.argv(1).'/'.$page['wiki']['urlName'].'/'.urlencode($page['page']['urlName']), 'success' => true)); - } else { + $page = Zlib\NativeWikiPage::create_page($owner['channel_id'],$observer_hash, $name, $resource_id); + + if($page['success']) { + $ob = \App::get_observer(); + $commit = Zlib\NativeWikiPage::commit(array( + 'commit_msg' => t('New page created'), + 'resource_id' => $resource_id, + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'pageUrlName' => $name + )); + + if($commit['success']) { + json_return_and_die(array('url' => '/' . argv(0) . '/' . argv(1) . '/' . $page['wiki']['urlName'] . '/' . $page['page']['urlName'], 'success' => true)); + } + else { + json_return_and_die(array('message' => 'Error making git commit','url' => '/' . argv(0) . '/' . argv(1) . '/' . $page['wiki']['urlName'] . '/' . urlencode($page['page']['urlName']),'success' => false)); + } + } + else { logger('Error creating page'); json_return_and_die(array('message' => 'Error creating page.', 'success' => false)); } } // Fetch page list for a wiki - if ((argc() === 5) && (argv(2) === 'get') && (argv(3) === 'page') && (argv(4) === 'list')) { + if((argc() === 5) && (argv(2) === 'get') && (argv(3) === 'page') && (argv(4) === 'list')) { $resource_id = $_POST['resource_id']; // resource_id for wiki in db - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(!$perms['read']) { logger('Wiki read permission denied.' . EOL); json_return_and_die(array('pages' => null, 'message' => 'Permission denied.', 'success' => false)); @@ -392,27 +467,33 @@ class Wiki extends \Zotlabs\Web\Controller { } // Determine if observer has permission to save content - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['write']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); } - $saved = wiki_save_page(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'content' => $content)); + $saved = Zlib\NativeWikiPage::save_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'content' => $content)); + if($saved['success']) { $ob = \App::get_observer(); - $commit = wiki_git_commit(array( - 'commit_msg' => $commitMsg, - 'resource_id' => $resource_id, - 'observer' => $ob, - 'files' => array($pageUrlName.'.md') - )); + $commit = Zlib\NativeWikiPage::commit(array( + 'commit_msg' => $commitMsg, + 'pageUrlName' => $pageUrlName, + 'resource_id' => $resource_id, + 'channel_id' => $owner['channel_id'], + 'observer_hash' => $observer_hash, + 'revision' => (-1) + )); + if($commit['success']) { json_return_and_die(array('message' => 'Wiki git repo commit made', 'success' => true)); - } else { + } + else { json_return_and_die(array('message' => 'Error making git commit','success' => false)); } - } else { + } + else { json_return_and_die(array('message' => 'Error saving page', 'success' => false)); } } @@ -427,67 +508,74 @@ class Wiki extends \Zotlabs\Web\Controller { // Determine if observer has permission to read content - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['read']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['read']) { logger('Wiki read permission denied.' . EOL); json_return_and_die(array('historyHTML' => '', 'message' => 'Permission denied.', 'success' => false)); } $historyHTML = widget_wiki_page_history(array( - 'resource_id' => $resource_id, - 'pageUrlName' => $pageUrlName + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName, + 'permsWrite' => $perms['write'] )); json_return_and_die(array('historyHTML' => $historyHTML, 'message' => '', 'success' => true)); } // Delete a page if ((argc() === 4) && (argv(2) === 'delete') && (argv(3) === 'page')) { + $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; + if ($pageUrlName === 'Home') { - json_return_and_die(array('message' => 'Cannot delete Home','success' => false)); + json_return_and_die(array('message' => t('Cannot delete Home'),'success' => false)); } // Determine if observer has permission to delete pages - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['write']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); } - $deleted = wiki_delete_page(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + $deleted = Zlib\NativeWikiPage::delete_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); if($deleted['success']) { $ob = \App::get_observer(); - $commit = wiki_git_commit(array( - 'commit_msg' => 'Deleted ' . $pageUrlName, - 'resource_id' => $resource_id, - 'observer' => $ob, - 'files' => null - )); + $commit = Zlib\NativeWikiPage::git_commit(array( + 'commit_msg' => 'Deleted ' . $pageUrlName, + 'resource_id' => $resource_id, + 'observer' => $ob, + 'files' => null + )); if($commit['success']) { json_return_and_die(array('message' => 'Wiki git repo commit made', 'success' => true)); - } else { + } + else { json_return_and_die(array('message' => 'Error making git commit','success' => false)); } - } else { + } + else { json_return_and_die(array('message' => 'Error deleting page', 'success' => false)); } } // Revert a page if ((argc() === 4) && (argv(2) === 'revert') && (argv(3) === 'page')) { + +logger('revert was called: ' . print_r($_POST,true)); $resource_id = $_POST['resource_id']; $pageUrlName = $_POST['name']; $commitHash = $_POST['commitHash']; // Determine if observer has permission to revert pages - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['write']) { + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['write']) { logger('Wiki write permission denied.' . EOL); json_return_and_die(array('success' => false)); } - $reverted = wiki_revert_page(array('commitHash' => $commitHash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + $reverted = Zlib\NativeWikiPage::revert_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'commitHash' => $commitHash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); if($reverted['success']) { json_return_and_die(array('content' => $reverted['content'], 'message' => '', 'success' => true)); } else { @@ -503,15 +591,15 @@ class Wiki extends \Zotlabs\Web\Controller { $currentCommit = $_POST['currentCommit']; // Determine if observer has permission to revert pages - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + $perms = Zlib\NativeWiki::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); if(!$perms['read']) { logger('Wiki read permission denied.' . EOL); json_return_and_die(array('success' => false)); } - $compare = wiki_compare_page(array('currentCommit' => $currentCommit, 'compareCommit' => $compareCommit, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); + $compare = Zlib\NativeWikiPage::compare_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'currentCommit' => $currentCommit, 'compareCommit' => $compareCommit, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName)); if($compare['success']) { - $diffHTML = '<table class="text-center" width="100%"><tr><td class="lead" width="50%">Current Revision</td><td class="lead" width="50%">Selected Revision</td></tr></table>' . $compare['diff']; + $diffHTML = '<table class="text-center" width="100%"><tr><td class="lead" width="50%">' . t('Current Revision') . '</td><td class="lead" width="50%">' . t('Selected Revision') . '</td></tr></table>' . $compare['diff']; json_return_and_die(array('diff' => $diffHTML, 'message' => '', 'success' => true)); } else { json_return_and_die(array('diff' => '', 'message' => 'Error comparing page', 'success' => false)); @@ -531,34 +619,37 @@ class Wiki extends \Zotlabs\Web\Controller { } // Determine if observer has permission to rename pages - $perms = wiki_get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); - if(!$perms['write']) { + $perms = Zlib\NativeWikiPage::get_permissions($resource_id, intval($owner['channel_id']), $observer_hash); + if(! $perms['write']) { logger('Wiki write permission denied. ' . EOL); json_return_and_die(array('success' => false)); } - $renamed = wiki_rename_page(array('resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'pageNewName' => $pageNewName)); + $renamed = Zlib\NativeWikiPage::rename_page(array('channel_id' => $owner['channel_id'], 'observer_hash' => $observer_hash, 'resource_id' => $resource_id, 'pageUrlName' => $pageUrlName, 'pageNewName' => $pageNewName)); if($renamed['success']) { $ob = \App::get_observer(); $commit = wiki_git_commit(array( - 'commit_msg' => 'Renamed ' . urldecode($pageUrlName) . ' to ' . $renamed['page']['htmlName'], - 'resource_id' => $resource_id, - 'observer' => $ob, - 'files' => array($pageUrlName . '.md', $renamed['page']['fileName']), - 'all' => true - )); + 'commit_msg' => 'Renamed ' . urldecode($pageUrlName) . ' to ' . $renamed['page']['htmlName'], + 'resource_id' => $resource_id, + 'observer' => $ob, + 'files' => array($pageUrlName . substr($renamed['page']['fileName'], -3), $renamed['page']['fileName']), + 'all' => true + )); + if($commit['success']) { json_return_and_die(array('name' => $renamed['page'], 'message' => 'Wiki git repo commit made', 'success' => true)); - } else { + } + else { json_return_and_die(array('message' => 'Error making git commit','success' => false)); } - } else { + } + else { json_return_and_die(array('message' => 'Error renaming page', 'success' => false)); } } //notice( t('You must be authenticated.')); - json_return_and_die(array('message' => 'You must be authenticated.', 'success' => false)); + json_return_and_die(array('message' => t('You must be authenticated.'), 'success' => false)); } } diff --git a/Zotlabs/Render/Comanche.php b/Zotlabs/Render/Comanche.php index 562a9f791..048921670 100644 --- a/Zotlabs/Render/Comanche.php +++ b/Zotlabs/Render/Comanche.php @@ -104,8 +104,12 @@ class Comanche { $x = explode('.',$v); if($x[0] == 'config') return get_config($x[1],$x[2]); + elseif($x[0] === 'request') + return $_SERVER['REQUEST_URI']; elseif($x[0] === 'observer') { if(count($x) > 1) { + if($x[1] == 'language') + return \App::$language; $y = \App::get_observer(); if(! $y) return false; @@ -125,20 +129,35 @@ class Comanche { function test_condition($s) { // This is extensible. The first version of variable testing supports tests of the forms: + + // [if $config.system.foo ~= baz] which will check if get_config('system','foo') contains the string 'baz'; // [if $config.system.foo == baz] which will check if get_config('system','foo') is the string 'baz'; // [if $config.system.foo != baz] which will check if get_config('system','foo') is not the string 'baz'; - // You may check numeric entries, but these checks are evaluated as strings. + // [if $config.system.foo >= 3] which will check if get_config('system','foo') is greater than or equal to 3; + // [if $config.system.foo > 3] which will check if get_config('system','foo') is greater than 3; + + // [if $config.system.foo <= 3] which will check if get_config('system','foo') is less than or equal to 3; + // [if $config.system.foo < 3] which will check if get_config('system','foo') is less than 3; + // [if $config.system.foo {} baz] which will check if 'baz' is an array element in get_config('system','foo') // [if $config.system.foo {*} baz] which will check if 'baz' is an array key in get_config('system','foo') // [if $config.system.foo] which will check for a return of a true condition for get_config('system','foo'); // The values 0, '', an empty array, and an unset value will all evaluate to false. + if(preg_match('/[\$](.*?)\s\~\=\s(.*?)$/',$s,$matches)) { + $x = $this->get_condition_var($matches[1]); + if(stripos($x,trim($matches[2])) !== false) + return true; + return false; + } + if(preg_match('/[\$](.*?)\s\=\=\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if($x == trim($matches[2])) return true; return false; } + if(preg_match('/[\$](.*?)\s\!\=\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if($x != trim($matches[2])) @@ -146,6 +165,31 @@ class Comanche { return false; } + if(preg_match('/[\$](.*?)\s\>\=\s(.*?)$/',$s,$matches)) { + $x = $this->get_condition_var($matches[1]); + if($x >= trim($matches[2])) + return true; + return false; + } + if(preg_match('/[\$](.*?)\s\<\=\s(.*?)$/',$s,$matches)) { + $x = $this->get_condition_var($matches[1]); + if($x <= trim($matches[2])) + return true; + return false; + } + if(preg_match('/[\$](.*?)\s\>\s(.*?)$/',$s,$matches)) { + $x = $this->get_condition_var($matches[1]); + if($x > trim($matches[2])) + return true; + return false; + } + if(preg_match('/[\$](.*?)\s\>\s(.*?)$/',$s,$matches)) { + $x = $this->get_condition_var($matches[1]); + if($x < trim($matches[2])) + return true; + return false; + } + if(preg_match('/[\$](.*?)\s\{\}\s(.*?)$/',$s,$matches)) { $x = $this->get_condition_var($matches[1]); if(is_array($x) && in_array(trim($matches[2]),$x)) diff --git a/Zotlabs/Web/Router.php b/Zotlabs/Web/Router.php index a3bad29ae..271836ba9 100644 --- a/Zotlabs/Web/Router.php +++ b/Zotlabs/Web/Router.php @@ -137,15 +137,21 @@ class Router { killme(); } - logger("Module {$module} not found.", LOGGER_DEBUG, LOG_WARNING); - - if((x($_SERVER, 'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && \App::$config['system']['dreamhost_error_hack']) { - logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); + if((x($_SERVER, 'QUERY_STRING')) + && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') + && \App::$config['system']['dreamhost_error_hack']) { + logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI'],LOGGER_DEBUG); goaway(z_root() . $_SERVER['REQUEST_URI']); } - logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); - header($_SERVER['SERVER_PROTOCOL'] . ' 404 ' . t('Not Found')); + if(get_config('system','log_404',true)) { + logger("Module {$module} not found.", LOGGER_DEBUG, LOG_WARNING); + logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] + . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' + . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); + } + + header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); $tpl = get_markup_template('404.tpl'); \App::$page['content'] = replace_macros($tpl, array( '$message' => t('Page not found.') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index 0837be21a..d4d3bee1d 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -149,9 +149,13 @@ class Auth { // The actual channel sending the packet ($c[0]) is not important, but this provides a // generic zot packet with a sender which can be verified + $x = q("select site_crypto from site where site_url = '%s' limit 1", + dbesc($hubloc['hubloc_url']) + ); + $p = zot_build_packet($channel,$type = 'auth_check', array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), - $hubloc['hubloc_sitekey'], $this->sec); + $hubloc['hubloc_sitekey'], (($x) ? $x[0]['site_crypto'] : ''), $this->sec); $this->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); $this->Debug('packet contents: ' . $p); |